
Handlebars.js AST 주입 원격 코드 실행 취약점
이 게시물은 EQSTLab에서 발행한 연구 문서입니다.
참조 PoC: dinhvaren/cve-2026-33937
★ CVE-2026-33937 Handlebars.js AST 인젝션 원격 코드 실행 PoC ★
https://github.com/user-attachments/assets/99e383e7-e71f-4e10-8c62-f50abff8b4f5
CVE-2026-33937 : Handlebars.js AST 인젝션 원격 코드 실행 취약점
| 영향을 받는 버전 | 4.0.0 – 4.7.8 |
| 패치된 버전 | 4.7.9 |
| CVSS 점수 | 9.8 (Critical) |
Handlebars.js의 타입 혼동(Type Confusion) 취약점은 Handlebars.compile()이 템플릿 문자열뿐만 아니라 사전 파싱된 AST 객체(JSON)도 입력으로 허용한다는 사실에서 발생합니다. compile()에 전달되는 값을 제어할 수 있는 공격자는 NumberLiteral 노드의 value 필드가 임의의 JavaScript 문자열로 대체된 조작된 AST 객체를 공급할 수 있습니다. 내부 코드 생성기가 해당 필드를 검증 없이 생성된 JavaScript 함수 본문에 직접 삽입하므로, 결과 함수는 렌더링 시 공격자가 제어하는 코드를 실행하게 되어 서버에서 원격 코드 실행(RCE)을 달성합니다.
Docker를 사용하여 취약한 환경을 빌드하고 실행합니다:
이미지 빌드
docker build -t cve-2026-33937-server .
컨테이너 실행
docker run --name cve-2026-33937 -p 12701:12701 -p 9229:9229 cve-2026-33937-server
또는 제공된 npm 스크립트를 사용할 수 있습니다:
npm run docker:up # 빌드 및 실행을 한 번에 수행
애플리케이션은 http://localhost:12701에서 서비스됩니다.
웹 UI
브라우저에서 http://localhost:12701을 엽니다. 페이지에는 B2B 이메일 빌더 인터페이스가 표시됩니다. 사용자가 입력한 필드 값은 클라이언트 측에서 Handlebars 템플릿 문자열에 삽입된 후 렌더링을 위해 서버로 전송됩니다. 입력 필드를 통해 또는 API를 직접 통해 악성 Handlebars 페이로드를 주입하십시오.
직접 API 호출
# 조작된 템플릿을 취약한 엔드포인트로 POST 전송
curl -s -X POST http://localhost:12701/api/email/preview \
-H "Content-Type: application/json" \
-d '{"subject":"test","editorTemplateData":"<PAYLOAD>"}'
<PAYLOAD>를 Handlebars.compile()을 대상으로 하는 AST 인젝션 페이로드로 교체하십시오.
POST /api/email/preview
서버는 editorTemplateData를 포함하는 JSON 본문을 수락하고 이를 검증이나 허용 목록(allowlist) 적용 없이 Handlebars.compile()에 직접 전달합니다:
// app.js
const renderEmail = Handlebars.compile(editorTemplateData);
Handlebars.compile()은 템플릿 문자열을 입력으로 받는 것으로 문서화되어 있지만, 내부 타입 검사는 사전 파싱된 AST 객체도 직접 전달할 수 있게 허용합니다. javascript-compiler.js 내부에서 NumberLiteral() 방문자(visitor)는 노드의 value 필드를 타입 검증이나 검사 없이 생성된 JavaScript 소스에 그대로 삽입합니다:
// javascript-compiler.js (단순화된 코드)
NumberLiteral(number) {
this.pushStackLiteral(number.value); // value가 생성된 JS에 그대로 삽입됨
}
공격자가 number.value가 숫자 리터럴 대신 임의의 JavaScript를 포함하는 문자열(예: "1; require('child_process').execSync(...)")인 조작된 AST 객체를 공급하면, 생성된 함수 본문에 해당 코드가 포함되어 렌더링 시 실행됩니다.
취약한 애플리케이션의 위험한 패턴은 다음과 같습니다:
const render = Handlebars.compile(userInput); // userInput은 조작된 AST 객체일 수 있음
render(safeContextData); // 공격자 코드가 여기서 실행됨
공격 기법 참고 사항: 기존 공개 PoC(참조된 PoC 포함)는
NumberLiteral노드와lookup헬퍼를 결합하여 인젝션을 달성합니다. 이 PoC는BooleanLiteral노드와log내장 헬퍼를 결합하여 동일한 RCE 프리미티브를 확인함으로써, 타입 혼동이 특정 노드 유형이나 헬퍼 함수에 국한되지 않음을 입증합니다.
editorTemplateData 값을 포함하는 POST 요청을 /api/email/preview에 제출합니다.Handlebars.compile()을 호출합니다.render()를 호출하면 서버 프로세스에서 임의의 JavaScript가 실행됩니다.Handlebars가 Node.js 백엔드 환경에서 구현된 경우, 이 취약점은 일반적인 클라이언트 측 XSS와 달리 서버 측 코드 실행 경로로 이어집니다. 이 공격은 사용자에게 출력이 반영될 필요가 없으며, 페이로드는 애플리케이션과 동일한 권한으로 Node.js 프로세스 내에서 실행됩니다. 배포 방식에 따라:
/etc/passwd)을 읽고 외부로 유출할 수 있습니다.+-------------------------------------------+
| Attacker |
+-------------------------------------------+
|
| POST /api/email/preview
| {"editorTemplateData": "<malicious payload>"}
v
+-------------------------------------------+
| Handlebars.compile(editorTemplateData) |
| (No sanitization — app.js:17) |
+-------------------------------------------+
|
| AST node injection
| breaks template sandbox
v
+-------------------------------------------+
| Arbitrary JS Execution (Server Process) |
+-------------------------------------------+
|
| Read sensitive files, spawn shell,
| exfiltrate secrets, etc.
v
+-------------------------------------------+
| Remote Code Execution (RCE) |
+-------------------------------------------+
Handlebars.js를 4.7.9 이상으로 업그레이드하십시오.
버전 4.7.9는 compile()에 엄격한 입력 타입 검증을 도입하여 코드 생성이 시작되기 전에 문자열이 아닌 인수를 거부합니다.
compile()을 호출하기 전에 타입 검사를 강제하십시오.
즉시 업그레이드가 불가능한 경우, 호출 지점에서 인수가 항상 string인지 검증하십시오:
if (typeof templateInput !== 'string') throw new TypeError('Template must be a string');
const render = Handlebars.compile(templateInput);
빌드 타임 사전 컴파일을 위해 handlebars/runtime을 사용하십시오.
Handlebars CLI로 빌드 시 템플릿을 사전 컴파일하고 런타임 번들만 배포하십시오. 런타임 빌드에는 compile()이 포함되지 않으므로 프로덕션 배포에서 공격 표면이 완전히 제거됩니다.
이 저장소는 보안 연구, 교육 및 통제된 환경에서의 취약점 시연 목적으로만 제공됩니다. 시스템 소유자의 명시적인 서면 승인 없이 시스템을 테스트하거나 악용하는 데 사용해서는 안 됩니다. 이 프로젝트의 목적은 보안 연구자, 방어자 및 개발자가 취약점을 이해하고, 통제된 실습 환경에서 노출을 검증하며, 효과적인 완화 조치를 적용할 수 있도록 돕는 것입니다.