首页 > 编程语言 >【Java代码审计】SSRF篇

【Java代码审计】SSRF篇

时间:2024-04-02 16:31:57浏览次数:18  
标签:审计 return String SSRF url URL new Java

【Java代码审计】SSRF篇

1.SSRF漏洞

SSRF 是 Server-Side Request Forge 的英文首字母缩写,中文意思是服务器端请求伪造。Web 应用程序往往会提供一些能够从远程获取图片或是文件的接口,在这些接口上用户使用指定的 URL 便能完成远程获取图片、下载文件等操作。攻击者可以通过使用 file 协议来读取服务器本地/etc/passwd 和/proc/self/cmdline 等敏感文件,同时攻击者也可以利用被攻击的服务器绕过防火墙直接对处于内网的机器发起进一步的攻击

SSRF漏洞主要有以下几个危害:

  • 获取内网主机、端口和banner信息
  • 对内网的应用程序进行攻击,例如Redis、jboss等
  • 利用file协议读取文件
  • 可以攻击内网程序造成溢出

在这里插入图片描述

在Java中SSRF仅支持sun.net.www.protocol下所有的协议:http、https、file、ftp、mailto、jar及netdoc协议

在这里插入图片描述

正是由于上述协议的限制,以及传入的URL协议必须和重定向后的URL协议一致的原因,使得Java中的SSRF并不能像PHP中一样使用gopher协议来拓展攻击面

在 Java 中可以通过利用 file 协议或 netdoc 协议进行列目录操作,以读取到更多的敏感信息,对于无回显的文件读取可以利用 FTP 协议进行带外攻击,但值得注意的是:部分版本的 Java,即使使用 FTP 协议也无法读取多行文件

在这里插入图片描述


2.Java-SSRF漏洞常见接口

SSRF漏洞通常出现在社交分享、远程图片加载或下载、图片或文章收藏、转码、通过网址在线翻译、网站采集、从远程服务器请求资源等功能点处

SSRF 漏洞 URL 中常出现 url、f、file、page 等参数。SSRF 会使用 HTTP 请求远程地址,因此代码审计时我们要特别留意能够发起 HTTP 请求的类及函数,如

HttpURLConnection.getInputStream
URLConnection.getInputStream
HttpClient.execute
OkHttpClient.newCall.execute
Request.Get.execute
Request.Post.execute
URL.openStream
ImageIO.read

值得注意的是,虽然上面提到的方法都可以发起HTTP请求,导致SSRF漏洞;但若是想支持sun.net.www.protocol中的所有协议,则只能使用以下方法:

URLConnection
URL

若发起网络请求的是带 HTTP 的,那么其将只支持 HTTP、HTTPS 协议

HttpURLConnection
HttpClient
OkHttpClient.newCall.execute

1、urlConnection

urlConnection是一个抽象类,表示指向URL指定资源的活动链接,它有两个直接子类,分别是HttpURLConnection和JarURLConnection。在默认情况下,urlConnection的参数没有有效控制时会引起SSRF漏洞

try {
    // 从HTTP请求参数中获取名为"url"的URL字符串
    String url = request.getParameter("url"); 
    // 使用提取到的URL字符串创建一个URL对象
    URL u = new URL(url);
    // 打开与该URL的连接
    URLConnection urlConnection = u.openConnection();
    // 通过连接获取输入流,并使用BufferedReader逐行读取HTML内容
    BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
    
    // 读取HTML内容并保存到StringBuffer中
    String inputLine;
    StringBuffer html = new StringBuffer(); 
    while ((inputLine = in.readLine()) != null) {
        html.append(inputLine);
    }
    in.close();
    
    // 将HTML内容作为字符串返回
    return html.toString();
} catch(Exception e) {
    // 如果发生异常,打印异常堆栈跟踪
    e.printStackTrace(); 
    // 返回"fail"字符串
    return "fail";
}

2、HttpURLConnection

HttpURLConnection是Java的标准类,它继承自URLConnection,可用于向指定网站发送GET请求与POST请求。同样的,在没有过滤的默认情况下其会产生SSRF漏洞,但是与urlConnection不同的是,它只能利用HTTP、HTTPS协议进行攻击

