前记
- 今天是学习小迪安全的六十六天,本节课正式进入Java安全的第一讲,主要内容是SQL注入、SSTI注入、SPEL注入以及XXE注入
- 主要是以理解为主,也有实战案例
- 需要用到的资源已放至下方链接,需要自取:
WEB攻防——第六十六天
Java安全&SPEL表达式&SSTI模板注入&XXE&JDBC&MyBatis注入
环境搭建
Hello-Java-Sec
这个直接在
github
上下载即可,然后支持两种安装模式:docker
和手动安装这里就手动安装了,环境要求为
jdk1.8
,然后如果要测试SQL注入的话,需要先导入数据库,数据库版本为MySQL5.7
(用MySQL8+
有语法错误!)在
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
JDBCTemplate
是Spring
对JDBC
的封装,如果使用拼接语句便会造成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-z
、0-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中相关的类/方法:
XMLReader
SAXReader
SAXBuilder
Unmarshaller
DocumentBuilder
- 在代码审计中,除了上述的类/方法以外,还可以审计下列类/方法:
XMLStreamReader
SAXParser
SAXSource
TransformerFactory
SAXTransformerFactory
SchemaFactory
XPathExpression
靶场演示
- 这个在白盒中就是看有没有上面这些函数,有没有过滤,然后去进行测试
- 如果是在黑盒中,其实和PHP没什么不同,都是看有没有这种XML传入参数的地方,然后
payload
一个个测试就行了 - 无非就是使用的函数不一样,其他的测试方法都是一样的,所以这里就不演示了
Java安全 - SSTI模板-Thymeleaf&URL
原理
SSTI
(Server Side Template Injection
)服务器模板注入,服务端接收了用户的输入,将其作为Web
应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容- 这个东西前期也是讲过,当时就是介绍了一下,用的
Thymeleaf
,然后简单的看了它的历史漏洞 - Java中主要的SSTI出现的位置:
Thymeleaf
- URL作为视图
Freemarker
Velocity
- 关于其他语言模板注入的文章可以参考一下:1. SSTI(模板注入)漏洞(入门篇) - bmjoker - 博客园
靶场案例
这里的话就还是以
thymeleaf
作为演示吧,我们看一看源码:
主要出现的原因就是这个
return "lang/" + lang;
语句,后面的lang
参数是可控的所以我们传入
payload
就可以造成RCE等这些漏洞:
执行就会弹出计算器
其他的模板基本也是一样的,在黑盒测试中,主要就是看有没有这种模板切换的地方,比如切换语言、切换皮肤、切换主题等等,有没有可控的参数,就测测看
但一般没有提示是基本测不出来的
Java安全 - SPEL表达式-SpringBoot框架
原理
SPEL
(Spring 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
字符串 - 这个也是能够成功调出计算器的: