注册
web

(紧急修复!)老板急call:pdf阅读器不能用了?

客户那边说,发现了我们项目中有一个高危漏洞,需要修复下。我过去一看,好像不是我们的代码,是三方依赖的pdf.js 突然爆了高危,心想,这怎么可能,这东西都发布好多年了,用的好好的也没说高危啊。老板说,这他不管,客户那边的高危项一定要给解决。好吧,那我先去看看这个高危是个什么。


一番检索,发现真是 pdf.js 的高危漏洞,而且是今年24年4月26日内部报的,编号是 CVE-2024-4367,并且在今年24年4月30日的 4.2.67 版本上已经修复并发布了。


背景


不管如何,先看这个高危项,它允许攻击者在打开恶意 PDF 文件时立即执行任意 JavaScript 代码。主要是利用了pdf.js 中的字体渲染技术上的特性,当识别到当前浏览器支持 new Function("")并且在加载 pdf 资源时配置了 isEvalSupported 为 true(该值默认为true),此时如果我们在 pdf 资源中输入一些内容,用来控制字体渲染的参数,那么就可以在加载pdf 资源时,执行自己想要的任意的 JavaScript 代码,实现对应用系统的攻击。




解决方案


常规方案有三种



  • 完全杜绝的话可以直接将依赖的 pdf.js-dist 版本升级到 4.2.67+
  • 在使用 pdf.js-dist 的上层代码中将加载 pdf 的参数 isEvalSupported 设置为false
  • 禁用使用 eval 和 Function 构造函数

一般如果对兼容性要求不高的话就可以选择第一种,4.2.67 版本的兼容性legacy版本最低能兼容到以下版本的浏览器


Browser/environmentSupported
Firefox ESR+Yes
Chrome 98+Yes
OperaYes
EdgeYes
Safari 16.4+Mostly
Node.js 18+Mostly

但是如果像一些运行比较久远的至少要兼容到5年以上的设备的话,比如说我司产品,要兼容到 ios10.3(也不知道现在除了我司测试,到底还有谁在用 ios10.3),这种情况下,方案1就完全不可行了,那么就可以考虑使用方案2。


方案3与方案2有相似之处,通过重写 eval 和 Function 来控制内部的 isEvalSupported 的值,也可以避免 pdf文件在被渲染时使用 Function 加载 pdf 内容。


// 重写eval和Function
window.eval = function() {
throw new Error("eval() is disabled");
};

window.Function = function() {
throw new Error("Function constructor is disabled");
};

// pdf.js 中的Function 检测
function isEvalSupported() {
try {
new Function("");
return true;
} catch (e) {
return false;
}
}

const IsEvalSupportedCached = {
get value() {
return shadow(this, "value", isEvalSupported());
}
};

上述的重写会影响全局的 eval 和 Function,若项目中不使用上述功能,可以考虑。若一些内部使用模块使用了以上两个功能,则不建议如此修改。


但是,我们的客户不认,只认依赖版本,我们的 pdf.js-dist 版本低于 4.2.67,这件事在他们的安全报告中,属于完全不能容忍的高危漏洞,一定要解决的,解释也没用,那现在咋办?总不能不用吧?也不能抛弃大部分的低版本客户吧?



那么这个时候,上述三种方案都不能解决问题了,就要考虑其他的方式了。


那么回归我们程序员的本质,只能 fork-modify-push-publish 了。因为只有内部产品使用,也不需要 publish 了,将本来作为第三方的依赖,转成项目内置模块来使用,这个时候想怎么改就能怎么改了。


模块内置后,客户找的安全检测机构也不知道还能不能检测出来,以防万一,还是得把 pdf.js 关于这条安全漏洞的修复给同步到我们的低版本上来。


修复内容


根据官方发布,这条漏洞主要在 pr[18015] 中修复了,那我们把这条 pr 中有关上述漏洞的部分迁移过来即可。不用把这条内容都迁,比如其中对于cmds的重写部分,我们只需要将和isEvalSupported 相关的部分迁移即可,毕竟此漏洞也是由 isEvalSupported 引起的。


主要修复内容:



  • 去除 font_loader.FontFaceObject 中的入参 isEvalSupported 及相关使用该参数的内容
  • 如果使用的版本中isEvalSupported 只用来做字体渲染,可以去除整个 pdf.js 中使用了 isEvalSupported 逻辑的相关内容

通过上述修复方式,客户那边应该也能安心了吧?检索不到低于4.2.67版本的 pdf.js 引用,也不会在解析渲染pdf 资源时,出现外部的 pdf文件对系统造成攻击


关于漏洞


总所周知,pdf.js 里不仅对pdf 文件进行了资源解析,也做了资源的渲染,其中就包含了很多字体字形的绘制,而该漏洞就来源于字体绘制时使用了 new Function(""),导致可以在 pdf 文件中写一些能够被解析的内容,在应用系统中使用 pdf.js 去解析 pdf 文件并在绘制时执行任意的 JavaScript 内容,造成对系统的攻击。


// pdf.js font_loader 字体绘制中能够执行任意js内容的部分
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.js 中执行任意 JavaScript 中查看,以下为在 pdf 文件中输入任意代码的示例,


通过首先关闭c.transform(...)函数,并利用结尾括号来触发 alert:


/FontMatrix [1 2 3 4 5 (0); alert('foobar')]

将上述内容输入到 pdf 文件中,然后在火狐浏览器(未更新最新版本的 pdf 预览插件版本)中加载该 pdf 文件时的效果如下:



也可以使用 旧版本的 pdf.js 开源的 viewer 打开该文件,有一样的效果。


附录:


可用于攻击的 pdf 文件地址:codeanlabs.com/wp-content/…


CVE-2024-4367 漏洞详细攻击介绍:codeanlabs.com/blog/resear…


CVE-2024-4367 漏洞详情及相关修改 pr:github.com/mozilla/pdf…


pdf.js 相关文档推荐


前端接入 pdfjs-dist 渲染 pdf 文件踩坑


PDF.js 与 WebComponent:打造轻量级 PDF 预览器


作者:九酒
来源:juejin.cn/post/7408168213362507827

0 个评论

要回复文章请先登录注册