try {
    // 从HTTP请求参数中获取名为"url"的URL字符串
    String url = request.getParameter("url"); 
    // 使用提取到的URL字符串创建一个URL对象
    URL u = new URL(url);
    // 打开与该URL的连接
    URLConnection urlConnection = u.openConnection(); 
    // 将URLConnection转换为HttpURLConnection以支持HTTP特定功能
    HttpURLConnection httpUrl = (HttpURLConnection)urlConnection;
    // 通过连接获取输入流,并使用BufferedReader逐行读取HTML内容
    BufferedReader in = new BufferedReader(new InputStreamReader(httpUrl.getInputStream())); 
    
    // 读取HTML内容并保存到StringBuffer中
    String inputLine;
    StringBuffer html = new StringBuffer();
    while ((inputLine = in.readLine()) != null) { 
        html.append(inputLine); 
    }
    in.close();
    
    // 将HTML内容作为字符串返回
    return html.toString();
} catch(Exception e) {
    // 如果发生异常,打印异常堆栈跟踪
    e.printStackTrace(); 
    // 返回"error"字符串
    return "error";
}

3、Request

Request与Python中的request对象类似,其主要用来发送HTTP请求。在没有过滤的默认情况下会产生SSRF漏洞

try {
    // 从HTTP请求参数中获取名为"url"的URL字符串
    String url = request.getParameter("url");
    // 使用提取到的URL字符串创建一个HttpGet请求对象
    // 使用Apache HttpClient库的Request.Get方法
    // 通过execute()方法发送请求并获取响应内容
    // 使用returnContent()方法获取响应内容的字符串表示形式
    return Request.Get(url).execute().returnContent().toString();
} catch(Exception e) {
    // 如果发生异常,打印异常堆栈跟踪
    e.printStackTrace();
    // 返回"fail"字符串
    return "fail";
}

4、openStream

通过URL对象的openStream()方法,能够得到指定资源的输入流。这时如果URL对象可控,则会产生SSRF漏洞

try {
    // 根据URL获取下载的图片文件名,不含扩展名
    String downLoadImgFileName = Files.getNameWithoutExtension(url) + "." + Files.getFileExtension(url);
    // 设置响应头,指示浏览器以附件形式下载文件
    response.setHeader("content-disposition", "attachment;fileName=" + downLoadImgFileName);
    // 使用URL打开连接
    URL u = new URL(url);
    int length;
    byte[] bytes = new byte[1024];
    // 打开URL连接的输入流
    inputStream = u.openStream();
    // 获取响应输出流,用于向浏览器发送文件内容
    outputStream = response.getOutputStream();
    // 读取输入流中的数据,并将其写入响应输出流
    while ((length = inputStream.read(bytes)) > 0) {
        outputStream.write(bytes, 0, length);
    }
} catch (Exception e) {
    // 如果发生异常,打印异常堆栈跟踪
    e.printStackTrace();
} finally {
    // 在finally中关闭输入流和输出流
    if (inputStream != null) {
        inputStream.close();
    }
    if (outputStream != null) {
        outputStream.close();
    }
}

5、HttpClient

HttpClient是Apache Jakarta Common下的一个子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。但是在默认情况下,其也会产生SSRF漏洞

public class SSRFExample {
    public static void main(String[] args) {
        try {
            String userInputUrl = "http://example.com"; // 用户可控的URL输入
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpGet httpGet = new HttpGet(userInputUrl);
            String response = httpClient.execute(httpGet, httpResponse -> {
                int status = httpResponse.getStatusLine().getStatusCode();
                if (status >= 200 && status < 300) {
                    return EntityUtils.toString(httpResponse.getEntity());
                } else {
                    throw new Exception("Unexpected response status: " + status);
                }
            });

            System.out.println(response);
            httpClient.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.SSRF漏洞演示

URLConnection

漏洞代码:

public static String URLConnection(String url) {
    try {
        URL u = new URL(url);
        URLConnection conn = u.openConnection();
        // 通过getInputStream() 读取 URL 所引用的资源数据
        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));

        String content;
        StringBuffer html = new StringBuffer();

        while ((content = reader.readLine()) != null) {
            html.append(content);
        }
        reader.close();
        return html.toString();

    } catch (Exception e) {
        return e.getMessage();
    }
}

payload,使用file协议读取系统文件:

http://127.0.0.1:8888/SSRF/URLConnection/vul?url=file:///etc/passwd

读取成功:

在这里插入图片描述

URLConnection绕过

SSRF修复经常碰到的问题,虽然过滤了内网地址,但通过短链接跳转、IP进制的方式可以绕过:

public String URLConnection2(String url) {
    if (!Security.isHttp(url)) {
        return "不允许非http协议!!!";
    } else if (Security.isIntranet(Security.urltoIp(url))) {
        return "不允许访问内网!!!";
    } else {
        return HttpClientUtils.URLConnection(url);
    }
}

绕过:

短链接绕过:
http://127.0.0.1:8888/SSRF/URLConnection/vul2?url=http://xxx-8.cn/0
ip进制绕过:
http://127.0.0.1:8888/SSRF/URLConnection/vul2?url=http://12345678

4.SSRF修复

对于 SSRF 漏洞的修复比较简单,总结下来主要包括以下几点:

