diff --git a/README.md b/README.md index ddcdf9f..26aef1f 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,8 @@ Publically published Sep 4, 2018 -Vibe is a tool designed to preform post-ex lateral movement techniques while remaining undetected by network detection tools including Threat Hunting appliances. -Vibe works by pulling down all information about a domain, allowing users to perform the same domain net commands offline. Vibe also enumerates additional information that is not typically shown in these queries. -Vibe also provides the ability to scan systems to see what shares are available and what privileges the account used, has access to. Vibe also provides the ability to enumerate user’s currently logged into systems, as well as, who has been logged in, while remaining undetected. + +Vibe is a tool designed to perform post-ex lateral movement techniques while remaining undetected by network detection tools including Threat Hunting appliances. Vibe works by pulling down all information about a domain, allowing users to perform the same domain net commands offline. Vibe also enumerates additional information that is not typically shown in these queries. Vibe also provides the ability to scan systems to see what shares available and what privileges the account, has access to. Vibe also provides the ability to enumerate users currently logged into systems, as well as who has been logged in, while remaining undetected. @@ -12,7 +11,7 @@ Vibe also provides the ability to scan systems to see what shares are available ## Installation -Vibe was developed with Python version 2.7 +Vibe was developed with Python version 2.7 3.0 Tested and supported on Kali Linux and Ubuntu. @@ -27,8 +26,8 @@ Vibe uses the following external dependencies: To install run following commands: ``` -sudo apt-get install libsasl2-dev python-dev libldap2-dev libssl-dev -pip install -r requirements.txt +sudo apt-get install libsasl2-dev python3-dev libldap2-dev libssl-dev +pip3 install -r requirements.txt ``` @@ -37,9 +36,8 @@ pip install -r requirements.txt ``` -./vibe.py -h -usage: main [-h] -U username -P password -D domain -I IP [-o] [-r] [-p PORT] - [-u] +~/Vibe# python3 ./vibe.py -h +usage: main [-h] -U username [-P password] -D domain -I IP [-o] [-r] [-p PORT] [-u] optional arguments: -h, --help show this help message and exit @@ -53,9 +51,7 @@ optional arguments: -o, --offline Offline Mode -r, --remove Remove Database -p PORT, --port PORT Specify a specific port to connect on (default is 636) - -u, --unencrypted Specify a specific for unencrypted mode (if LDAPS is - not available) - + -u, --unencrypted Specify a specific for unencrypted mode (if LDAPS is not available) ``` @@ -78,13 +74,14 @@ root@kali:~/# ./vibe.py -U admin -P Password! -D STARLABS.local -I 172.16.144.18 (@Tyl0us) -[+] Credentials Valid, Generating Database -[+] Table 1/4 : Generating Group Table -[+] Table 2/4 : Generating User Table -[+] Table 3/4 : Generating Computer Table -[+] Table 4/4 : Generating Password Policy Table -[+] Sucessful Database Created -0.434292078018 +[+] Credentials valid, generating database +[+] Table 1/5 : Generating User Table +[+] Table 2/5 : Generating Group Table +[+] Table 3/5 : Generating Computer Table +[+] Table 4/5 : Generating Password Policy Table +[+] Table 5/5 : Generating SPN Table +[+] Database successfully created +[*] 0.11863517761230469 >>help Commands ======== @@ -99,7 +96,7 @@ search Searches for a key word(s) through every field of every tab share_hunter Scans target(s) enumerating the shares on the target(s) and the level of access the specified user, using -d/--domain, -u/--user, -p/--password. Can take a list or range of hosts, using -t/--target and --jitter/-j to add a delay show Shows the contents of Users, Computers, Credentials, Groups, Password policy, Store, Credentials, Files Servers and Access tables store Displays the contents of a specific table. Example: 'show [table name] (access, creds, computers, file servers, pwdpolicy, users) -export Export the contents of the database to a path in one of the following formats: CSV, HTML. (using with -f or --filetype) +export Export the contents of the database to a path in one of the following formats: csv, html. (using with -f or --filetype and -p or --path for the file path) exit Exit Vibe >> @@ -176,7 +173,8 @@ There are several different options that can be used with the ```show``` command * file servers * groups * pwdpolicy -* store +* store +* spn * users Below are some examples of the information stored: diff --git a/lib/AD.py b/lib/AD.py index 7cd705b..a71455b 100644 --- a/lib/AD.py +++ b/lib/AD.py @@ -36,6 +36,7 @@ def ldap_query(self,l, base_dn, subtree, objectFilter, attrs): def ADtime(self, time): global date + time = time.replace( "'", "" ) seconds = int(time) / 10000000 if seconds == 0: epoch = 0 @@ -81,13 +82,13 @@ def main(self, IP, lusername, domain, password, unencrypted, port): try: con.simple_bind_s(lusername + '@' + domain, password) - print colors.GRN + "[+] "+ colors.NRM + "Credentials valid, generating database" + print(colors.GRN + "[+] "+ colors.NRM + "Credentials valid, generating database") except ldap.INVALID_CREDENTIALS: - print colors.RD + "[-] "+ colors.NRM + "Username or password is incorrect." + print(colors.RD + "[-] "+ colors.NRM + "Username or password is incorrect.") os.remove ( ".db/" + domain + ".db") sys.exit() except ldap.SERVER_DOWN: - print colors.RD + "[-] "+ colors.NRM + "Domain Controller either down or unreachable" + print(colors.RD + "[-] "+ colors.NRM + "Domain Controller either down or unreachable") os.remove(".db/" + domain + ".db") sys.exit() dictionary=['distinguishedName', 'sAMAccountName'] @@ -96,8 +97,9 @@ def main(self, IP, lusername, domain, password, unencrypted, port): group_attributes = ['distinguishedName', 'sAMAccountName', 'description', 'memberOf', 'member', 'objectSid'] computer_attributes = ['description', 'memberOf', 'operatingSystem', 'operatingSystemVersion'] FGPP_attributes = ['msDS-LockoutDuration', 'msDS-MaximumPasswordAge', 'msDS-LockoutThreshold', 'msDS-MinimumPasswordLength', 'msDS-PasswordComplexityEnabled', 'msDS-PasswordHistoryLength','msDS-PSOAppliesTo','pwdProperties'] + Kerb_attributes=['servicePrincipalName', 'sAMAccountName', 'description', 'pwdLastSet', 'memberOf'] searchScope = ldap.SCOPE_SUBTREE - object = ['user', 'group', 'computer', 'msDS-PasswordSettings', 'FGPP-PasswordSettings'] + object = ['user', 'group', 'computer', 'msDS-PasswordSettings', 'FGPP-PasswordSettings', 'SPN'] if unencrypted == True: l = ldap.initialize('ldap://' + IP) else: @@ -112,26 +114,30 @@ def main(self, IP, lusername, domain, password, unencrypted, port): try: for obj in object: searchFilter = '(&(objectCategory=' + obj + '))' + if obj == 'SPN': + print(colors.GRN + "[+] " + colors.NRM + "Table 5/5 : Generating SPN Table") + searchFilter = "(&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512)(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(!(objectCategory=computer)))" + results = self.ldap_query(l, baseDN, searchScope, searchFilter, Kerb_attributes) if obj == 'group': - print colors.GRN + "[+] " + colors.NRM + "Table 1/4 : Generating Group Table" + print(colors.GRN + "[+] " + colors.NRM + "Table 2/5 : Generating Group Table") results = self.ldap_query(l, baseDN, searchScope, searchFilter, group_attributes) memberof = [] Desc = [] username = [] - dictionary = dict ( zip ( usernamelist, samnamelist ) ) + dictionary = dict ( list(zip( usernamelist, samnamelist )) ) if obj == 'user': - print colors.GRN + "[+] " + colors.NRM + "Table 2/4 : Generating User Table" + print(colors.GRN + "[+] " + colors.NRM + "Table 1/5 : Generating User Table") results = self.ldap_query ( l, baseDN, searchScope, searchFilter, user_attributes ) memberof = [] Desc = [] username = [] if obj == 'computer': - print colors.GRN + "[+] " + colors.NRM + "Table 3/4 : Generating Computer Table" + print(colors.GRN + "[+] " + colors.NRM + "Table 3/5 : Generating Computer Table") results = self.ldap_query ( l, baseDN, searchScope, searchFilter, computer_attributes ) memberof = [] Desc = [] if obj == 'msDS-PasswordSettings': - print colors.GRN + "[+] " + colors.NRM + "Table 4/4 : Generating Password Policy Table" + print(colors.GRN + "[+] " + colors.NRM + "Table 4/5 : Generating Password Policy Table") results = l.search_s( baseDN, ldap.SCOPE_BASE ) if obj == 'FGPP-PasswordSettings': FGPPDN = 'CN=Password Settings Container,CN=System,' + (baseDN) @@ -145,42 +151,42 @@ def main(self, IP, lusername, domain, password, unencrypted, port): results_attrs = result[1] name = result_dn[0] if 'sAMAccountName' in results_attrs: - sAMAccountName = str( results_attrs['sAMAccountName'] ).replace( '[', '' ).replace( ']', '' )[1:-1] + sAMAccountName = str( results_attrs['sAMAccountName'] ).replace( '[b\'', '' ).replace( '\']', '' ) else: sAMAccountName = "" if 'description' in results_attrs: - description = str( results_attrs['description'] ).replace( '[', '' ).replace( ']', '' )[1:-1] + description = str( results_attrs['description'] ).replace( '[b\'', '' ).replace( '\']', '' ) else: description = "" if 'profilePath' in results_attrs: - profilepath = str( results_attrs['profilePath'] ).replace( '[', '' ).replace( ']', '' )[ + profilepath = str( results_attrs['profilePath'] ).replace( '[b\'', '' ).replace( '\']', '' )[ 1:-1] else: profilepath = "" if 'homeDirectory' in results_attrs: - homeDirectory = str ( results_attrs['homeDirectory'] ).replace ( '[', '' ).replace ( ']', '' )[ - 1:-1] + homeDirectory = str ( results_attrs['homeDirectory'] ).replace ( '[b\'', '' ).replace ( '\']', '' ) else: homeDirectory = "" if 'pwdLastSet' in results_attrs: - time = str( results_attrs['pwdLastSet'] ).replace( '[', '' ).replace( ']', '' )[1:-1] + time = str( results_attrs['pwdLastSet'] ).replace( '[b\'', '' ).replace( '\']', '' ) self.ADtime( time ) pwdLastSet = date else: pwdLastSet = "" if 'lastLogon' in results_attrs: - time = str( results_attrs['lastLogon'] ).replace( '[', '' ).replace( ']', '' )[1:-1] + time = str( results_attrs['lastLogon'] ).replace( '[b\'', '' ).replace( '\']', '' ) self.ADtime( time ) lastLogon = date else: lastLogon = "" if 'primaryGroupID' in results_attrs: - gidnum = str( results_attrs['primaryGroupID'] ).replace( '[', '' ).replace( ']', '' ).replace('\'', '' ) + gidnum = str( results_attrs['primaryGroupID'] ).replace( '[b\'', '' ).replace( '\']', '\n' ) else: pass if 'memberOf' in results_attrs: membersOf = [] for memberOf in results_attrs["memberOf"]: + memberOf = memberOf.decode("utf-8") r = memberOf.split( ',CN=' )[0].replace( 'CN=', '' ).replace(',Builtin,'+baseDN+'', '' ).replace(baseDN,'').replace('OU=',' ') r = r.split(',') membersOf.append(r[0]) @@ -196,8 +202,9 @@ def main(self, IP, lusername, domain, password, unencrypted, port): del trimmed_results['description'] if 'memberOf' in trimmed_results: del trimmed_results['memberOf'] - for key, value in trimmed_results.iteritems (): + for key, value in trimmed_results.items (): for v in value: + v = v.decode("utf-8") t = v.split ( ',CN=' )[0].replace ( 'CN=', '' ).replace ( baseDN,' ' ).replace ( 'OU=',' ' ) t = t.split ( ',' ) if str ( t[0] ) in usernamelist: @@ -207,6 +214,7 @@ def main(self, IP, lusername, domain, password, unencrypted, port): else: for member in results_attrs["member"]: + member = member.decode("utf-8") t = member.split ( ',CN=' )[0].replace ( 'CN=', '' ).replace ( baseDN, ' ' ).replace ( 'OU=', ' ' ) t = t.split ( ',' ) mem.append ( str ( t[0] ) ) @@ -235,54 +243,54 @@ def main(self, IP, lusername, domain, password, unencrypted, port): 'PASSWORD_EXPIRED': 0x00800000 } UAC = results_attrs["userAccountControl"] - for trigger, val in uac.items(): - vUAC = ''.join(UAC) + for trigger, val in list(uac.items()): + vUAC = b''.join(UAC) if int(vUAC) & val: userac.append(trigger) userac = str(userac).replace('[','').replace(']','').replace(',','\n').replace(' ', '').replace('\'','') if 'operatingSystem' in results_attrs: - OS = str(results_attrs["operatingSystem"]).replace('\']', '').replace('[\'', '') + OS = str(results_attrs["operatingSystem"]).replace('\']', '').replace('[b\'', '') else: OS = "" if 'operatingSystemVersion' in results_attrs: - OSV = str( results_attrs["operatingSystemVersion"] ).replace( '\']', '' ).replace( '[\'', '' ) + OSV = str( results_attrs["operatingSystemVersion"] ).replace( '\']', '' ).replace( '[b\'', '' ) else: OSV = "" if "msDS-MinimumPasswordLength" in results_attrs: - FGminpwd = (str ( results_attrs["msDS-MinimumPasswordLength"] ).replace ( '[\'', '' ).replace ('\']', '' )) + FGminpwd = (str ( results_attrs["msDS-MinimumPasswordLength"] ).replace ( '[b\'', '' ).replace ('\']', '' )) else: FGminpwd = ' ' if "msDS-LockoutThreshold" in results_attrs: - FGlcknum = (str ( results_attrs["msDS-LockoutThreshold"] ).replace ( '[\'', '' ).replace ( '\']', '' )) + FGlcknum = (str ( results_attrs["msDS-LockoutThreshold"] ).replace ( '[b\'', '' ).replace ( '\']', '' )) else: FGlcknum = ' ' if "msDS-PasswordHistoryLength" in results_attrs: - FGpwdhis = (str ( results_attrs["msDS-PasswordHistoryLength"] ).replace ( '[\'', '' ).replace ('\']', '' )) + FGpwdhis = (str ( results_attrs["msDS-PasswordHistoryLength"] ).replace ( '[b\'', '' ).replace ('\']', '' )) else: FGpwdhis = ' ' if "msDS-LockoutDuration" in results_attrs: FGlckduration = results_attrs["msDS-LockoutDuration"] - FGlckdur = (str ((int ( str ( FGlckduration ).replace ( '[\'-', '' ).replace ( '\']', '' ) ) * 0.0000001) / 60 )) + FGlckdur = (str ((int ( str ( FGlckduration ).replace ( '[b\'-', '' ).replace ( '\']', '' ) ) * 0.0000001) / 60 )) else: FGlckdur = ' ' if "msDS-PasswordComplexityEnabled" in results_attrs: - FGpwdp = (str ( results_attrs["msDS-PasswordComplexityEnabled"] ).replace ( '[\'', '' ).replace ( '\']', '' )) + FGpwdp = (str ( results_attrs["msDS-PasswordComplexityEnabled"] ).replace ( '[b\'', '' ).replace ( '\']', '' )) else: FGpwdp = ' ' if "msDS-PSOAppliesTo" in results_attrs: - FGmember = (str ( results_attrs["msDS-PSOAppliesTo"] ).replace ( '[\'', '' ).replace ( '\']', '' ).replace (',CN=Users,DC=STARLABS,DC=local', '\n' ).replace('CN=','')) + FGmember = (str ( results_attrs["msDS-PSOAppliesTo"] ).replace ( '[b\'', '' ).replace ( '\']', '' ).replace (',CN=Users,DC=STARLABS,DC=local', '\n' ).replace('CN=','')) else: FGmember = ' ' if obj == 'msDS-PasswordSettings': pwdpar = [] - minpwd = str( results_attrs["minPwdLength"] ).replace( '[\'', '' ).replace( '\']', '' ) - lcknum = str( results_attrs["lockoutThreshold"] ).replace( '[\'', '' ).replace( '\']', '' ) - pwdhis = str( results_attrs["pwdHistoryLength"] ).replace( '[\'', '' ).replace( '\']', '' ) + minpwd = str( results_attrs["minPwdLength"] ).replace( '[b\'', '' ).replace( '\']', '' ) + lcknum = str( results_attrs["lockoutThreshold"] ).replace( '[b\'', '' ).replace( '\']', '' ) + pwdhis = str( results_attrs["pwdHistoryLength"] ).replace( '[b\'', '' ).replace( '\']', '' ) lckdur = results_attrs["lockoutDuration"] lckdur = str( - (int( str( lckdur ).replace( '[\'-', '' ).replace( '\']', '' ) ) * 0.0000001) / 60 ) - pwdp = str( results_attrs["pwdProperties"] ).replace( '[\'', '' ).replace( '\']', '' ) + (int( str( lckdur ).replace( '[b\'-', '' ).replace( '\']', '' ) ) * 0.0000001) / 60 ) + pwdp = str( results_attrs["pwdProperties"] ).replace( '[b\'', '' ).replace( '\']', '' ) pwdpa = { 'DOMAIN_PASSWORD_COMPLEX': 1, 'DOMAIN_PASSWORD_NO_ANON_CHANGE': 2, @@ -290,25 +298,30 @@ def main(self, IP, lusername, domain, password, unencrypted, port): 'DOMAIN_LOCKOUT_ADMINS': 8, 'DOMAIN_PASSWORD_STORE_CLEARTEXT': 16, 'DOMAIN_REFUSE_PASSWORD_CHANGE': 32} - for trigger, val in pwdpa.items(): - vpwdp = ''.join( pwdp ) + for trigger, val in list(pwdpa.items()): + vpwdp = ''.join( pwdp ).replace("[b'","") if int( vpwdp ) & val: pwdpar.append( trigger ) pwdpar = str( pwdpar ).replace( '[\'', '' ).replace( '\']', '' ) sql.Insert_Passwd(minpwd, lcknum, lckdur, pwdhis, (pwdp + " " + pwdpar)) + if 'servicePrincipalName' in results_attrs: + for SPN in results_attrs["servicePrincipalName"]: + SPN = SPN.decode("utf-8") + r = SPN.split( ',CN=' )[0].replace( 'CN=', '' ).replace(',Builtin,'+baseDN+'', '' ).replace(baseDN,'').replace('OU=',' ') + if obj == "SPN": + sql.Insert_SPN(SPN, sAMAccountName, description, pwdLastSet, memberOf) if obj == 'FGPP-PasswordSettings': sql.Insert_FGPasswd(FGminpwd, FGlcknum, FGpwdhis, FGlckdur, FGpwdp, FGmember) if obj == 'group': sql.Insert_Groups(name, sids, description, str( membersOf), str(mem)) if obj == 'user': - usernamelist.append(name) samnamelist.append(sAMAccountName) sql.insert_Users(sAMAccountName, description, sids, homeDirectory, profilepath, pwdLastSet, lastLogon, userac, str(gidnum), (str(membersOf))) if obj == 'computer': sql.Insert_Computers(name, description, OS, OSV, (str(membersOf))) except ldap.REFERRAL: - print colors.RD + "[-] "+ colors.NRM + "Incorrect fully qualified domain name. Please check your settings and try again." + print(colors.RD + "[-] "+ colors.NRM + "Incorrect fully qualified domain name. Please check your settings and try again.") sys.exit() sql.Close() # try: @@ -317,5 +330,5 @@ def main(self, IP, lusername, domain, password, unencrypted, port): # print colors.RD + "[-] " + colors.NRM + "Error occured generating list of file shares" # print "[*] Skipping Fileshare enumeration. As result show fileshare will not work (however manual queries will work" # pass - print colors.GRN + "[+] "+ colors.NRM + "Database successfully created" + print(colors.GRN + "[+] "+ colors.NRM + "Database successfully created") groupIDquery () diff --git a/lib/DB.py b/lib/DB.py index 3ae573b..9a2cc5b 100644 --- a/lib/DB.py +++ b/lib/DB.py @@ -54,6 +54,8 @@ def create_connection(): ("Minimum Password Length", "Lockout Threshold", "Lockout Duration", "Passwords Remembered", "Password Properties")''') conn.execute('''CREATE TABLE FGPasswordPolicyTB ("Minimum Password Length", "Lockout Threshold", "Lockout Duration", "Passwords Remembered", "Password Properties", 'members')''') + conn.execute('''CREATE TABLE SPNTB + ("SPN", "Username", "Description", "Password Last Set", "Member Of")''') except Error as e: conn.close() finally: @@ -92,7 +94,7 @@ def fileshare(): uname_list = uname_list.to_string ( header=False, index=False ) uname_list = uname_list.split () l3 = [x for x in FS_list if x in uname_list] - l3 = filter ( None, l3 ) + l3 = [_f for _f in l3 if _f] final = dp.DataFrame ( l3 ) final = final.drop_duplicates () final.to_sql("FileServer", connection, index=False, if_exists="replace") @@ -119,6 +121,9 @@ def Insert_Passwd(self, minpwd, lcknum, lckdur, pwdhis, pwdpar): def Insert_FGPasswd(self, FGminpwd, FGlcknum, FGpwdhis, FGlckdur, FGpwdp, FGmember): connection.execute ("insert into FGPasswordPolicyTB('Minimum Password Length', 'Lockout Threshold', 'Lockout Duration', 'Passwords Remembered', 'Password Properties', 'members') values (?,?,?,?,?,?)", (FGminpwd, FGlcknum, FGpwdhis, FGlckdur, FGpwdp, FGmember)) + def Insert_SPN(self, SPN, username, Desc, PLS, memberof): + connection.execute("insert into SPNTB ('SPN', 'Username', 'Description', 'Password Last Set', 'Member Of') values (?,?,?,?,?)", (SPN, username, Desc, PLS, memberof)) + def Close(self): connection.commit() connection.close() diff --git a/lib/net.py b/lib/net.py index cdf1913..5d3d7fc 100644 --- a/lib/net.py +++ b/lib/net.py @@ -28,7 +28,7 @@ def IP(netaddr): for iprange in IPNetwork(IPString): targets.append(str(iprange)) else: - print colors.RD + "[-] " + colors.NRM + "Invalid IP address" + print(colors.RD + "[-] " + colors.NRM + "Invalid IP address") else: range = IPString.split(' ') for ips in range: diff --git a/lib/smb.py b/lib/smb.py index 18fbebd..dafb4e7 100644 --- a/lib/smb.py +++ b/lib/smb.py @@ -28,7 +28,7 @@ def __init__(self, domain, username, password, jitter): self._lmhash = '' self._nthash = '' self._jitter = jitter - print "Scanning Using: " + self._domain + "\\" + self._user + ": " + self._password + print("Scanning Using: " + self._domain + "\\" + self._user + ": " + self._password) def id_generator(self, size=6, chars=string.ascii_uppercase + string.digits): return ''.join(random.choice(chars) for _ in range(size)) @@ -40,8 +40,8 @@ def share_hunter(self, targets): for target in targets: time.sleep(int(self._jitter)) try: - print target - print "-----------------" + print(target) + print("-----------------") smb = SMBConnection('*SMBSERVER', target, sess_port=445, timeout=10) smb.login(self._user, self._password, self._domain) list = smb.listShares() @@ -58,13 +58,13 @@ def share_hunter(self, targets): write = smb.createDirectory(share, path) if write: smb.deleteDirectory(share, path) - print " " + colors.GRN + "[+]" + colors.NRM + " " + share + ": Read\Write" + print(" " + colors.GRN + "[+]" + colors.NRM + " " + share + ": Read\Write") perm = 'Read\Write' except Exception: - print " " + colors.BLU + "[*]" + colors.NRM + " " + share + ": Read" + print(" " + colors.BLU + "[*]" + colors.NRM + " " + share + ": Read") perm = 'Read' except Exception: - print " " + colors.RD + "[-]" + colors.NRM + " " + share + ": No Access" + print(" " + colors.RD + "[-]" + colors.NRM + " " + share + ": No Access") del perm continue if perm: @@ -72,7 +72,7 @@ def share_hunter(self, targets): sharelist.append(share) permissionlist.append(perm) except Exception: - print colors.BLU + " [*] " + colors.NRM + "Host either not accessible or port 445 closed" + print(colors.BLU + " [*] " + colors.NRM + "Host either not accessible or port 445 closed") continue except KeyboardInterrupt: return @@ -93,7 +93,7 @@ def __init__(self, domain, username, password, jitter): self._lmhash = '' self._nthash = '' self._jitter = jitter - print "Authenticating Using: "+ self._domain + "\\" + self._user + ": " + self._password + print("Authenticating Using: "+ self._domain + "\\" + self._user + ": " + self._password) def _create_rpc_connection(self, target_computer): @@ -114,15 +114,15 @@ def sessions(self, targets): try: target_computer = target self._create_rpc_connection(target_computer) - print target_computer - print "-----------------" + print(target_computer) + print("-----------------") smb = SMBConnection('*SMBSERVER', target_computer, sess_port=445, timeout=5) smb.login(self._user, self._password, self._domain) try: sess = wkst.hNetrWkstaUserEnum(self._rpc_connection, 1) - except DCERPCException, e: + except DCERPCException as e: users = [] - print colors.RD + " [-]" + colors.NRM + " User does not have access" + print(colors.RD + " [-]" + colors.NRM + " User does not have access") continue for wksta_user in sess['UserInfo']['WkstaUserInfo']['Level1']['Buffer']: userName = wksta_user['wkui1_username'][:-1] @@ -135,31 +135,31 @@ def sessions(self, targets): pass else: users.append(user) - print " Currently Logged On" - print " -------------------" + print(" Currently Logged On") + print(" -------------------") for user in users: - print " " + colors.GRN + "[+] " + colors.NRM + user + print(" " + colors.GRN + "[+] " + colors.NRM + user) del users share = 'C$' path = '\\Users\\*' read = smb.listPath(share, path) - print "\n Users Who Have Logged On" - print " -------------------------" + print("\n Users Who Have Logged On") + print(" -------------------------") for r in read: if r.get_longname() == "Public" or r.get_longname() == "All Users" or r.get_longname() == "Default" or r.get_longname() == "Default User" or r.get_longname() == "." or r.get_longname() == "..": pass else: if r.is_directory(): - print colors.GRN + " [+] " + colors.NRM + r.get_longname() + " lastlogon: " + time.ctime(float(r.get_mtime_epoch())) + print(colors.GRN + " [+] " + colors.NRM + r.get_longname() + " lastlogon: " + time.ctime(float(r.get_mtime_epoch()))) except UnboundLocalError as e: - print target + print(target) users = [] - print e - print colors.RD + " [-] " + colors.NRM + "User does not have access" + print(e) + print(colors.RD + " [-] " + colors.NRM + "User does not have access") continue except socket.error: users = [] - print colors.BLU + " [*] " + colors.NRM + "Host either not accessible or port 445 closed" + print(colors.BLU + " [*] " + colors.NRM + "Host either not accessible or port 445 closed") continue except KeyboardInterrupt: @@ -169,13 +169,13 @@ def sessions(self, targets): share = 'C$' path = '\\Documents and Settings\\*' read = smb.listPath(share, path) - print "\nUsers who have logged on" - print "--------------------------" + print("\nUsers who have logged on") + print("--------------------------") for r in read: if r.get_longname() == "Public" or r.get_longname() == "All Users" or r.get_longname() == "Default" or r.get_longname() == "Default User" or r.get_longname() == "." or r.get_longname() == "..": pass else: if r.is_directory(): - print " [*] " + r.get_longname() + " lastlogon: " + time.ctime(float(r.get_mtime_epoch())) + print(" [*] " + r.get_longname() + " lastlogon: " + time.ctime(float(r.get_mtime_epoch()))) except SessionError: continue diff --git a/vibe.py b/vibe.py index ccfd18d..31741c1 100755 --- a/vibe.py +++ b/vibe.py @@ -18,12 +18,12 @@ class colors: BLU = '\033[34m' -show = ['users', 'groups', 'computers', 'pwdpolicy', 'store', 'creds', 'fgpolicy', 'file servers', 'access'] +show = ['users', 'groups', 'computers', 'pwdpolicy', 'store', 'creds', 'fgpolicy', 'file servers', 'access', 'SPN'] net = ['user', 'group', 'computer'] parser = argparse.ArgumentParser(prog='main') parser.add_argument('-U', '--username', metavar='username', dest='username', action='store', help='Username\n', required=True) -parser.add_argument('-P', '--password', metavar='password', dest='password', action='store', help='Password\n', required=True) +parser.add_argument('-P', '--password', metavar='password', dest='password', action='store', help='Password\n', required=False) parser.add_argument('-D', '--domain', metavar='domain', dest='domain', action='store', help='Fully Qualified Domain Name\n', required=True) parser.add_argument('-I', '--ip', metavar='IP', dest='IP', action='store', help='IP address of Domain Controller\n', required=True) parser.add_argument('-o', '--offline', dest='offline', action='store_true', help='Offline Mode\n', required=False) @@ -34,6 +34,9 @@ class colors: args = parser.parse_args() +if args.password is None: + from getpass import getpass + args.password = getpass("Password:") class menu(cmd.Cmd): global conn @@ -57,18 +60,23 @@ def do_show(self, option, intro=None): del tb['Primary Group Name'] del tb['Profile Path'] del tb['Description'] - print (tabulate(tb, showindex=False, headers=tb.columns, tablefmt="grid")) + print((tabulate(tb, showindex=False, headers=tb.columns, tablefmt="grid"))) result(tb) return if option == 'groups': tb = dp.read_sql('select * from GroupTB', conn) tb['Description'] = tb['Description'].str[:35] + '...' - print (tabulate(tb, showindex=False, headers=tb.columns, tablefmt="grid")) + print((tabulate(tb, showindex=False, headers=tb.columns, tablefmt="grid"))) result(tb) return cmd.Cmd.cmdloop(self, intro) if option == 'computers': tb = dp.read_sql('select * from ComputerTB', conn) - print (tabulate(tb, showindex=False, headers=tb.columns, tablefmt="grid")) + print((tabulate(tb, showindex=False, headers=tb.columns, tablefmt="grid"))) + result(tb) + return cmd.Cmd.cmdloop(self, intro) + if option == 'SPN': + tb = dp.read_sql('select * from SPNTB', conn) + print((tabulate(tb, showindex=False, headers=tb.columns, tablefmt="grid"))) result(tb) return cmd.Cmd.cmdloop(self, intro) if option == 'store': @@ -76,22 +84,22 @@ def do_show(self, option, intro=None): cmd.Cmd.onecmd(self, store) return cmd.Cmd.cmdloop(self, intro) except NameError: - print "Nothing is stored" + print("Nothing is stored") return cmd.Cmd.cmdloop(self, intro) if option == 'fgpolicy': tb = dp.read_sql('select * from FGPasswordPolicyTB', conn) - print (tabulate(tb, showindex=False, headers=tb.columns, tablefmt="grid")) + print((tabulate(tb, showindex=False, headers=tb.columns, tablefmt="grid"))) result(tb) return cmd.Cmd.cmdloop(self, intro) if option == 'pwdpolicy': tb = dp.read_sql('select * from PasswordPolicyTB', conn) - print "Password Policy" - print "---------------" - print "Minimum Password Length: " + tb['Minimum Password Length'].to_string(index=False, header=False) - print "Lockout Threshold: " + tb['Lockout Threshold'].to_string(index=False, header=False) - print "Lockout Duration: " + tb['Lockout Duration'].to_string(index=False, header=False) + ' minutes' - print "Passwords Remembered: " + tb['Passwords Remembered'].to_string(index=False, header=False) - print "Password Properties: " + tb['Password Properties'].to_string(index=False, header=False) + print("Password Policy") + print("---------------") + print("Minimum Password Length: " + tb['Minimum Password Length'].to_string(index=False, header=False)) + print("Lockout Threshold: " + tb['Lockout Threshold'].to_string(index=False, header=False)) + print("Lockout Duration: " + tb['Lockout Duration'].to_string(index=False, header=False) + ' minutes') + print("Passwords Remembered: " + tb['Passwords Remembered'].to_string(index=False, header=False)) + print("Password Properties: " + tb['Password Properties'].to_string(index=False, header=False)) return cmd.Cmd.cmdloop(self, intro) if option.startswith('access'): try: @@ -100,25 +108,25 @@ def do_show(self, option, intro=None): if "." in user: user.replace(".", "") tb = dp.read_sql('select * from ' + user + '', conn) - print (tabulate(tb, showindex=False, headers=tb.columns, tablefmt="grid")) + print((tabulate(tb, showindex=False, headers=tb.columns, tablefmt="grid"))) return cmd.Cmd.cmdloop(self, intro) except IndexError: - print "Nothing recorded for this user" + print("Nothing recorded for this user") return cmd.Cmd.cmdloop(self, intro) if option == 'file servers': FS_list1 = dp.read_sql('select "Home Directory" from UserTB', conn) FS_list2 = dp.read_sql('select "Profile Path" from UserTB', conn) FS_list = FS_list2 FS_list = FS_list.drop_duplicates() - print FS_list + print(FS_list) FS_list = tabulate(FS_list, showindex=False) - print "File Servers Discovered" - print FS_list + print("File Servers Discovered") + print(FS_list) return cmd.Cmd.cmdloop(self, intro) else: - print colors.BLU + "[-] " + colors.NRM + "Error: " + option + " does not exist or is empty, please try again." + print(colors.BLU + "[-] " + colors.NRM + "Error: " + option + " does not exist or is empty, please try again.") except pandas.io.sql.DatabaseError: - print colors.RD + "[-] " + colors.NRM + "Error: Empty database. Object may not exist." + print(colors.RD + "[-] " + colors.NRM + "Error: Empty database. Object may not exist.") except KeyboardInterrupt: return return cmd.Cmd.cmdloop(self, intro) @@ -133,30 +141,30 @@ def do_export(self, option, intro=None): if arg == "-p" or arg == "--path": path = next(args) if path == '': - print "Error: Unsupported file type specified. Please try again with either csv or html" + print("Error: Unsupported file type specified. Please try again with either csv or html") return - tablesnames = ['User','Group', 'Computer'] + tablesnames = ['User','Group', 'Computer', 'SPN'] if format =="csv": for tablename in tablesnames: tb = dp.read_sql('select *from '+tablename+'TB', conn) tb.replace('\n', ',') - print '[+] File Saving to: ' + path + '/' + db_domain + '_' + tablename + 's.csv' + print('[+] File Saving to: ' + path + '/' + db_domain + '_' + tablename + 's.csv') export_csv = tb.to_csv(''+ path+'/'+db_domain+'_'+tablename+'s.csv', index=True, header=True) return cmd.Cmd.cmdloop(self, intro) if format == "html": for tablename in tablesnames: tb = dp.read_sql('select *from ' + tablename + 'TB', conn) tb = tb.replace({'\n':'
'}, regex=True) - print '[+] File Saving to: ' + path + '/' + db_domain + '_' + tablename + 's.html' + print('[+] File Saving to: ' + path + '/' + db_domain + '_' + tablename + 's.html') export_html = tb.to_html('' + path + '/' + db_domain + '_' + tablename + 's.html', escape=False) return cmd.Cmd.cmdloop(self, intro) else: - print "Error: Unsupported file type specified. Please try again with either csv, html" + print("Error: Unsupported file type specified. Please try again with either csv, html") except pandas.io.sql.DatabaseError: dp.options.display.max_colwidth = 40000 - print colors.RD + "[-] " + colors.NRM + "Error: Empty database. Object may not exist." + print(colors.RD + "[-] " + colors.NRM + "Error: Empty database. Object may not exist.") except UnboundLocalError: - print "Error: Missing opinion, please make sure you provide the format and export pathway, using the appropriate flags." + print("Error: Missing opinion, please make sure you provide the format and export pathway, using the appropriate flags.") dp.options.display.max_colwidth = 40000 return cmd.Cmd.cmdloop(self, intro) @@ -170,19 +178,19 @@ def do_net(self, option, intro=None): if type in ["user"]: tb = dp.read_sql('select * from UserTB where Username = \'' + value + '\' COLLATE NOCASE', conn) if tb.empty: - print colors.BLU + "[*] " + colors.NRM + 'User: ' + value + ' does not exist, please try again' + print(colors.BLU + "[*] " + colors.NRM + 'User: ' + value + ' does not exist, please try again') return - print 'Username: ' + tb['Username'].to_string(index=False, header=False) - print 'SID: ' + tb['SID'].to_string(index=False, header=False) - print 'Description: ' + tb['Description'].to_string(index=False, header=False) - print 'Home Directory : ' + tb['Home Directory'].to_string(index=False, header=False) - print 'Profile Path : ' + tb['Profile Path'].to_string(index=False, header=False) - print 'Password Last Set: ' + tb['Password Last Set'].to_string(index=False, header=False) - print 'Last Logged On: ' + tb['Last Logged On'].to_string(index=False, header=False) - print 'Account Settings: ' + tb['Account Settings'].to_string(index=False, header=False).replace('\'', '').replace('\\n', ',') - print '----------------------------------------------------------------------------------' - print 'Primary Group Name : ' + tb['Primary Group Name'].to_string(index=False, header=False).replace('\\n', '') - print 'Group Membership: ' + print('Username: ' + tb['Username'].to_string(index=False, header=False)) + print('SID: ' + tb['SID'].to_string(index=False, header=False)) + print('Description: ' + tb['Description'].to_string(index=False, header=False)) + print('Home Directory : ' + tb['Home Directory'].to_string(index=False, header=False)) + print('Profile Path : ' + tb['Profile Path'].to_string(index=False, header=False)) + print('Password Last Set: ' + tb['Password Last Set'].to_string(index=False, header=False)) + print('Last Logged On: ' + tb['Last Logged On'].to_string(index=False, header=False)) + print('Account Settings: ' + tb['Account Settings'].to_string(index=False, header=False).replace('\'', '').replace('\\n', ',')) + print('----------------------------------------------------------------------------------') + print('Primary Group Name : ' + tb['Primary Group Name'].to_string(index=False, header=False).replace('\\n', '')) + print('Group Membership: ') display_Members_Of = tb['Member Of'].to_string(index=False, header=False).split('\\n') display_Members_Of.append(' ') display_Members_Of.append(' ') @@ -190,24 +198,24 @@ def do_net(self, option, intro=None): c1 = c1.encode('ascii', 'ignore') c2 = c2.encode('ascii', 'ignore') c3 = c3.encode('ascii', 'ignore') - print '{:<30}{:<30}{:<}'.format(c1, c2, c3) + print('{:<30}{:<30}{:<}'.format(c1.decode('utf-8'), c2.decode('utf-8'), c3.decode('utf-8'))) result(tb) return cmd.Cmd.cmdloop(self, intro) elif type in ["group"]: tb = dp.read_sql('select * from GroupTB where Name = \'' + value + '\' COLLATE NOCASE', conn) if tb.empty: - print colors.BLU + "[*] " + colors.NRM + 'Group: ' + value + ' does not exist, please try again' + print(colors.BLU + "[*] " + colors.NRM + 'Group: ' + value + ' does not exist, please try again') return - print 'Group name: ' + tb['Name'].to_string(index=False, header=False) - print 'Description: ' + tb['Description'].to_string(index=False, header=False) - print 'Group Membership:' + print('Group name: ' + tb['Name'].to_string(index=False, header=False)) + print('Description: ' + tb['Description'].to_string(index=False, header=False)) + print('Group Membership:') display_Membership = tb['Member Of'].to_string(index=False, header=False).split('\\n') display_Membership.append(' ') display_Membership.append(' ') for c1, c2, c3 in zip(display_Membership[::3], display_Membership[1::3], display_Membership[2::3]): - print'{:<30}{:<30}{:<}'.format(c1, c2, c3) - print '----------------------------------------------------------------------------------' - print 'Members:' + print('{:<30}{:<30}{:<}'.format(c1, c2, c3)) + print('----------------------------------------------------------------------------------') + print('Members:') display_Members = tb['Members'].to_string(index=False, header=False).split('\\n') display_Members.append(' ') display_Members.append(' ') @@ -215,20 +223,20 @@ def do_net(self, option, intro=None): c1 = c1.encode('ascii', 'ignore') c2 = c2.encode('ascii', 'ignore') c3 = c3.encode('ascii', 'ignore') - print '{:<30}{:<30}{:<}'.format(c1, c2, c3) + print('{:<30}{:<30}{:<}'.format(c1.decode('utf-8'), c2.decode('utf-8'), c3.decode('utf-8'))) result(tb) return cmd.Cmd.cmdloop(self, intro) elif type in ["computer"]: tb = dp.read_sql('select * from ComputerTB where Name = \'' + value + '\' COLLATE NOCASE', conn) if tb.empty: - print colors.BLU + "[*] " + colors.NRM + 'Computer: ' + value + ' does not exist, please try again' + print(colors.BLU + "[*] " + colors.NRM + 'Computer: ' + value + ' does not exist, please try again') return - print 'Computer Name: ' + tb['Name'].to_string(index=False, header=False) - print 'Description: ' + tb['Description'].to_string(index=False, header=False) - print 'Operating System: ' + tb['Operating System'].to_string(index=False, header=False) - print 'Version Number: ' + tb['Operating System Version Number'].to_string(index=False, header=False) - print '----------------------------------------------------------------------------------' - print 'Group Membership: ' + print('Computer Name: ' + tb['Name'].to_string(index=False, header=False)) + print('Description: ' + tb['Description'].to_string(index=False, header=False)) + print('Operating System: ' + tb['Operating System'].to_string(index=False, header=False)) + print('Version Number: ' + tb['Operating System Version Number'].to_string(index=False, header=False)) + print('----------------------------------------------------------------------------------') + print('Group Membership: ') display_Members_Of = tb['Member Of'].to_string(index=False, header=False).split('\\n') display_Members_Of.append(' ') display_Members_Of.append(' ') @@ -236,33 +244,33 @@ def do_net(self, option, intro=None): c1 = c1.encode('ascii', 'ignore') c2 = c2.encode('ascii', 'ignore') c3 = c3.encode('ascii', 'ignore') - print '{:<30}{:<30}{:<}'.format(c1, c2, c3) + print('{:<30}{:<30}{:<}'.format(c1.decode('utf-8'), c2.decode('utf-8'), c3.decode('utf-8'))) result(tb) return cmd.Cmd.cmdloop(self, intro) else: - print colors.RD + "[-] " + colors.NRM + "Error: Invalid net request, please try again." + print(colors.RD + "[-] " + colors.NRM + "Error: Invalid net request, please try again.") except pandas.io.sql.DatabaseError: - print colors.RD + "[-] " + colors.NRM + "Error: Empty database." + print(colors.RD + "[-] " + colors.NRM + "Error: Empty database.") return cmd.Cmd.cmdloop(self, intro) def do_columns(self, option, intro=None): try: if option == "users": - print "[-] Displaying the columns in the User Table" + print("[-] Displaying the columns in the User Table") tb = dp.read_sql('select *from UserTB', conn) - print tb.columns.values.tolist() + print(tb.columns.values.tolist()) elif option == "groups": - print "[-] Displaying the columns in the Group Table" + print("[-] Displaying the columns in the Group Table") tb = dp.read_sql('select *from GroupTB', conn) - print tb.columns.values.tolist() + print(tb.columns.values.tolist()) elif option == "computers": - print "[-] Displaying the columns in the Computer Table" + print("[-] Displaying the columns in the Computer Table") tb = dp.read_sql('select *from ComputerTB', conn) - print tb.columns.values.tolist() + print(tb.columns.values.tolist()) else: - print "Not one of the tables." + print("Not one of the tables.") except pandas.io.sql.DatabaseError: - print colors.RD + "[-] " + colors.NRM + "Error: Empty database." + print(colors.RD + "[-] " + colors.NRM + "Error: Empty database.") return cmd.Cmd.cmdloop(self, intro) def do_list(self, option, intro=None): @@ -270,38 +278,49 @@ def do_list(self, option, intro=None): if "users" in option: tb = dp.read_sql('select Username from UserTB', conn) Users = tabulate(tb, showindex=False) - print Users.strip() + print(Users.strip()) result(Users) if "-f" in option or "--file" in option: option = option.split(" ") filepath = option[2] tb.to_csv(filepath, sep='\t', index=False) - print colors.GRN + "[+] " + colors.NRM + "Exported List of Users to: "+ filepath + print(colors.GRN + "[+] " + colors.NRM + "Exported List of Users to: "+ filepath) return cmd.Cmd.cmdloop(self, intro) if "groups" in option: tb = dp.read_sql('select Name from GroupTB', conn) Groups = tabulate(tb, showindex=False) - print Groups.strip() + print(Groups.strip()) result(Groups) if "-f" in option or "--file" in option: option = option.split(" ") filepath = option[2] tb.to_csv(filepath, sep='\t', index=False) - print colors.GRN + "[+] " + colors.NRM + "Exported List of Groups to: " + filepath + print(colors.GRN + "[+] " + colors.NRM + "Exported List of Groups to: " + filepath) return cmd.Cmd.cmdloop(self, intro) if "computers" in option: tb = dp.read_sql('select Name from ComputerTB', conn) Computers = tabulate(tb, showindex=False) - print Computers.strip() + print(Computers.strip()) result(Computers) if "-f" in option or "--file" in option: option = option.split(" ") filepath = option[2] tb.to_csv(filepath, sep='\t', index=False) - print colors.GRN + "[+] " + colors.NRM + "Exported List of Computers to: " + filepath + print(colors.GRN + "[+] " + colors.NRM + "Exported List of Computers to: " + filepath) return cmd.Cmd.cmdloop(self, intro) + if "spns" in option: + tb = dp.read_sql('select Username from SPNTB', conn) + SPNs = tabulate(tb, showindex=False) + print(SPNs.strip()) + result(SPNs) + if "-f" in option or "--file" in option: + option = option.split(" ") + filepath = option[2] + tb.to_csv(filepath, sep='\t', index=False) + print(colors.GRN + "[+] " + colors.NRM + "Exported List of SPNS to: " + filepath) + return cmd.Cmd.cmdloop(self, intro) except pandas.io.sql.DatabaseError: - print colors.RD + "[-] " + colors.NRM + "Error: Empty Database." + print(colors.RD + "[-] " + colors.NRM + "Error: Empty Database.") def do_query(self, option, intro=None): try: @@ -313,7 +332,7 @@ def do_query(self, option, intro=None): if arg == "-p" or arg == "--path": path = next(args) if path == '': - print "Error: Unsupported file type specified. Please try again with either csv or html" + print("Error: Unsupported file type specified. Please try again with either csv or html") return if 'User' in option: option = option.replace("User", "UserTB") @@ -339,15 +358,23 @@ def do_query(self, option, intro=None): option = option.replace("Computer", "ComputerTB") elif 'computers' in option: option = option.replace("computer", "ComputerTB") + elif 'SPNS' in option: + option = option.replace("SPNS", "SPNTB") + elif 'spns' in option: + option = option.replace("spns", "SPNTB") + elif 'SPN' in option: + option = option.replace("SPN", "SPNTB") + elif 'spn' in option: + option = option.replace("spn", "SPNTB") else: - print "Error: Invalid query, please try again." + print("Error: Invalid query, please try again.") return cmd.Cmd.cmdloop(self, intro) tb = dp.read_sql(option, conn) - print (tabulate(tb, showindex=False, headers=tb.columns, tablefmt="psql")) + print((tabulate(tb, showindex=False, headers=tb.columns, tablefmt="psql"))) result(tb) return cmd.Cmd.cmdloop(self, intro) except pandas.io.sql.DatabaseError: - print colors.RD + "[-] " + colors.NRM + "Error: Empty Database." + print(colors.RD + "[-] " + colors.NRM + "Error: Empty Database.") def do_search(self, option, intro=None): try: @@ -358,9 +385,9 @@ def do_search(self, option, intro=None): if group_tb.empty: pass else: - print "Groups" - print "---------" - print (tabulate(group_tb, showindex=False, headers=group_tb.columns, tablefmt="grid")) + print("Groups") + print("---------") + print((tabulate(group_tb, showindex=False, headers=group_tb.columns, tablefmt="grid"))) user_tb = dp.read_sql('select * from UserTB where Username like "%' + value + '%" or Description like "%' + value + '%" or SID like "%' + value + '%" or "Profile Path" like "%' + value + '%" or "Home Directory" like "%' + value + '%" or "Password Last Set" like "%' + value + '%" or "Last Logged On" like "%' + value + '%" or "Account Settings" like "%' + value + '%" or "Primary Group Name" like "%' + value + '%" or "Member Of" like "%' + value + '%" COLLATE NOCASE', conn) user_tb['Description'] = user_tb['Description'].str[:35] + '...' user_tb['Member Of'] = user_tb[['Primary Group Name', 'Member Of']].sum(axis=1) @@ -368,16 +395,23 @@ def do_search(self, option, intro=None): if user_tb.empty: pass else: - print "Users" - print "---------" - print (tabulate(user_tb, showindex=False, headers=user_tb.columns, tablefmt="grid")) + print("Users") + print("---------") + print((tabulate(user_tb, showindex=False, headers=user_tb.columns, tablefmt="grid"))) computer_tb = dp.read_sql('select * from ComputerTB where Name like "%' + value + '%" or Description like "%' + value + '%" or "Operating System" like "%' + value + '%" or "Operating System Version Number" like "%' + value + '%" or "Member Of" like "%' + value + '%" COLLATE NOCASE', conn) if computer_tb.empty: pass else: - print "Computers" - print "---------" - print (tabulate(computer_tb, showindex=False, headers=computer_tb.columns, tablefmt="grid")) + print("Computers") + print("---------") + print((tabulate(computer_tb, showindex=False, headers=spn.columns, tablefmt="grid"))) + spn_tb = dp.read_sql('select * from SPNTB where SPN like "%' + value + '%" or Username like "%' + value + '%" or Description like "%' + value + '%" or "Member Of" like "%' + value + '%" COLLATE NOCASE', conn) + if spn_tb.empty: + pass + else: + print("SPNs") + print("---------") + print((tabulate(spn_tb, showindex=False, headers=spn_tb.columns, tablefmt="grid"))) return cmd.Cmd.cmdloop(self, intro) except KeyboardInterrupt: return @@ -414,7 +448,7 @@ def cmd_sub_arg_parse(self, option): else: netaddr = addr except Error as e: - print "Bad file" + print("Bad file") if arg == "-j" or arg == "--jitter": jitter = next(args) return @@ -423,20 +457,20 @@ def do_session(self, option, intro=None): try: self.cmd_sub_arg_parse(option) if not netaddr: - print colors.RD + "[-] " + colors.NRM + "No target provided. Please try again" + print(colors.RD + "[-] " + colors.NRM + "No target provided. Please try again") return else: targets = IP(netaddr) if not user and not password and not domain: - print colors.RD + "[-] " + colors.NRM + "Missing user information (i.e Domain, Username, Password). Please try again" + print(colors.RD + "[-] " + colors.NRM + "Missing user information (i.e Domain, Username, Password). Please try again") else: pass r = Sessions(domain, user, password, jitter) r.sessions(targets) except IndexError: - print colors.RD + "[-] " + colors.NRM + "Invalid Option" - print colors.BLU + "[*] " + colors.NRM + "Scans target(s) enumerating who has or is currently logged into the target(s), using -u/--user. Can take a list or range of hosts, using -t/--target. To throttle the speed use the -j/--jitter and the number of seconds (default 1). Example session --user admin -t 192.168.1./24 -j 3." + print(colors.RD + "[-] " + colors.NRM + "Invalid Option") + print(colors.BLU + "[*] " + colors.NRM + "Scans target(s) enumerating who has or is currently logged into the target(s), using -u/--user. Can take a list or range of hosts, using -t/--target. To throttle the speed use the -j/--jitter and the number of seconds (default 1). Example session --user admin -t 192.168.1./24 -j 3.") except KeyboardInterrupt: return return cmd.Cmd.cmdloop(self, intro) @@ -445,42 +479,42 @@ def do_share_hunter(self, option, intro=None): try: self.cmd_sub_arg_parse(option) if not netaddr: - print colors.RD + "[-] " + colors.NRM + "No target provided. Please try again" + print(colors.RD + "[-] " + colors.NRM + "No target provided. Please try again") return else: targets = IP(netaddr) if not user and not password and not domain: - print colors.RD + "[-] " + colors.NRM + "Missing user information (i.e Domain, Username, Password). Please try again" + print(colors.RD + "[-] " + colors.NRM + "Missing user information (i.e Domain, Username, Password). Please try again") else: pass sh = Share_Hunting(domain, user, password, jitter) - print targets + print(targets) sh.share_hunter(targets) except IndexError: - print colors.RD + "[-] " + colors.NRM + "Invalid Option" - print colors.BLU + "[*] " + colors.NRM + "Scans target(s) enumerating the shares on the target(s) and the level of access the specified user, using -u/--user. Can take a list or range of hosts, using -t/--target. Example share_hunter --user admin -t 192.168.1./24 -j 3." + print(colors.RD + "[-] " + colors.NRM + "Invalid Option") + print(colors.BLU + "[*] " + colors.NRM + "Scans target(s) enumerating the shares on the target(s) and the level of access the specified user, using -u/--user. Can take a list or range of hosts, using -t/--target. Example share_hunter --user admin -t 192.168.1./24 -j 3.") except KeyboardInterrupt: return return cmd.Cmd.cmdloop(self, intro) def do_help(self, intro=None): - print "Commands" - print "========" - print "clear Clears the screen" - print "help Displays this help menu" - print "list Lists either all Users, Computers, or Groups. Use the -f option to pipe the contents to a file" - print "session Scans target(s) to see who has/is currently logged in. Can take a list or range of hosts, using -t/--target and specify a user using -d/--domain, -u/--user, -p/--password and --jitter/-j to add a delay. Requires: read/write privileges on either Admin$ or C$ share" - print "net Perform a query to view all information pertaining to a specific user, group, or computer (Similar to the Windows net user, net group commands). example: \'net group Domain Admins\'" - print "columns Displays the column names in each of the three major tables (users, groups and computers" - print "query Executes a query on the contents of tables" - print "search Searches for a key word(s) through every field of every table for any matches, displaying row" - print "share_hunter Scans target(s) enumerating the shares on the target(s) and the level of access the specified user, using -d/--domain, -u/--user, -p/--password. Can take a list or range of hosts, using -t/--target and --jitter/-j to add a delay" - print "show Shows the contents of Users, Computers, Credentials, Groups, Password policy, Store, Credentials, Files Servers and Access tables" - print "store Displays the contents of a specific table. Example: \'show [table name] (access, creds, computers, file servers, pwdpolicy, users)" - print "export Export the contents of the database to a path in one of the following formats: CSV, HTML. (using with -f or --filetype)" - print "exit Exit Vibe" + print("Commands") + print("========") + print("clear Clears the screen") + print("help Displays this help menu") + print("list Lists either all Users, Computers, or Groups. Use the -f option to pipe the contents to a file") + print("session Scans target(s) to see who has/is currently logged in. Can take a list or range of hosts, using -t/--target and specify a user using -d/--domain, -u/--user, -p/--password and --jitter/-j to add a delay. Requires: read/write privileges on either Admin$ or C$ share") + print("net Perform a query to view all information pertaining to a specific user, group, or computer (Similar to the Windows net user, net group commands). example: \'net group Domain Admins\'") + print("columns Displays the column names in each of the three major tables (users, groups and computers") + print("query Executes a query on the contents of tables") + print("search Searches for a key word(s) through every field of every table for any matches, displaying row") + print("share_hunter Scans target(s) enumerating the shares on the target(s) and the level of access the specified user, using -d/--domain, -u/--user, -p/--password. Can take a list or range of hosts, using -t/--target and --jitter/-j to add a delay") + print("show Shows the contents of Users, Computers, Credentials, Groups, Password policy, Store, Credentials, Files Servers and Access tables") + print("store Displays the contents of a specific table. Example: \'show [table name] (access, creds, computers, file servers, pwdpolicy, users)") + print("export Export the contents of the database to a path in one of the following formats: csv, html. (using with -f or --filetype and -p or --path for the file path)") + print("exit Exit Vibe") return cmd.Cmd.cmdloop(self, intro) def do_store(self, intro=None): @@ -501,7 +535,7 @@ def do_add_cred(self, text, intro=None): password = ' ' cred_db(domain, username, password, hash) except IndexError: - print "[-] Invalid Option" + print("[-] Invalid Option") return cmd.Cmd.cmdloop(self, intro) def do_clear(self, intro=None): @@ -539,28 +573,30 @@ def complete_net(self, text, line, start_index, end_index): return completions def do_exit(self, line): - print colors.BLU + "[*] " + colors.NRM + "Exiting..." + ("\n") + print(colors.BLU + "[*] " + colors.NRM + "Exiting...") + sys.exit(0) return True def do_EOF(self, line): - return True + return self.do_exit(line) except IndexError: - print colors.BLU + "[*] " + colors.NRM + "Missing argument" + print(colors.BLU + "[*] " + colors.NRM + "Missing argument") -print "\n" -print" ___ ___ ___ ________ _______ " -print"|\ \ / /||\ \ |\ __ \ |\ ____\ " -print"\ \ \ / / /\ \ \ \ \ \|\ /_ \ \ \_____ " -print" \ \ \/ / / \ \ \ \ \ __ \ \ \ ____\ " -print" \ \ / / \ \ \ \ \ \|\ \ \ \ \____ " -print" \ \__/ / \ \__\ \ \_______\ \ \______\ " -print" \|__|/ \|__| \|_______| \|______| " -print" (@Tyl0us) " -print "\n" +print("\n") +print(" ___ ___ ___ ________ _______ ") +print("|\ \ / /||\ \ |\ __ \ |\ ____\ ") +print("\ \ \ / / /\ \ \ \ \ \|\ /_ \ \ \_____ ") +print(" \ \ \/ / / \ \ \ \ \ __ \ \ \ ____\ ") +print(" \ \ / / \ \ \ \ \ \|\ \ \ \ \____ ") +print(" \ \__/ / \ \__\ \ \_______\ \ \______\ ") +print(" \|__|/ \|__| \|_______| \|______| ") +print(" (@Tyl0us) ") +print("\n") if args.remove: - print colors.GRN + "[+] " + colors.NRM + "Removed Domain Database: " +args.domain + print(colors.GRN + "[+] " + colors.NRM + "Removed Domain Database: " +args.domain) subprocess.call("rm -rf .db/" + args.domain + ".db", shell=True) sys.exit() @@ -588,10 +624,10 @@ def do_EOF(self, line): if not args.port: args.unencrypted = "False" l.main(args.IP, args.username, args.domain, args.password, args.port, args.unencrypted) - print colors.BLU + "[*] " + colors.NRM + str(time.time() - t) + print(colors.BLU + "[*] " + colors.NRM + str(time.time() - t)) except Error as e: - print e - print colors.RD + "[-] " + colors.NRM + "Error Occurred While Generating The Domain" + print(e) + print(colors.RD + "[-] " + colors.NRM + "Error Occurred While Generating The Domain") subprocess.call("rm -rf .db/" + args.domain + ".db", shell=True) if __name__ == '__main__':