链接:https://github.com/j3ers3/Hello-Java-Sec
系统学习一下java代码审计,故留下笔记
若有错误之处,还望指出~~

搭建靶场

环境

JDK 1.8环境 //高了就报错,推荐用宝塔面板,能一键管理java,可以省下一部分琐事
phpMyAdmin 5.0 
PHP 7.4.33
MySQL 8.0.41

配置mvn镜像源

mvn -v // 可看到mvn的路径

找到settings文件里的 标签,将其下内容覆盖掉

    <!-- 阿里云Maven镜像 -->
    <mirror>
        <id>aliyunmaven</id>
        <mirrorOf>central</mirrorOf>
        <url>https://maven.aliyun.com/repository/public</url>
    </mirror>
    <!-- 清华源(备用) -->
    <mirror>
        <id>tsinghua</id>
        <mirrorOf>central</mirrorOf>
        <url>https://mirrors.tuna.tsinghua.edu.cn/maven-central/</url>
    </mirror>
    <!-- 匹配所有仓库(防止漏下) -->
    <mirror>
        <id>mirror-all</id>
        <mirrorOf>*</mirrorOf>
        <url>https://maven.aliyun.com/repository/public</url>
    </mirror>

调整数据库

新增一个数据库,最后修改src/main/resources/application-dev.properties

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/javasce?serverTimezone=Asia/Shanghai
spring.datasource.username=javasce
spring.datasource.password=1234567
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
server.port=8888
server.address=0.0.0.0

server.servlet.session.timeout=72h

启动靶场

docker拉去太慢了,还是手动编译

git clone https://github.com/j3ers3/Hello-Java-Sec
cd Hello-Java-Sec
mvn clean package -DskipTests
java -jar target/javasec-*.jar

这里可以用bt,方便一键管理靶场
在这里插入图片描述

开始实战

SQL注入

语句拼接 (Statement)

采用报错注入,获取数据库信息

vul1?id=1' and updatexml(1,concat(0x7e,(SELECT user()),0x7e),1)-- +

报错注入函数主要有三个

updatexml:上面写了

extractvalue()

1' and extractvalue(1,concat(0x7e,(SELECT user()),0x7e))-- +

floor()

1' and (select * from (select count(*),concat((SELECT user()),floor(rand(0)*2))x from information_schema.tables group by x)a)-- +
语句拼接(PrepareStatement)
public String vul2(String id) {
    Class.forName("com.mysql.cj.jdbc.Driver");
    Connection conn = DriverManager.getConnection(db_url, db_user, db_pass);
    String sql = "select * from users where id = " + id;
    PreparedStatement st = conn.prepareStatement(sql);
    ResultSet rs = st.executeQuery();
}
                
漏洞代码 - JdbcTemplate
public Map<String, Object> vul3(String id) {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUrl(db_url);
    dataSource.setUsername(db_user);
    dataSource.setPassword(db_pass);

    JdbcTemplate jdbctemplate = new JdbcTemplate(dataSource);

    String sql = "select * from users where id = " + id;

    // 安全代码:使用参数化查询,避免SQL注入风险
    // String sql = "select * from users where id = ?";

    return jdbctemplate.queryForMap(sql);
}
                    

MyBatis

漏洞代码 - order by 注入
@GetMapping("/vul/order")
public List<User> orderBy(String field, String sort) {
    return userMapper.orderBy(field, sort);
}

// 不安全的注解写法
public interface UserMapper {
    @Select("select * from users order by ${field} ${sort}")
    List<User> orderBy(@Param("field") String field, @Param("sort") String sort);
}

// 不安全的XML映射写法
<select id="orderBy" resultType="com.best.hello.entity.User">
    select * from users order by ${field} ${sort}
</select>
           
漏洞代码 - 搜索注入
/**
 * 产生原因:由于在 LIKE 查询中直接使用 '%#{q}%' 会导致语法错误,一些开发者为了简化操作而改用 '%${q}%' 导致SQL注入
 */
@GetMapping("/vul/search")
public List<User> searchVul(@RequestParam("user") String user) {
    return userMapper.searchVul(user);
}

// 不安全的注解
@Select("select * from users where user like '%${q}%'")
List<User> search(String q);
                    

目录遍历

漏洞代码 - 任意文件下载
/**
 * 产生原因:文件路径没做限制,可通过../递归下载任意文件
 * 如下载 filename=../../etc/passwd
 */
