Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
cve-2010-4221-lab — 패치에서 RCE까지: CVE-2010-4221(ProFTPD TELNET IAC 스택 오버플로)용 수제 익스플로잇, 실패 중심의 전체 과정 문서화 포함 | Kitploit
도구/GitHubGitHub/diegslva/cve-2010-4221-lab
Exploit FrameworksVulnerability AnalysisExploitationReverse EngineeringPenetration TestingLearning & EducationBinary ExploitationLabs & Practice
GitHubdiegslva/cve-2010-4221-lab

cve-2010-4221-lab

패치에서 RCE까지: CVE-2010-4221(ProFTPD TELNET IAC 스택 오버플로)용 수제 익스플로잇, 실패 중심의 전체 과정 문서화 포함

저장소 보기
12시간 14분 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2010-4221 — ProFTPD TELNET IAC 스택 오버플로우: 패치에서 RCE까지

완전히 재현 가능한 실습 환경과 CVE-2010-4221 — ProFTPD의 pr_netio_telnet_gets()에서 발생하는 인증 전 스택 버퍼 오버플로우 — 를 위한 직접 작성한(raw-socket) 익스플로잇입니다. 취약점 연구와 익스플로잇 개발 학습 목적으로 제작되었습니다.

모든 실패는 기록되어 있습니다. 성공 경로는 거짓이며, 우회로가 곧 교훈입니다.


법적 및 윤리적 고지 — 먼저 읽으십시오

이 저장소는 교육용 산출물입니다. 멘토나 교육을 받을 여유가 없는 사람들이 메모리 손상 익스플로잇이 실제로 어떻게 탄생하는지 — 패치에서 시작해 실패를 거쳐 자신이 소유한 실습 환경 안에서 동작하는 개념 증명에 이르기까지 — 배울 수 있도록 존재합니다.

레드팀 작업 — 실제 전문적인 공격 보안 — 은 한 단어로 정의됩니다: 승인(authorization). 전문가가 하는 모든 일은 서면 계약 안에서 이루어집니다: 범위, 대상, 허용된 기법, 시간 창, 승인한 사람들을 명시한 서명된 Rules of Engagement 문서 말입니다. 그 서류가 없다면, 동일한 키 입력은 직업이 아니라 — 지구상의 사실상 모든 관할권에서 범죄입니다.

따라서 이 저장소에 대한 계약은 다음과 같으며, 협상 불가입니다:

  • 포함된 Docker 실습 환경 또는 자신이 소유한 시스템에서만 실행하십시오.
  • 명시적이고 서면화된 승인 없이 어떤 것에도 절대 실행하지 마십시오.
  • 배우는 중이라면: 환영합니다. 이것은 당신을 위해 만들어졌습니다.
  • 다른 사람을 공격할 무기를 찾고 있다면: 이 탭을 닫으십시오. 이 버그는 2010년 것이며, 범죄 기록 외에는 아무것도 얻지 못할 것입니다.

이 기술은 배울 가치가 있습니다. 이 기술은 그에 따르는 규율이 있을 때만 가치가 있습니다.


버그

ProFTPD는 FTP 제어 채널에서 TELNET 이스케이프 시퀀스를 처리합니다. TELNET에서 0xFF(IAC, "Interpret As Command")는 이스케이프 바이트이며, 리터럴 는 로 전송됩니다.

0xFF
0xFF 0xFF

pr_netio_telnet_gets()는 클라이언트 바이트를 스택 버퍼(pr_cmd_read의 char buf[PR_DEFAULT_CMD_BUFSZ+1], glibc의 MAXPATHLEN=4096 기준 4104바이트)에 복사하면서 남은 공간을 buflen — size_t, 부호 없는(unsigned) — 으로 추적합니다.

취약한 경로(1.3.3a, netio.c):

root@kitploit:~
case TELNET_IAC:
  switch (cp) {
    ...
    default:
      *bp++ = TELNET_IAC;   // 쓰기 #1
      buflen--;             // 감소 #1
      telnet_mode = 0;
      break;
  }
  break;
...
*bp++ = cp;                 // 쓰기 #2
buflen--;                   // 감소 #2  <-- 사이에 검사 없음

두 번의 쓰기, 두 번의 감소, 그 사이에 0 검사가 없습니다. buflen이 정확히 1일 때, 이 쌍은 이를 0으로 감소시킨 후 SIZE_MAX(1800경)로 언더플로우됩니다. 이제 루프는 버퍼가 무한하다고 믿고 공격자가 제어하는 바이트를 스택 위로 계속 씁니다 — 저장된 레지스터, 저장된 RBP, 반환 주소를 덮어씁니다.

