From skotttt228 at gmail.com Tue Dec 9 09:58:06 2025 From: skotttt228 at gmail.com (=?UTF-8?B?0JDQu9C10LrRgdCw0L3QtNGAINCR0LDQutCw0L3QvtCy0YHQutC40Lk=?=) Date: Tue, 9 Dec 2025 17:58:06 +0300 Subject: KDC audit plugin potential use-after-free Message-ID: Hello! While working with audit plugin I found 2 use-after-free issues 1. So right before passing a state.reply to a kau_as_req() audit function it frees state.reply.enc_part.ciphertext.data so from the plugin side we can use-after-free (which happened to me:)) https://github.com/krb5/krb5/blob/master/src/kdc/do_as_req.c#L334 I guess we should free after passing the struct to the kau_as_req() This behavior occurs for both AS_REP and TGS_REP 2. For TGS_REP there also an issue https://github.com/krb5/krb5/blob/master/src/kdc/do_tgs_req.c#L1087 - here reply.ticket is a reference to a ticket_reply https://github.com/krb5/krb5/blob/master/src/kdc/do_tgs_req.c#L1125 - here reply is passed to an audit plugin in a kau_tgs_req() https://github.com/krb5/krb5/blob/master/src/kdc/do_tgs_req.c#L1129 - cleanup happens here However from the audit plugin perspective ticket.enc_part is already free'd So if the operation is TGS_REP and ticket is not NULL => enc_part field is now use-after-free here's valgrind: ==7== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) ==16== Invalid read of size 1 ==16== at 0x5558591: ::next (in /usr/lib/x86_64-linux-gnu/krb5/plugins/audit/libkdc_audit.so) ==16== by 0x5556DA1: >::from_iter (in /usr/lib/x86_64-linux-gnu/krb5/plugins/audit/libkdc_audit.so) ==16== by 0x5551D25: >::from_ref (in /usr/lib/x86_64-linux-gnu/k rb5/plugins/audit/libkdc_audit.so) ==16== by 0x55574D1: >::from_ref (in /usr/lib/x86_64-linux-gnu/krb5/p lugins/audit/libkdc_audit.so) ==16== by 0x554E2A3: >::from_ref (in /usr/lib/x86_64-linux-gn u/krb5/plugins/audit/libkdc_audit.so) ==16== by 0x554DAA3: >::from_ref (in /usr/lib/x86_64-linux-gnu/krb5 /plugins/audit/libkdc_audit.so) ==16== by 0x554009C: kdc_audit::kdc_state::Krb5AuditState::new (in /usr/lib/x86_64-linux-gnu/krb5/plugins/audit/libkdc_audit.so) ==16== by 0x5542C88: audit_process_request (in /usr/lib/x86_64-linux-gnu/krb5/plugins/audit/libkdc_audit.so) ==16== by 0x120103: ??? (in /usr/sbin/krb5kdc) ==16== by 0x112543: ??? (in /usr/sbin/krb5kdc) ==16== Address 0x4cd8fe0 is 0 bytes inside a block of size 377 free'd ==16== at 0x484417B: free (vg_replace_malloc.c:872) ==16== by 0x114935: ??? (in /usr/sbin/krb5kdc) ==16== by 0x110985: ??? (in /usr/sbin/krb5kdc) ==16== by 0x124CAD: ??? (in /usr/sbin/krb5kdc) ==16== by 0x49C4137: verto_fire (in /usr/lib/x86_64-linux-gnu/libverto.so.1.0.0) ==16== by 0x5048632: ev_invoke_pending (in /usr/lib/x86_64-linux-gnu/libev.so.4.0.0) ==16== by 0x504BE70: ev_run (in /usr/lib/x86_64-linux-gnu/libev.so.4.0.0) ==16== by 0x10F7D6: ??? (in /usr/sbin/krb5kdc) ==16== by 0x49EF249: (below main) (libc_start_call_main.h:58) ==16== Block was alloc'd at ==16== at 0x48417B4: malloc (vg_replace_malloc.c:381) ==16== by 0x48E706C: krb5_encrypt_helper (in /usr/lib/x86_64-linux-gnu/libkrb5.so.3.3) ==16== by 0x48E73BC: krb5_encrypt_tkt_part (in /usr/lib/x86_64-linux-gnu/libkrb5.so.3.3) ==16== by 0x113B77: ??? (in /usr/sbin/krb5kdc) ==16== by 0x110985: ??? (in /usr/sbin/krb5kdc) ==16== by 0x124CAD: ??? (in /usr/sbin/krb5kdc) ==16== by 0x49C4137: verto_fire (in /usr/lib/x86_64-linux-gnu/libverto.so.1.0.0) ==16== by 0x5048632: ev_invoke_pending (in /usr/lib/x86_64-linux-gnu/libev.so.4.0.0) ==16== by 0x504BE70: ev_run (in /usr/lib/x86_64-linux-gnu/libev.so.4.0.0) ==16== by 0x10F7D6: ??? (in /usr/sbin/krb5kdc) ==16== In my code I used workarounds to not parse reply.ticket.enc_part for TGS_REP and reply.enc_part.cipher when AS_REP/TGS_REP. I'm not sure if that is an issue or a safety design not to access these fields Thanks in advance! From ghudson at mit.edu Wed Dec 10 01:52:03 2025 From: ghudson at mit.edu (Greg Hudson) Date: Wed, 10 Dec 2025 01:52:03 -0500 Subject: KDC audit plugin potential use-after-free In-Reply-To: References: Message-ID: <22c2f09d-b810-49b2-a597-c2ab780140b6@mit.edu> On 12/9/25 09:58, ????????? ??????????? wrote: > Hello! While working with audit plugin I found 2 use-after-free issues I am always interested to hear about people's uses of the audit code. We never made it public, and I considered removing it a few years ago, but I decided to leave it in after learning that Oracle makes use of it in their fork. > 1. So right before passing a state.reply to a kau_as_req() audit function > it frees state.reply.enc_part.ciphertext.data so from the plugin side we > can use-after-free (which happened to me:)) > https://github.com/krb5/krb5/blob/master/src/kdc/do_as_req.c#L334 Out of curiosity, what interest does an audit module have in the raw ciphertext of the encrypted part of the reply? Regardless, I agree that the cleanup should be moved later. It's not safe to leave a dangling pointer inside a structure that continues to see use, especially by third-party code. > 2. For TGS_REP there also an issue > https://github.com/krb5/krb5/blob/master/src/kdc/do_tgs_req.c#L1087 - here > reply.ticket is a reference to a ticket_reply > https://github.com/krb5/krb5/blob/master/src/kdc/do_tgs_req.c#L1125 - here > reply is passed to an audit plugin in a kau_tgs_req() > https://github.com/krb5/krb5/blob/master/src/kdc/do_tgs_req.c#L1129 - > cleanup happens here > > However from the audit plugin perspective ticket.enc_part is already free'd I'm not following this one. It seems like ticket_reply.enc_part is cleaned up after the last use of ticket and reply. au_state continues to be used after tgs_issue_ticket() returns, but it shouldn't reference any dangling pointers as au_state->reply is nulled at line 1125. I can't get much from the stack traces in the valgrind output due to lack of debugging symbols. Is it possible you're seeing this with an older version of the code? 1.20 had a similar pattern to the AS code, where ticket_reply.enc_part.ciphertext and reply.enc_part.ciphertext are cleaned up before a call to kau_tgs_req() with au_state->reply set to &reply. From abokovoy at redhat.com Wed Dec 10 02:30:42 2025 From: abokovoy at redhat.com (Alexander Bokovoy) Date: Wed, 10 Dec 2025 09:30:42 +0200 Subject: KDC audit plugin potential use-after-free In-Reply-To: <22c2f09d-b810-49b2-a597-c2ab780140b6@mit.edu> References: <22c2f09d-b810-49b2-a597-c2ab780140b6@mit.edu> Message-ID: On ???, 10 ??? 2025, Greg Hudson wrote: >On 12/9/25 09:58, ????????? ??????????? wrote: >>Hello! While working with audit plugin I found 2 use-after-free issues > >I am always interested to hear about people's uses of the audit code. >We never made it public, and I considered removing it a few years ago, >but I decided to leave it in after learning that Oracle makes use of >it in their fork. FreeIPA uses audit plugins to provide counters for successful and unsuccessful authentication attempts per client principal. Samba AD uses audit plugins to do the same, reset or increase bad password authentication counts per client principal. While we can perform these through the policy plugin as well, audit plugin usage seems a clearer place. Heimdal version of Samba AD also performs extensive audit logging with the data from the request, namely IP addresses, PA type, and some internal information about the object used as a client principal. We haven't ported this yet to MIT Kerberos version but plan to do so. On FreeIPA side we were planning to add the same logging for different events as well. >>1. So right before passing a state.reply to a kau_as_req() audit function >>it frees state.reply.enc_part.ciphertext.data so from the plugin side we >>can use-after-free (which happened to me:)) >>https://github.com/krb5/krb5/blob/master/src/kdc/do_as_req.c#L334 > >Out of curiosity, what interest does an audit module have in the raw >ciphertext of the encrypted part of the reply? > >Regardless, I agree that the cleanup should be moved later. It's not >safe to leave a dangling pointer inside a structure that continues to >see use, especially by third-party code. >>2. For TGS_REP there also an issue >>https://github.com/krb5/krb5/blob/master/src/kdc/do_tgs_req.c#L1087 - here >>reply.ticket is a reference to a ticket_reply >>https://github.com/krb5/krb5/blob/master/src/kdc/do_tgs_req.c#L1125 - here >>reply is passed to an audit plugin in a kau_tgs_req() >>https://github.com/krb5/krb5/blob/master/src/kdc/do_tgs_req.c#L1129 - >>cleanup happens here >> >>However from the audit plugin perspective ticket.enc_part is already free'd > >I'm not following this one. It seems like ticket_reply.enc_part is >cleaned up after the last use of ticket and reply. au_state continues >to be used after tgs_issue_ticket() returns, but it shouldn't >reference any dangling pointers as au_state->reply is nulled at line >1125. I can't get much from the stack traces in the valgrind output >due to lack of debugging symbols. > >Is it possible you're seeing this with an older version of the code? >1.20 had a similar pattern to the AS code, where >ticket_reply.enc_part.ciphertext and reply.enc_part.ciphertext are >cleaned up before a call to kau_tgs_req() with au_state->reply set to >&reply. > >_______________________________________________ >krbdev mailing list krbdev at mit.edu >https://mailman.mit.edu/mailman/listinfo/krbdev -- / Alexander Bokovoy Sr. Principal Software Engineer Security / Identity Management Engineering Red Hat Limited, Finland