  • 正确处理 302 跳转(在业务角度看,不能直接禁止 302,而是对跳转的地址重新进行检查)
  • 限制协议只能为 HTTP/HTTPS,防止跨协议
  • 设置内网 IP 黑名单(正确判定内网 IP、正确获取 host)
  • 在内网防火墙上设置常见的 Web 端口白名单(防止端口扫描,则可能业务受限比较大)

白名单方式

public String URLConnection3(String url) {
    if (!Security.isHttp(url)) {
        return "不允许非http/https协议!!!";
    } else if (!Security.isWhite(url)) {
        return "非可信域名!";
    } else {
        return HttpClientUtils.URLConnection(url);
    }
}

此时,尝试访问内网IP,被拦截:

在这里插入图片描述

过滤方式

public String HTTPURLConnection(String url) {
    // 校验 url 是否以 http 或 https 开头
    if (!Security.isHttp(url)) {
        log.error("[HTTPURLConnection] 非法的 url 协议:" + url);
        return "不允许非http/https协议!!!";
    }

    // 解析 url 为 IP 地址
    String ip = Security.urltoIp(url);
    log.info("[HTTPURLConnection] SSRF解析IP:" + ip);

    // 校验 IP 是否为内网地址
    if (Security.isIntranet(ip)) {
        log.error("[HTTPURLConnection] 不允许访问内网:" + ip);
        return "不允许访问内网!!!";
    }

    // 访问 url
    try {
        return HttpClientUtils.HTTPURLConnection(url);
    } catch (Exception e) {
        log.error("[HTTPURLConnection] 访问失败:" + e.getMessage());
        return "访问失败,请稍后再试!!!";
    }
}

此时,尝试访问内网IP,被拦截:

在这里插入图片描述

通用预防SSRF方法

private static int connectTime = 5 * 1000; // 连接超时时间,单位为毫秒

/**
 * 检查URL是否存在SSRF漏洞
 * @param url 要检查的URL
 * @return 返回是否存在SSRF漏洞,存在返回true,否则返回false
 */
public static boolean checkSsrf(String url) {
    HttpURLConnection httpURLConnection;
    String finalUrl = url;
    try {
        do {
            // 只允许 http/https 协议
            if (!Pattern.matches("^https?://.*/.*$", finalUrl)) {
                return false;
            }
            // 判断是否为内网 IP
            if (isInnerIp(url)) {
                return false;
            }
            // 打开URL连接
            httpURLConnection = (HttpURLConnection) new URL(finalUrl).openConnection();
            // 不跟随跳转
            httpURLConnection.setInstanceFollowRedirects(false);
            // 不使用缓存
            httpURLConnection.setUseCaches(false);
            // 设置超时时间
            httpURLConnection.setConnectTimeout(connectTime);
            // 发送DNS请求
            httpURLConnection.connect();
            
            int statusCode = httpURLConnection.getResponseCode();
            // 如果是重定向状态码,则获取重定向的URL
            if (statusCode >= 300 && statusCode <= 307 && statusCode != 304 && statusCode != 306) {
                String redirectedUrl = httpURLConnection.getHeaderField("Location");
                if (redirectedUrl == null) break;
                // 获取到跳转之后的 URL,再次进行检查
                finalUrl = redirectedUrl;
            } else {
                break;
            }
        } while (httpURLConnection.getResponseCode() != HttpURLConnection.HTTP_OK); // 如果没有返回200,则继续对跳转后的链接进行检查
        httpURLConnection.disconnect();
    } catch (Exception e) {
        // 发生异常,返回true表示存在SSRF漏洞
        return true;
    }
    // 没有发生异常,返回true表示存在SSRF漏洞
    return true;
}

/**
 * 判断IP地址是否为内网IP
 * @param url 要判断的URL
 * @return 返回是否为内网IP,是则返回true,否则返回false
 */
private static boolean isInnerIp(String url) throws URISyntaxException, UnknownHostException {
    URI uri = new URI(url);
    String host = uri.getHost(); // URL转换为host
    // 发送DNS请求,host转IP,各种进制也会转换为常见的x.x.x.x格式
    InetAddress inetAddress = InetAddress.getByName(host);
    String ip = inetAddress.getHostAddress();

    // 内网IP段
    String blackSubnetlist[] = {"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8"};
    for (String subnet : blackSubnetlist) {
        SubnetUtils subnetUtils = new SubnetUtils(subnet); // 使用commons-net 3.6
        if (subnetUtils.getInfo().isInRange(ip)) {
            return true; // 如果IP在内网段中,则直接返回true
        }
    }
    // 如果IP不在内网段中,则返回false
    return false;
}

标签:审计,return,String,SSRF,url,URL,new,Java
From: https://blog.csdn.net/Gherbirthday0916/article/details/135553486

相关文章

