通过滥用 CSS 解析来窃取本地文件内容

原文来自:https://bugs.chromium.org/p/chromium/issues/detail?id=788936
翻译:聂心明
来自file:///的资源没有定义一个Content-Type,一个恶意页面将加载任何本地资源作为css,并且它独立于MIME类型,浏览器会将其进行解析。他允许通过css注入的方式来跨域泄露本地文件数据。这个是这个漏洞 https://bugs.chromium.org/p/chromium/issues/detail?id=419383 的小变体。
chrome浏览器似乎用文件拓展名作为MIME类型,当怀疑解析了一个错误的资源的时候,就会弹出一个警告:“资源被解析成样式表,但是被传输的MIME类型是text/html”,这个警告暗示避免使用file:/// 去探测主机文件。

漏洞详细

攻击需要一个受害者打开一个本地恶意的HTML页面。我不在这里讨论怎么做到这点,但是有很多方法可以欺骗用户(强制下载,从本地PDF中跳转,邮件附件……),未来,我猜会在安卓或者其他的特殊电子产品中会更加有用。
在这个文章中我只展示三个POC,完成攻击只需要两个条件

  • 敏感文件在固定(或者可预测)路径
  • 攻击者能注入内容到那些文件中
    Chrome的SQLite正好满足这两个条件,成为这次攻击者的理想对象。比如:
    //互联网上的随机页面
<script>
document.cookie = "foo{}*{--:bar=1337"; // 几分钟之后写入sqlite数据库  
</script>

本地文件(从默认路径~/Downloads/打开的文件)

<link rel="stylehseet" href="../Library/Application Support/Chrome/Default/Cookies">
<script>
var leak = getComputedStyle(document.body).getPropertyValue('--');
alert(leak);
</script>

上面的代码有时会很有效,但是由于文件巨大,很难控制payload在字符前面还是后面。他们中的有一些会破坏css的解析。为了提高成功的机会,我们使用一种叫"ile-massaging"的方法。
下面的poc不是很可靠,但是已经在Chrome stable 62 和 Canary 64 (Linux和osx机器上)测试成功,下面用例子来演示攻击者的操作

探针的概念

  • PoC 1 - 跨域重定向的泄露
    文件: redir.html
    描述:’~/Library/Application Support/Google/Chrome/Default/Current Session’这个文件里面包含当前请求的信息。有两个比较有趣的事情是,一部分信息被UTF-16编码(用了这种编码可以减少解析中断的提醒),并且框架中请求被很完美的收集到父类请求中。因此,我们能用框架来跨域并且能读到重定向的结果。我们仅仅需要确定css解析器是否通过提前关闭所有的块(blocks)[1]来处于适当的状态,我们只要简单添加“}])”字符就可以判断。

  • PoC 2 - 本地存储SQLite泄露(localstorage.html):
    文件: localstorage.html
    描述:在这个案例中,目标数据库存储文件存储在‘~/Library/Application Support/Google/Chrome/Default/Local Storage/leveldb/’。因为数据是被直接存储的并且我们能完全控制二进制数据,我们很容易把我们的payload编码成UTF-16,并且能被css解析器解析。而且,文件名会采用数字自增的方式命名,我们就可以爆破文件名,直到读取到我们注入的内容。
    这就会泄露其他网站和插件的敏感信息。

  • PoC 3 - cookie怪物
    文件: cookiemonster.html
    描述:正如上面提到,当页面设置cookie的之后,它会在几分钟之后写入到本地的SQLite数据库中。数据库的表结构为:

/*
creation_utc INTEGER NOT NULL UNIQUE PRIMARY KEY,
host_key TEXT NOT NULL,
name TEXT NOT NULL,
value TEXT NOT NULL,
path TEXT NOT NULL,
expires_utc INTEGER NOT NULL,
secure INTEGER NOT NULL,
httponly INTEGER NOT NULL,
last_access_utc INTEGER NOT NULL,
has_expires INTEGER NOT NULL DEFAULT 1,
persistent INTEGER NOT NULL DEFAULT 1,
priority INTEGER NOT NULL DEFAULT 1,
encrypted_value BLOB DEFAULT '',
firstpartyonly INTEGER NOT NULL DEFAULT 0
*/

