小迪安全v2023学习笔记(六十六讲)—— Java安全&SQL注入&SSTI&SPEL&XXE

发布于:2025-08-20 ⋅ 阅读:(16) ⋅ 点赞:(0)

前记

  • 今天是学习小迪安全的六十六天,本节课正式进入Java安全的第一讲,主要内容是SQL注入、SSTI注入、SPEL注入以及XXE注入
  • 主要是以理解为主,也有实战案例
  • 需要用到的资源已放至下方链接,需要自取:

WEB攻防——第六十六天

Java安全&SPEL表达式&SSTI模板注入&XXE&JDBC&MyBatis注入

环境搭建

Hello-Java-Sec

在这里插入图片描述

  • 这个直接在github上下载即可,然后支持两种安装模式:docker和手动安装

  • 这里就手动安装了,环境要求为jdk1.8,然后如果要测试SQL注入的话,需要先导入数据库,数据库版本为MySQL5.7MySQL8+有语法错误!

  • application-dev.properties文件中修改为本地的数据库:
    在这里插入图片描述

  • 并且将db.sql文件改为如下内容:

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`)
);

-- 触发器:插入时自动生成 UUID
DELIMITER $$
CREATE TRIGGER `users_before_insert`
BEFORE INSERT ON `users`
FOR EACH ROW
BEGIN
    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`)
);
  • 然后连接到数据库,添加这里填写的数据库名,导入db.sql文件进去:
    在这里插入图片描述
    在这里插入图片描述

  • 之后在根目录下maven打包,然后运行jar包即可:
    在这里插入图片描述
    在这里插入图片描述

  • 默认端口是8888,这里我改成了8890,然后直接打开就行,默认账号密码为admin/admin
    在这里插入图片描述

JavaSec
  • 这个的下载地址是:bewhale/JavaSec: Java安全 学习记录,环境要求Jdk1.8

  • 然后下载源码之后,改动src/main/resources/application.yml文件中的数据库配置为你自己的数据库配置

  • 接着创建数据库javasec数据库版本为MySQL5.7),我这里改成了javaseclab,导入javasec.sql文件:
    在这里插入图片描述
    在这里插入图片描述

  • 然后回到根目录用maven打包,运行jar包即可:
    在这里插入图片描述
    在这里插入图片描述

  • 这里默认端口是8000,我改成了8891,然后也是直接打开即可,默认账号密码为admin/admin
    在这里插入图片描述

Java安全 - SQL注入-JDBC&MyBatis

JDBC注入
原理
  • JDBC是 Java 平台最核心的数据库访问标准,用来完成「Java 程序 ↔ 任何关系型数据库」之间的通信。
  • 但是它比较复杂和难以管理,所以在企业开发中用得不多
  • 这里主要是看它使用不当的SQL语法导致的SQL注入,主要有四种
语句拼接
  • 第一种就是直接拼接语句,这个很简单漏洞语句一般是这样:
String sql = "select * from users where id = '" + id + "'";
  • 直接尝试注入就OK了:
    在这里插入图片描述

  • 如果坚持使用这种方式,就需要严格的黑名单过滤才行

预编译的错误使用
  • 在使用预编译时,也不能用拼接的方式去执行SQL语句:
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);
JdbcTemplate
  • JDBCTemplateSpringJDBC的封装,如果使用拼接语句便会造成SQL注入
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);
正则过滤
  • 正则表达式只允许a-z0-9出现,然后继续使用拼接语句,虽然感觉过滤了其他的字符,但是也可能存在绕过的风险
  • 正确的做法限制传入数据的数据类型,比如限定为整型、boolean、浮点型等
MyBatis注入
原理
  • MyBatis 是一个流行的Java持久性框架,通过预处理和参数绑定的方式,有效地防止SQL注入攻击,但如果 MyBatis 框架的使用不当或未正确配置,仍然可能存在SQL注入的风险。
  • MyBatis中,#{}语法表示使用预编译${}语法表示使用拼接
Like注入
  • 在使用like语句进行模糊查询时,语法如下:
Select * from users where username like '%${username}%'
  • 在这种情况下,直接使用#程序会报错,大部分程序员解决错误的方式是将#改为$,由此便产生SQL注入
  • POC为:
