miller
发布于

Java 代码审计 - SSRF 漏洞

本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com

Java 代码审计 - SSRF 漏洞

哆啦安全 哆啦安全

微信号 gh_060e91811143

功能介绍 移动安全 (Android/iOS / 鸿蒙)、车联网安全、Web 安全、终端安全、隐私合规、数据安全、防作弊、溯源取证、软件安全开发等的技术研究、分享,安全服务、安全培训,创新安全产品的研发,为政企等用户提供安全解决方案 (欢迎商务合作)!

2023-04-26 06:58 发表于四川

收录于合集

[ 亿人安全 .

0x00 前言

SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。比如从指定 URL 地址获取网页文本内容,加载指定地址的图片,下载等等。这里主要介绍 java 中URLConnection()openStream()两个方法产生 SSRF 的原理和修复方法

0x01 URLConnection

    @RequestMapping(value = "/urlConnection/vuln", method = {RequestMethod.POST, RequestMethod.GET})
    public String URLConnectionVuln(String url) {
        return HttpUtils.URLConnection(url);
    }

这里调用的是HttpUtils.URLConnection(url)

    public static String URLConnection(String url) {
        try {
            URL u = new URL(url);
            URLConnection urlConnection = u.openConnection();
            BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); //send request
            // BufferedReader in = new BufferedReader(new InputStreamReader(u.openConnection().getInputStream()));
            String inputLine;
            StringBuilder html = new StringBuilder();

            while ((inputLine = in.readLine()) != null) {
                html.append(inputLine);
            }
            in.close();
            return html.toString();
        } catch (Exception e) {
            logger.error(e.getMessage());
            return e.getMessage();
        }
    }

跟进URLConnection方法,而URLConnection里又调用了URL.openConnection()来发起请求, 这个请求可以直接执行 url 协议(伪协议)
漏洞利用:
使用 file 协议读文件

使用 http 协议访问百度

修复方法:
这里先是对 url 调用了SecurityUtil.isHttp()来进行检查

    @GetMapping("/urlConnection/sec")
    public String URLConnectionSec(String url) {

        // Decline not http/https protocol
        if (!SecurityUtil.isHttp(url)) {
            return "[-] SSRF check failed";
        }

        try {
            SecurityUtil.startSSRFHook();
            return HttpUtils.URLConnection(url);
        } catch (SSRFException | IOException e) {
            return e.getMessage();
        } finally {
            SecurityUtil.stopSSRFHook();
        }

    }

SecurityUtil.isHttp() 比较简单,就是判断 url 是否是以 或 开头

    public static boolean isHttp(String url) {
        return url.startsWith("http://") || url.startsWith("https://");
    }

单纯的 ban 掉其他协议显然是不够的,还不能够防止对内网进行探测,于是在获取 url 内容之前,开启了一个 hook 来对用户行为进行监听,SecurityUtil.startSSRFHook(),就有效防止了 ssrf 攻击

0x02 openStream

openStream()方法的实现也是调用了openConnection生成一个URLConnection 对象,然后再通过这个对象调用的getInputStream()方法的

    @GetMapping("/openStream")
    public void openStream(@RequestParam String url, HttpServletResponse response) throws IOException {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            String downLoadImgFileName = WebUtils.getNameWithoutExtension(url) + "." + WebUtils.getFileExtension(url);
            // download
            response.setHeader("content-disposition", "attachment;fileName=" + downLoadImgFileName);

            URL u = new URL(url);
            int length;
            byte[] bytes = new byte[1024];
            inputStream = u.openStream(); // send request
            outputStream = response.getOutputStream();
            while ((length = inputStream.read(bytes)) > 0) {
                outputStream.write(bytes, 0, length);
            }

        } catch (Exception e) {
            logger.error(e.toString());
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
        }
    }

通过WebUtils.getNameWithoutExtension(url) + "." + WebUtils.getFileExtension(url)来获取下载文件名, 然后执行inputStream = u.openStream(); 来看一下 openStream(),也是调用了openConnection(),也会根据传入的协议的不同来进行处理

    public final InputStream openStream() throws java.io.IOException {
        return openConnection().getInputStream();
    }

由此可以得知,openStream()方法同样也可以进行 ssrf 来探测内网以及文件下载,修复方案同上

浏览 (416)
点赞
收藏
评论