스스로 취약점을 증명하는 보안 도구를 만들었습니다 — 그 후 댓글을 통해 더 나은 위협 모델 (Threat Model)을 얻었습니다
요약
자동화된 공격 도구의 오탐 문제를 해결하기 위해 예측 불가능한 논스(nonce)를 활용하여 취약점 증명을 검증하는 새로운 보안 도구 설계 방식을 소개합니다. 리버스 쉘, 바인드 쉘, 블라인드 콜백 등 다양한 전달 방식을 통해 환경에 구애받지 않고 실행력을 확보합니다.
핵심 포인트
- 단순 문자열 매칭 대신 논스를 활용한 증거 기반 검증으로 오탐 방지
- 환경에 따라 리버스/바인드/블라인드 쉘로 유연하게 전환되는 전달 구조
- 다양한 인터프리터를 스스로 선택하여 실행 범용성 극대화
- 실제 실험 환경을 통한 익스플로잇 및 논스 기반 증명 성공 사례 제시
자동화된 공격 (Automated offense)에는 한 가지 당혹스러운 실패 모드가 있습니다. 바로 승리했다고 당신에게 거짓말을 하는 것입니다.
도구를 타겟에 겨누었을 때, 순진한 성공 확인 방식은 부분 문자열 일치 (substring match)입니다. 예를 들어 응답에서 uid=0(root)를 보고 쉘 (shell)이라고 판단하는 식이죠. 하지만 서비스 배너 (service banner)가 이를 출력할 수도 있습니다. 타르핏 (tarpit)이 연결 시점에 이를 스트리밍할 수도 있습니다. 그리고 성공 신호가 틀리는 순간, 보고서, "어떤 호스트가 점유되었는지"에 대한 상태, 다음 단계 등 하위의 모든 과정이 그 거짓말을 상속받게 됩니다. 당신은 확신에 차서 틀린 답을 내놓는 자신만만한 엔진을 갖게 되는 것입니다.
제가 만든 도구는 대신 이를 어떻게 증명하는지, 오늘 실제 실행에서 무엇이 작동했는지, 그리고 이미 설계를 더 좋게 만들어준 예리한 독자들의 피드백은 무엇인지 소개합니다.
아이디어: 타겟이 추측할 수 없는 비밀을 에코 (echo)하게 만들기
인증 (authentication)에서 가장 오래된 트릭을 빌려옵니다. 각 시도 전에 오케스트레이터 (orchestrator)는 예측 불가능한 시도별 논스 (nonce)를 생성하여 주입합니다. 전달된 명령은 반드시 해당 논스를 다시 보내야 합니다:
import os
def make_nonce() -> str:
return os.urandom(12).hex() # 예측 불가능함 — 타겟이 추측할 수 없음
결과는 정확히 그 논스가 구조화된 증거 라인에 담겨 돌아올 때만 신뢰됩니다:
HALO-EVIDENCE nonce=c609007176813c9110fccc27 level=shell uid=0 host= exit=0
_EVIDENCE = re.compile(r"HALO-EVIDENCE nonce=(\S+) level=(\S+)")
def breach_confirmed(output, ok, *, nonce) -> bool:
m = _EVIDENCE.search(output or "")
return bool(ok and m and m.group(1) == nonce)
전달 (Delivery)은 사다리 구조입니다, 실제 호스트들은 일관적이지 않기 때문입니다.
페이로드 (payload)를 전달할 수 없다면 증명은 가치가 없습니다. 따라서 전달 방식은 우아하게 성능을 낮추며(degrades gracefully) 진행됩니다 — 모든 stdlib socket을 사용합니다:
리버스 쉘(Reverse shell) — 대상 시스템이 임시 리스너로 연결하고, 노스(nonce)를 알린 후 /bin/sh을 돌려줍니다.
바인드 쉘(Bind shell) — 아웃바운드 연결이 차단된 경우, 대상 시스템이 쉘을 바인딩하면 사용자가 그에 접속합니다.
블라인드 콜백(Blind callback) — 상호작용 채널이 유지되지 않을 경우, 대상 시스템이 단순히 다시 연결하여 노스만 전송합니다. 이것만으로도 유효한 쉘은 없지만 코드 실행을 증명할 수 있습니다.
각 단계는 사용 가능한 첫 번째 인터프리터(bash /dev/tcp, python3, perl, nc)를 스스로 선택하므로, 동일한 원시 기능(primitive)이 하드 코딩된 단일 박스가 아닌 임의의 호스트에서 작동합니다.
실시간 실행
고의적으로 취약점을 가진 실험실 호스트(192.0.2.3, 문서화 범위 플레이스홀더)를 대상으로 에이전트가 23개의 열린 포트를 식별하고 각각을 테스트했습니다. 두 단계 게이트(isolated, network-less self-check 후 live attack)를 통해 선별된 세 가지 익스플로잇이 실행되었고, 각기 고유한 노스를 담은 실제 root 쉘을 띄웠습니다:
침해된 포트:
['21', '1524', '6667']
21 vsftpd 2.3.4 HALO-EVIDENCE nonce=c609… uid=0(root)
1524 ingreslock HALO-EVIDENCE nonce=4a8c… root@…:/#
6667 UnrealIRCd 3.2.8.1 HALO-EVIDENCE nonce=6ced… uid=0(root)
세 번의 성공, 스무 번의 정직한 실패. 가짜 23/23은 아닙니다. 이 마지막 부분이 핵심입니다. 자신감 넘치는
이것은 배너(banners)나 uid=0을 맹목적으로 스트리밍하는 타핏(tarpits)과 같은 우발적인 오탐(false positives)을 제거합니다.
또한 시도당 신선도(freshness)를 제공합니다. 재전송된 오래된 트랜스크립트(transcript)는 이번 실행의 논스(nonce)를 포함하지 않을 것입니다.
하지만 이것이 보장해 주지 않는 것: 증명(attestation)입니다. 논스(nonce)가 페이로드(payload)에 포함되어 전달되기 때문에, 입력을 그대로 반사(reflect)하는 서비스는 아무것도 실행하지 않고도 논스를 다시 에코(echo)할 수 있습니다. 문자 그대로의 에코는 적대적 대상(adversarial target)에 대한 실행의 증거가 될 수 없습니다.
그 피드백에서 직접 얻은 해결책이 바로 로드맵입니다:
- 각 논스(nonce)를 {attempt_id, target, payload_hash, expected_channel, expiry}에 바인딩(bind)하십시오. 논스를 한 번만 소비하고, 중복 및 시도 간 콜백(cross-attempt callbacks)을 거부하십시오.
- 엄격하게 파싱(parse)하십시오. 예상되는 채널(channel)로부터 정확히 하나의 구조화된 프레임(structured frame)만 수락하고, 원본 트랜스크립트(raw transcript)와 리스너 메타데이터(listener metadata)를 해싱(hash)하십시오.
- 에코(echo)에서 계산(computation)으로 이동하십시오. 문자 그대로의 에코가 아니라, 챌린지(challenge)와 결합된 실행 유도 사실(execution-derived fact)을 요구하십시오. 서비스가 코드를 실행함으로써만 생성할 수 있는 값은 반사(reflection)를 무력화합니다.
- 증거를 계층화하십시오. "코드 실행됨", "uid 검증됨", "대화형 채널 사용 가능"은 서로 다른 주장이며, 별도의 단계로 보고되어야 합니다.
- 부정적인 케이스를 테스트하십시오. 반사된 페이로드(reflected payloads), 재전송/지연된 콜백(replayed/delayed callbacks), 두 시도의 경합(two attempts racing), 잘린 프레임(truncated frames), 잘못된 대상으로부터의 논스(nonce), 그리고 주장된 것보다 낮은 권한의 셸(shell) 등을 테스트하십시오.
교훈(Takeaways)
출력을 믿지 마십시오. 당신이 발행한 비밀(secret)을 믿으십시오. 챌린지-응답(Challenge–response) 방식은 "uid=0이라고 말했다"를 "내 토큰을 반환했다"로 바꿉니다.
하지만 당신의 토큰이 정확히 무엇을 증명하는지 아십시오. 반사되지 않는 모델(non-reflecting model) 하에서의 신선도(freshness)는 실제적이고 유용합니다. 증명(attestation)은 더 어렵고 별개의 문제입니다. 챌린지(challenge)를 실행(execution)에 바인딩하기 전까지는 증명을 주장하지 마십시오.
추측이 아닌 사다리(ladder)를 제공하십시오. 역추적(Reverse) → 바인딩(bind) → 블라인드 콜백(blind-callback)은 복잡한 현실을 포괄합니다.
정직한 숫자를 제시하십시오. 증명된 3번이 주장된 23번을 이깁니다.
증명은 낙관론을 이깁니다. 그리고 공개적이고 구체적인 비판은 그 둘 모두를 이깁니다. 먼저 관문(gate)을 구축한 다음, 더 똑똑한 누군가가 당신의 위협 모델(threat model)을 좁히도록 하십시오.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기