FastJson库是Java的一个Json库,其作用是将Java对象转换成json数据来表示,也可以将json数据转换成Java对象。使用非常方便,号称是执行速度最快的库,看了一下使用的那个算法的分析,确实挺快的。
在1.2.24版本的Fastjson出现了一个反序列化的漏洞,本篇将写一下具体的利用和漏洞的分析,先写利用。
运行环境:
apache-tomcat-7.0.85 java version "1.8.0_60" Java(TM) SE Runtime Environment (build 1.8.0_60-b27) Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)NGLIU.TTC,PMingLiUMSGOTHIC.TTC,MS UI GothicMALGUN.TTF,128,9 windows7
开始写之前吐槽一下firefox的新版hackbar,对于比较多的字节,POST不过去,搞得我以为payload有问题,纠结了半天,结果用burp成功了。
将FastJson跑起来,打开是这样的。
然后我们将POC用JavaC编译成class文件并且转换成base64编码。
这里不要用IDE的编译器去编译,结果是不一样的,具体javac的编译器与ide的编译器的差异性,还没开始了解,已经加入日程。
编译后的class文件base64处理
使用burp发一个post包。
然后接下来看看漏洞分析
我用IDEA导入了war,把网站架了起来,这里讲一下细节吧,主要是怕自己会忘。
#1 首先将war包放到tomcat的webapp目录下,让tomcat去解压出来,然后会看到网站正常打开。
#2 关掉tomcat,把解压出来的目录复制到另外一个盘下。
#3 用IDEA打开这个目录,创建web.xml文件 IDEA会自动提醒你创建
#4 然后添加SDK的外部lib和tomcat7.0的lib
根据fastjson官方对这个bug的修复,我们可以准确定位到相关类文件
https://github.com/alibaba/fastjson/commit/d52085ef54b32dfd963186e583cbcdfff5d101b5
那么根据官方的修复,加了一个autotype的黑名单验证,如果反序列化类在黑名单里的,就禁止反序列化。
根据官方的修复方案,断点分别如下:
可以看到这里我们提交的json进来了,那么进来之后,要做的是decode,然后是反序列化,这样才行执行我们的恶意代码。
然后分析就陷入了僵局,很蛋疼,最后看了一下绿盟的师傅的分析,发现自己还是太菜了,直接在exec下断点,看调用栈发现三个方法
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}
在这个类中,会调用newTransofrmer,看看newTransofrmer方法
public synchronized Transformer newTransformer()
throws TransformerConfigurationException
{
TransformerImpl transformer;
transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
_indentNumber, _tfactory);
if (_uriResolver != null) {
transformer.setURIResolver(_uriResolver);
}
if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
transformer.setSecureProcessing(true);
}
return transformer;
}
在这个方法看到了getTransletInstance方法,看看这个方法
private Translet getTransletInstance()
throws TransformerConfigurationException {
try {
if (_name == null) return null;
if (_class == null) defineTransletClasses();
// The translet needs to keep a reference to all its auxiliary
// class to prevent the GC from collecting them
AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
translet.postInitialization();
translet.setTemplates(this);
translet.setServicesMechnism(_useServicesMechanism);
translet.setAllowedProtocols(_accessExternalStylesheet);
if (_auxClasses != null) {
translet.setAuxiliaryClasses(_auxClasses);
}
return translet;
}
catch (InstantiationException e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
catch (IllegalAccessException e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
}
发现这个方法比较牛逼,这个方法是newinstance的,new完之后直接调用exec执行外部命令了,但是在这之前,要先调用defineTransletClasses方法,我们看看这个方法
private void defineTransletClasses()
throws TransformerConfigurationException {
if (_bytecodes == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
throw new TransformerConfigurationException(err.toString());
}
TransletClassLoader loader = (TransletClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
}
});
try {
final int classCount = _bytecodes.length;
_class = new Class[classCount];
if (classCount > 1) {
_auxClasses = new Hashtable();
}
for (int i = 0; i < classCount; i++) {
_class[i] = loader.defineClass(_bytecodes[i]);
final Class superClass = _class[i].getSuperclass();
// Check if this is the main class
if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
_transletIndex = i;
}
else {
_auxClasses.put(_class[i].getName(), _class[i]);
}
}
if (_transletIndex < 0) {
ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
}
catch (ClassFormatError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
catch (LinkageError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
}
这个方法直接new了一个类,从_bytecodes里,_bytecodes存放的是我们的evilcode,然后利用ACC去提升了一下权限,doPrivileged的成功执行,然后new了一个类。
然后回到getTransletInstance方法,执行外部命令
Fastjson 1.2.24 反序列化分析
https://www.secpulse.com/archives/72391.html