问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
ADSelfService Plus未授权RCE漏洞(CVE-2021-40539)利用与分析
漏洞分析
睡一觉就把漏洞给分析了
#### 题外话 故事要从一段中式英语对话开始说起,起先是看到这是个危险程度严重的新漏洞,然后疯狂在官网找相应漏洞版本的应用找不到,就更有意思了(谁知道暂时没客服回应我,可能是因为美国时间客服正在睡觉吧) [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-4730ccc016db62c335d702d5f55b525075bda930.png)](1) 于是睡了一觉,早上看到邮件。于是就兴奋的开始了漏洞复现,顺便细心分析了一波(好心人已公开链接,不过是免费版) [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-2c6b4c51c07c0c92d6131e6dc07ff5bd1854f455.png)](2) #### 步入正题 漏洞描述 ---- ZOHO ManageEngine ADSelfService Plus是美国卓豪(ZOHO)公司的针对 Active Directory 和云应用程序的集成式自助密码管理和单点登录解决方案。 [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-e5cf28557eecafe7014d00376156c517bd047bbc.png)](3) Zoho ManageEngine ADSelfService Plus 6113版本及更早版本存在授权问题漏洞,该漏洞源于软件很容易绕过REST API认证,从而导致远程代码执行。 漏洞利用与分析 ------- 应用搭建安装后的效果如下(首次及默认账号密码为admin\\admin): [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-20a11a223f0a0cc23b932165290b6da8d47d0643.png)](4) 根据官方修复资料来看,先去 `ManageEngineADSFrameworkJava.jar`中 `com.manageengine.ads.fw.api.RestAPIUtil`类看看验证绕过漏洞的修复前后 ```java public static boolean isRestAPIRequest(HttpServletRequest request, JSONObject filterParams) { String restApiUrlPattern = "/RestAPI/.*"; try { restApiUrlPattern = filterParams.optString("API_URL_PATTERN", restApiUrlPattern); } catch (Exception ex) { out.log(Level.INFO, "Unable to get API_URL_PATTERN.", ex); } String reqURI = request.getRequestURI(); String contextPath = request.getContextPath() != null ? request.getContextPath() : ""; reqURI = reqURI.replace(contextPath, ""); reqURI = reqURI.replace("//", "/"); return Pattern.matches(restApiUrlPattern, reqURI); } ``` 修复后的reqURI将通过`getNormalizedURI`方法对url进行`/.`和`/..`的过滤和多余/的删除 [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-87a60c6d676d40bb8e23463ac5aa1888039c925a.png)](5) 根据 <https://github.com/projectdiscovery/nuclei-templates/blob/77c3dc36ac7df4c04e3ff7cd97f5f63ec8dc7311/cves/2021/CVE-2021-40539.yaml> 的描述,尝试发送验证是否存在身份验证绕过的漏洞。经测试url路径前以下字符均可绕过身份验证 ```java /./ /./// ///./// /.//./// /../ ``` 返回包表明路径遍历请求实际上绕过了认证过程 [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-2db5adf2948d876b7fd9d56be3f1e7dcbd9bd3e9.png)](6) 接下来利用`/./`的身份验证绕过实现任意及恶意文件的上传,通过跟踪漏洞中所利用到的文件上传的接口,找到 `AdventNetADSMClient.jar`中的 `LogonCustomization`类,类中的`unspecified`方法( 原本`unspecified`是添加智能卡相关配置参数的方法)存在与文件上传相关的函数,对`unspecified`方法进行分析,确定了达到文件上传条件所需要的参数 [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-942cfd881b6a662755d6b03d255111aee10699f8.png)](7) [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-0742d8fdc1f307a82bd2b735deb45bf904537efd.png)](8) 试着跟进`sCAction.addSmartCardConfig()`看看是如何写文件的,`sCAction.addSmartCardConfig()` —> `mapping.findForward(addSmartCardConfig()` —> `FileActionHandler.getFileFromRequest()`,结果是直接将form下要上传的文件内容以form中`filename`参数命名方式直接写进了`应用启动程序bin`目录下,还以为会稍微有点过滤 [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-ecd72757e86e6728a9920baae9393e08dbca1d50.png)](9) [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-1f134d12ffec5b41be576b0fcf8200b7f2c651e7.png)](10) 那这边我先上传个编译后的弹计算器的`class`文件,以备后续使用 [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-3736290740398cca61fb19ebccd67c7af25c637d.png)](11) 接着在`AdventNetADSMClient.jar`下`ConnectionAction`类的`openSSLTool`方法,可不需要过滤接受http请求下的`action`参数值,如果等于`generateCSR`即执行`SSLUtil.createCSR()` [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-cb51a4ff864223c55bc1e31e7ad8b72f7f80bcc9.png)](12) 跟踪`SSLUtil.createCSR()`,映入眼帘的是keytool这个工具的执行和执行keytool所必需的参数,这边看到`keysize`和`validity`这两个参数值可通过http传入可控且没受任何过滤,将拼接进`keyCmd`的keytool命令执行语句中,最后执行`runCommand(keyCmd.toString());` ```java public class SSLUtil { private static Logger logger = Logger.getLogger(SSLUtil.class.getName()); public static void createCSR(HttpServletRequest request) throws Exception { JSONObject sslParams = new JSONObject(); sslParams.put("COMMON_NAME", request.getParameter("NAME")); sslParams.put("SAN_NAME", request.getParameter("SAN_NAME")); sslParams.put("OU", request.getParameter("OU")); sslParams.put("ORGANIZATION", request.getParameter("ORGANIZATION")); sslParams.put("LOCALITY", request.getParameter("LOCALITY")); sslParams.put("STATE", request.getParameter("STATE")); sslParams.put("COUNTRY_CODE", request.getParameter("COUNTRY_CODE")); sslParams.put("PASSWORD", request.getParameter("PASSWORD")); sslParams.put("VALIDITY", request.getParameter("VALIDITY")); sslParams.put("KEY_LENGTH", request.getParameter("KEY_LENGTH")); JSONObject csrStatus = createCSR(sslParams); if (csrStatus.has("eStatus")) { request.setAttribute("status", customizeError(csrStatus.optString("eStatus", null))); } else { request.setAttribute("status", "success"); } } public static JSONObject createCSR(JSONObject sslSettings) throws Exception { String name = "\"" + sslSettings.optString("COMMON_NAME", null) + "\""; String pass = sslSettings.optString("PASSWORD", null); pass = ClientUtil.keyToolEscape(pass); String password = "\"" + pass + "\""; logger.log(Level.INFO, "Keystore will be created for " + name); File keyFile = new File("..\\jre\\bin\\SelfService.keystore"); if (keyFile.exists()) { File bckFile = new File("..\\jre\\bin\\SelfService_" + System.currentTimeMillis() + ".keystore"); keyFile.renameTo(bckFile); } StringBuilder keyCmd = new StringBuilder("..\\jre\\bin\\keytool.exe -J-Duser.language=en -genkey -alias tomcat -sigalg SHA256withRSA -keyalg RSA -keypass "); keyCmd.append(password); keyCmd.append(" -storePass ").append(password); String keyLength = sslSettings.optString("KEY_LENGTH", null); if ((keyLength != null) && (!keyLength.equals(""))) { keyCmd.append(" -keysize ").append(keyLength); } String validity = sslSettings.optString("VALIDITY", null); if ((validity != null) && (!validity.equals(""))) { keyCmd.append(" -validity ").append(validity); } String san_name = sslSettings.optString("SAN_NAME", null); keyCmd.append(" -dName \"CN=").append(ClientUtil.keyToolEscape(sslSettings.optString("COMMON_NAME", null))); keyCmd.append(", OU= ").append(ClientUtil.keyToolEscape(sslSettings.optString("OU", null))); keyCmd.append(", O=").append(ClientUtil.keyToolEscape(sslSettings.optString("ORGANIZATION", null))); keyCmd.append(", L=").append(ClientUtil.keyToolEscape(sslSettings.optString("LOCALITY", null))); keyCmd.append(", S=").append(ClientUtil.keyToolEscape(sslSettings.optString("STATE", null))); keyCmd.append(", C=").append(ClientUtil.keyToolEscape(sslSettings.optString("COUNTRY_CODE", null))); keyCmd.append("\" -keystore ..\\jre\\bin\\SelfService.keystore"); if ((san_name != null) && (!san_name.equals(""))) { keyCmd.append(" -ext SAN="); String[] san_name_arr = san_name.split(","); for (int i = 0; i < san_name_arr.length; i++) { if (i != 0) { keyCmd.append(","); } keyCmd.append("dns:" + ClientUtil.keyToolEscape(san_name_arr[i])); } } JSONObject jStatus = new JSONObject(); String status = runCommand(keyCmd.toString()); ``` 看到`runCommand(keyCmd.toString());`,顾名思义毫无疑问就是直接执行命令,跟踪`runCommand` —> `RunCmd` —> `super(cmd)` 寻其父类 `runRuntimeExec()`,很明显通过`Runtime.getRuntime`执行了该命令 [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-b3c4f2b0e649d8155e47974e915409fdc45ec52a.png)](13) `import com.adventnet.sym.adsm.common.server.util.RunCmd;` [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-699cea8bd4d21d16669ae39767122a4a502b20a2.png)](14) 刚刚说到`keysize`和`validity`这两个参数值可通过http传入可控且没受任何过滤,那么我们可以通过这两个参数进行keytool中有效命令的注入。该程序自带keytool,那么看看其在创建密钥时可用到的options,在可用options可以看到`-providerclass`和`-providerpath`两个可选参数以执行二进制文件即上面所传入的恶意`class文件` [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-2ed0dbe7027387d12900de7a59998de845a798f1.png)](15) 发送以下poc去执行刚刚上传恶意class文件内容的jsp文件 [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-ca9f6a3bd8685fd7d755b51d53399fe222d63b67.png)](16) 原先任意文件上传默认上传在bin目录下,而keytool也是在当前目录下,那么`就可以通过keytool的命令注入有效参数以实现来加载任意二进制的java类即class类` [![](https://shs3.b.qianxin.com/attack_forum/2021/11/attach-f5881456b1fed20128b1f266de0886a7595ce1b4.png)](17) #### 总结 将以上每个漏洞利用的过程结合在一块就是通过`/./`绕过`RestAPI`的身份验证以上传任意文件到`程序启动bin`目录下,接着利用`keytool命令`的执行拼接有效参数,指向任意`class类以实现任意代码的执行` 修复建议 ---- - 目前厂商已发布升级补丁以修复漏洞,补丁获取链接:<https://www.manageengine.com/products/self-service-password/service-pack.html> - 这是官方文档中给出的危险检测工具,则是通过日志报错信息、相应路径下是否含有key和证书、不常规web路径下是否含有jsp文件去检测是否被入侵 <https://www.manageengine.com/products/self-service-password/kb/how-to-fix-authentication-bypass-vulnerability-in-REST-API.html>
发表于 2021-11-12 16:57:59
阅读 ( 10154 )
分类:
漏洞分析
1 推荐
收藏
0 条评论
请先
登录
后评论
w1nk1
12 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!