xxx%' union select database(),user(),@@version,4,5 -- -
  • 正确的写法为:
Select * from users where username like concat('%',#{username}, '%')
Order By注入
  • 在使用Order By注入进行排序的时候,语法如下:
select * from users order by ${field}
  • 同样在这种情况下,直接使用#程序会报错,大部分程序员解决错误的方式是将#改为$,由此便产生SQL注入
  • POC为:
 id and (updatexml(1,concat(0x7e,(select user())),0))-- -
  • 正确的写法为:
<mapper namespace="com.best.hello.mapper.UserMapper">
    <select id="orderBySafe" resultType="com.best.hello.entity.User">
        select * from users
        <choose>
            <!-- 根据字段选择排序方式,避免 SQL 注入 -->
            <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>
  • 固定排序方式,或者根据字段选择排序方式,避免让用户自己写入
In注入
  • 在使用in语句进行条件查询的时候,语法如下:
Select * from users where id in (${ids})
  • 直接使用#仍然会报错,大部分程序员解决错误的方式是将#改为$,由此便产生SQL注入
  • POC为:
1,2,3) and (updatexml(1,concat(0x7e,(select user())),0))-- -
  • 正确的写法如下:
"<script>" + "SELECT * FROM users WHERE id IN " + "<foreach item='item' index='index' collection='ids' open='(' separator=',' close=')'>" + "#{item}" + "</foreach>" + "</script>"
  • 使用foreach语句进行循环遍历