인증 전(pre-auth). 이 함수는 USER/PASS가 처리되기 전에 실행됩니다.

패치

수정(커밋 3cc69b8388, "Bug#3521 - Telnet IAC processing stack overflow", 1.3.3c에서 릴리스)은 12줄입니다. 전체 보안 경계는 다음과 같습니다:

root@kitploit:~
if (buflen == 0) {
  break;
}

patch.diff를 참조하십시오. 패치를 읽으면 상처가 어디 있었는지 알 수 있습니다 — 그것이 기술입니다.

실습 환경

Dockerfile은 역사적인 Debian 스냅샷 소스에서 ProFTPD 1.3.3a를 컴파일하며, 의도적으로 안전하지 않게 만듭니다(이것이 2010년의 모습입니다):

  • -fno-stack-protector — 카나리 없음
  • -z execstack — 실행 가능한 스택(NX 없음)
  • -no-pie — 고정된 바이너리 주소
  • gdb 아래에서 실행 — 기본적으로 ASLR을 비활성화 → 결정적 스택
root@kitploit:~
docker build -t proftpd-133a .
docker rm -f lab133 2>/dev/null
docker run -d --name lab133 --cap-add SYS_PTRACE \
  --security-opt seccomp=unconfined -p 127.0.0.1:2122:21 \
  proftpd-133a sh -c 'gdb -batch -ex "set follow-fork-mode child" \
  -ex "run" -ex "continue" --args /usr/local/sbin/proftpd -n -d1 \
  > /tmp/gdb.txt 2>&1; sleep 600'
python3 exploit.py

예상 출력:

root@kitploit:~
[S] 220 ProFTPD 1.3.3a Server (lab-iac) ...
[S] THE SERVER SAID: b'PWNED!!PWNED!!'

(셸코드는 fd 0, 1, 2에 씁니다. 어느 것이 제어 채널을 담당하는지 알 필요가 없도록 하기 위함입니다 — 그중 두 개가 응답합니다.)

익스플로잇 구조

root@kitploit:~
"SITE " + NOP 슬레드 + 셸코드 + [\xff\xff 플러드] + 패딩 + [ret] + "\n"
 ^^^^^^^^^^^^^^^^^^^^                             ^^^^
 셸코드는 명령 버퍼 내부에 존재 —      오버플로우 꼬리는 단 하나의
 아무도 건드리지 않는 영역              주소만 전달
  1. "SITE " 는 FTP 파서를 살려둡니다 — 명령이 깨끗하게 처리됩니다.
  2. 셸코드는 명령의 내용입니다. 버퍼는 스택에서 가장 안전한 위치입니다: 읽기 후에는 buf[4102]만 건드려집니다(잘림 NUL). 프레임의 활성 로컬 변수 아래는 모두 조용합니다.
  3. IAC 플러드는 buflen을 언더플로우로 몰아갑니다(패리티 문제는 "여정" 참조).
  4. ret 슬롯(buf + 4152)은 NOP 슬레드 중간의 주소를 받습니다. pr_cmd_read가 파싱 후 return 0에 도달하면 CPU는 슬레드에 착지하여 셸코드로 미끄러져 들어갑니다.

이 역전된 구조 — 페이로드 먼저, 플러드 둘째, 주소 마지막 — 은 동일한 레이아웃을 사용하는 표준 Metasploit 모듈(proftp_telnet_iac)에 대해 검증되었습니다. 해당 타깃은 NX가 있었으므로 res 포인터의 "4중 역참조"를 가진 ROP 체인이 필요했습니다. 우리 실습 환경은 실행 가능한 스택이 있으므로 단일 직접 반환으로 충분합니다.

여정 (이 저장소의 실제 핵심)

