D^3CTF2023ezjava
分析
给了registry和server两个服务,看到registry有一个hessian反序列化路由,和fastjson2的2.0.24版本依赖。故纯在反序列化漏洞。但是有黑名单,限制了TemplatesImpl加载字节码和JNDI,该怎样绕过呢?;还有一个问题就是flag在server服务下,那应该怎样打到server?
ok,看到server的controller就可以解决第二个问题了。
1 | package com.example.server.controller; |
访问registry的/client/status路由会调用server的/status,在这个/status路由有调用了update()方法
而这个update()方法又会访问registry的/blacklist/jdk/get路由返回一个json字符串,这里会用parseObject根据字符串生成一个对象。
思路就是通过registry的hessian反序列化修改registry的/blacklist/jdk/get路由返回一个json字符串为一个恶意数据串,让sever反序列化后达到攻击的目的。
题解
分析的大概思路是这样的,但打又是另一回事情了。有很多细节需要注意。
接下来就看了别人的wp学习一下。征程漫漫,好累。
在
JNDI
服务中,RMI
服务端除了直接绑定远程对象以外,还可以通过References
类来绑定一个外部的远程对象,这个远程对象是当前名称目录系统之外的对象,绑定了Reference
之后,服务端会先通过Referenceable.getReference()
获取绑定对象的引用,并且在目录中保存。当客户端在lookup()
查找这个远程对象时,客户端会获取相应的object factory
,最终通过factory
类将reference
转换为具体的对象实例。
绕过JDK高版本限制JNDI
利用限制tomcat8+
RegistryContext#decodeObject类
在JDK 6u132, JDK 7u122, JDK 8u113 中Java提升了JNDI 限制了Naming/Directory服务中JNDI Reference远程加载Object Factory类的特性。系统属性 com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase 的默认值变为false,即默认不允许从远程的Codebase加载Reference工厂类。如果需要开启 RMI Registry 或者 COS Naming Service Provider的远程类加载功能,需要将前面说的两个属性值设置为true。
寻找Reference的替代
使用ResourceRef对象进行响应
为什么是ResourceRef?
- 继承Reference
- getFactoryClassLocation 可以为null
要注意的是只能绑定remote的对象,这不是还是没办法吗?
通过**ReferenceWrapper(reference)**包裹这个对象,就可以实现在被攻击目标起一个额外的服务,就是remote对象了。
这样就绕过了高JDK版本的限制。
不出网JNDI
但是还是限制了远程加载ObjectFactory需要寻找依赖的getObjectInstance对ResourceRef进行实例化。这个时候我们需要找一个新的factory的进行利用,要求继承Factory并且该类getObjectInstance方法可以进行利用。
这里使用的是org.apache.naming.factory.BeanFactory
这是tomcat8+才有的类,也就是为何需要依赖tomcat8+的原因。
看到他的getObjectInstance方法,来分析如何利用
在Tomcat的
catalina.jar
中有一个org.apache.naming.factory.BeanFactory
类,这个类会把Reference
对象的className
属性作为类名去调用无参构造方法实例化一个对象。然后再从Reference
对象的Addrs参数集合中取得 AddrType 是 forceString 的 String 参数。接着根据取到的 forceString 参数按照
,
逗号分割成多个要执行的方法。再按=
等于号分割成 propName 和 param。最后会根据 propName 作为方法名称去反射获取一个参数类型是
String.class
的方法,并按照 param 从 Addrs 中取到的 String 对象作为参数去反射调用该方法。
EL表达式又正好是Sring类型,浅析EL表达式注入漏洞 - 先知社区 (aliyun.com)这样就可以进行命令执行。
当然这只是绕过的一种方法,满足这些条件都可以。
- 找到一个受害者本地CLASSPATH中的类作为恶意的Reference Factory工厂类,并利用这个本地的Factory类执行命令。
- 利用LDAP直接返回一个恶意的序列化对象,JNDI注入依然会对该对象进行反序列化操作,利用反序列化Gadget完成命令执行。
paylaod
正确的思路与我的思路有些大同小异,主要是在细节的处理上。
hessian反序列化的CVE-2021-43297通过构造恶意数据包来导致异常处理来调用toString方法,来接toJSONString。这样将黑名单置空,并且打入内存马修改registry的/client/status路由返回一个内存马打入server
两个内存马
FastJson内存马
1 | import com.sun.org.apache.xalan.internal.xsltc.DOM; |
register的内存马
1 | package org.example; |
用IDEA编译整个项目,可以获得.class文件,如果直接javac会报错
1 | import com.alibaba.fastjson.JSONObject; |
本题参考:
Hessian CVE-2021-43297 & D3CTF 2023 ezjava - X1r0z Blog (exp10it.io)
d3ctf-ezjava | Pupi1’s Blog (pupil857.github.io)
[D3CTF x AntCTF 2023 Web 赛后复现 - Boogiepop Doesn’t Laugh (boogipop.com)](https://boogipop.com/2023/05/06/D3CTF x AntCTF 2023 Web 赛后复现/#ezjava)
Exploiting JNDI Injections in Java | Veracode blog
JNDI-RMI 注入的EL 绕过思路分析 (zsxq.com)
探究bypass JNDI限制及利用链扩展 - FreeBuf网络安全行业门户
探索高版本 JDK 下 JNDI 漏洞的利用方法 - 跳跳糖 (tttang.com)
AliyunCTF2023bypassit
分析
一个简单粗暴的反序列化入口的路由。
以及snakeyaml依赖,在反序列化时会触发任意setter方法。与FastJson有异曲同工之妙。但是是直接JDK反序列化,怎么才能触发load方法?
无语了jackson依赖才能打,为啥一种钻牛角尖???
没了解过jackson先学习一下。
题目是2.13.3版本,Spring Boot 默认带了 Jackson 包
之前FastJson的templateImpl利用链相似的地方在于Jackson 里的 POJONode 类也会触发getter方法。然后入口也是是BadAttributeValueExpException的readObjet方法会调用toString,
com.fasterxml.jackson.databind.ser.BeanPropertyWriter#serializeAsField
方法会通过反射去执行
从BadAttributeValueExpException到POJONode的调用堆栈。
1 | serializeAsField:689, BeanPropertyWriter (com.fasterxml.jackson.databind.ser) |
注意2.13.3版本
1 | // 去除 writeReplace 方法, 避免 Payload 生成阶段触发链子 |
从 Jackson 链的进化到高版本 JNDI 注入的转机 (harmless.blue)
题解
1 | package bypassit; |
题目没有提供base64解码,直接将反序列化的内容输出为文件并且用python传来避免错误
1 | import requests |
总结
这个打tomcat的打法也被修复了,具体见65736 – Improve org.apache.naming.factory.BeanFactory to mitigate JNDI injection
DubheCTF2024Javalotion
分析
首先来看一下题目的源码
/pal路由下是本题的关键所在,就是一个game,要打败帕鲁boss升级才能触发反序列化
org.dubhe.javolution.service.PalService#genPal
方法会调用PalUtils.base64Deserialize方法来base64解码并且反序列化。
/pal/cheat路由的post代码又调用了genPal。
接下来具体分析。
JDK17后TemplatesImpl被移除了。这个ppt的第65页有绕过方法。AS-23-Yuanzhen-A-new-attack-interface-in-Java.pdf (blackhat.com)
browser通过启用OpenID Connect (OIDC)和JDBC URL参数的Teradata服务器配置利用基于浏览器的SSO。client OIDC处理要求服务器确认已配置OIDC,这允许JDBC驱动程序在启用OIDC的任何Teradata服务器上使用基于浏览器的SSO代码路径。
Teradata
SSO的全程为Single Sign On,翻译成中文为单点登陆。在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。
这里直接拿脚本来做简单的复现。
一个fake Teradata服务
1 | import asyncore |
使用flask起一个SSO的浏览器
1 | from flask import Flask |
一个获取jdbc的demo
1 | import java.sql.DriverManager; |
成功执行命令
还有一个知识点就是整型溢出,分配一个过大的整型的值21474836478 = MAX_VALUE + 1
并不会报错而变量值会是-2147483648(最小值)
baeldung.xiaocaicai.com/java-overflow-underflow/
本题在
1 | http://localhost:8888/pal/cheat?defense=-21474836478 |
设置为比最小值还小,这样会判断为最大值,就可以打败jetragon了
这个路由中写了具体的逻辑
如果大于等于0就胜利,我们的攻击力无法击穿,直接把防御力拉满就能返回0了。
可以看到我们成功设置了防御值并且打败了jetragon
下一层就是SSRF了,判断了是不是local
有以下几种绕过方式:
1 | dubhe.sudo.cc //sudo.cc本身就是localhost |
题解
如何稳定触发jacson的getter方法
为何不能稳定触发getter方法呢?因为触发是随机的,如果报错后面的就无法继续执行。那为何是随机的呢?
主要出在虚拟机的逻辑里面JVM源码分析之不保证顺序的Class.getMethods (qq.com)
一个类里的方法经过排序之后,顺序可能会不一样,取决于方法名对应的Symbol对象的地址的先后顺序
那该如何才能稳定触发呢?
从JSON1链中学习处理JACKSON链的不稳定性 - 先知社区 (aliyun.com)
Spring中通过动态代理实现的AOP生成代理类的对象,再包裹这个对象来实现稳定调用。
1 | public static Object getProxy(Object obj,Class<?> clazz) throws Exception |
这里我们注意到一个类就是org.springframework.aop.framework.JdkDynamicAopProxy
基于JDK的AopProxy实现,用于Spring AOP框架,基于JDK动态代理。
创建一个动态代理,实现AopProxy公开的接口。动态代理不能用于代理类(而不是接口)中定义的方法。
这种类型的对象应该通过代理工厂获得,由AdvisedSupport类配置。该类位于Spring的AOP框架内部,不需要由客户端代码直接使用。所以通过反射来获取。
这个类中的有invoke方法,可以来执行方法。
使用反射获取一个代理类上的所有方法时,只能获取到其代理的接口方法。所以我们需要让代理类只有我们需要的getter方法即可。
本题我们需要调用题目中所给的org.dubhe.javolution.pool.PalDataSource#getConnection
才可以
思路如下
- 构造一个
JdkDynamicAopProxy
类型的对象,将PalDataSource
类型的对象设置为targetSource
- 使用这个
JdkDynamicAopProxy
类型的对象构造一个代理类,代理com.teradata.jdbc.TeraDataSource
接口 - JSON 序列化库只能从这个
JdkDynamicAopProxy
类型的对象上找到getconnect
方法 - 通过代理类的
invoke
机制,触发org.dubhe.javolution.pool.PalDataSource#getConnection
方法,实现攻击JDBC
突破模块化访问机制
这里又遇到一个新问题就是,由于Java 9引入的模块系统,这个系统限制了对内部API和包的访问,在Java17中强制开启,反射调用com.sun.org.apache.xpath.internal.objects.XString
来触发toString方法就会出错,所以exp中要添加代码来绕过模块化的限制,并且在运行时添加虚拟机选项
1 | --add-opens java.xml/com.sun.org.apache.xpath.internal.objects=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED |
exp(from S1uM4i)
1 | import com.fasterxml.jackson.databind.node.POJONode; |
总结
这就是被现代Java的题目支配吗?好难啊,自己复现学习到了很多东西。这题同时涉及到了众多知识点,打JDBC和绕JDK高版本,以后多多注意JDBC,感觉遇到的挺多的。
还有一种触发toString的方法
EventListenerList触发toString
1 | public static Object makeReadObjectToStringTrigger(Object obj) throws Exception { |
那么现在有三种readObject可以触发toString的链子
- BadAttributeValueExpException(JDK<17)
- com.sun.org.apache.xpath.internal.objects.XString来hash碰撞调用equals方法
- EventListenerList
使用unsafe类来实现修改私有属性
1 | Class unsafeClass = Class.forName("sun.misc.Unsafe"); |
另一种绕过JDK17无法反射修改私有属性
通过java agent hook掉判断是否可以访问属性的方法
MyAgent
1 | public class MyAgent { |
MyClassFileTransformer
1 | public class MyClassFileTransformer implements ClassFileTransformer { |
参考
Javolution出题小记 | H4cking to the Gate . (h4cking2thegate.github.io)
[DubheCTF 2024 Web Writeup - Boogiepop Doesn’t Laugh (boogipop.com)](https://boogipop.com/2024/03/19/DubheCTF 2024 Web Writeup/#Javolution)
2024 XCTF 联赛 DubheCTF 部分题解 - S1uM4i
AliyunCTF2024Chain17简析
先来审一下题目吧
两个服务,暴露了一个agent服务的端口。agent添加了命令允许访问模块内部的包(将java.base
模块中的java.util.concurrent.atomic
包开放给所有未命名的模块)。
这个agent也是直接给出了Hessian反序列化的路由
server也是有read路由但是是JDK反序列化。思路也比较清晰,就找链子打agent再打server。
题解
对依赖包触发方法积累不是特别多,这题就知道jackson触发getter方法。JDK17。这里就看了看别人的wp来简单分析一下流程吧。
ttonys/CodeQLAnalyseJar: CodeQL分析闭源Jar包脚本,基于Apache Ant构建CodeQL数据库 (github.com)
打agent是
h2执行sql命令来RCE
JDBC-Attack 攻击利用汇总 - 先知社区 (aliyun.com)
1 | import cn.hutool.core.map.SafeConcurrentHashMap; |
打server是一条新的利用链,看有人用CodeQL审出来了。
有两个问题一个是如何引入题目jar包的依赖,还有一个是CodeQL怎么去生成jar包的database。。。期末了,放到暑假解决吧。
总结
之前一直在看漏洞,找了几个题目练习了一下。发现CTF中确实有很多细节值得注意,感觉对于题目的整体把握比较重要,信息收集及时找到思路来寻找漏洞进行攻击,经验比较少,而且这些漏洞利用也比较复杂。要期末了,重点学一下,计算机网络和操作系统吧。CTF还是要机灵一点。
再学一下攻击JDBC,国赛也考到了,最近才注意到的点。