""" 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(' 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', self.auth_types[auth_type]) ipmi += pack('>B', self.priv_lvls[priv_lvl]) ipmi += authcode #challenge string 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('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', 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('" 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: yongchuan.koh@mwrinfosecurity.com\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: "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()