최종 익스플로잇은 60줄입니다. 그 비용은 다음과 같습니다:

  1. 맹목적인 \xff 플러드 → 아무 일도 없음. 서버가 정중하게 세션을 종료했습니다. 근본 원인: buflen은 4102(짝수)에서 시작하고 각 IAC 쌍은 2씩 감소하므로 — 0에 깨끗하게 도달하며, 결코 1에 도달하지 않습니다. 언더플로우는 패리티가 깨져야 합니다. 교훈: 무작위 전송보다 상태 머신을 읽는 것이 낫습니다.

  2. 잘못된 버퍼 크기. 첫 번째 보정 시도는 1024바이트 버퍼를 가정했습니다. 실제는 Linux/glibc에서 MAXPATHLEN+8 = 4104입니다. 플러드는 타깃에서 3KB 부족하게 멈췄습니다. 교훈: 타깃을 측정하십시오. 타깃을 가정하지 마십시오.

  3. 첫 번째 SIGSEGV. 순환 de Bruijn 패턴(Aa0Aa1...)이 ret 슬롯을 buf+4152에 배치했으며, 두 번 교차 검증되었습니다(프레임 계산 + 패턴 오프셋). 교훈: 순환 패턴은 줄자이지 익스플로잇이 아닙니다.

  4. RIP 제어. 슬롯을 0x4141414141414141로 설정하면 ret 명령 자체가 충돌했습니다 — x86-64는 비정규(non-canonical) 주소를 거부하며, 결함은 ret 위에 발생하고 우리 값은 백트레이스에 남습니다. 교훈: 프레임에 우리 값이 있는 상태에서 ret에서 충돌 = 제어권 확보.

  5. ret 슬롯 위의 셸코드 → 덮어써짐. 힙 포인터(0x4d7838 — 이후 cmd_rec 풀 할당으로 식별)에 의해 8바이트가 덮어써졌습니다. 슬롯 위의 스택 프레임은 착지와 하이재킹 사이에 계속 작동하는 함수들에 속합니다. 교훈: 오버플로우가 마지막 쓰기가 아닙니다. 프로그램은 당신이 훼손한 스택 위에서 계속 살아있습니다.

  6. 슬롯 아래의 "죽은 영역" → 역시 훼손됨. pr_cmd_read의 자체 로컬 변수(cmd, buflen, cp)가 바로 거기에 있으며 파싱 중 계속 저장됩니다.

  7. 하드웨어 워치포인트 포렌식. gdb에서 watch *(long*)ADDR는 미스터리를 카메라로 바꿨습니다: 덮어써진 주소에 대한 모든 쓰기와 백트레이스가 순서대로 기록됩니다. 교훈: "누가 이 메모리를 썼는가?"라는 질문에 대한 답은 gdb 명령 하나면 충분합니다.

  8. 참고 자료를 읽되, 먼저 이해하십시오. 표준 모듈이 역전된 구조를 확인해 주었습니다. 자신의 정신적 모델을 구축한 후에 다른 익스플로잇을 읽는 것은 학습입니다. 그 전에 읽는 것은 복사입니다.

얻은 교훈

  • size_t는 음수가 되지 않습니다 — 엄청나게 커질 뿐입니다. 남은 공간 카운터의 정수 언더플로우는 단계만 추가된 스택 오버플로우입니다.
  • 패리티는 무기입니다. 프리미티브가 2씩 감소할 때, 크기뿐만 아니라 홀수/짝수를 제어하여 언더플로우를 제어할 수 있습니다.
  • 나쁜 문자는 프로토콜 문제입니다. 우리 셸코드는 \x0a(읽기를 종료)를 피하고 설계상 \xff(TELNET 이스케이프)를 견딥니다.
  • Prefork 서버는 충돌을 용서합니다. 자식 프로세스가 죽어도 부모는 계속 수락합니다: 무한 시도가 가능합니다. 신뢰성 엔지니어링은 익스플로잇의 일부입니다.
  • 동일한 산출물을 측정하고 익스플로잇하십시오. argv[0]의 11바이트 차이(빌드 트리 바이너리 vs 설치된 바이너리)는 전체 스택을 0x40만큼 이동시켜 완벽한 익스플로잇을 조용히 무효화했습니다.
  • 시그널 핸들러가 자백합니다. ProFTPD는 SIGSEGV를 포착하고 "terminating (signal 11)"을 기록합니다 — 커널이 조용히 있어도 타깃이 죽었음을 알려줍니다.

참고 자료

  • 수정 커밋: https://github.com/proftpd/proftpd/commit/3cc69b8388
  • CVE: https://nvd.nist.gov/vuln/detail/CVE-2010-4221
  • 표준 모듈: modules/exploits/linux/ftp/proftp_telnet_iac.rb (rapid7/metasploit-framework)
  • 자매 실습 환경(로직 버그, 동일 데몬): CVE-2015-3306 mod_copy

작성자

diegslva가 공개적으로 학습하며 제작 — "익스플로잇을 한 번도 작성해 본 적 없음"에서 손수 제작한 셸코드를 사용한 인증 전 RCE까지, 기록된 하루 만에. 이 저장소가 당신에게 무언가를 가르쳐 주었다면, 그 지식을 나누어 주십시오.

도구 다운로드