-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Supporting no-encryption NTLM message signing & GSS MIC calculation #338
Comments
A small patch applied locally can address this issue by implementing the following: impl Sspi for Ntlm {
...
#[instrument(level = "debug", ret, fields(state = ?self.state), skip(self, _flags))]
fn encrypt_message(
&mut self,
_flags: EncryptionFlags,
message: &mut [SecurityBuffer],
sequence_number: u32,
) -> crate::Result<SecurityStatus> {
if self.send_sealing_key.is_none() {
self.complete_auth_token(&mut [])?;
}
SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Token)?; // check if exists
// Deep copy the data buffer and create a fresh token buffer.
let mut data_buffer = SecurityBuffer::find_buffer(message, SecurityBufferType::Data)?.data().to_vec();
let data_copy_buffer = SecurityBuffer::Data(&mut data_buffer);
let mut token_buffer = vec![0; MESSAGE_INTEGRITY_CHECK_SIZE];
let token_data_buffer = SecurityBuffer::Token(&mut token_buffer);
let mut signing_message = vec![data_copy_buffer, token_data_buffer];
let mut data = SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Data)?;
let encrypted_data = self.send_sealing_key.as_mut().unwrap().process(data.data());
if encrypted_data.len() < data.buf_len() {
return Err(Error::new(ErrorKind::BufferTooSmall, "The Data buffer is too small"));
}
data.write_data(&encrypted_data)?;
self.sign_data(_flags, &mut signing_message, sequence_number)?;
let mut token = SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Token)?;
token.write_data(&token_buffer)?;
Ok(SecurityStatus::Ok)
}
}
impl Ntlm {
...
fn sign_data(
&mut self,
_flags: EncryptionFlags,
messages: &mut [SecurityBuffer],
sequence_number: u32,
) -> crate::Result<SecurityStatus> {
let data = SecurityBuffer::find_buffer_mut(messages, SecurityBufferType::Data)?;
let digest = compute_digest(&self.send_signing_key, sequence_number, data.data())?;
let checksum = self
.send_sealing_key
.as_mut()
.unwrap()
.process(&digest[0..SIGNATURE_CHECKSUM_SIZE]);
let signature_buffer = SecurityBuffer::find_buffer_mut(messages, SecurityBufferType::Token)?;
if signature_buffer.buf_len() < SIGNATURE_SIZE {
return Err(Error::new(ErrorKind::BufferTooSmall, "The Token buffer is too small"));
}
let signature = compute_signature(&checksum, sequence_number);
signature_buffer.write_data(signature.as_slice())?;
Ok(SecurityStatus::Ok)
}
pub fn sign_and_revert_state(&mut self, messages: &mut [SecurityBuffer], sequence_number: u32) -> crate::Result<()> {
let original_key_state = self.send_sealing_key.clone().ok_or(Error::new(ErrorKind::OutOfSequence, "send_sealing_key is None"))?;
self.sign_data(EncryptionFlags::empty(), messages, sequence_number)?;
self.send_sealing_key = Some(original_key_state);
Ok(())
}
} Although it is not yet a perfect patch, I believe I can propose a PR and incorporate it. However, I would appreciate your insights on the optimal design for this feature. |
Thank you for opening this issue. @TheBestTvarynka what do you think about this? |
@CBenoit @TheBestTvarynka -- Any updates regarding this suggestion? |
@AvivNaaman sorry for the big delay 😔 |
Hi, @AvivNaaman. I finally got a chance to look at your problem. About your proposed patch: I have a suspicion that it will break a regular RDP auth in RDP. I think it's better to add the How urgent this is for you? I can implement it by myself when finish current tasks. Or I can guide you 😊 |
Hi @TheBestTvarynka, Thanks for getting back to me. This isn’t super urgent right now, since I’m already working on a local fix for the issue. I’m pretty sure I can handle this on my own, but I wanted to mention something important about the key state after signing, as described in MS-SPNG: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-spng/b87587b3-9d72-4027-8131-b76b5368115f -- the signing key must be reset after its usage for generating the I think it would be a good idea to add a function to reset the key state or maybe let us clone keys or instances. What do you think? |
👍
I think you can do it in the |
@TheBestTvarynka As you can see, I pushed a basic commit that implements the What should we do regarding other providers (such as Kerberos)? Should we leave the implementations as I will add some documentation and tests down the road; I have also seen a reference to some QoS flag (which may be used for NTLM signing, but didn't dig into it just yet). |
Great! I am going to review it soon.
I suggest to do the following:
|
Thanks! I'll make the changes to the PR soon (including the CR). Regarding the API of Kerberos for signing, I encountered the following in MSDN, under SSPI/Kerberos: |
🤔 got it. thank you for the clarification. I haven't seen It before 😅 |
Hello,
In an attempt to utilize the NTLM SSPI provider for signing a GSS-API negotiation message, I encountered an issue. According to the GSS-API RFC (here), when the underlying scheme supports MIC calculation, it is mandatory to provide the
mechListMIC
. In my case, since NTLM was required to provide integrity, themechListMIC
had to be included.I attempted to call the
Ntlm::encrypt_message
function to sign themechTypeList
value, as per the RFC’s requirements. However, the validation of themechListMIC
failed. Upon examining the implementation of theencrypt_message
function, it became evident that it encrypts the input data before signing the digest of the data using the same RC4 state. Consequently, the RC4 state is not the initial state when signing the data. Additionally, there is an extraneous and irrelevant outcome of generating encrypted data for themechTypeList
data.To address this issue, I devised a proof-of-concept (PoC) solution to sign the MIC message. I proceeded by commenting out the message encryption section from the
encrypt_message
function. This modification enabled the signing process to proceed successfully.Certainly, attempting to utilize the code for actual encryption of data will be futile, as that is not my current requirement. I merely need a signing mechanism as a preliminary step.
Scapy incorporates the
Gss_GstMICEx
function as part of its NTLM module, which is also utilized in the encryption process (due to the shared logic).Another noteworthy aspect is the necessity to “back up” the original send key state prior to signing the
mechTypeList
and restoring it immediately afterward (as specified in MS-SPNG 3.3.5.1).Do you have any insights regarding message signing within the NTLM SSPI (or, alternatively, in other SSPIs as well)? Specifically, would it be feasible to add such a function to the
Ntlm
implementation or, at the very least, provide a mechanism for direct access to the send key and its state, suitable for such purposes?Thank you for your consideration.
The text was updated successfully, but these errors were encountered: