复现
路径:/evo-apigw/admin/API/Developer/GetClassValue.jsp
数据包:
{"data": {"clazzName": "com.dahua.admin.util.RuntimeUtil","methodName": "syncexecReturnInputStream","fieldName": ["id"] }}
注意一下发包数据格式为json方式,数据包拼接好后发包即可
响应体里可以看到我们传递的命令执行参数的返回值
原理
从漏洞复现角度可以看到给fieldName参数传递系统命令直接会造成rce且有回显
反编译相关jar包进入Developer路由控制器内,全局搜索GetClassValue这个接口,它被定义为@RequestMapping可以被http请求远程加载,247行定义了一个info封装响应信息类getCache
getCache它的返回信息由HttpResult封装后打印在Responsebody内容里,例如exp打成功后的样子
代码247到254行所做的是把响应体通过代码封装然后回显出来,如果没有这点,将构成无回显命令执行,不过这里不是本次研究重点,不再赘述
257之后行代码声明了两个string类型字符串,分别是clazzName ,methodName,他们都能远程传递数据在之前的249行
249 "Map<Object, Object> data = requestInfo.getData();"
data被声明出了Map接口,这使得里面的数据都可以通过键值对应的方式传递,重点是
257 "String clazzName = (String) data.get('clazzName');"
clazzName参数可以被 Class.forName(clazzName);直接加载,这导致如果代码内存在其他类之间父类的子类,继承类也将会被调用其下的方法也是如此,这构成了Rce的第一个条件,poc中clazzName的值是com.dahua.admin.util.RuntimeUtil,为什么是他?
定位到com/dahua/admin/util/RuntimeUtil.class类里看一下,首先他是公开类,不包含父子继承关系,这使得构造链变得极为简单直接传入public类名即可,它的类里内存在syncexecReturnInputStream方法,你可以理解为一个和runtime类似的方法体,这使得声明的类不通过实例化即可调用,得益于开发人员赋予的public static属性,主要就是cmd参数覆盖问题了,这要求入参可控即可传递系统命令
再来看一下fieldName参数数组传值问题,代码要求传递的类型是要数组list类型,然后判断这个数组内的元素长度,如果大于0将进行下一步判断,这里是为了防止参数非空,isNotBlank做非空检查传入的methodName值是否存在,这里给他传的是syncexecReturnInputStream方法,随即就被调用了
243行指定类,方法,参数的构造分别指向,com.dahua.admin.util.RuntimeUtil,syncexecReturnInputStream,cmd参数=fieldName,然后这时候只是构造成功了但是缺少反射调用,所以245行开发人员又实现了个
245 "Object object = method.invoke(instance, new Object[0]);"
,object反射调用前面构造好的类和方法,并传入cmd相对应的参数达到命令执行效果
原文始发于微信公众号(进击安全):某华命令执行Rce原理分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论