Security Advisory: MULTIPLE DENIAL OF SERVICE VULNERABILITIES IN KRB4 KDC

2000-06-10T00:00:00
ID SECURITYVULNS:DOC:323
Type securityvulns
Reporter Securityvulns
Modified 2000-06-10T00:00:00

Description

-----BEGIN PGP SIGNED MESSAGE-----

    MULTIPLE DENIAL OF SERVICE VULNERABILITIES IN KRB4 KDC

2000-06-09

SUMMARY:

A buffer overrun capable of creating a denial of service exists in implementations of Kerberos 4 KDC programs. This is IN ADDITION to the krb_rd_req() vulnerability that was previously announced. Many Kerberos 4 KDC implementations derived from MIT sources are believed to be vulnerable.

Another denial of service vulnerability exists in the krb5-1.1.x KDC implementations (and krb5-1.2-beta1, but not krb5-1.0.x) that can cause the Kerberos 4 compatibility code to perform a double-free, possibly resulting in a crash of the KDC process.

IMPACT:

A remote user may be able to cause the KDC to issue bogus tickets, or to return an error of the form "principal unknown" for all principals, necessitating a restart of the KDC to resume proper operation.

A remote user may also be able to cause a krb5-1.1.x KDC to experience a segmentation violation or malloc pool corruption, causing the KDC process to crash.

DETAILS:

A static buffer can be overrun by corrupt requests sent to a KDC process. It is believed that this overrun does not lead to a root compromise, but it can lead to a denial of service by corrupting long-term state in the KDC process.

The krb5-1.1.x KDC contains in its Kerberos 4 compatibility mode some code which tickles a memory management bug in the library. This can result in a double-free of memory and corruption of the malloc pool, possibly leading to a crash of the KDC. Whether or not a crash occurs depends on the idiosyncrasies of the malloc implementation used.

VULNERABLE DISTRIBUTIONS:

Source distributions which may contain vulnerable code include:

    MIT Kerberos 5 releases krb5-1.0.x, krb5-1.1, krb5-1.1.1

    MIT Kerberos 4 patch 10, and probably earlier releases as well

    KerbNet (Cygnus implementation of Kerberos 5)

    Cygnus Network Security (CNS -- Cygnus implementation of
            Kerberos 4)

    KTH-krb4 before version 0.10

NON-VULNERABLE DISTRIBUTIONS:

Source distributions that are believed not to be vulnerable include:

    KTH-krb4 -- version 0.10 and above

    Heimdal (KTH implementation of Kerberos 5) -- any version

FIXES:

The best course of action is to patch your KDC. If you have not done so already, install the patches to deal with the krb_rd_req() vulnerability that was previously announced. Patches and the original announcement may be found at:

    http://web.mit.edu/kerberos/www/advisories/index.html

MIT will soon release krb5-1.2, which will have these changes incorporated. The krb5-1.2-beta1 release does not have this fix, though the upcoming krb5-1.2-beta2 release, tentatively scheduled for the week of June 5, will. The two recent beta patch releases, krb5-1.0.7-beta2 and krb5-1.1.2-beta1, which were intended to fix the krb4 buffer overrun problems, have not been patched for this problem yet.

The following patches for this bug will be posted to the aforementioned security advisories web page once this announcement is made public.

PATCHES AGAINST krb5-1.0.x:

These patches are against krb5-1.0.7-beta1, but will probably apply cleanly against krb5-1.0.6 and earlier releases as well.

These patches also will be made available at:

    http://web.mit.edu/kerberos/www/advisories/krb4kdc_10x_patch.txt

Index: kerberos_v4.c

