0x 01 Content Injection

文中首先介绍了 Content Injection 的概念,主要包括两个方面:

{C}Cross Site Scripting (XSS): 这大家应该都懂Scriptless attacks:无脚本攻击,即攻击者不通过执行Javascript代码,而通过插入HTML标签 (HTML markup injection) 的方式来完成攻击,如窃取敏感信息等。具体可以参考 Postcards from the post-XSS world 和 Scriptless Attacks – Stealing the Pie Without Touching the Sill 这两篇文章

因此仅防止XSS无法解决所有 Content Injection 问题。

在防止 Content Injection 上,GitHub使用了自动转义模板(auto-escaping templates)、code review和静态分析( static analysis)的方法 。但之前的漏洞证明,内容注入问题是无法彻底避免的。虽然我们无法通过单一方法来解决,我们可以结合多种防护措施来增加攻击者利用漏洞的难度,比如 Content Security Policy (CSP),它是单独使用中最有效的缓解措施。

0x 02 Content Security Policy

Content Security Policy能够用来限制页面的 web 资源的加载和执行,如JavaScript、CSS、form表单提交等。GitHub三年前的CSP 策略如下:

CONTENT-SECURITY-POLICY: default-src *; script-src 'self' assets-cdn.github.com jobs.github.com ssl.google-analytics.com secure.gaug.es; style-src 'self' assets-cdn.github.com 'unsafe-inline'; object-src 'self' assets-cdn.github.com;

初始的策略为了保证向后兼容性,主要通过限制资源加载的domain来完成,但是对于注入HTML标签来窃取敏感信息(后面会举例说明)不起作用。

后来,GitHub对第三方依赖脚本进行了重构和整理,增加了许多新的CSP策略,具体如下:

CONTENT-SECURITY-POLICY: default-src 'none'; base-uri 'self'; block-all-mixed-content; child-src render.githubusercontent.com; connect-src 'self' uploads.github.com status.github.com api.github.com wss://live.github.com; font-src assets-cdn.github.com; form-action 'self' github.com gist.github.com; frame-ancestors 'none'; frame-src render.githubusercontent.com; img-src 'self' data: assets-cdn.github.com identicons.github.com collector.githubapp.com *.gravatar.com *.wp.com *.githubusercontent.com; media-src 'none'; object-src assets-cdn.github.com; plugin-types application/x-shockwave-flash; script-src assets-cdn.github.com; style-src 'unsafe-inline' assets-cdn.github.com

注:上面的策略中,有少部分和防止 content injection 没有直接的联系。

下一章我们将会讨论上述CSP策略的具体细节、策略是如何阻止特定的攻击场景,并通过一些案例(bounty submissions )来帮助我们理解策略的用途。

0x 03 CSP details script-src

和最初的策略相比,当前的策略只允许从CDN来获取JavaScript。

前:

script-src 'self' assets-cdn.github.com jobs.github.com ssl.google-analytics.com secure.gaug.es;

后:

script-src assets-cdn.github.com;

因此只要保证CDN上的资源是可靠即可阻止外部恶意脚本的加载和执行。

此外,GitHub还采用了 subresource integrity 来减少加载恶意外部 JavaScript 的风险,Subresource Integrity 通过在标签中添加 integrity 属性,其值为资源对应的hash,比如:

<script src="/assets/application.js" integrity="sha256-TvVUHzSfftWg1rcfL6TIJ0XKEGrgLyEq6lEpcmrG9qs="></script>

浏览器在加载 application.js 时,会验证其文件的 sha256 hash值是否和 integrity 的值相同,不相同则拒绝加载。这个可以防止 CDN 被撸后加载恶意 js 文件的场景,虽然 CDN 基本不太可能被撸~

这里需要特别注意的是,修改后的 script-src 值是没有包含 self 的,虽然一般来说从 self 加载JavaScript相对来说是安全的(被使用的也比较多),但还是应该尽可能避免。

比如下面这几种特殊的情况,开发者应该考虑阻止从 self 加载和允许js脚本。

JSONP 接口没有过滤回调函数名,导致恶意js代码执行在 content 为用户可控的情况下,某些 content-type 会被浏览器解析成为JavaScript。Github有多个这样的接口,比如 在查看 commit diffs 时,页面内容是用户可以控制的,content-type 为 text/plain 。

通过将 self 从策略中移除,即使是出现了上面两种情况,js代码也无法执行。

我们也可以通过增加响应头 X-Content-Type-Options: nosniff 来阻止浏览器对内容的嗅探解析(sniffed)行为。与之相比,CSP能够提供强有力的保证,即使存在一个攻击者能够控制 content-type bug,也无需担心js代码会被执行。

object-src

在旧的的CSP策略中,对于 object 和 embed 标签是允许 self 的。

object-src 'self' assets-cdn.github.com;

是因为GitHub依赖自己网站上的 ZeroClipboard 库。 将依赖资源移动到CDN后,self 就不再需要了,但因为某些原因(懒得改 or 觉得不会有安全问题?),在后来的策略中并没有被移除,直到一名 bounty hunter 发现了一种利用方式。攻击者利用了一个 content injection bug 和 一个Chrome浏览器的bug 来 bypass CSP,并且成功执行js代码。攻击过程如下:

首先,攻击者用以下内容创建一个 Wiki 项:

domain

GitHub拥有一个特性,能够在多个地方(Issues, Pull Requests, Comments)渲染用户提供的HTML(通常是通过Markdown)。但用户提供的HTML会经过过滤处理,防止注入任意的HTML。

这里存在一种特殊情况,当HTML 标签的class 属性被设置为 choose_plan 和 js-domain 时,会触发 JavaScript 一些自动的操作,即自动请求标签的 href ,并将 response 插入到 DOM中。

而这里用户是可以自定义HTML 标签中的class属性值的。但因为 response 中的HTML仍然会受到 CSP 的制约,无法执行任意的 JS 代码。但此时,攻击者已经可以插入任意的HTML到DOM中了。

这里我的理解是因为 some_evil_site.com 不在 'self' assets-cdn.github.com 里,所以 自动请求标签href资源 的行为会被浏览器blocked。这里需要href 对应的domain为self 或 CDN,才能成功加载资源并且把响应插入到DOM中。

这里bounty hunter给出的 POC也符合我的推测,domain使用的是self,即github.com: