以小白视角进行JAVA代码审计——SSRF漏洞
字数 1269 2025-11-07 08:41:54
Java代码审计入门:SSRF漏洞详解与实战指南
一、SSRF漏洞基础概念
1.1 什么是SSRF漏洞
SSRF(Server-Side Request Forgery,服务端请求伪造)是一种Web安全漏洞,攻击者能够诱使服务器端应用程序向非预期目标发起请求。该漏洞的主要危害在于可以攻击从外网无法直接访问的内网系统。
1.2 SSRF漏洞的危害范围
- 探测内网服务:通过响应差异判断服务存活状态
- 端口扫描:识别内网开放端口
- 文件读取:利用file协议读取本地文件
- 协议利用:配合gopher协议攻击Redis等内网服务
- 内网系统攻击:访问和攻击内部网络中的敏感系统
1.3 常见出现场景
- 社交分享功能(获取超链接标题)
- 网页转码服务(移动端适配)
- 在线翻译功能
- 图片加载/下载功能
- 内容采集功能
- 云服务存活检测
- 邮件系统接收处理
- 文件处理功能(FFmpeg、ImageMagick等)
二、Java网络请求支持的协议
Java原生支持以下协议:
- http / https
- file(文件读取)
- ftp
- mailto
- jar
- netdoc
三、SSRF漏洞代码实例分析
3.1 HttpClient方式漏洞示例
Maven依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
</dependency>
漏洞代码:
@RestController
@RequestMapping("/ssrfvul")
public class HttpClientController {
@GetMapping("/httpclient/vul")
public String HttpClientDemo(@RequestParam String url) throws IOException {
StringBuilder result = new StringBuilder();
CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url); // 直接使用用户输入
HttpResponse httpResponse = client.execute(httpGet);
// ... 处理响应
return result.toString();
}
}
攻击向量:
http://127.0.0.1:8088/ssrfvul/httpclient/vul?url=http://www.baidu.com
http://127.0.0.1:8088/ssrfvul/httpclient/vul?url=file:///c:/windows/win.ini
3.2 HttpURLConnection方式漏洞示例
漏洞代码:
@RestController
@RequestMapping("/ssrfvul")
public class UrlConnectionController {
@GetMapping("/urlconnection/vul")
public String UrlConnectionDemo(@RequestParam String url) throws IOException {
StringBuilder result = new StringBuilder();
URL url1 = new URL(url); // 直接使用用户输入
URLConnection urlConn = url1.openConnection();
urlConn.connect();
// ... 处理响应
return result.toString();
}
}
四、实战漏洞案例分析
4.1 环境搭建与漏洞定位
- 搭建目标系统环境(数据库+Maven配置)
- 全局搜索关键函数:
openConnection - 定位漏洞触发点:
ueditorCatchImage方法
4.2 漏洞代码分析
protected void ueditorCatchImage(Site site, HttpServletRequest request,
HttpServletResponse response) throws IOException {
String[] source = request.getParameterValues("source[]"); // 用户可控输入
for (int i = 0; i < source.length; i++) {
String src = source[i];
// 格式验证(仅检查扩展名,存在绕过可能)
String extension = FilenameUtils.getExtension(src);
if (!gu.isExtensionValid(extension, Uploader.IMAGE)) {
continue;
}
// 设置不跟随重定向(但存在其他绕过方式)
HttpURLConnection.setFollowRedirects(false);
// 漏洞触发点
HttpURLConnection conn = (HttpURLConnection) new URL(src).openConnection();
}
}
4.3 漏洞利用方式
触发URL:
http://127.0.0.1:8080/cmscp/core/ueditor.do?action=catchimage&source[]=http://www.baidu.com/
内网探测:
- 开放端口(3306):正常响应
- 关闭端口(3307):异常响应(500错误)
五、高级利用技巧
5.1 Gopher协议利用
配合Redis进行RCE攻击:
curl -v 'http://target/xx.php?url=gopher://172.21.0.2:6379/_*1%250d%250a%248%250d%250aflushall%250d%250a...'
攻击流程:
- 通过SSRF访问内网Redis服务
- 发送特定格式的Redis命令
- 实现命令执行和权限获取
5.2 文件读取利用
- 读取系统配置文件:
file:///c:/windows/win.ini - 读取应用配置文件(如向日葵配置)
- 结合信息泄露进一步渗透
六、SSRF漏洞修复方案
6.1 白名单域名校验(推荐)
public static boolean checkSSRFByWhitehosts(String url) {
return SSRFChecker.checkURLFckSSRF(url);
}
public static boolean isHttp(String url) {
return url.startsWith("http://") || url.startsWith("https://");
}
public static String gethost(String url) {
try {
URI uri = new URI(url);
return uri.getHost().toLowerCase();
} catch (URISyntaxException e) {
return "";
}
}
6.2 内网IP检测
public static boolean isIntranet(String url) {
Pattern reg = Pattern.compile("^(127\\.0\\.0\\.1)|(localhost)|(10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|(172\\.((1[6-9])|(2\\d)|(3[01]))\\.\\d{1,3}\\.\\d{1,3})|(192\\.168\\.\\d{1,3}\\.\\d{1,3})$");
Matcher match = reg.matcher(url);
return match.find();
}
6.3 完整安全校验方案
public static String checkURL(String url) {
if (null == url) return null;
ArrayList<String> safeDomains = WebConfig.getSafeDomains();
ArrayList<String> blockDomains = WebConfig.getBlockDomains();
try {
String host = gethost(url);
// 协议校验
if (!isHttp(url)) return null;
// 黑名单检测
if (blockDomains.contains(host)) return null;
for(String blockDomain: blockDomains) {
if(host.endsWith("." + blockDomain)) return null;
}
// 白名单检测(支持多级域名)
if (safeDomains.contains(host)) return url;
for(String safedomain: safeDomains) {
if(host.endsWith("." + safedomain)) return url;
}
return null;
} catch (NullPointerException e) {
return null;
}
}
6.4 其他防护措施
- 禁用重定向:
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
conn.setInstanceFollowRedirects(false);
- Socket Hook监控:
// 开启监控
SecurityUtil.startSSRFHook();
// 业务逻辑
// 关闭监控
SecurityUtil.stopSSRFHook();
- 输入过滤:
public static String replaceSpecialStr(String str) {
StringBuilder sb = new StringBuilder();
str = str.toLowerCase();
for(int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if ((ch >= 48 && ch <= 57) || // 数字
(ch >= 97 && ch <= 122) || // 字母
ch == '/' || ch == '.' || ch == '-') {
sb.append(ch);
}
}
return sb.toString();
}
七、代码审计要点总结
7.1 关键函数识别
URL.openConnection()HttpClient.execute()Request.Get().execute()- 其他HTTP客户端库的请求方法
7.2 参数追踪
- 关注用户可控的URL参数
- 检查参数是否经过严格校验
- 验证重定向处理逻辑
7.3 测试验证
- 尝试不同协议(http、file、gopher等)
- 测试内网地址访问
- 验证防护措施的有效性
通过以上详细的代码审计指南,安全人员可以系统性地识别、验证和修复Java应用中的SSRF漏洞,有效提升应用程序的安全性。