public String download(String filename, HttpServletResponse response) {
    String filePath = System.getProperty("user.dir") + "/logs/" + filename;
    try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(Paths.get(filePath)))) {
        response.setHeader("Content-Disposition", "attachment; filename=" + filename);
        response.setContentLength((int) Files.size(Paths.get(filePath)));
        response.setContentType("application/octet-stream");

        IOUtils.copy(inputStream, response.getOutputStream());
        return "下载文件成功:" + filePath;
    } catch (IOException e) {
        return "未找到文件:" + filePath;
    }
}
       

代码分析

  • 路径拼接风险:直接拼接filename,会导致路径遍历攻击

请求

/vulnapi/Traversal/download?filename=../../../../../../../../../../../../etc/passwd
漏洞代码 - 任意路径遍历
/**
 * 攻击者可以传入 "../../" 来访问项目目录以外的文件
 */
public String fileList(String filename) {
    String filePath = System.getProperty("user.dir") + "/logs/" + filename;
    StringBuilder sb = new StringBuilder();

    File f = new File(filePath);
    File[] fs = f.listFiles();

    if (fs != null) {
        for (File ff : fs) {
            sb.append(ff.getName()).append("<br>");
        }
        return sb.toString();
    }
    return filePath + "目录不存在!";
}
                    

漏洞分析

  • 代码直接拼接用户输入的filename参数,且未做任何路径校验

正确做法

  • 危险字符过滤
  • 路径规范化与前缀校验

XSS

反射型

问题代码

/**
 * 简单的反射型XSS,没对输出做处理,当攻击者输入恶意js语句时可触发
 */
@GetMapping("/reflect")
public static String input(String content) {
    return content;
}
                    

恶意请求

/vulnapi/XSS/reflect?content=test<script>alert(1)</script>

代码分析

  • 没对输出做处理,当攻击者输入恶意js语句时可触发,刚好前端又可以直接解析导致的

存储型

问题代码

@PostMapping("/save")
public String save(HttpServletRequest request) {
    String content = request.getParameter("content");
    xssMapper.add(content);
    return "success";
 }

同理

SSRF

SSRF 漏洞本质是 “服务器端请求未做任何访问控制”,

漏洞代码

/**
 * 产生原因:url参数没做限制,可调用URLConnection发起任意请求,比如请求内网,或使用file等协议读取文件
 * url=file:///etc/passwd
 * url=http://172.17.0.1:80
 */
public static String URLConnection(String url) {
    try {
        URL u = new URL(url);
        URLConnection conn = u.openConnection();
        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();
    }
}
                    

请求测试

/vulnapi/SSRF/URLConnection/vul?url=file:///etc/passwd

审计分析

        // 漏洞核心1:无任何过滤,直接解析用户传入的URL(支持任意协议)
        URL u = new URL(url);
        // 漏洞核心2:直接打开连接,发起请求(无地址/权限限制)
        URLConnection conn = u.openConnection();
        // 漏洞核心3:读取并返回完整的响应内容(泄露敏感信息)
        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        
        // 返回所有响应内容,攻击者可直接获取敏感信息
        return html.toString();

解决方案

① 仅允许 HTTP/HTTPS 协议;

② 校验目标 IP 为合法公网 IP(禁止内网 / 本地 IP);

③ 限制请求超时、返回内容长度;

④ 过滤危险 URL 字符。

漏洞代码 - 绕过

/**
 * 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);
    }
}

审计分析

  • 核心问题是过滤机制只覆盖了 “初始 URL **的静态校验”,未处理 URL 访问过程中的动态跳转、**IP 的多格式表示、URL 解析特性等场景

绕过方法

  1. 只校验初始 URL 的协议 / IP,未处理 URL 跳转(短链接 / 302 跳转)
http://short.url/redirect(短链接)
校验阶段:short.url的 IP 是1.2.3.4(公网),通过所有校验;
实际访问:short.url返回 302,跳转至http://127.0.0.1/etc/passwd,代码自动访问该内网地址。

2:IP 多格式表示绕过(isIntranet 只识别点分十进制)

构造 URL:http://2130706433/etc/passwd

3:URL 解析特性绕过(@符号 / 特殊域名)

@符号绕过:构造 URLhttp://www.baidu.com@127.0.0.1
特殊域名绕过(xip.io):构造 URLhttp://127.0.0.1.xip.io

4.DNS 重绑定绕过

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