RCS file: /cvs/krbdev/krb5/src/kdc/kerberos_v4.c,v retrieving revision 5.56.4.3 diff -c -r5.56.4.3 kerberos_v4.c kerberos_v4.c 1998/05/10 03:02:55 5.56.4.3 - --- kerberos_v4.c 2000/05/19 19:38:17 ** 173,183 *** return(retval);

  if (!*local_realm) {              /* local-realm name already set up */
    • / XXX assumes realm is null-terminated! / lrealm = master_princ->realm.data; ! if (strlen(lrealm) < sizeof(local_realm)) ! strcpy(local_realm, lrealm); ! else retval = KRB5_CONFIG_NOTENUFSPACE; } /* convert client_fulladdr to client_sockaddr:
  • --- 173,183 ---- return(retval);

    if (!local_realm) { / local-realm name already set up / lrealm = master_princ->realm.data; ! if (master_princ->realm.length < sizeof(local_realm)) { ! memcpy(local_realm, lrealm, master_princ->realm.length); ! local_realm[master_princ->realm.length] = '\0'; ! } else retval = KRB5_CONFIG_NOTENUFSPACE; } / convert client_fulladdr to client_sockaddr:


    196,201 * - --- 196,202 ---- return KRB5KRB_ERR_FIELD_TOOLONG; } v4_pkt.length = pkt->length; + v4_pkt.mbz = 0; memcpy( v4_pkt.dat, pkt->data, pkt->length);

    kerberos_v4( &client_sockaddr, &v4_pkt);


    507,512 * - --- 508,516 ----

    req_act_vno = req_version;

  • / set these to point to something safe /

  • req_name_ptr = req_inst_ptr = req_realm_ptr = ""; + / check packet version / if (req_version != KRB_PROT_VERSION) { lt = klog(L_KRB_PERR,

574,580 *

        if &#40;&#40;i = check_princ&#40;req_name_ptr, req_inst_ptr, 0,
                             &amp;a_name_data&#41;&#41;&#41; {

! kerb_err_reply(client, pkt, i, lt); a_name_data.key_low = a_name_data.key_high = 0; return; } - --- 578,584 ----

        if &#40;&#40;i = check_princ&#40;req_name_ptr, req_inst_ptr, 0,
                             &amp;a_name_data&#41;&#41;&#41; {

! kerb_err_reply(client, pkt, i, "check_princ failed"); a_name_data.key_low = a_name_data.key_high = 0; return; }


586,592 / this does all the checking / if ((i = check_princ(service, instance, lifetime, &s_name_data))) { ! kerb_err_reply(client, pkt, i, lt); a_name_data.key_high = a_name_data.key_low = 0; s_name_data.key_high = s_name_data.key_low = 0; return; - --- 590,596 ---- / this does all the checking / if ((i = check_princ(service, instance, lifetime, &s_name_data))) { ! kerb_err_reply(client, pkt, i, "check_princ_failed"); a_name_data.key_high = a_name_data.key_low = 0; s_name_data.key_high = s_name_data.key_low = 0; return; ** 664,681 tk->length = 0; k_flags = 0; / various kerberos flags */

        auth-&gt;length = 4 + strlen&#40;&#40;char *&#41;pkt-&gt;dat + 3&#41;;
        auth-&gt;length += &#40;int&#41; *&#40;pkt-&gt;dat + auth-&gt;length&#41; +
            &#40;int&#41; *&#40;pkt-&gt;dat + auth-&gt;length + 1&#41; + 2;

        memcpy&#40;auth-&gt;dat, pkt-&gt;dat, auth-&gt;length&#41;;

        strncpy&#40;tktrlm, &#40;char *&#41;auth-&gt;dat + 3, REALM_SZ&#41;;
        if &#40;set_tgtkey&#40;tktrlm&#41;&#41; {
            lt = klog&#40;L_ERR_UNK,
                &quot;FAILED realm &#37;s unknown. Host: &#37;s &quot;,
                      tktrlm, inet_ntoa&#40;client_host&#41;&#41;;

