Node.js CVE-2017-14849 漏洞分析

作者:niubl@TSRC

1. 漏洞简介

2017年9月28日,公司扫描器发现某业务存在一例任意文件读取漏洞,团队跟进分析后发现这是Node.js和Express共同导致的一个通用漏洞。在我们准备告知官方时,发现Node.js官网于9月29号给出了漏洞公告,对应的CVE编号为CVE-2017-14849。

1.1 关于Node.js

Node.js是一个Javascript运行环境(runtime),发布于2009年5月,由Ryan Dahl开发,实质是对Chrome V8引擎进行了封装。Node.js对一些特殊用例进行优化,提供替代的API,使得V8在非浏览器环境下运行得更好。

1.2 关于Express

Express 是一个简洁而灵活的 node.js Web应用框架, 提供一系列强大特性帮助你创建各种Web应用。Express 不对 node.js 已有的特性进行二次抽象,我们只是在它之上扩展了Web应用所需的功能。丰富的HTTP工具以及来自Connect框架的中间件随取随用,创建强健、友好的API变得快速又简单。

1.3 漏洞影响

Node.js 8.5.0 + Express 3.19.0-3.21.2
Node.js 8.5.0 + Express 4.11.0-4.15.5

2. 漏洞演示

  1. 安装Node.js 8.5.0
  2. 下载并解压express-4.15.5.zip
  3. expresss-4.15.5目录下执行npm install
  4. expresss-4.15.5/examples/static-files目录里执行nodejs index.js
  5. 使用POC测试(web监听了5678端口,默认3000)如下图

3. 漏洞分析

Express依赖Send组件,Send组件0.11.0-0.15.6版本pipe()函数中,如图:

Send模块通过normalize('.' + sep + path)标准化路径path后,并没有赋值给path,而是仅仅判断了下是否存在目录跳转字符。如果我们能绕过目录跳转字符的判断,就能把目录跳转字符带入545行的join(root, path)函数中,跳转到我们想要跳转到的目录中,这是Send模块的一个bug,目前已经修复。

再来看Node.js,Node.js 8.5.0对path.js文件中的normalizeStringPosix函数进行了修改,使其能够对路径做到如下的标准化:

assert.strictEqual(path.posix.normalize('bar/foo../..'), 'bar');

新的修改带来了问题,通过单步调试我们发现,可以通过foo../../和目录跳转字符一起注入到路径中,foo../../可以把变量isAboveRoot设置为false(代码161行),并且在代码135行把自己删掉;变量isAboveRootfalse的情况下,可以在foo../../两边设置同样数量的跳转字符,让他们同样在代码135行把自己删除,这样就可以构造出一个带有跳转字符,但是通过normalizeStringPosix函数标准化后又会全部自动移除的payload,这个payload配合上面提到的Send模块bug就能够成功的返回一个我们想要的物理路径,最后在Send模块中读取并返回文件。normalizeStringPosix函数如下图:

4. 参考链接