Integrate create-shadowcopy and skip-preauth-probe features - #6
Merged
Conversation
Standalone impacket script to create, list, and delete VSS shadow copies on a remote machine using Win32_ShadowCopy over DCOM. No vssadmin process is spawned on the target, avoiding process creation telemetry.
The original getKerberosTGT() sent an initial AS-REQ without PA_ENC_TIMESTAMP to discover the KDC's supported etypes and salt, then followed up with an authenticated AS-REQ. This two-step pattern is detectable as "AS-REQ without pre-authentication data followed by TGS-REQ" and fingerprints impacket on the wire. This change computes the Kerberos salt locally using the standard AD convention (REALM + sAMAccountName) and includes PA_ENC_TIMESTAMP in the very first AS-REQ. If the salt guess is wrong (e.g. case mismatch or renamed account), the correct salt is extracted from the KDC_ERR_PREAUTH_FAILED error response's e-data and the request is retried — still with PA_ENC_TIMESTAMP, so no bare probe ever hits the wire. The kerberoast_no_preauth path (AS-REP roasting) is preserved as a separate early-return branch that intentionally omits PA_ENC_TIMESTAMP.
Rewrite getKerberosTGT() and sendReceive() so the Kerberos AS-REQ is indistinguishable from MIT krb5 kinit on the wire: - Transport: UDP first with TCP fallback on KRB_ERR_RESPONSE_TOO_BIG - kdc-options: renewable_ok only (kinit default), overridable via KRBTGTFLAGS env var for delegation attacks - sname type: NT_SRV_INST (2) for krbtgt, per RFC 4120 - etype list: broad 8-type list matching MIT default_enctype_list - rtime: omitted (kinit only sets it with explicit renew lifetime) - padata: two-step flow with PA_AS_FRESHNESS (150) and PA_REQ_ENC_PA_REP (149) instead of PA_PAC_REQUEST (128) Also adds missing EncryptionTypes (19, 20, 25, 26) and PreAuthenticationDataTypes (149, 150) to constants.
Older domains may only support RC4. The probe AS-REQ advertises the broad kinit etype list, but the KDC returns only its supported etypes in the PREAUTH_REQUIRED response. Pick the first KDC-supported etype instead of assuming our preference is available.
Match getKerberosTGS() fields to what kvno produces on the wire: - kdc-options: add canonicalize flag (renewable+canonicalize) - etype list: kinit-style AES-first broad list, remove DES-CBC-MD5 - sname type: NT_PRINCIPAL (1) matching kvno behavior
Accounts with DONT_REQUIRE_PREAUTH return an AS-REP directly from the probe. Without PA_PAC_REQUEST the KDC response could be missing enc-part, causing PyAsn1Error. Add PA_PAC_REQUEST alongside the kinit-style PA_AS_FRESHNESS and PA_REQ_ENC_PA_REP padata.
Two fixes: - Move step 2 (authenticated AS-REQ) out of the password-only else block into its own if-preAuth-is-True block, fixing enc-part error when using NTLM hash or AES key authentication - Remove PA_PAC_REQUEST from probe AS-REQ to match kinit exactly, fixing extra UDP-to-TCP fallback caused by larger KDC response
CCache.parseFile() failed to find service tickets and TGTs in cross-realm scenarios because it hardcoded the lookup realm to the ccache's default principal realm. When a ticket was stored under a different realm (e.g. HOST/target@CHILD.DOMAIN vs the ccache's PARENT.DOMAIN), the lookup missed it entirely. Add fallback searches that scan all credentials by SPN prefix regardless of realm. This fixes atexec, dcomexec, smbexec, wmiexec, and psexec when using cross-domain Kerberos tickets.
New tool: cross_realm_tgs.py - Follows Kerberos referral chains to obtain service tickets across realm boundaries automatically - Requests multiple service types (default: cifs,host) in one run, saving all tickets to a single ccache file - Handles multi-hop referrals with automatic KDC resolution via SRV records or manual -kdc-map ccache.py: Fix cross-realm credential lookup - Add fallback search by hostname when exact SPN+realm match fails - Add fallback search for any krbtgt ticket when home-realm TGT is not found - Fixes atexec/dcomexec/smbexec/wmiexec/psexec with cross-domain Kerberos tickets kerberosv5.py: Fix preauth flow for hash/key auth - Move step 2 AS-REQ into correct scope so NTLM hash and AES key authentication work with the kinit-aligned flow - Remove PA_PAC_REQUEST from probe to match kinit wire format
Three bugs prevented sapphire tickets from working with the -k (ccache) flow: 1. oldSessionKey was never assigned in the ccache code path, causing _extract_reply_ticket_times to fail with an unbound variable error. When loading from ccache there is no key exchange, so oldSessionKey equals sessionKey. 2. _extract_reply_ticket_times and the cipher-validation checks tried to read the enc-part etype from the ccache-sourced AS_REP structure, which may contain a stale/placeholder etype (e.g. DES etype 1) that is absent from impacket's _enctype_table, raising a KeyError. These checks are unnecessary for the -k path — the real cipher negotiation happens during S4U2Self+U2U — so skip them entirely. 3. saveTicket loaded the existing KRB5CCNAME ccache and appended the new ticket, carrying over the original TGT (e.g. DomainA\userA). Downstream tools then picked the first matching krbtgt credential instead of the sapphire ticket, causing cross-realm service tickets to be issued for the wrong principal. Always create a fresh ccache so the saved file contains only the forged ticket. Tested with sapphire ticket generation using a ccache TGT, followed by cross-realm TGS referral (DomainA → forest root → DomainB) to obtain cifs/ and host/ service tickets under the impersonated identity.
…agement via WMI DCOM
…ng and cross-realm support
1. impacket/krb5/ccache.py: the cross-realm TGT fallback matched ANY
krbtgt credential in the cache, which made parseFile("unexistent_domain")
return a TGT for whatever realm happened to be cached. Restrict the
scan to referral TGTs whose target realm equals the requested domain
(krbtgt/<domain>@*). Fixes tests/misc/test_ccache.py::test_ccache_parseFile.
2. tests/misc/test_ticketer.py: build_options() constructs a minimal
SimpleNamespace of the option flags ticketer.py accesses. The ccache
auth commit added self.__options.k to createBasicTicket() without
adding a default to the fixture, so the mock raised AttributeError.
Add k=False to the fixture (matches the argparse default).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Combines two feature branches into master:
examples/create_shadowcopy.py: VSS shadow copy management via WMI DCOM (create, list, expose, delete). Complements the raw-ntfs-parse toolkit for offline NTDS/hive extraction.impacket/krb5/:kinitto avoid honeypot fingerprintingPA_ENC_TIMESTAMPincluded in first AS-REQ to skip the bare preauth probePA_PAC_REQUESTadded to probe AS-REQ (removed once the preauth flow completes) for accounts without preauthkinit/kvnoKeyErrorfix when KDC does not offer AES256examples/cross_realm_tgs.py-k)Both branches were rebased onto current master (after the recent fortra merge that added NEGOEX phase 1/2 and DFS support) so no upstream work is reverted.
Files changed
examples/create_shadowcopy.pyexamples/cross_realm_tgs.pyexamples/ticketer.pyimpacket/krb5/ccache.pyimpacket/krb5/constants.pyimpacket/krb5/kerberosv5.pyTotal: +1,142 / -142 across 6 files.
Test plan
create_shadowcopy.py create <target>against a lab domain controller — confirm shadow copy is created and device path is printedcreate_shadowcopy.py list <target>— confirm existing snapshots enumerateGetUserSPNs.py,secretsdump.py -k) against a lab KDC — confirm no regressionsDONT_REQ_PREAUTHset — confirm the probe path still workscross_realm_tgs.pyagainst a trusted realm pair — confirm ticket acquisitionticketer.py -k(ccache auth path)Notes
Uses
--no-ffmerges so each source branch has its own merge commit + preserved history on the integration branch.