! kerb_err_reply(client, pkt, kerno, lt); return; } kerno = krb_rd_req(auth, "ktbtgt", tktrlm, client_host.s_addr, - --- 668,706 ---- tk->length = 0; k_flags = 0; / various kerberos flags /

  • auth->mbz = 0; / pkt->mbz already zeroed / auth->length = 4 + strlen((char *)pkt->dat + 3);
  • if (auth->length + 1 > MAX_KTXT_LEN) {
  • lt = klog(L_KRB_PERR,
  • "APPL request with realm length too long from %s",
  • inet_ntoa(client_host));
  • kerb_err_reply(client, pkt, RD_AP_INCON,
  • "realm length too long");
  • return;
  • } + auth->length += (int) (pkt->dat + auth->length) + (int) (pkt->dat + auth->length + 1) + 2;
  • if (auth->length > MAX_KTXT_LEN) {
  • lt = klog(L_KRB_PERR,
  • "APPL request with funky tkt or req_id length from %s",
  • inet_ntoa(client_host));
  • kerb_err_reply(client, pkt, RD_AP_INCON,
  • "funky tkt or req_id length");
  • return;
  • }

        memcpy&#40;auth-&gt;dat, pkt-&gt;dat, auth-&gt;length&#41;;
    
        strncpy&#40;tktrlm, &#40;char *&#41;auth-&gt;dat + 3, REALM_SZ&#41;;
    
    • tktrlm[REALM_SZ-1] = '\0'; if (set_tgtkey(tktrlm)) { lt = klog(L_ERR_UNK, "FAILED realm %s unknown. Host: %s ", tktrlm, inet_ntoa(client_host)); ! / no better error code / ! kerb_err_reply(client, pkt, ! KERB_ERR_PRINCIPAL_UNKNOWN, lt); return; } kerno = krb_rd_req(auth, "ktbtgt", tktrlm, client_host.s_addr,

    720,726 kerno = check_princ(service, instance, req_life, &s_name_data); if (kerno) { ! kerb_err_reply(client, pkt, kerno, lt); return; } / Bound requested lifetime with service and user / - --- 745,751 ---- kerno = check_princ(service, instance, req_life, &s_name_data); if (kerno) { ! kerb_err_reply(client, pkt, kerno, "check_princ failed"); return; } / Bound requested lifetime with service and user / ** 844,850 * static char e_msg[128];

    strcpy(e_msg, "\nKerberos error -- "); ! strcat(e_msg, string); cr_err_reply(e_pkt, req_name_ptr, req_inst_ptr, req_realm_ptr, req_time_ws, err, e_msg); krb4_sendto(f, (char *) e_pkt->dat, e_pkt->length, 0, - --- 869,875 ---- static char e_msg[128];

    strcpy(e_msg, "\nKerberos error -- "); ! strncat(e_msg, string, sizeof(e_msg) - 1 - 19); cr_err_reply(e_pkt, req_name_ptr, req_inst_ptr, req_realm_ptr, req_time_ws, err, e_msg); krb4_sendto(f, (char *) e_pkt->dat, e_pkt->length, 0,


    989,995 kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); krb_set_key((char ) key, 0); ! strcpy(lastrealm, r); return (KSUCCESS); }

  • --- 1014,1021 ---- kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); krb_set_key((char *) key, 0); ! strncpy(lastrealm, r, sizeof(lastrealm) - 1); ! lastrealm[sizeof(lastrealm) - 1] = '\0'; return (KSUCCESS); }

PATCHES AGAINST krb5-1.1.1:

These patches should apply cleanly against krb5-1.2-beta1 as well. These patches will also be made available at:

    http://web.mit.edu/kerberos/www/advisories/krb4kdc_111_patch.txt