白盒案例演示
  • 这里演示的案例是inxedu,首先我们在IDEA中打开,找到src/main/resources/project.properties文件,修改为自己的配置:
    在这里插入图片描述

  • 然后和搭建教程那里一样,导入根目录下的demo_inxedu_v2_0_open.sql(数据库版本为MySQL5.5):
    在这里插入图片描述

  • 之后在IDEA中,点击右边的Maven按钮,clean之后再package得到war工件:
    在这里插入图片描述

  • 点击当前文件,然后编辑配置,点击+号配置Tomcat服务器:
    在这里插入图片描述

  • 这里我选择的是Tomcat9,注意这里的端口要与配置中的一致

  • 然后点击“部署”,选择+号添加工件,然后将下面的根路径改成/
    在这里插入图片描述

  • 配置完成后直接启动Tomcat即可,如果这里一直显示工件配置出错,记得看看数据库账号密码是否正确

  • 配置完成之后页面:
    在这里插入图片描述

  • 这里既然是白盒测试,我们首先要看他是用的JDBC还是MyBatis去管理数据库吧,这里怎么看呢?

  • 直接在pom.xml中搜有没有用到MyBatis就行了:
    在这里插入图片描述

  • 用到了呀,既然用到了,那针对MyBatis出现SQL注入的情况就那三种,全局搜索一下有没有关键字就行了

  • 比如用到like有没有%${,用到Order By有没有${,用到in有没有(${
    在这里插入图片描述

  • 这里不就是吗?点进去可以看到是删除文章的一个功能,这里用的是delete标签,然后ID值为deleteArticleByIds,通过ID去删除文章

  • 我们可以直接搜一下这个id值看看哪个java文件调用了它:
    在这里插入图片描述

  • 有这么多,其实你慢慢看可以找到AdminArticleController.java这个文件是真正的功能点,点进去找它的触发路径为/admin/article
    在这里插入图片描述

  • 请求路径为delete
    在这里插入图片描述

  • 现在我们在源码中找到了它可能存在漏洞的点,那么我们就在网站中去找到这个功能点

  • 这个是个后台功能点,需要登入后台,账号密码为admin/111111
    在这里插入图片描述

  • 直接访问admin/article显示404,那这里就找哪里有文章删除的功能,在文章资讯管理下的文章列表这里就有删除的按钮:
    在这里插入图片描述

  • 随便删除一篇文章,然后f12抓一下数据包看是不是我们在java中看到的链接:
    在这里插入图片描述

  • 这里确实是admin/article/delete,所以我们找到了删除的功能点,接下来就是找它的注入点了,那这里看负载也是找到了它使用的POST提交articelId,这里也是对应上了:
    在这里插入图片描述

  • 接下来就直接BP抓包,然后用Sqlmap跑一下:
    在这里插入图片描述
    在这里插入图片描述

  • 这里接下来的步骤就不演示了,之前的课讲过

总结
  • 不管是JDBC还是MyBatis,只要是使用拼接语法,都可能造成SQL注入
  • 在代码审计的时候,也可以抓住这一点去快速判断是否存在注入

Java安全 - XXE注入-Reader&Builder

原理
  • XXE (XML External Entity Injection), XML 外部实体注入,当开发人员配置其 XML 解析功能允许外部实体引用时,攻击者可利用这一可引发安全问题的配置方式,实施任意文件读取、内网端口探测、命令执行、拒绝服务等攻击。
  • Java中相关的类/方法:
    1. XMLReader
    2. SAXReader
    3. SAXBuilder
    4. Unmarshaller
    5. DocumentBuilder
  • 代码审计中,除了上述的类/方法以外,还可以审计下列类/方法:
    1. XMLStreamReader
    2. SAXParser
    3. SAXSource
    4. TransformerFactory
    5. SAXTransformerFactory
    6. SchemaFactory
    7. XPathExpression
靶场演示
  • 这个在白盒中就是看有没有上面这些函数,有没有过滤,然后去进行测试
  • 如果是在黑盒中,其实和PHP没什么不同,都是看有没有这种XML传入参数的地方,然后payload一个个测试就行了
  • 无非就是使用的函数不一样,其他的测试方法都是一样的,所以这里就不演示了

Java安全 - SSTI模板-Thymeleaf&URL

原理
  • SSTIServer Side Template Injection)服务器模板注入,服务端接收了用户的输入,将其作为Web应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容
  • 这个东西前期也是讲过,当时就是介绍了一下,用的Thymeleaf,然后简单的看了它的历史漏洞
  • Java中主要的SSTI出现的位置:
    1. Thymeleaf
    2. URL作为视图
    3. Freemarker
    4. Velocity
  • 关于其他语言模板注入的文章可以参考一下:1. SSTI(模板注入)漏洞(入门篇) - bmjoker - 博客园
靶场案例
  • 这里的话就还是以thymeleaf作为演示吧,我们看一看源码:
    在这里插入图片描述

  • 主要出现的原因就是这个return "lang/" + lang;语句,后面的lang参数是可控的

  • 所以我们传入payload就可以造成RCE等这些漏洞:
    在这里插入图片描述

  • 执行就会弹出计算器

  • 其他的模板基本也是一样的,在黑盒测试中,主要就是看有没有这种模板切换的地方,比如切换语言、切换皮肤、切换主题等等,有没有可控的参数,就测测看

  • 但一般没有提示是基本测不出来的

Java安全 - SPEL表达式-SpringBoot框架

原理
  • SPELSpring Expression Language)表达式注入,是一种功能强大的表达式语句,用于在运行时查询和操作对象图,由于未对参数做过过滤可造成RCE
  • 参考文章:Spring-SpEL表达式超级详细使用全解-CSDN博客
  • 可以将其类似于PHP中的eval函数,将字符串解析为命令之类的函数
靶场案例
  • 这里首先需要判断有没有这个漏洞,可以尝试输入一些字符串让他解析,看他能不能解析

  • 比如输入100-1这种运算字符串看它会不会执行计算:
    在这里插入图片描述

  • 这里返回99,说明它会解析字符串,那么我们就可以尝试这个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;
}            
  • 然后我们注入的POC为:
T(java.lang.Runtime).getRuntime().exec("calc.exe")
  • T(...)SpEL专用的“取类对象”的操作符,这里就是取到Runtime这个类,然后调用该类的静态方法调用exec方法调用计算器
  • 如果存在黑名单过滤的话就可以结合Java反射进行绕过,比如过滤Rumtime字符串,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"})
  • 就是利用它Java的反射机制,让他利用forName去加载类,利用字符串拼接的方式绕过直接写入Runtime字符串
  • 这个也是能够成功调出计算器的:
    在这里插入图片描述

网站公告

今日签到

点亮在社区的每一天
去签到