CTF

PWNABLE - Start

Analysis of the start binary

Reading the ELF Headers

By reading the ELF headers we learn some details about the binary:

sh# readelf -a ./start 

ELF Header:

  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 

  Class:                             ELF32

  Data:                              2's complement, little endian

  Version:                           1 (current)

  OS/ABI:                            UNIX - System V

  ABI Version:                       0

  Type:                              EXEC (Executable file)

  Machine:                           Intel 80386

  Version:                           0x1

  Entry point address:               0x8048060

  Start of program headers:          52 (bytes into file)

  Start of section headers:          364 (bytes into file)

  Flags:                             0x0

  Size of this header:               52 (bytes)

  Size of program headers:           32 (bytes)

  Number of program headers:         1

  Size of section headers:           40 (bytes)

  Number of section headers:         5

  Section header string table index: 2


Section Headers:

  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al

  [ 0]                   NULL            00000000 000000 000000 00      0   0  0

  [ 1] .text             PROGBITS        08048060 000060 000043 00  AX  0   0 16

  [ 2] .shstrtab         STRTAB          00000000 00014b 000021 00      0   0  1

  [ 3] .symtab           SYMTAB          00000000 0000a4 000080 10      4   4  4

  [ 4] .strtab           STRTAB          00000000 000124 000027 00      0   0  1

Key to Flags:

  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),

  L (link order), O (extra OS processing required), G (group), T (TLS),

  C (compressed), x (unknown), o (OS specific), E (exclude),

  p (processor specific)


There are no section groups in this file.


Program Headers:

  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align

  LOAD           0x000000 0x08048000 0x08048000 0x000a3 0x000a3 R E 0x1000


 Section to Segment mapping:

  Segment Sections...

   00     .text 


There is no dynamic section in this file.


There are no relocations in this file.


The decoding of unwind sections for machine type Intel 80386 is not currently supported.


Symbol table '.symtab' contains 8 entries:

   Num:    Value  Size Type    Bind   Vis      Ndx Name

     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 

     1: 08048060     0 SECTION LOCAL  DEFAULT    1 

     2: 00000000     0 FILE    LOCAL  DEFAULT  ABS start.s

     3: 0804809d     0 NOTYPE  LOCAL  DEFAULT    1 _exit

     4: 08048060     0 NOTYPE  GLOBAL DEFAULT    1 _start

     5: 080490a3     0 NOTYPE  GLOBAL DEFAULT    1 __bss_start

     6: 080490a3     0 NOTYPE  GLOBAL DEFAULT    1 _edata

     7: 080490a4     0 NOTYPE  GLOBAL DEFAULT    1 _end


No version information found in this file.

Dissassembling the binary

By dissassembling the binary we can study what the binary is actually doing in order to find a weakness we can exploit.

sh# objdump -d ./start


./start:     file format elf32-i386



Disassembly of section .text:


08048060 <_start>:

; safe value of current stack pointer on stack (!)

 8048060:  54                    push   %esp


; push the address of the exit function on the stack, which will be used at the end of the _start function

 8048061: 68 9d 80 04 08        push   $0x804809d


; initialize registers eax, ebx, ecx, edx to zero

 8048066: 31 c0                 xor    %eax,%eax

 8048068: 31 db                 xor    %ebx,%ebx

 804806a: 31 c9                 xor    %ecx,%ecx

 804806c: 31 d2                 xor    %edx,%edx


; Pushes the message "Let's start the CTF:" on the stack

 804806e: 68 43 54 46 3a        push   $0x3a465443

 8048073: 68 74 68 65 20        push   $0x20656874

 8048078: 68 61 72 74 20        push   $0x20747261

 804807d: 68 73 20 73 74        push   $0x74732073

 8048082: 68 4c 65 74 27        push   $0x2774654c


; sets the register ecx to the address of esp

 8048087: 89 e1                 mov    %esp,%ecx


; dl is the 8bit part of edx which is set to 20 = 0x14, which is the 3rd argument of the write-syscall

; write( int fd, const void *buf, size_t count) => write( ebx=1, ecx, edx=20 )

 8048089: b2 14                 mov    $0x14,%dl

; bl is the 8bit part of ebx, which is the 1st argument passed to syscall write - the file descriptor )

 804808b: b3 01                 mov    $0x1,%bl

; al is the 8bit part of eax, which is the syscall id - 4 = write

 804808d: b0 04                 mov    $0x4,%al

; make the syscall

 804808f: cd 80                 int    $0x80


; zero out ebx

 8048091: 31 db                 xor    %ebx,%ebx

; dl is set to 60 = 0x3c - 3rd argument of read (count)

; read(int fd, void *buf, size_t count) => read( ebx=0, ecx, edx=60 )

 8048093: b2 3c                 mov    $0x3c,%dl

; al ist set to 3, which stands for syscall with id =3, read(int fd, void *buf, size_t count)

 8048095: b0 03                 mov    $0x3,%al

; make the syscall

 8048097: cd 80                 int    $0x80


; set esp to the location before we wrote the message "Let's start the CTF:" wich is 20 bytes long

; because the stack addresses get smaller when we put stuff on the stack, we have to add 20

; to get to the location prior writing that message

 8048099: 83 c4 14              add    $0x14,%esp


; our stack pointer points to the address 0x804809d, which we pushed there in the second instruction of _start

; thus we give control to the function _exit, which just exits the executable

 804809c: c3                    ret    


0804809d <_exit>:

 804809d: 5c                    pop    %esp

 804809e: 31 c0                 xor    %eax,%eax

 80480a0: 40                    inc    %eax

 80480a1: cd 80                 int    $0x80

So now we know,  if we start the binary, it will output the String "Let's start the CTF:" and await our input.

We also know as first instruction of the _start function the address of esp get pushed on the stack and after that the address of the _exit function. 

 8048087: 89 e1                 mov    %esp,%ecx

That means if we input more than 20 bytes (60 bytes are allowed in the read), we can overwrite the return address of the _start function which happens to start at the 21st byte and is 4 bytes long, because we deal with a 32-bit binary.