공부한 것
CVE-2026-28466
hsnyus
2026. 5. 14. 02:34
CVSS: 9.9
대상 서비스
OpenClaw
- 게이트웨이와 노드 간 WebSocket 기반 통신을 통해 명령 실행, 도구 호출, 원격 작업을 처리하는 시스템
- 연결된 노드 호스트에 대해 system.run 같은 명령 실행 기능을 제공함
- 게이트웨이에서 인증된 클라이언트 요청을 받아 노드 레지스트리를 통해 대상 노드로 전달하는 구조임
- 승인 기반 실행 통제 로직을 통해 위험 명령의 실행 여부를 제어하는 보안 게이트를 포함하고 있음
대상 버전
- 영향 받는 버전: 2026.2.14 이전의 OpenClaw 버전
- 패치 버전: 2026.2.14 이후 수정 버전
취약점 개요
취약점의 종류
- RCE
- 인증 후 원격 코드 실행
- 승인 우회
- node.invoke 호출 시 내부 승인 필드가 정화되지 않아 실행 승인 게이트를 우회할 수 있음.
- 파라미터 인젝션
- 공격자가 node.invoke의 params 내부에 승인 제어 필드를 삽입할 수 있음.
- 신뢰 경계 붕괴
- 게이트웨이가 클라이언트가 제공한 승인 관련 필드를 신뢰한 채 노드로 그대로 전달함.
발생한 원인
- OpenClaw 게이트웨이의 node.invoke 전달 로직에 설계적 결함이 있었음.
- 인증된 클라이언트가 WebSocket을 통해 node.invoke 요청을 전송하면, 게이트웨이는 이를 처리기로 매핑한 뒤 AJV 검증을 수행함.
- 그런데 해당 검증에서 params가 Type.Unknown()으로 정의되어 있어 내부 구조가 검증되지 않음.
- 그 결과 공격자가 params 내부에 approved, approvalDecision 같은 내부 승인용 필드를 포함시켜도 그대로 유효한 요청으로 통과함.
- 이후 node.invoke 처리기에서는 명령 권한 여부를 검사하지만, p.params 내부 내용에 대해서는 별도의 필터링이나 제거를 수행하지 않음.
- 따라서 공격자가 주입한 승인 제어 필드가 원형 그대로 노드 레지스트리로 전달되고, JSON 직렬화를 거쳐 대상 노드로 전송됨.
- 노드 측에서는 전달받은 JSON을 다시 파싱한 뒤 승인 여부를 확인하는데, approved === true이거나 approvalDecision 값이 존재하면 명령 실행을 허용하는 구조였음.
- 특히 approvalDecision === "allow-always"인 경우 영구 허용 목록에 추가되는 동작까지 가능했음.
- 즉, 원래는 게이트웨이 내부 승인 절차를 통해서만 설정되어야 할 필드를, 클라이언트가 직접 주입할 수 있었고, 게이트웨이가 이를 정화하지 않은 채 신뢰 경계를 넘어 전달했기 때문에 승인 우회형 RCE가 발생함.
취약점 원리
공격 흐름
- 공격자 → 게이트웨이에 인증된 클라이언트 자격으로 WebSocket 연결
- node.invoke 요청 전송
- params 내부에 승인 제어 필드 삽입
- 서버 측 처리
- WebSocket 연결 수락
- 연결 ID 생성
- attachGatewayWsMessageHandler가 후속 메시지 처리
- 핸들러 매핑
- extraHandlers를 통해 node.invoke 문자열이 GatewayRequestHandlers의 처리기로 매핑됨
- 요청 검증
- AJV 검증기가 요청을 검사하지만 params가 Type.Unknown()이라 내부 구조를 검증하지 않음
- 공격자가 삽입한 승인 필드가 그대로 통과함
- node.invoke 처리기 실행
- 필수 필드 존재 여부 검사
- 명령 권한 검사 수행
- 그러나 p.params 내부 승인 필드는 제거하거나 차단하지 않음
- 노드로 전달
- 노드 레지스트리가 해당 요청을 JSON 직렬화 후 대상 노드로 전송
- 노드 측 실행
- 노드가 JSON을 파싱해 params를 복원
- approved === true 또는 approvalDecision 존재 여부를 기준으로 승인된 요청으로 처리
- system.run 명령 실행
- 결과
- 연결된 노드 호스트에서 임의 명령 실행 가능
- 응답이 공격자에게 반환됨
공격자가 이미 인증된 게이트웨이 클라이언트 자격을 보유하고 있는 시점에서, node.invoke 요청을 직접 구성해 보낼 수 있음. 이때 요청 본문의 params 내부에 원래 내부 승인 절차에서만 설정되어야 하는 approved: true, approvalDecision: "allow-always" 같은 필드를 함께 넣어 전송하면, 게이트웨이는 이를 별도로 제거하지 않고 그대로 받아들임.
이후 게이트웨이의 AJV 검증은 params를 구조적으로 검사하지 않기 때문에, 공격자가 삽입한 승인 필드는 그대로 유효한 요청처럼 통과함. node.invoke 처리기에서도 명령 자체가 허용 목록에 있는지만 확인할 뿐, 내부 파라미터에 포함된 승인 제어 필드는 건드리지 않음.
그 결과 해당 승인 필드는 노드 레지스트리를 통해 JSON 형태로 대상 노드에 전달되고, 노드 측에서는 이를 신뢰한 채 승인된 실행 요청으로 처리하게 됨. 결국 공격자는 정상적인 승인 절차를 거치지 않고도 system.run 명령을 실행시킬 수 있으며, 연결된 개발자 워크스테이션이나 구성 실행기 같은 노드 호스트에서 임의 명령 실행이 가능해짐.
이후 패치 사항
OpenClaw 2026.2.14
게이트웨이에서 node.invoke 전달 파라미터를 정화하도록 수정되었음.
- sanitizeNodeInvokeParamsForForwarding 함수 추가
- 노드로 전달되는 params에서 허용된 화이트리스트 필드만 남기도록 변경
- 내부 승인 필드 직접 주입 차단
- 클라이언트가 approved, approvalDecision 같은 승인 제어 필드를 임의로 넣어도 전달되지 않도록 수정
- 승인 ID 바인딩 강화
- 승인 ID가 요청 디바이스와 결합되도록 하여 다른 장치에서 재사용하거나 재생하는 공격을 방지
- 승인 결정 강제 제한
- 승인 결정을 allow-once 수준으로 강등해 임의의 영구 허용 설정 주입을 막음
즉, 패치 이후에는 게이트웨이가 클라이언트 입력을 그대로 신뢰하지 않고, 노드로 전달하기 전에 승인 관련 내부 필드를 정화하는 구조로 변경되어 승인 우회형 RCE가 차단되었음.
참고 정보
- GHSA: GHSA-gv46-4xfq-jv58
- 관련 커밋: 0af76f5f0e93540efbdf054895216c398692afcd