CTF
PWNABLE - Start
Analysis of the start binary
Reading the ELF Headers
By reading the ELF headers we learn some details about the binary:
it's a 32-bit executable (ELF32)
it's written in assembler (start.s)
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.