![[$4500] Meta의 Manus AI에 존재했던 RCE (Remote Code Execution) 취약성 대표 이미지](https://static.zenn.studio/user-upload/bd28bb2c0096-20260629.png)
[$4500] Meta의 Manus AI에 존재했던 RCE (Remote Code Execution) 취약성
요약
Meta의 Manus AI 데스크톱 앱에서 발견된 RCE(원격 코드 실행) 취약점을 분석합니다. Deep Link를 통해 인증 토큰을 조작할 수 있는 보안 결함과 그로 인한 위험성을 다룹니다.
핵심 포인트
- Deep Link를 통한 임의의 세션 토큰 주입 가능성 확인
- OAuth 인증 과정에서 state 검증 및 PKCE 미사용 문제 지적
- Electron 기반 앱의 커스텀 URL 스킴 취약점 분석
- 조작된 토큰을 통한 앱 재연결 및 계정 탈취 위험
서론
안녕하세요. 주식회사 INDX에서 엔지니어로 근무하고 있는 재젠(財前)입니다.
평소 업무에서는 주로 LLM의 보안을 담당하고 있습니다.
얼마 전, 사내 기술 교류회를 위해 AI 에이전트를 비교하던 중, 작년에 화제가 되었던 Manus AI가 데스크톱 앱을 공개했다는 사실을 알게 되었습니다.
데스크톱 앱화에 잠재된 보안 우려
AI 에이전트를 실용적으로 사용하기 위해서는 일정 수준의 권한을 부여해야 합니다.
하지만 그와 동시에, 프롬프트 인젝션 (Prompt Injection)을 비롯한 LLM 특유의 취약성도 보고되고 있는 현상입니다.
메인 머신 도입은 리스크가 높기 때문에, 우선적으로 보안 리스크를 파악해 보기로 했습니다.
표층 분석
Manus My Computer에는 Electron이 사용되고 있습니다.
Electron 프레임워크로 개발된 애플리케이션에서는 JS 파일 등의 애플리케이션 본체가 app.asar라는 아카이브로 묶여 있는 경우가 있습니다.
$ npx asar extract app.asar /tmp/manus_asar
$ ls /tmp/manus_asar/dist/
autoUpdaterManager.js index.js myComputer/ preload.js setupIPC.js utils/ windowManager/
| 파일 | 역할 |
|---|---|
index.js | 기동 처리, Deep Link 처리 |
myComputer.js | My Computer 기능의 메인 처리 |
sidecarManager.js | 보조 프로세스의 기동 관리 |
storageHelper.js | 스토리지 처리 |
발견된 버그
1. Deep Link를 통해 임의의 토큰을 주입할 수 있음
index.js를 확인해보니, manus:// Deep Link를 등록하는 처리가 있었습니다.
const APP_PROTOCOL = 'manus';
// ...
app.setAsDefaultProtocolClient(APP_PROTOCOL);
Deep Link, 이른바 커스텀 URL 스킴 (Custom URL Scheme)은 브라우저나 앱 등에서 데스크톱 앱을 실행하기 위해 자주 사용되는 메커니즘입니다.
예를 들어, manus://example을 열면 OS가 대응하는 앱에 URL을 전달합니다.
function handleProtocolCallback(urlStr) {
try {
const parsedUrl = new URL(urlStr);
...
여기서 문제가 되는 것은 Deep Link를 통해 전달되는 토큰의 취급입니다.
구현상으로는 manus://...?token=...와 같이 URL에 token이 포함되어 있는 경우, 그 값이 session_id로서 저장됩니다.
본래 인증 토큰과 같은 값을 외부 입력으로부터 받을 경우에는, 수신한 URL이 앱 자신이 시작한 인가 플로우 (Authorization Flow)에 대한 응답임을 랜덤한 state 등을 사용하여 검증해야 합니다. 또한, OAuth를 이용하는 네이티브 앱에서는 토큰을 URL로 직접 받지 않고, 인가 코드 (Authorization Code)와 PKCE를 사용하는 설계가 권장됩니다. 하지만 확인해 본 결과, 이러한 검증은 이루어지지 않고 있었습니다.
조작된 Deep Link를 사용자가 열 경우, 앱의 토큰을 공격자가 지정한 값으로 교체할 수 있습니다.
2. 전달된 토큰으로 앱이 재연결됨
handleProtocolCallback()에서는 myComputer.reinit(token)이 호출되고 있습니다.
async reinit(token) {
loggerHelper.warning(
'reinitializing my computer for account switch:', token
...
URL로부터 전달된 토큰은 저장될 뿐만 아니라, 그대로 init()에 의한 재초기화에 사용되고 있었습니다.
reinit()은 기존의 sidecar나 세션을 정지한 후, 전달된 토큰을 사용하여 Manus Cloud에 재연결합니다.
3. 클라우드를 통해 sidecar가 기동됨
Manus My Computer는 Manus Cloud와 WebSocket으로 통신하고 있으며, 클라우드 측으로부터 device_config라는 이벤트를 받는 처리 로직이 존재합니다.
handleDeviceConfigMessage(message) {
const deviceId = getOrGenerateDeviceId();
if (message.deviceId !== deviceId) {
...
이벤트가 발생하면 클라우드로부터 받은 값을 바탕으로 로컬 측의 설정이 업데이트되는데, 현재의 단말 ID와 message.deviceId가 일치하지 않는 경우에도 처리가 계속 진행됩니다.
RCE까지의 흐름
sidecar는 두 개의 인자를 받아 기동됩니다.
spawnProcess(sharedFolder, wsUrl) {
const binaryPath = this.resolveSidecarBinary();
const child = spawn(binaryPath, [
...
주목해야 할 점은 wsUrl이 sidecar의 접속 대상으로서 그대로 전달된다는 것입니다.
validateSharedFolder(pathValue) {
const trimmed = pathValue.trim();
let stats = fs.statSync(trimmed);
...
또한, 공유 폴더 검증 로직을 확인한 결과, 사용자에 의한 허가 여부가 확인되지 않고 있습니다.
sidecar의 구현을 확인해 보겠습니다. 터미널 실행이나 통신 처리와 관련이 있을 법한 문자열을 확인합니다.
$ file /Applications/Manus.app/Contents/Resources/bin/sidecar
Mach-O 64-bit executable arm64
$ strings sidecar | grep -E "terminal|pty|shell|exec|websocket|protobuf|bridge" | head -40
*main.terminalSession
*main.terminalManager
...
terminal |
터미널 세션 관리 |
pty |
PTY를 사용한 가상 터미널 처리 |
exec, /bin/sh |
로컬 명령 실행 |
websocket |
WebSocket 통신 |
protobuf |
protobuf를 통한 메시지 처리 |
protobuf의 구조체 태그를 확인하면, 실행 명령 필드가 포함되어 있습니다.
$ strings sidecar | grep 'protobuf:"' | grep -E "terminal|command|exec_dir|sandbox|bridge" | head -30
message Envelope {
oneof msg {
bytes sandbox = 1;
...
정리하면, 다음과 같은 흐름을 통해 원격 코드 실행 (RCE)으로 이어집니다.
- 공격자가 임의의 토큰을 심은 딥 링크 (Deep Link)를 피해자에게 열도록 유도한다.
- Manus My Computer가 토큰을 수신하고, Manus Cloud에 재연결한다.
- 공격자가 Manus Cloud를 경유하여
device_config를 전송하면, 피해자의 단말에서 sidecar가 기동된다. - sidecar가 WebSocket을 통해 명령을 수신하고, 피해자의 단말 위에서 명령을 실행한다.
버그 바운티 보고
당초에는 Manus AI에 직접 보고했으나, 바로 다음 날 Meta Bug Bounty의 스코프 (Scope) 대상으로 포함되었으며, 포상금 $4,838 (약 78만 엔)을 받았습니다.

Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기