java.net.URL getHost因#@同时存在时优先级问题导致的SSRF
#
作为锚点,后面的会忽略;@
作为登录信息,前面的会忽略
对于java.net.URL取主机 getHost()函数,当#@同时存在时,my_host#@baidu.com
高版本#
优先,取#
前的,即 my_host;
低版本@
优先,取@
后的,即 baidu.com。
从而导致低版本(具体各小版本号没测试)用getHost时, my_host#@baidu.com 取@
后baidu.com绕过白名单,而请求函数等取#
前my_host,造成漏洞。
源自真实漏洞
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>demo_collection</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>7</maven.compiler.source>
<maven.compiler.target>7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
</dependencies>
</project>
漏洞代码
import org.apache.commons.lang3.StringUtils;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class ssrf_bypass_case {
public static void main(String[] args){
// 正常情况,#作为锚点,后面的会忽略;@ 作为登录信息,前面的会忽略
// 同时遇到 # 和 @ 时,不同处理函数优先级不同
// JAVA 7版本中,java.net.URL gethost @比#优先,取后面a.baidu.com
// (JAVA 8中,取#前面)
boolean check_result = isValidPictureUrl("https://aa.dnslog.cn#@a.baidu.com/");
if(check_result)
System.out.println("pass");
// 接下来HTTP请求,#比@优先,取#前面aa.dnslog.cn
else
System.out.println("not pass");
}
// 图片的协议
private static final String[] PICTURE_PROTOCOL_ARR = new String[]{
"https",
"http"
};
// cdn域名的后缀
private static final Set<String> CDN_DOMAIN_SUFFIX_SET = new HashSet<>(
Arrays.asList("a.baidu.com", "b.baidu.com"));
public static boolean isValidPictureUrl(String urlStr) {
// 判断是否为空
if (StringUtils.isBlank(urlStr)) {
return false;
}
// 判断是否以http、https开头
boolean protocolFlag = false;
for (String pictureProtocol : PICTURE_PROTOCOL_ARR) {
if (urlStr.startsWith(pictureProtocol)) {
protocolFlag = true;
break;
}
}
if (!protocolFlag) {
return false;
}
URL url;
try {
url = new URL(urlStr);
} catch (Exception e) {
return false;
}
String host = url.getHost();
return CDN_DOMAIN_SUFFIX_SET.contains(host);
}
}