Velocity.js: 함수 생성자에 대한 속성 읽기를 통한 원격 코드 실행(GHSA-j658-c2gf-x6pq 수정 우회)
함수 생성자에 대한 속성 읽기를 통해 속도 js v2.1.6의 원격 코드 실행(RCE). 이는 GHSA-j658-c2gf-x6pq("#set 경로 할당의 프로토타입 오염")에 대한 수정 사항을 우회합니다. 해당 권고는 #set 할당 핸들러(set.cjs)에서만 생성자/proto/prototype을 차단하지만 속성 읽기 표현식은 필터링되지 않습니다. 공격자가 제어하는 Velocity 템플릿을 렌더링하는 모든 애플리케이션은 서버의 임의 코드 실행에 취약합니다.
GHSA-j658-c2gf-x6pq는 proto, 생성자 및 프로토타입 키를 차단하는 isBlockedPathKey()를 dist/cjs/compile/set.cjs:35-43에 추가했습니다. 그러나 이 검사는 #set 지시문이 값을 할당할 때만 실행됩니다. 즉, 평가 중인 값 표현식이 아닌 할당 대상 경로의 유효성을 검사합니다. 값 표현식은 라인 81에서 getAttributes()를 호출하는 dist/cjs/compile/references.cjs:16의 getReferences()를 통해 평가됩니다. 라인 88-89의 속성 액세스에는 필터링이 없습니다. // 참조.cjs:81-91 getAttributes(property, baseRef, ast) { if (property.type === "속성") { return baseRef[property.id]; // ← "생성자", "프로토타입" 등에는 블록이 없습니다. } ... } 한편, set.cjs:35-43은 이러한 키를 적절하게 차단하지만 #set 대상에만 해당됩니다. // set.cjs:35-43 isBlockedPathKey(baseRef, key, isEnd) { if (key === PROTO_KEY)는 true를 반환합니다. // "프로토" if (key === "prototype" && typeof baseRef === "function") return true; return !isEnd && PROTOTYPE_CHAIN_KEYS.has(key) && !hasOwnProperty(baseRef, key); } 익스플로잇 체인:
const 속도 = require('velocityjs');
const template = "#set($f=$x.constructor.constructor("return process.mainModule.require('child_process').execSync('whoami').toString()"))#set($r=$f())$r";
console.log(velocity.render(template, { x: {} })); // 출력: <현재 OS 사용자 이름> speedjs v2.1.6, Node.js v24.15.0, Windows에서 확인되었습니다.
유형: 원격 코드 실행(RCE)
Velocityjs를 사용하여 공격자가 제어하는 Velocity 템플릿을 렌더링하는 모든 애플리케이션은 전체 서버 손상에 취약합니다. 공격자는 임의의 셸 명령을 실행하고, 환경 변수를 읽고, 클라우드 자격 증명에 액세스하고, 내부 네트워크 리소스로 전환할 수 있습니다. 기존 권고 GHSA-j658-c2gf-x6pq는 "공격자가 제어하는 템플릿을 렌더링하는 모든 앱"이라는 위협 모델을 확립했습니다. 이 결과는 수정이 불완전했음을 보여줍니다. #set 차단이 추가되었지만 속성 읽기 표현식은 필터링되지 않은 상태로 유지되어 문제가 훨씬 더 악화됩니다(RCE 대 프로토타입 오염).
velocityjs 2.1.7에서 수정되었습니다. 수정 사항은 풀 요청 #192에 병합되어 v2.1.7로 릴리스되었습니다. 사용자는 버전 2.1.7 이상으로 업그레이드해야 합니다.
왜 이 VPI인가 (설명가능 · 실험적)
VPI 산정 기준
| 영향도 | 98.00 |
| 악용 신호(추가 악용신호 없음) | ×1.00 |
| VPI | 98.00 |
VPI 공식 vpi-v1 기준