Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
df6a18a
Fix ST request when using RC4 TGT agains AES_only (#2235)
termanix Jul 24, 2026
d1c267a
Ldap examples update phase 1 (#2200)
anadrianmanrique Jul 28, 2026
bbbc912
Check cached TGT matches requested user in getST.py (#2218)
p-nowodzinski Jul 28, 2026
02044d0
dpapi.py QoL improvements (#2167)
Aptimex Jul 28, 2026
b909478
httpRelayServer: restore POST body handling and add HTTPS/TLS support…
Coontzy1 Jul 29, 2026
65534a2
Add MSSQLClient/TDS.py capabilities for named pipe exposed instances …
Dfte Jul 29, 2026
570f283
Allow server-trust accounts in NetLogon pass-through validation (#2239)
GregDurys Jul 29, 2026
141be7a
ntlmrelayx: Add --dump-pre2k to enumerate Pre-Windows 2000 vulnerable…
H4ckT0Th3Futur3 Jul 31, 2026
701354e
Fix DCOM interface context loss after same-target disconnect + test (…
alexisbalbachan Aug 5, 2026
239b3bc
atexec: add -author-log and -overflow for Event 4698 Author tampering…
rubenformation Aug 7, 2026
2dae5c7
retry with RC4 when no PA-ETYPE-INFO for requested etype (#2246)
azoxlpf Aug 10, 2026
243d64a
Fix IndexError in CCache.getCredential when handling 3-part SPNs in m…
0x00Jeff Aug 10, 2026
8ea54fe
Modernize byte string normalization (#2243)
gabrielg5 Aug 12, 2026
4c09897
Fix some SMB relay server syntax error (#2245)
AndreySolod Aug 12, 2026
c23b3d5
smbserver: add SMB 3.1.1 dialect support (#2216)
n3rada Aug 14, 2026
29424fb
Standardize Kerberos TGS enctype negotiation (#2241)
gabrielg5 Aug 19, 2026
3c37ab1
keylistattack: support PAC-hardened DCs (#2233)
lacedsec Aug 19, 2026
c986b8f
Diamond ticket: preserve KDC-issued PAC_REQUESTOR and PAC_ATTRIBUTES …
0xCZR1 Aug 19, 2026
7ab9a40
secretsdump: respect -just-dc-ntlm for trust key Kerberos derivation …
Goultarde Aug 19, 2026
ec94d72
feat(smb3): add AES-128-GCM support to the SMB3 client (#2259)
n3rada Aug 19, 2026
f133bb8
Adding logic for DCs with RestrictRemoteClients = 2 (#2166)
clou42 Aug 19, 2026
8134f37
Merge upstream fortra/impacket master into BLS fork
aconite33 Aug 19, 2026
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
7 changes: 7 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ Project owner's main page is at www.coresecurity.com.
Complete list of changes can be found at:
https://github.com/fortra/impacket/commits/master

## Unreleased:

1. Examples improvements

* [atexec.py](examples/atexec.py):
* Added mutually exclusive `-author-log` and `-overflow` options to poison or overflow the Task Scheduler Security Event 4698 Author field via task XML RegistrationInfo.

## Impacket v0.13.1 (May 2026):

1. Library improvements
Expand Down
8 changes: 5 additions & 3 deletions examples/GetUserSPNs.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
from impacket.krb5 import constants
from impacket.krb5.asn1 import TGS_REP, AS_REP
from impacket.krb5.ccache import CCache
from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS, RC4_PREFERRED_TGS_ENCTYPES
from impacket.krb5.types import Principal
from impacket.ldap import ldap, ldapasn1
from impacket.ntlm import compute_lmhash, compute_nthash
Expand Down Expand Up @@ -376,7 +376,8 @@ def run(self):
tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(principalName, self.__domain,
self.__kdcIP,
TGT['KDC_REP'], TGT['cipher'],
TGT['sessionKey'])
TGT['sessionKey'],
etypes=RC4_PREFERRED_TGS_ENCTYPES)
self.outputTGS(tgs, oldSessionKey, sessionKey, sAMAccountName,
self.__targetDomain + "/" + sAMAccountName, fd)
except Exception as e:
Expand Down Expand Up @@ -435,7 +436,8 @@ def request_multiple_TGSs(self, usernames):
tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(principalName, self.__domain,
self.__kdcIP,
TGT['KDC_REP'], TGT['cipher'],
TGT['sessionKey'])
TGT['sessionKey'],
etypes=RC4_PREFERRED_TGS_ENCTYPES)
self.outputTGS(tgs, oldSessionKey, sessionKey, username, username, fd)
except Exception as e:
logging.debug("Exception:", exc_info=True)
Expand Down
69 changes: 36 additions & 33 deletions examples/addcomputer.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,22 @@
# [ ]: Complete the process of joining a client computer to a domain via the SAMR protocol
#

from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

from impacket import version
from impacket.examples import logger
from impacket.examples.utils import parse_identity
from impacket.dcerpc.v5 import samr, epm, transport
from impacket.spnego import SPNEGO_NegTokenInit, TypesMech

from impacket.examples.utils import init_ldap_session, ldap3_kerberos_login

import ldap3
import argparse
import logging
import sys
import string
import random
import ssl
from binascii import unhexlify

from impacket.ldap import ldap
from impacket.ldap import ldapasn1
from impacket.examples.utils import ldap_login


class ADDCOMPUTER:
Expand Down Expand Up @@ -148,32 +144,34 @@ def run_samr(self):

def run_ldaps(self):
try:
ldapServer, ldapConn = init_ldap_session(self.__domain, self.__username, self.__password, self.__lmhash, self.__nthash, self.__doKerberos, self.__targetIp, self.__target, self.__aesKey, True)
ldapConn = ldap_login(self.__target, self.__baseDN, self.__targetIp, self.__target, self.__doKerberos, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey, ldaps_flag=True)

if self.__noAdd or self.__delete:
if not self.LDAPComputerExists(ldapConn, self.__computerName):
raise Exception("Account %s not found in %s!" % (self.__computerName, self.__baseDN))

computer = self.LDAPGetComputer(ldapConn, self.__computerName)
computerDn = self.LDAPGetComputerDN(ldapConn, self.__computerName)

if self.__delete:
res = ldapConn.delete(computer.entry_dn)
message = "delete"
else:
res = ldapConn.modify(computer.entry_dn, {'unicodePwd': [(ldap3.MODIFY_REPLACE, ['"{}"'.format(self.__computerPassword).encode('utf-16-le')])]})
message = "set password for"


if not res:
if ldapConn.result['result'] == ldap3.core.results.RESULT_INSUFFICIENT_ACCESS_RIGHTS:
try:
if self.__delete:
ldapConn.delete(computerDn)
else:
ldapConn.modify(computerDn, {'unicodePwd': [(ldap.MODIFY_REPLACE, ['"{}"'.format(self.__computerPassword).encode('utf-16-le')])]})
except ldap.LDAPSessionError as e:
if e.getErrorCode() == 50: # insufficientAccessRights
raise Exception("User %s doesn't have right to %s %s!" % (self.__username, message, self.__computerName))
else:
raise Exception(str(ldapConn.result))
raise Exception(str(e))

if self.__noAdd:
logging.info("Succesfully set password of %s to %s." % (self.__computerName, self.__computerPassword))
else:
if self.__noAdd:
logging.info("Succesfully set password of %s to %s." % (self.__computerName, self.__computerPassword))
else:
logging.info("Succesfully deleted %s." % self.__computerName)
logging.info("Succesfully deleted %s." % self.__computerName)

else:
if self.__computerName is not None:
Expand Down Expand Up @@ -204,18 +202,19 @@ def run_ldaps(self):
'unicodePwd': ('"%s"' % self.__computerPassword).encode('utf-16-le')
}

res = ldapConn.add(computerDn, ['top','person','organizationalPerson','user','computer'], ucd)
if not res:
if ldapConn.result['result'] == ldap3.core.results.RESULT_UNWILLING_TO_PERFORM:
error_code = int(ldapConn.result['message'].split(':')[0].strip(), 16)
try:
ldapConn.add(computerDn, ['top','person','organizationalPerson','user','computer'], ucd)
except ldap.LDAPSessionError as e:
if e.getErrorCode() == 53: # unwillingToPerform
error_code = int(e.getErrorString().split(':')[1].strip(), 16)
if error_code == 0x216D:
raise Exception("User %s machine quota exceeded!" % self.__username)
else:
raise Exception(str(ldapConn.result))
elif ldapConn.result['result'] == ldap3.core.results.RESULT_INSUFFICIENT_ACCESS_RIGHTS:
raise Exception(str(e))
elif e.getErrorCode() == 50: # insufficientAccessRights
raise Exception("User %s doesn't have right to create a machine account!" % self.__username)
else:
raise Exception(str(ldapConn.result))
raise Exception(str(e))
else:
logging.info("Successfully added machine account %s with password %s." % (self.__computerName, self.__computerPassword))
except Exception as e:
Expand All @@ -227,12 +226,16 @@ def run_ldaps(self):


def LDAPComputerExists(self, connection, computerName):
connection.search(self.__baseDN, '(sAMAccountName=%s)' % computerName)
return len(connection.entries) ==1

def LDAPGetComputer(self, connection, computerName):
connection.search(self.__baseDN, '(sAMAccountName=%s)' % computerName)
return connection.entries[0]
results = connection.search(searchBase=self.__baseDN, searchFilter='(sAMAccountName=%s)' % computerName)
entries = [item for item in results if isinstance(item, ldapasn1.SearchResultEntry)]
return len(entries) == 1

def LDAPGetComputerDN(self, connection, computerName):
results = connection.search(searchBase=self.__baseDN, searchFilter='(sAMAccountName=%s)' % computerName)
for item in results:
if isinstance(item, ldapasn1.SearchResultEntry):
return str(item['objectName'])
return None

def generateComputerName(self):
return 'DESKTOP-' + (''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) + '$')
Expand Down
35 changes: 31 additions & 4 deletions examples/atexec.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#
# Author:
# Alberto Solino (@agsolino)
# Log Poisoning / Overflow implementation by Ruben Enkaoua (@rubenlabs with Kopnex / Cymulate)
#
# Reference for:
# DCE/RPC for TSCH
Expand All @@ -41,10 +42,11 @@
from six import PY2

CODEC = sys.stdout.encoding
AUTHOR_OVERFLOW_LEN = 4000

class TSCH_EXEC:
def __init__(self, username='', password='', domain='', hashes=None, aesKey=None, doKerberos=False, kdcHost=None,
command=None, sessionId=None, silentCommand=False):
command=None, sessionId=None, silentCommand=False, authorLog=None, overflow=False):
self.__username = username
self.__password = password
self.__domain = domain
Expand All @@ -55,6 +57,8 @@ def __init__(self, username='', password='', domain='', hashes=None, aesKey=None
self.__kdcHost = kdcHost
self.__command = command
self.__silentCommand = silentCommand
self.__authorLog = authorLog
self.__overflow = overflow
self.sessionId = sessionId

if hashes is not None:
Expand Down Expand Up @@ -123,9 +127,23 @@ def cmd_split(cmdline):
cmd = "cmd.exe"
args = "/C %s > %%windir%%\\Temp\\%s 2>&1" % (self.__command, tmpFileName)

registrationInfo = ''
if self.__overflow is True:
logging.info('Using Author overflow (%d characters) for Task Scheduler log tampering' % AUTHOR_OVERFLOW_LEN)
registrationInfo = """ <RegistrationInfo>
<Author>%s</Author>
</RegistrationInfo>
""" % ('A' * AUTHOR_OVERFLOW_LEN)
elif self.__authorLog is not None:
logging.info('Using fake Author "%s" for Task Scheduler log poisoning' % self.__authorLog)
registrationInfo = """ <RegistrationInfo>
<Author>%s</Author>
</RegistrationInfo>
""" % xml_escape(self.__authorLog)

xml = """<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<Triggers>
%s <Triggers>
<CalendarTrigger>
<StartBoundary>2015-07-15T20:35:13.2757294</StartBoundary>
<Enabled>true</Enabled>
Expand Down Expand Up @@ -165,7 +183,8 @@ def cmd_split(cmdline):
</Exec>
</Actions>
</Task>
""" % ((xml_escape(cmd) if self.__silentCommand is False else self.__command.split()[0]),
""" % (registrationInfo,
(xml_escape(cmd) if self.__silentCommand is False else self.__command.split()[0]),
(xml_escape(args) if self.__silentCommand is False else " ".join(self.__command.split()[1:])))
taskCreated = False
try:
Expand Down Expand Up @@ -251,6 +270,13 @@ def cmd_split(cmdline):
parser.add_argument('-ts', action='store_true', help='adds timestamp to every logging output')
parser.add_argument('-silentcommand', action='store_true', default = False, help='does not execute cmd.exe to run '
'given command (no output)')
author_group = parser.add_mutually_exclusive_group()
author_group.add_argument('-overflow', action='store_true', default=False,
help='overflow Security Event 4698 by setting a %d-byte Author in the task XML '
'(mutually exclusive with -author-log)' % AUTHOR_OVERFLOW_LEN)
author_group.add_argument('-author-log', action='store', metavar='author',
help='poison Security Event 4698 Author field with the given value '
'(mutually exclusive with -overflow)')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
parser.add_argument('-codec', action='store', help='Sets encoding used (codec) from the target\'s output (default '
'"%s"). If errors are detected, run chcp.com at the target, '
Expand Down Expand Up @@ -310,5 +336,6 @@ def cmd_split(cmdline):
options.k = True

atsvc_exec = TSCH_EXEC(username, password, domain, options.hashes, options.aesKey, options.k, options.dc_ip,
' '.join(options.command), options.session_id, options.silentcommand)
' '.join(options.command), options.session_id, options.silentcommand,
options.author_log, options.overflow)
atsvc_exec.play(address)
Loading
Loading