攻击者能够控制cookie的‘name’和‘path’(这些值都是被加密的),可是,还是很容易创造包含payload的cookie,如果幸运的话,可以泄露出一些cookie。一件有趣的事情是,新的cookie会比老的cookie先写入(从文件偏移的角度来看),原因似乎是,SQLite开辟新的空间后,填充数据的方式是从底部到顶部(至少在某种程度上)。这些将帮助我们,因为我们对泄露已存在的cookie特别感兴趣(并且,css的解析是从顶到底的方式)

不幸的是,多数情况下,特殊字符会截断css的解析,并且似乎不能泄露整改cookie……,我试图用UTF-16来解决这个问题,但是,令我失望的是,cookie不支持空字符
比较明显的解决方法是,设置cookie为一个有效的值(可打印的ascii字符,分号,等号,引号),当cookie被编码之后,会产生我们期望得到的payload。让我们用神奇的POC吧 !!

cookie是被128-aes加密的,加密模式采用CBC,aes的IV是固定的为,IV = 0x20202020202020202020202020202020。在Linux中,key是被硬编码的2并且,对于key的推导,它使用单次迭代的PBKDF2加密方式,并且salt也是固定的(“saltysalt”)。在OSX中,key只会被保存在Keychain中。所以我们就把精力放在Linux中。
事实证明,我们的payload"{}*{–?"其实只有8个字节的长度。这就意味着如果我们用UTF-16编码的话,就会产生16个字节的长度(8 + 8 NULL bytes)[3]。
下图(payload,key和iv都是已知的并且是固定值)
image
我们要找到一个有效的块B,它解码之后满足:

  • P0的字节是有效的字符(每个字节有2位的限制,且要小于32字节的熵)
  • P1的字节也是有效的字符。可是,因为payload的aes输出是固定的,我们可以限制B0的初始空间搜索,使P1 = B0 ^ AES(key,payload) 是一个有效的文本
    我们要找到一个有效的块B2,它解码之后满足:
  • P2 = payload ^ AES(key, B2) 的字节是有效的字符
  • P2的结尾是byte = 0x01 (最简单的有效的填充)

幸亏的是,B0和B2是独立的。在一两分钟之后(在Skylake的i5的电脑上经过至少十亿次aes计算),我们得到一个有效的cookie值,这个cookie被编码之后可以被存储到db中,这个cookie也包含UTF-16格式的payload。

document.cookie="whatever=i+GW*e@afGR]sYo{Wa>7[[[[[[[[[[[[xBLGWAJ|VCX<T*P;"

(你可以通过开发工具为这次实例设置cookie,然后加载本地HTML文件40秒之后)
通过这个cookie,我们已经可以可靠的泄露一部分cookie的数据库,用UTF-16解码,解析被加密的值(‘V10’做前缀),然后使用web的加密API加密它们。在文章后面附上POC文件。
注意,我们现在仅仅使用一个payload设置单个cookie。这就意味着,如果UTF-16 payload解析失败,或者css解析被截断,那么测试就会失败。改善的方法是设置一些不同cookie值(–a, --b, --c),这样可以提高成功率。在后面我依然会附上成功的截图。

建议:

为了避免读取到敏感信息,在配置文件名前加一串随机的字符串(火狐中有类似的方式),这将增加那些攻击者难度。
并且,火狐的css字符串解析应该更加严格,如果发现一个NULL字节,就停止解析,这样可以减少泄露。
似乎最明智(最简单)的方式是在每一个Linux系统中使用独一无二的key。我猜想,本地的攻击者也会有寻找key的动机,但是攻击者只有部分读取权限,这还是能保护一些人。
这可能不是一个很严重问题,但是我觉得很有趣,希望你能喜欢。

[1] https://www.w3.org/TR/css-syntax-3/#{}-block-diagram
[2] https://cs.chromium.org/chromium/src/components/os_crypt/os_crypt_posix.cc?l=44
[3] 它可以从周围块中占有更多字节,因为我们我们不需要用16字节来寻找有效的块

附件在原文的最底部

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值