Linux Software-Security - practical things

Tags: #<Tag:0x00007f8a214e22a0> #<Tag:0x00007f8a214e21d8> #<Tag:0x00007f8a214e2110> #<Tag:0x00007f8a214e2048> #<Tag:0x00007f8a214e1f80> #<Tag:0x00007f8a214e1eb8> #<Tag:0x00007f8a214e1df0>

Linux Software-Security - practical things

This is an unsorted collection of Linux software-security stuff. It may assume some knowledge of Stack and Heap exploitation. And show some Linux internals.

Inspect userland ASLR with ltrace

Assume a basic C program that uses malloc (ptmalloc3 via the GNU C library):

#include <stdlib.h>

void main() {
    int *p = malloc(10 * sizeof(int));

For reference:

gcc main.c -o main

The output of repeated executions of ltrace on an Ubuntu 16.04 (libc AMD64 2.23-0ubuntu11):

(base) [email protected]:~$ ltrace ./main 2>&1 | grep malloc
malloc(40)                                       = 0xd55010
(base) [email protected]:~$ ltrace ./main 2>&1 | grep malloc
malloc(40)                                       = 0x1e93010
(base) [email protected]:~$ ltrace ./main 2>&1 | grep malloc
malloc(40)                                       = 0x8ae010

We can see that the start address of the Heap chunk is different each time. This is an effect of userland ASLR on the Ubuntu (example 4.4.0-178-generic x86_64).

The kernel is configured:

cat /proc/sys/kernel/randomize_va_space

A value of 2 indicates[1] that the Linux kernel will attempt to randomise shared memory regions as well as the positions of the Stack.

A short-trip experience through the ELF binary standard

Executable and Linkable Format

Assuming that a little reverse engineering is needed…

(base) [email protected]:~$ readelf -h main
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x400430
  Start of program headers:          64 (bytes into file)
  Start of section headers:          6624 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         31
  Section header string table index: 28

– “UNIX - System V” states that the object expects the standard ABI, essentially the calling convention, linker format, … and of course the binary standard.

We are using shared object libraries… the That’s what the target binary is linked against:

    (base) [email protected]:~$ ldd main =>  (0x00007fff44fbf000) => /lib/x86_64-linux-gnu/ (0x00007f0a88162000)
    	/lib64/ (0x00007f0a8852c000)

We can strip the compiled ELF binary of the debugging symbols and sections. In this case strip -s from binutils. This is roughly the same as gcc -s but you can be more selective about what to strip.

Of course you can still enumerate the sections of the process address space as they are specified.

(base) [email protected]:~$ readelf -S main
There are 31 section headers, starting at offset 0x19e0:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [12] .plt              PROGBITS         00000000004003f0  000003f0
       0000000000000030  0000000000000010  AX       0     0     16
  [13]          PROGBITS         0000000000400420  00000420
       0000000000000008  0000000000000000  AX       0     0     8
       00000000000001d0  0000000000000010  WA       6     0     8
  [23] .got              PROGBITS         0000000000600ff8  00000ff8
       0000000000000008  0000000000000008  WA       0     0     8
  [24] .got.plt          PROGBITS         0000000000601000  00001000
       0000000000000028  0000000000000008  WA       0     0     8
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

Starting our experiential tour now we load the binary in GDB:

(gdb) file main
Reading symbols from main...(no debugging symbols found)...done.
(gdb) info file
Symbols from "/home/marius/main".
Local exec file:
`/home/marius/main', file type elf64-x86-64.
Entry point: 0x400430

Let’s break at the entry point and walk down the path of resolving malloc:

(gdb) break *0x400430
Breakpoint 1 at 0x400430
(gdb) run
Starting program: /home/marius/main 
Breakpoint 1, 0x0000000000400430 in ?? ()
(gdb) x/i $pc
=> 0x400430: xor %ebp,%ebp

  1. search for the “randomize” option in kernel.txt ↩︎