CVSS: 10.0
대상 서비스
SandboxJS
- JavaScript 코드를 격리된 환경에서 실행하기 위한 JavaScript 샌드박스 라이브러리
- 호스트 전역 객체 및 위험한 내장 객체에 대한 접근을 제한해 비신뢰 코드 실행 환경을 제공하는 목적의 라이브러리
- 내부적으로 허용된 프로토타입과 속성 집합, 래퍼 객체, taint 추적, 실행 제한 등을 기반으로 샌드박스 경계를 유지함
- Node.js 환경 등에서 동적 코드 실행, 플러그인 처리, 사용자 스크립트 격리 용도로 사용될 수 있음
대상 버전
- 영향 받는 버전: SandboxJS 0.8.34 이전 모든 버전
- 패치 버전: SandboxJS 0.8.34
취약점 개요
취약점의 종류
- Sandbox Escape
- RCE
- 비인증 임의 코드 실행 가능 구조
- taint 추적 우회
- 위험한 호스트 함수가 배열 및 객체 재구성 과정에서 안전한 값처럼 오인됨
- Function 생성자 노출
- 호스트 Function 생성자 참조를 획득할 수 있음
- Promise 체인 기반 실행
- Promise.prototype.finally와 재구성된 then 속성을 이용해 호스트 전역 컨텍스트에서 코드 실행 가능
발생한 원인
- SandboxJS의 taint 추적 메커니즘에 구조적 결함이 있었음.
- 공격자가 Object.values 같은 내장 메서드를 이용해 위험한 호스트 함수 참조를 새 배열로 옮기는 과정에서, 샌드박스 엔진이 배열 내부 원소까지 taint를 재귀적으로 전파하지 못함.
- 그 결과 호스트 Function 생성자가 배열 접근과 Object.fromEntries 재구성 과정을 거친 뒤 위험 객체가 아닌 일반 값처럼 취급됨.
- SandboxExec.ts의 SAFE_PROTOTYPES 설정에서 Object.values, Object.fromEntries 등이 허용되어 있어 공격자가 이 데이터 변환 경로를 정상 기능처럼 사용할 수 있음.
- executor.ts의 LispType.Call 처리에서는 원생성 메서드를 직접 호출한 뒤 getGlobalProp(ret, context) || ret 형태의 얕은 정리만 수행하는데, 이 과정에서 반환값이 배열이나 객체인 경우 내부 원소나 속성은 검사하지 않음.
- 이후 LispType.CreateArray 처리에서 valueOrProp(item, context)로 래퍼를 해제하면서도, 해제된 값이 위험한 호스트 참조인지 다시 검증하지 않음.
- 이로 인해 누출된 호스트 Function 참조가 샌드박스 내부에서 “일반 함수 참조”로 가공되어 최종적으로 샌드박스 탈출이 가능해짐.
취약점 원리
공격 흐름
- 공격자 → 샌드박스 내부 코드에서 Object.values(this) 호출
- 호스트 전역 객체에 매핑된 값들 중 Function 같은 위험한 생성자 참조가 포함된 배열을 획득
- 샌드박스 내부 처리
- executor.ts의 LispType.Call이 원생성 Object.values를 직접 호출
- 반환된 배열 전체에 대해서만 얕은 검사 수행
- 배열 내부의 위험한 함수 참조는 정리되지 않은 채 그대로 반환됨
- 데이터 형태 변환
- 공격자가 배열 전개 및 Object.fromEntries를 사용해 { then: Function } 형태의 객체를 생성
- 이 과정에서도 내부 값에 대한 재귀 정화가 일어나지 않음
- Promise 체인 악용
- 네이티브 Promise.prototype.finally를 가진 객체와 재구성된 then 속성을 조합
- finally 실행 시 호스트 엔진이 객체의 then 속성을 직접 읽어들임
- 호스트 Function 생성자 호출
- then이 실제 호스트 Function 생성자를 가리키므로, 전달된 문자열이 전역 호스트 컨텍스트에서 실행됨
- 샌드박스 탈출 및 임의 코드 실행
공격자가 샌드박스 코드에서 Object.values(...)를 실행하면, SAFE_PROTOTYPES 설정상 이 호출은 허용됨. 이후 executor.ts는 호스트 환경의 원생성 Object.values를 직접 호출하고, 그 반환값을 getGlobalProp(ret, context) || ret로 처리함. 하지만 이 검사는 반환된 배열 자체만 보며, 배열 안에 포함된 Function 같은 위험한 호스트 참조까지 정리하지 않음. 그 결과 호스트 Function이 들어 있는 더러운 배열이 샌드박스 변수로 들어오게 됨.
이후 공격자가 배열 전개나 비슷한 동작을 수행하면 LispType.CreateArray가 실행되고, 여기서 각 원소는 valueOrProp(item, context)를 통해 해제됨. 문제는 이 단계에서도 해제된 값이 위험한 호스트 전역 객체인지 다시 검사하지 않는다는 점임. 즉, 한 번 누출된 호스트 Function은 더 이상 위험 값으로 취급되지 않고 샌드박스 내부의 정상 값처럼 유통됨.
이 취약점의 실제 악용에서는 Object.fromEntries([['then', ...Object.values(this).slice(1)]])를 사용해 배열 원소를 객체 속성으로 재구성함. 이렇게 하면 호스트 Function 생성자가 then 속성에 매달린 평범한 객체처럼 보이게 됨. 이어서 네이티브 Promise.prototype.finally를 가진 객체와 이를 합쳐 finally를 호출하면, 호스트 엔진은 샌드박스 바깥에서 해당 객체의 then 속성을 읽고 이를 호출함. 이때 then이 실제 호스트 Function 생성자이므로 결과적으로 Function('악성코드')가 실행되고, 코드가 호스트 전역 스코프에서 수행되어 샌드박스가 완전히 무력화됨.
이후 패치 사항
SandboxJS 0.8.34
호스트 참조 누출 및 샌드박스 상태 오염 가능성을 줄이기 위한 수정이 반영되었음.
- 취약점은 0.8.34 버전에서 수정됨
- 호스트 Function 참조가 배열/객체 재구성 과정에서 안전한 값처럼 남는 문제를 차단
- 실행 제한과 관련된 전역/외부 의존 상태를 줄이는 방향의 수정 포함
- 구체적으로 ticks || currentTicks.current 같은 fallback 로직이 제거되고, 고정된 context.ctx.ticks를 사용하도록 변경됨
- 기존 구조에서는 외부에서 전달된 ticks 또는 전역 수준의 currentTicks.current에 의존할 수 있어 샌드박스 실행 예산이 외부 상태에 의해 영향을 받을 여지가 있었음
- 수정 후에는 실행 제한이 샌드박스 생성 시점의 context에 고정되어 함수 생성 단계에서 외부에 의해 바뀌지 않도록 개선됨
'공부한 것' 카테고리의 다른 글
| Shellcode - orw Shellcode (0) | 2026.05.17 |
|---|---|
| CVE-2026-28466 (0) | 2026.05.14 |
| CVE-2026-3672 (0) | 2026.05.12 |
| CVE-2026-21236 (0) | 2026.05.10 |
| CVE-2026-33017 (0) | 2026.05.09 |