漏洞简述
Firefox for Android(68.11.0 及更低版本)会发送定期发送SSDP信息,在其解析LCATION时没有正确验证LOCATION字段的的信息,它错误地将 LOCATION 字段里的任何字符串(包括 intent:// 这种非标准的 URI)直接传递给了 Android 系统的 Intent 处理机制。(打开 Web 浏览器的 URI 方案定义为 http
或 https
。但是,Firefox 移动版中的原生 SSDP 代码会自行处理这些 URL,而不会将它们传递给 Intent 系统。)
环境搭建
低版本的Firefox for Android下载地址:https://archive.mozilla.org/pub/mobile/releases/68.11.0/
测试脚本:https://initblog.com/images/post-firefox/ffssdp.py
测试设备:安装了目标版本的pixel4,kali Linux 虚拟机(wireshark,python3)
环境搭建:电脑和手机连接同一热点,使用wireshark进行抓包
在此次测试中的ip地址:
测试手机:192.168.165.49
电脑物理机:192.168.165.247
电脑虚拟机:192.168.165.40
注意事项:
1.虚拟机使用桥接模式,ip addr查看当前网段是否正确。在物理主机ipconfig和测试手机ifconfig查看当前网络信息,确保网段一致
2.使用wireshark抓包接口,比如现在就要抓包eth0处的流量
1 | ─# ip addr |
3.不要使用测试手机开热点,手机作为NAT网关时,默认隔离客户端间通信(类似AP隔离模式),此时抓包,只能收到热点手机大量的MDNS流量,局域网内的其他设备流量是正常的
漏洞分析
SSDP
简单服务发现协议**( SSDP,Simple Service Discovery Protocol)是一种应用层协议,是构成通用即插即用(UPnP)技术的核心协议之一。用于在本地网络中自动发现设备和服务。它基于HTTPU(HTTP over UDP),通常使用UDP端口1900,多播地址239.255.255.250。M-SEARCH方法是SSDP协议中的一种请求类型,用于设备主动搜索特定类型的服务。
在 SSDP 中,发起方将通过本地网络发送广播,询问有关任何可用服务的详细信息,例如第二屏幕设备或联网扬声器。任何系统都可以回复有关描述其特定服务的 XML 文件位置的详细信息。然后,发起方将自动转到该位置以解析 XML。
它发出的包和响应包如下所示
1 | M-SEARCH * HTTP/1.1 |
SSDP 在 UDP 上使用两种形式的 HTTP:
- HTTPMU:基于多播 UDP 的 HTTP。这意味着请求通过 UDP 广播到整个网络。这用于客户端发现设备和服务器通告设备。
- HTTPU:基于单播 UDP 的 HTTP。这意味着请求通过 UDP 从一个主机直接发送到另一个主机。这用于响应搜索请求。
LOCATION的值是一个URL,这个URL会指向一个xml文件,它遵循 UPnP 设备描述规范。它里面包含了关于设备的元数据和指向其他控制/事件 URL 的信息。
需要注意的是,漏洞源于 Firefox 浏览器对 SSDP 响应中 LOCATION 字段的解析逻辑,而非 SSDP 协议本身或 XML 文件的解析过程。
脚本分析
ffssdp.py来自作者之前编写的工具evil-ssdp
它只能在linux中使用,因为它查找ip的途径是ifconfig
1 | def get_ip(args): |
它不主动攻击,而是等待局域网中出现SSDP信息,然后对其进行回应
核心部分
伪造回应
1 | def send_location(self, address, requested_st): |
总结攻击流程:
- 被动监听: 脚本在指定的网络接口上监听发往 SSDP 多播地址 (239.255.255.250:1900) 的 UDP 包。
- 捕获请求: 当收到一个包含 “M-SEARCH” 和有效 ST: 头部的 UDP 包时,记录下发送方的 IP 和端口。
- 条件判断: 如果不是分析模式,并且 ST 格式基本正确。
- 构造响应: 创建一个虚假的 SSDP 200 OK 响应。
- 注入载荷: 将用户指定的恶意 Intent URI 放入响应的 LOCATION 头部。
- 定向回击: 将这个伪造的、包含恶意 LOCATION 的响应直接发送回发送 M-SEARCH 请求的设备 IP 和端口。
- 漏洞触发 (在目标设备上): 如果接收设备是运行着易受攻击 Firefox 的 Android 设备,Firefox 会错误处理这个响应,将 LOCATION 字段的恶意 URI 交给 Android Intent 系统执行,导致非预期的行为(如打开拨号器)。
构建payload
将-t后面替换成这里构造的语句就可以进行测试了
拨号器
Android 的核心应用程序之一 “拨号器” (Dialer) 在其 AndroidManifest.xml 中注册了一个 Intent Filter。这个 Intent Filter 声明它可以处理 ACTION_DIAL 或 ACTION_VIEW 类型的 Intent,并且其数据 (Data) 的 Scheme 是 tel。当你点击一个 tel://20250418 链接,或者某个应用(像这个漏洞中的 Firefox)将这个 URI 传递给系统时,Android 的 PackageManager 会查找哪个应用注册了能够处理 tel Scheme 的 Intent Filter。
PackageManager 找到了拨号器应用,系统启动拨号器应用,并将 20250418 这个号码传递给它,拨号器就会显示这个号码,等待用户确认拨打。
还有这样可以用于测试的scheme吗?
- 标准化组织: 一些最常见的 Schemes(如 http, https, ftp, mailto)是由互联网工程任务组 (IETF) 通过 RFC 文档标准化的。
- 平台约定: 其他一些 Schemes(如 tel, sms, geo - 地理位置)虽然可能没有严格的全球标准,但在特定平台(如 Android、iOS)上被广泛支持和理解,成为了事实上的标准。操作系统或核心应用程序(如拨号器、短信应用)会注册自己来处理这些 Schemes。
这里有一些例子,除了拨号器和文件,其他的我没有进行测试,仅供参考
sms: 或 smsto:
- 触发应用: 默认的短信/彩信应用。
- 作用: 打开短信编辑界面。
- 示例:
- sms:10086 (打开短信应用,收件人填好 10086)
- sms:10086?body=Hello (打开短信应用,填好收件人和短信内容 “Hello”)
- smsto:10086 (功能类似,sms: 更常用)
mailto:
- 触发应用: 默认的电子邮件客户端 (如 Gmail)。
- 作用: 打开邮件编辑界面。
- 示例:
- mailto:user@example.com (打开邮件应用,填好收件人)
- mailto:user@example.com?subject=Inquiry&body=Hi%20there (填好收件人、主题和正文,注意正文需要 URL 编码)
- mailto:?to=user1@example.com&cc=user2@example.com (指定收件人和抄送)
http: 或 https:
- 触发应用: 默认的网络浏览器 (如 Chrome, Firefox)。
- 作用: 在浏览器中打开指定的网页。
- 示例:
- http://www.google.com
geo:
- 触发应用: 默认的地图应用 (如 Google Maps)。
- 作用: 显示地理位置或进行地点搜索。
- 示例:
- geo:39.9042,116.4074 (显示北京的经纬度位置)
- geo:0,0?q=Eiffel+Tower (搜索埃菲尔铁塔的位置)
- geo:0,0?q=39.9042,116.4074(Beijing) (显示经纬度位置并添加标签 “Beijing”)
market:
- 触发应用: Google Play Store 应用商店。
- 作用: 打开 Play Store 到指定的应用详情页、开发者页面或搜索结果。
- 示例:
- market://details?id=com.google.android.apps.maps (打开 Google Maps 的应用详情页)
- market://search?q=browser (在 Play Store 中搜索 “browser”)
- market://developer?id=Google+LLC (打开 Google LLC 的开发者页面)
file:
- 触发应用: 文件管理器应用(如果安装并注册了处理此 Scheme)。
- 作用: 尝试打开本地文件。
- 重要说明: 出于安全原因,Android 对 file:// URI 的访问有严格限制。应用通常不能随意访问其他应用的私有文件或任意的本地文件路径。直接从浏览器或不可信来源触发 file:// URI 通常会被阻止或功能受限。应用内部访问文件通常使用 content:// URI (通过 ContentProvider) 或 Storage Access Framework。
- 示例 (可能受限): file:///sdcard/Download/document.pdf
Android intent URI
具体来源:cs.android.com
查找frameworks/base/core/java/android/content/Intent.java 文件,这里面有具体的原因,更详细
在这里给出已经总结好的结构
1 | intent://host/path#Intent; |
解释
- intent:Scheme (协议头):这是整个 URI 的协议标识符。它告诉 Android 系统,这个 URI 不是一个普通的网页链接、文件路径或电话号码,而是一个需要被解析成 Android Intent 对象的特殊指令。
- #Intent;…;end:- Fragment (片段标识符): 这是 Intent URI 的核心部分,使用 # 号开始。它包含了一系列**键值对 (key=value)**,用分号 (;) 分隔,用来描述 Intent 对象的各种属性(Action, Category, Package, Component, Extras 等)。Intent代表开始,end代表结束
- 键值对属性 (在 #Intent; 和 ;end 之间):
- package=
(可选):指定唯一 能够处理此 Intent 的应用程序包名。 - scheme=
(可选):指定 Intent 的 data URI 部分应该使用的协议方案 (Scheme)。注意: 这与整个 URI 的 intent: scheme 不同。 - 示例: scheme=http, scheme=tel, scheme=geo。如果你在前面提供了 //host/path,这个 scheme 会被用来构成完整的 Data URI,例如 scheme=http + //example.com/ -> Data 是 http://example.com/。
- -S. 前缀:传递附加数据(键值对)。
- package=
漏洞验证
检测ActivityManage日志
它负责管理应用程序的生命周期(启动、暂停、停止)、管理 Activity 栈(用户看到的界面切换)、启动服务,以及最关键的——处理和分发 Intent
安卓默认的URL
拨号器
1 | └─# python3 ./ffssdp.py eth0 -t "tel://20250419" |
系统响应并启动了拨号器
1 | flame:/ # logcat | grep ActivityManager |
这是其中一组相关流量信息
1 | M-SEARCH * HTTP/1.1 |
文件
1 | └─# python3 ./ffssdp.py eth0 -t "file://storage/emulated/0/Download/access.log" |
firefox无事发生
Android系统上使用file://协议的限制。Android的WebView和浏览器出于安全考虑,通常不允许通过file://协议访问本地文件
所以无法访问
各浏览器策略对比
浏览器 | file:// 支持情况 |
安全策略 |
---|---|---|
Firefox | 默认禁用,需 about:config 调整 |
设置 security.fileuri.strict_origin_policy 为 false 可部分启用 |
Chrome | 完全禁止 | 控制台显示 Not allowed to load local resource |
Samsung Internet | 允许访问 /sdcard/ 子目录 |
需手动授权文件夹访问权限 |
Via Browser | 支持自定义本地文件访问 | 内置文件管理器导航 |
构建应用关联的Android Intent URL
首先是和firefox相关联的url
1 | └─# python3 ./ffssdp.py eth0 -t "intent://example.com/#Intent;scheme=http;package=org.mozilla.firefox;end" |
流量信息
1 | M-SEARCH * HTTP/1.1 |
结果
然后是尝试触发chrome
1 | └─# python3 ./ffssdp.py eth0 -t "intent://example.com/#Intent;scheme=http;package=com.android.chrome;end" |
流量信息
1 | HTTP/1.1 200 OK |
结果
当然了firefox必须是在启动状态的,不然无法触发
检测http
然后检测http头的信息
”打开 Web 浏览器的 URI 方案定义为 http
或 https
。但是,Firefox 移动版中的原生 SSDP 代码会自行处理这些 URL,而不会将它们传递给 Intent 系统。“
现在就是检测,当出现http://ip.xml时是不是由firefox进行处理
这里进行了两次测试
直接访问example.com
运行
1 | └─# python3 ./ffssdp.py eth0 -t "http://example.com" |
信息成功传递
1 | M-SEARCH * HTTP/1.1 |
但是firefox无事发生
访问http://example.com/device.xml
指令
1 | └─# python3 ./ffssdp.py eth0 -t "http://example.com/device.xml" |
pcap
1 | M-SEARCH * HTTP/1.1 |
firefox无事发生
部署http服务设备
这一步是为了检测,是不是由firefox启动的intent。自己部署服务器就能更好的看到日志信息。
1.在termux安装python
1 | pkg install python |
2.创建updp文件
1 | mkdir ./upnp-server && cd ./upnp-server |
device.xml
1 | <root xmlns="urn:schemas-upnp-org:device-1-0"> |
3.启动服务
1 | python -m http.server 8080 --bind 0.0.0.0 |
4.在虚拟机启动脚本
1 | └─# python3 ./ffssdp.py eth0 -t "http://192.168.165.49:8080/device.xml" |
这是部分流量文件
1 | M-SEARCH * HTTP/1.1 |
文件被访问了
此外在调试中发现,开启reqable尝试对firefox进行抓包时,无反应,关掉reqable时才开始正常访问xml文件
启用 Firefox 网络监控
在 Firefox 地址栏输入:
1 | about:networking |
可以看到的确是访问了目标xml文件
为了追踪其来源,编写了server.py文件
1 | from http.server import SimpleHTTPRequestHandler |
这是部分日志
1 | 2025-04-19 16:18:18,891 - UA:Mozilla/5.0 (Android 10; Mobile; rv:68.0) Gecko/68.0 Firefox/68.0 | IP:192.168.165.49 | PATH:/device.xml |
在这份日志里只有firefox的记录,也就是说,http请求的确是firefox在处理
这说明该漏洞不是简单的将LOCATION当成Intent 执行,对于http://和https://这样的标准的字段它有自己的处理模式,而对于非常规范围内的tel:// 或 intent:// 的处理路径它将其转发给了安卓处理
参考资料
https://initblog.com/2020/firefox-android/
另一个视角的博客:https://blog.mozilla.org/attack-and-defense/2020/11/10/firefox-for-android-lan-based-intent-triggering/?utm_source=chatgpt.com
firefox官网,在这里查找漏洞信息:https://www.mozilla.org/en-US/security/advisories/
https://developer.android.com/reference/android/app/ActivityManager
https://developer.android.com/guide/components/intents-filters?hl=zh-cn
Android 开源项目 (AOSP) 源代码 :cs.android.com