phithon_xg가 재미있는 트릭을 트윗에 공개했는데, 실제로 분석애서 유용하게 쓰일 수 있어 간단하게 정리해서 글로 공유드려봅니다.
Bypass protocol check in Java
아래 url:
접두사가 있는 URL들은 Java URL에서 각각 http://
, file://
과 동일하게 동작합니다.
url:http://127.0.0.1:8080
url:file:///etc/passwd
그래서 만약에 deny-list 기반으로 프로토콜을 검증하고 있는 경우, 이러한 url 접두사를 통해 우회할 수 있는 포인트가 됩니다.
// check() 함수가 file://로 시작하는 url을 차단하는 함수라고 가정하고,
// 만약 inputURL에 url:file:///sdcard/blahblah... 가 들어왔다면 ..
String checkedURL = check(inputURL);
// file://로 시작하지 않기 때문에 통과되고 URL Class로 넘어가서 생성자에 의해 파싱되면
URL url = new URL(checkedURL);
// checkedURL에는 file:///sdcard/blahblah... 가 남게됩니다.
Example
실제로 되는지 테스트를 위해 Java 코드를 작성해봅시다. 단순하게 URL:
prefix가 있는 URL을 읽어 출력하는 코드입니다. 만약 Exception이 걸리면 StackTrace가 찍히겠죠.
package main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
public class main {
public static void main(String[] args) {
try {
URL url1 = new URL("url:https://www.hahwul.com");
System.out.println("URL1: " + url1.toString());
} catch (MalformedURLException mue) {
mue.printStackTrace(System.err);
}
}
}
컴파일 후 실행해보면 prefix는 처리되지 않고 URL만 남는 것을 확인할 수 있습니다 👍
javac main/main.java
java main.main
URL1: https://www.hahwul.com
URL: Prefix
RFC3986에는 이런 부분이 있습니다.
The prefix “URL:” (with or without a trailing space) was formerly recommended as a way to help distinguish a URI from other bracketed designators, though it is not commonly used in practice and is no longer recommended.
URL:
prefix는 일반적으로 사용되진 않지만 URI를 괄호로 묶은 다른 URI와 구별하는 데 도움이 될 수 있다는 이야기인데요. 이 prefix에 대해 좀 더 알아보기 위해 RFC1738을 보면, 이렇게 이야기되고 있습니다.
prefix “URL:”, be used to delimit the boundaries of the URL. This wrapper does not form part of the URL and should not be used in contexts in which delimiters are already specified.
즉 URL의 경계를 구분하는데 사용되는 prefix고 URL에 포함되는 wrapper는 아니라고 합니다. 즉 유효한 URL은 아니라는 거죠.
Restrictions
curl로 URL:
prefix를 포함한 요청을 발생시켜보면 아래와 같이 URL 포맷 에러가 나타납니다.
curl -i -k url:https://www.hahwul.com
curl: (3) URL using bad/illegal format or missing URL
이는 curl의 url library 또는 코드는 이 prefix를 처리하지 않고 그대로 URL이라고 판단했기 때문인데요. 위에 문제가 됬던 Java URL의 경우 prefix가 포함된 URL을 받으면 prefix를 제거하고 체크하기 때문에 단순히 사용자로부터 들어온 URL의 프로토콜을 Deny-list로 검사하는 경우 이를 우회할 수 있게 된겁니다. 결국 사용하는 언어나 라이브러리, 프레임워크에서 URL을 어떻게 처리하는가가 중요한 제약이 되곘네요.
Attack vector
우선은 확인된건 Java의 URL Class라 Java기반 웹이랑 Android 앱 중 URL을 사용하는 부분은 전반적으로 영향받을 것 같습니다. 아무래도 꼼꼼한 체크가 필요할 것 같구요. 관련있는 공격 기법들은 아래 정도일 것 같네요.
- SSRF
- Open Redirect
- Bypass Protection
- Webview Hijacking
- Deep Link, Etc…..
참고로 Android의 경우 Golden technic이라고 해서 URL에 대한 여러가지 우회 방법들이 있습니다. 혹시 들어보신 적이 없다면 “Bypass host validation Technique in Android(Common+Golden+Mythink)” 글을 봐주셔도 될 것 같습니다.
Mitigation
모든 제약은 allow-list 기반의 제약이 훨씬 강합니다. 다만 상황에 따라서 deny-list를 사용해야할 수 밖에 없는 경우가 있는데요. 일단 Java application에서 protocol 체크를 하고 있다면 prefix 또한 체크하여 검증하는 것이 좋을 것 같습니다.
References
- https://twitter.com/phithon_xg/status/1498153253350961152
- https://datatracker.ietf.org/doc/html/rfc3986#appendix-C
- https://datatracker.ietf.org/doc/html/rfc1738#section-7