Index: krb5/src/kdc/kerberos_v4.c diff -c krb5/src/kdc/kerberos_v4.c:5.65.2.2 krb5/src/kdc/kerberos_v4.c:5.65.2.3 krb5/src/kdc/kerberos_v4.c:5.65.2.2 Wed Sep 22 20:47:22 1999 - --- krb5/src/kdc/kerberos_v4.c Mon Jun 5 13:58:34 2000 ** 233,243 *** return(retval);

  if &#40;!*local_realm&#41; {              /* local-realm name already set up */
    • / XXX assumes realm is null-terminated! / lrealm = master_princ->realm.data; ! if (strlen(lrealm) < sizeof(local_realm)) ! strcpy(local_realm, lrealm); ! else retval = KRB5_CONFIG_NOTENUFSPACE; } /* convert client_fulladdr to client_sockaddr:
  • --- 233,243 ---- return(retval);

    if (!local_realm) { / local-realm name already set up / lrealm = master_princ->realm.data; ! if (master_princ->realm.length < sizeof(local_realm)) { ! memcpy(local_realm, lrealm, master_princ->realm.length); ! local_realm[master_princ->realm.length] = '\0'; ! } else retval = KRB5_CONFIG_NOTENUFSPACE; } / convert client_fulladdr to client_sockaddr:


    256,261 * - --- 256,262 ---- return KRB5KRB_ERR_FIELD_TOOLONG; } v4_pkt.length = pkt->length; + v4_pkt.mbz = 0; memcpy( v4_pkt.dat, pkt->data, pkt->length);

    kerberos_v4( &client_sockaddr, &v4_pkt);


    293,299 case L_APPL_REQ: strcpy(log_text, "PROCESS_V4:"); vsprintf(log_text+strlen(log_text), format, pvar); ! krb5_klog_syslog(logpri, log_text); / ignore the other types... / } va_end(pvar); - --- 294,300 ---- case L_APPL_REQ: strcpy(log_text, "PROCESS_V4:"); vsprintf(log_text+strlen(log_text), format, pvar); ! krb5_klog_syslog(logpri, "%s", log_text); / ignore the other types... / } va_end(pvar); ** 622,627 * - --- 623,631 ----

    req_act_vno = req_version;

  • / set these to point to something safe /

  • req_name_ptr = req_inst_ptr = req_realm_ptr = ""; + / check if disabled, but we tell client / if (kdc_v4 == KDC_V4_DISABLE) { lt = klog(L_KRB_PERR,

700,706 *

        if &#40;&#40;i = check_princ&#40;req_name_ptr, req_inst_ptr, 0,
                             &amp;a_name_data, &amp;k5key, 0&#41;&#41;&#41; {

! kerb_err_reply(client, pkt, i, lt); a_name_data.key_low = a_name_data.key_high = 0; krb5_free_keyblock_contents(kdc_context, &k5key); return; - --- 704,710 ----

        if &#40;&#40;i = check_princ&#40;req_name_ptr, req_inst_ptr, 0,
                             &amp;a_name_data, &amp;k5key, 0&#41;&#41;&#41; {

! kerb_err_reply(client, pkt, i, "check_princ failed"); a_name_data.key_low = a_name_data.key_high = 0; krb5_free_keyblock_contents(kdc_context, &k5key); return;


715,721 / this does all the checking / if ((i = check_princ(service, instance, lifetime, &s_name_data, &k5key, 1))) { ! kerb_err_reply(client, pkt, i, lt); a_name_data.key_high = a_name_data.key_low = 0; s_name_data.key_high = s_name_data.key_low = 0; krb5_free_keyblock_contents(kdc_context, &k5key); - --- 719,725 ---- / this does all the checking / if ((i = check_princ(service, instance, lifetime, &s_name_data, &k5key, 1))) { ! kerb_err_reply(client, pkt, i, "check_princ failed"); a_name_data.key_high = a_name_data.key_low = 0; s_name_data.key_high = s_name_data.key_low = 0; krb5_free_keyblock_contents(kdc_context, &k5key); ** 806,824 tk->length = 0; k_flags = 0; / various kerberos flags */

        auth-&gt;length = 4 + strlen&#40;&#40;char *&#41;pkt-&gt;dat + 3&#41;;
        auth-&gt;length += &#40;int&#41; *&#40;pkt-&gt;dat + auth-&gt;length&#41; +
            &#40;int&#41; *&#40;pkt-&gt;dat + auth-&gt;length + 1&#41; + 2;

        memcpy&#40;auth-&gt;dat, pkt-&gt;dat, auth-&gt;length&#41;;

        strncpy&#40;tktrlm, &#40;char *&#41;auth-&gt;dat + 3, REALM_SZ&#41;;
        kvno = &#40;krb5_kvno&#41;auth-&gt;dat[2];
        if &#40;set_tgtkey&#40;tktrlm, kvno&#41;&#41; {
            lt = klog&#40;L_ERR_UNK,
                      &quot;FAILED set_tgtkey realm &#37;s, kvno &#37;d. Host: &#37;s &quot;,
                      tktrlm, kvno, inet_ntoa&#40;client_host&#41;&#41;;

! kerb_err_reply(client, pkt, kerno, lt); return; } kerno = krb_rd_req(auth, "krbtgt", tktrlm, client_host.s_addr, - --- 810,849 ---- tk->length = 0; k_flags = 0; / various kerberos flags /

  • auth->mbz = 0; / pkt->mbz already zeroed / auth->length = 4 + strlen((char *)pkt->dat + 3);
  • if (auth->length + 1 > MAX_KTXT_LEN) {
  • lt = klog(L_KRB_PERR,
  • "APPL request with realm length too long from %s",
  • inet_ntoa(client_host));
  • kerb_err_reply(client, pkt, RD_AP_INCON,
  • "realm length too long");
  • return;
  • } + auth->length += (int) (pkt->dat + auth->length) + (int) (pkt->dat + auth->length + 1) + 2;
  • if (auth->length > MAX_KTXT_LEN) {
  • lt = klog(L_KRB_PERR,
  • "APPL request with funky tkt or req_id length from %s",
  • inet_ntoa(client_host));
  • kerb_err_reply(client, pkt, RD_AP_INCON,
  • "funky tkt or req_id length");
  • return;
  • }
        memcpy&#40;auth-&gt;dat, pkt-&gt;dat, auth-&gt;length&#41;;
    
        strncpy&#40;tktrlm, &#40;char *&#41;auth-&gt;dat + 3, REALM_SZ&#41;;
    
    • tktrlm[REALM_SZ-1] = '\0'; kvno = (krb5_kvno)auth->dat[2]; if (set_tgtkey(tktrlm, kvno)) { lt = klog(L_ERR_UNK, "FAILED set_tgtkey realm %s, kvno %d. Host: %s ", tktrlm, kvno, inet_ntoa(c