  • 【javaWeb & 功能介绍第一篇】阿里云OSS文件上传
    文件上传文件上传存储文件本地存储云服务阿里云文件上传文件上传是将本地的图片,视频,音频等文件上传到服务器,供其他用户浏览或下载的过程文件上传在项目中应用十分广泛,我们经常发微博,发微信都用到了文件上传的功能在前端的开发之中,如果需要文件上传功能,则必须在......
  • 基于java的ssm电影后台管理系统
    MVC设计模式可以将这些对象、显示、控制分离以提高软件的的灵活性和复用性,MVC结构可以使程序具有对象化的特征,也更容易维护。在需要改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑,达到减少编码的时间,提高代码复用性 系统思维导图系统环境和技术使用编译器:In......
  • HTML设置定时执行代码 JavaScript 计时事件
    1、https://www.runoob.com/js/js-timing.htmlJavaScript一个设定的时间间隔之后来执行代码我们称之为计时事件JavaScript计时事件通过使用JavaScript,我们有能力做到在一个设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行。我们称之为计时事件。在JavaScript......
  • 【附源码】计算机毕业设计银行资金账户管理系统(java+springboot+mysql+mybatis+论文)
    本系统(程序+源码)带文档lw万字以上  文末可领取本课题的JAVA源码参考系统程序文件列表系统的选题背景和意义银行资金账户管理系统是一种基于互联网技术的信息化管理平台,旨在提高银行资金管理的效率和安全性。随着金融市场的快速发展和金融产品的多样化,银行资金管理面临着......
  • 【附源码】计算机毕业设计疫情居家隔离服务系统(java+springboot+mysql+mybatis+论文)
    本系统(程序+源码)带文档lw万字以上  文末可领取本课题的JAVA源码参考系统程序文件列表系统的选题背景和意义随着新冠疫情的爆发,居家隔离成为了防控疫情的重要手段之一。然而,在实际操作中,居家隔离存在着许多问题,如隔离人员的生活保障、健康状况监测等。因此,设计并实现一个......
  • java安装(找不到jre还苦恼的同志们)-彗星,请放弃jre
    我写了那么多的文章,自我感觉python爬虫是最有含金量的一片了。结果Java安装阅读量始终是第一位,哭笑不得啊。2023.06.11改名博文名称为java安装(找不到jre还苦恼的同志们)-彗星,请放弃jre。jre就是一道彗星,从java的生涯已经结束了,大家不必纠结。看这个文章的人大部分都是刚刚入......
  • [附源码]JAVA计算机毕业设计道路桥梁工程知识文库系统(源码+开题)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息化技术的快速发展,传统的道路桥梁工程知识管理方式已经无法满足现代工程领域的实际需求。传统的知识管理多依赖于纸质文档和人工整理,这种方式......
  • [附源码]JAVA计算机毕业设计德云社票务系统(源码+开题)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在数字化和信息化的时代背景下,票务管理系统的应用已经深入到各行各业,特别是在文化娱乐产业中,票务系统的运用更是成为了行业发展的重要支撑。德云社作......
  • [附源码]JAVA计算机毕业设计第二课堂选课系统(源码+开题)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展,计算机技术在教育领域的应用日益广泛。传统的选课方式已经无法满足高校日益增长的教学需求和学生个性化发展的要求。第二课堂......
  • [附源码]JAVA计算机毕业设计电烤烟房综合管理系统(源码+开题)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着现代农业的快速发展,电烤烟房作为烟草产业的关键环节,其管理效率和质量直接影响到烟草的品质和产量。传统的电烤烟房管理方式往往依赖于人工操作和......