URIBuilder与SSRF

在使用一个静态扫描工具时,报了一个SSRF的问题,经过数据流的分析,导致此工具报SSRF的原因是在调用URIBuilder的setPath函数时,参数是从请求里获取的,导致了数据流被污染,因此认为由URIBuilder构造的URL也被污染,最终导致URIBuilder构造出来的URI被污染,所以,认为可能会导致SSRF问题。

为了弄清楚到底是否会导致SSRF问题,针对URIBuilder进行了以下探索。

URIBuilder有多个实现,首先看看apache的【org.apache.http.client.utils.URIBuilder】实现如下:

public URIBuilder setPath(String path) {
        return this.setPathSegments(path != null ? URLEncodedUtils.splitPathSegments(path) : null);
    }

    public URIBuilder setPathSegments(String... pathSegments) {
        this.pathSegments = pathSegments.length > 0 ? Arrays.asList(pathSegments) : null;
        this.encodedSchemeSpecificPart = null;
        this.encodedPath = null;
        return this;
    }

path变量经过segments处理之后直接就赋值给了pathSegments,没有进行任何处理。从代码的角度可以看出,这里的path如果含有../../之类的字符,URIBuilder应该没有处理。于是写了以下代码进行测试:

public static void testPath(){
        URIBuilder uriBuilder = new URIBuilder();
        uriBuilder.setPath("../../example/path");
        URI uri = null;
        try {
            uri = uriBuilder.build();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        System.out.println("test path result:"+uri.toString());
    }

打印的结果如下:

可以看出这里的路径如预料的一样没有被处理。

由于URIBuilder还有setParameter和setParameters方法,于是为了测试设置参数时,是否进行了处理,进一步测试代码如下:

public static void testPatameter(){
        URIBuilder uriBuilder = new URIBuilder();
        uriBuilder.setScheme("https");
        uriBuilder.setHost("www.test.com");
        uriBuilder.setPath("../../example/path");
        uriBuilder.setParameter("para1", "val1&para2=val2");
        uriBuilder.setParameter("para2", "val2");
        URI uri = null;
        try {
            uri = uriBuilder.build();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        System.out.println("test parameter result:"+uri.toString());
    }

    public static void testPatameterList(){
        URIBuilder uriBuilder = new URIBuilder();
        uriBuilder.setScheme("https");
        uriBuilder.setHost("www.test.com");
        uriBuilder.setPath("../../example/path");

        List<NameValuePair> paraList = new ArrayList<NameValuePair>();
        paraList.add(new BasicNameValuePair("para3", "val3&para4=val4"));
        paraList.add(new BasicNameValuePair("para4", "val4"));
        uriBuilder.setParameters(paraList);
        URI uri = null;
        try {
            uri = uriBuilder.build();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        System.out.println("test parameter list result:"+uri.toString());
    }

运行结果如下图:

查看代码setParameter时,发现在设置时也没有编码,于是进一步探测在构建URI时的代码如下:

    private String buildString() {
        StringBuilder sb = new StringBuilder();
        if (this.scheme != null) {
            sb.append(this.scheme).append(':');
        }

        if (this.encodedSchemeSpecificPart != null) {
            sb.append(this.encodedSchemeSpecificPart);
        } else {
            if (this.encodedAuthority != null) {
                sb.append("//").append(this.encodedAuthority);
            } else if (this.host != null) {
                sb.append("//");
                if (this.encodedUserInfo != null) {
                    sb.append(this.encodedUserInfo).append("@");
                } else if (this.userInfo != null) {
                    sb.append(this.encodeUserInfo(this.userInfo)).append("@");
                }

                if (InetAddressUtils.isIPv6Address(this.host)) {
                    sb.append("[").append(this.host).append("]");
                } else {
                    sb.append(this.host);
                }

                if (this.port >= 0) {
                    sb.append(":").append(this.port);
                }
            }

            if (this.encodedPath != null) {
                sb.append(normalizePath(this.encodedPath, sb.length() == 0));
            } else if (this.pathSegments != null) {
                sb.append(this.encodePath(this.pathSegments));
            }

            if (this.encodedQuery != null) {
                sb.append("?").append(this.encodedQuery);
            } else if (this.queryParams != null && !this.queryParams.isEmpty()) {
                sb.append("?").append(this.encodeUrlForm(this.queryParams));
            } else if (this.query != null) {
                sb.append("?").append(this.encodeUric(this.query));
            }
        }

        if (this.encodedFragment != null) {
            sb.append("#").append(this.encodedFragment);
        } else if (this.fragment != null) {
            sb.append("#").append(this.encodeUric(this.fragment));
        }

        return sb.toString();
    }

在buildString里,将参数进行了URIEncode,所以,转成的URL字符串里参数的值是URL编码的。

在组装Path时,虽然调用了normalizePath,但是,根据normalizePath的实现代码如下:

    private static String normalizePath(String path, boolean relative) {
        String s = path;
        if (TextUtils.isBlank(path)) {
            return "";
        } else {
            if (!relative && !path.startsWith("/")) {
                s = "/" + path;
            }

            return s;
        }
    }

可以了解normalizePath只是在不是空的情况下在path前面添加一个斜线,其他并没有对输入的path进行任何处理。

通过以上实现可以看出,针对URL中的Path的处理是不安全的,但是对Parameter的处理是安全的。所以,在使用path之前,需要对path进行有效性验证,否则可能组装出以上例子里给出的路径。

接着,又测试了一下Spring framework的org.springframework.web.util.UriComponentsBuilder,测试代码如下:

public static void testPath(){
        UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
        builder.scheme("https")
                .host("www.example.com")
                .port(443)
                .path("../../example/path")
                .queryParam("page", 1)
                .queryParam("size", 10);

        System.out.println("Spring test path result:"+builder.toUriString());
    }

    public static void testPatameter(){
        UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
        builder.scheme("https")
                .host("www.example.com")
                .port(443)
                .path("../../example/path")
                .queryParam("para1", "val1&para2=val2")
                .queryParam("para2", "val2");

        System.out.println("Spring test parameter result:"+builder.toUriString());
    }

测试结果如下:

看来可能各个库对路径和参数的实现还是比较一致的,设置参数时,库会对参数进行编码处理,不会导致HPP问题【HPP的介绍可以参考:应用安全系列之九:HTTP参数污染_java http参数污染怎么解决-CSDN博客】。

但是关于路径,就需要使用者在调用URIBuilder之前对path的值进行验证之后才能使用。

不过,根据SSRF在OWASP TOP10网站的定义如下:

SSRF flaws occur whenever a web application is fetching a remote resource without validating the user-supplied URL. It allows an attacker to coerce the application to send a crafted request to an unexpected destination, even when protected by a firewall, VPN, or another type of network access control list (ACL).

可见,这里定义只是把SSRF定义为访问远程的资源。如果设置host时,输入的参数没有进行白名单范围验证,毫无争议地就是SSRF问题。但是,由于路径遍历导致访问此服务器的资源也已经偏离了这个URL原始的初衷,也可以认为它时一种影响范围比较小的SSRF。无论如何都需要在使用输入的参数作为URL的path的一部分之前,要进行输入验证,避免路径跳跃的问题出现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值