Skip to content

Commit

Permalink
Refactored NtlmAuthenticate message and changed MIC calculation
Browse files Browse the repository at this point in the history
Signed-off-by: Jeroen van Erp <[email protected]>
  • Loading branch information
hierynomus committed Jun 5, 2023
1 parent 900621d commit 5aba7cf
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 44 deletions.
1 change: 1 addition & 0 deletions src/it/docker-image/smb.conf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ server string = %h server (Samba, Ubuntu)
dns proxy = no
interfaces = 192.168.2.0/24 eth0
bind interfaces only = yes
log level = 5
log file = /var/log/samba/log.%m
max log size = 1000
syslog = 0
Expand Down
63 changes: 36 additions & 27 deletions src/main/java/com/hierynomus/ntlm/messages/NtlmAuthenticate.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ public class NtlmAuthenticate extends NtlmMessage {
private byte[] domainName;
private byte[] workstation;
private byte[] encryptedRandomSessionKey;
private boolean useMic;
private byte[] mic;
private boolean integrityEnabled;
private boolean omitVersion;

public NtlmAuthenticate(
byte[] lmResponse, byte[] ntResponse,
String userName, String domainName, String workstation,
byte[] encryptedRandomSessionKey, Set<NtlmNegotiateFlag> negotiateFlags,
WindowsVersion version) {
byte[] encryptedRandomSessionKey, Set<NtlmNegotiateFlag> negotiateFlags,
WindowsVersion version, boolean isIntegrityEnabled, boolean isOmitVersion) {
super(negotiateFlags, version);
this.lmResponse = ensureNotNull(lmResponse);
this.ntResponse = ensureNotNull(ntResponse);
Expand All @@ -53,16 +54,31 @@ public NtlmAuthenticate(
this.workstation = ensureNotNull(workstation);
this.encryptedRandomSessionKey = ensureNotNull(encryptedRandomSessionKey);
this.negotiateFlags = negotiateFlags;
this.integrityEnabled = isIntegrityEnabled;
this.omitVersion = isOmitVersion;
}

@Override
public void write(Buffer.PlainBuffer buffer) {
int baseMessageSize = 64;
if (integrityEnabled) {
baseMessageSize += 16;
}

if (!omitVersion) {
baseMessageSize += 8;
}

writeAutentificateMessage(buffer);
writeNtlmAuthenticate(buffer, baseMessageSize);

// MIC (16 bytes)
if (mic != null) {
// MIC (16 bytes) provided if in AvPairType is key MsvAvFlags with value & 0x00000002 is true
buffer.putRawBytes(mic);
} else if (integrityEnabled) {
buffer.putUInt64(0L);
buffer.putUInt64(0L);
} else {
// Skipping MIC, not enabled.
}

// Payload
Expand All @@ -78,35 +94,29 @@ public void setMic(byte[] mic) {
this.mic = mic;
}

public void writeAutentificateMessage(Buffer.PlainBuffer buffer) {
public void writeNtlmAuthenticate(Buffer.PlainBuffer buffer, int baseMessageSize) {
buffer.putString("NTLMSSP\0", Charsets.UTF_8); // Signature (8 bytes)
buffer.putUInt32(0x03); // MessageType (4 bytes)

int offset = 64; // for the offset

if (useMic) {
offset += 16;
}

if (negotiateFlags.contains(NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_VERSION)) {
offset += 8;
}

int offset = baseMessageSize; // for the offset
offset = writeOffsettedByteArrayFields(buffer, lmResponse, offset); // LmChallengeResponseFields (8 bytes)
offset = writeOffsettedByteArrayFields(buffer, ntResponse, offset); // NtChallengeResponseFields (8 bytes)
offset = writeOffsettedByteArrayFields(buffer, domainName, offset); // DomainNameFields (8 bytes)
offset = writeOffsettedByteArrayFields(buffer, userName, offset); // UserNameFields (8 bytes)
offset = writeOffsettedByteArrayFields(buffer, workstation, offset); // WorkstationFields (8 bytes)
if (negotiateFlags.contains(NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_KEY_EXCH)) {
if (negotiateFlags.contains(NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_KEY_EXCH)) { // TODO probably unnecessary check
offset = writeOffsettedByteArrayFields(buffer, encryptedRandomSessionKey, offset);
} else {
offset = writeOffsettedByteArrayFields(buffer, EMPTY, offset);
}

buffer.putUInt32(EnumWithValue.EnumUtils.toLong(negotiateFlags)); // NegotiateFlags (4 bytes)

if (negotiateFlags.contains(NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_VERSION)) {
// If `omitVersion`, we skip rendering the Version, as some servers don't like it.
if (!omitVersion && negotiateFlags.contains(NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_VERSION)) {
buffer.putRawBytes(getVersion()); // Version (8 bytes)
} else if (!omitVersion) {
buffer.putUInt64(0L);
}
}

Expand All @@ -126,17 +136,16 @@ public byte[] getVersion() {
return plainBuffer.getCompactData();
}


@Override
public String toString() {
return "NtlmAuthenticate{\n" +
" mic=" + (mic != null ? ByteArrayUtils.printHex(mic) : "[]") + ",\n" +
" lmResponse=" + ByteArrayUtils.printHex(lmResponse) + ",\n" +
" ntResponse=" + ByteArrayUtils.printHex(ntResponse) + ",\n" +
" domainName='" + NtlmFunctions.unicode(domainName) + "',\n" +
" userName='" + NtlmFunctions.unicode(userName) + "',\n" +
" workstation='" + NtlmFunctions.unicode(workstation) + "',\n" +
" encryptedRandomSessionKey=[<secret>],\n" +
'}';
" mic=" + (mic != null ? ByteArrayUtils.printHex(mic) : "[]") + ",\n" +
" lmResponse=" + ByteArrayUtils.printHex(lmResponse) + ",\n" +
" ntResponse=" + ByteArrayUtils.printHex(ntResponse) + ",\n" +
" domainName='" + NtlmFunctions.unicode(domainName) + "',\n" +
" userName='" + NtlmFunctions.unicode(userName) + "',\n" +
" workstation='" + NtlmFunctions.unicode(workstation) + "',\n" +
" encryptedRandomSessionKey=[<secret>],\n" +
'}';
}
}
34 changes: 18 additions & 16 deletions src/main/java/com/hierynomus/smbj/auth/NtlmAuthenticator.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
import org.slf4j.LoggerFactory;

import com.hierynomus.asn1.types.primitive.ASN1ObjectIdentifier;
import com.hierynomus.msdtyp.MsDataTypes;
import com.hierynomus.msdtyp.FileTime;
import com.hierynomus.ntlm.NtlmConfig;
import com.hierynomus.ntlm.NtlmException;
import com.hierynomus.ntlm.av.AvId;
Expand Down Expand Up @@ -148,12 +148,14 @@ private AuthenticateResponse doNegotiate(AuthenticationContext context, byte[] g
this.negotiateFlags.add(NTLMSSP_NEGOTIATE_ANONYMOUS);
}

if (Strings.isNotBlank(context.getDomain())) {
this.negotiateFlags.add(NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED);
}
if (!this.negotiateFlags.contains(NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_VERSION)) {
if (Strings.isNotBlank(context.getDomain())) {
this.negotiateFlags.add(NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED);
}

if (Strings.isNotBlank(config.getWorkstationName())) {
this.negotiateFlags.add(NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED);
if (Strings.isNotBlank(config.getWorkstationName())) {
this.negotiateFlags.add(NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED);
}
}

this.negotiateMessage = new NtlmNegotiate(negotiateFlags, context.getDomain(), config.getWorkstationName(), config.getWindowsVersion(), config.isOmitVersion());
Expand All @@ -172,7 +174,7 @@ private AuthenticateResponse doAuthenticate(AuthenticationContext context, NtlmC
// [MS-NLMP] 3.2.2 -- Special case for anonymous authentication
if (context.isAnonymous()) {
NtlmAuthenticate msg = new NtlmAuthenticate(null, null, context.getUsername(), context.getDomain(),
config.getWorkstationName(), null, negotiateFlags, config.getWindowsVersion());
config.getWorkstationName(), null, negotiateFlags, config.getWindowsVersion(), true, config.isOmitVersion());
response.setNegToken(negTokenTarg(msg));
return response;
}
Expand All @@ -181,9 +183,9 @@ private AuthenticateResponse doAuthenticate(AuthenticationContext context, NtlmC
negotiateFlags.add(NTLMSSP_NEGOTIATE_TARGET_INFO);
TargetInfo clientTargetInfo = createClientTargetInfo(serverNtlmChallenge);

ComputedNtlmV2Response computedNtlmV2Response = functions.computeResponse(context.getUsername(),
context.getDomain(), context.getPassword(), serverNtlmChallenge, MsDataTypes.nowAsFileTime(),
clientTargetInfo);
long time = FileTime.now().getWindowsTimeStamp();
ComputedNtlmV2Response computedNtlmV2Response = functions.computeResponse(context.getUsername(), context.getDomain(), context.getPassword(), serverNtlmChallenge, time, clientTargetInfo);

byte[] sessionBaseKey = computedNtlmV2Response.getSessionBaseKey();
byte[] ntResponse = computedNtlmV2Response.getNtResponse();
byte[] lmResponse = new byte[0]; // computedNtlmV2Response.getLmResponse();
Expand All @@ -195,7 +197,7 @@ private AuthenticateResponse doAuthenticate(AuthenticationContext context, NtlmC
if (serverFlags.contains(NTLMSSP_NEGOTIATE_KEY_EXCH) && (serverFlags.contains(NTLMSSP_NEGOTIATE_SEAL) || serverFlags.contains(NTLMSSP_NEGOTIATE_SIGN) || serverFlags.contains(NTLMSSP_NEGOTIATE_ALWAYS_SIGN))) {
exportedSessionKey = new byte[16];
random.nextBytes(exportedSessionKey);
encryptedRandomSessionKey = NtlmFunctions.rc4k(securityProvider, sessionBaseKey, exportedSessionKey);
encryptedRandomSessionKey = NtlmFunctions.rc4k(securityProvider, keyExchangeKey, exportedSessionKey);
} else {
exportedSessionKey = keyExchangeKey;
encryptedRandomSessionKey = exportedSessionKey; // TODO
Expand All @@ -204,19 +206,19 @@ private AuthenticateResponse doAuthenticate(AuthenticationContext context, NtlmC
// If NTLM v2 is used, KeyExchangeKey MUST be set to the given 128-bit
// SessionBaseKey value.

NtlmAuthenticate msg = new NtlmAuthenticate(lmResponse, ntResponse, context.getUsername(), context.getDomain(), config.getWorkstationName(), encryptedRandomSessionKey, serverFlags, config.getWindowsVersion());
NtlmAuthenticate msg = new NtlmAuthenticate(lmResponse, ntResponse, context.getUsername(), context.getDomain(), config.getWorkstationName(), encryptedRandomSessionKey, serverFlags, config.getWindowsVersion(), true, config.isOmitVersion());
// MIC (16 bytes) provided if in AvPairType is key MsvAvFlags with value &
// 0x00000002 is true
AvPairFlags pair = serverNtlmChallenge.getTargetInfo() != null ? serverNtlmChallenge.getTargetInfo().getAvPair(AvId.MsvAvFlags) : null;
if (pair != null && (pair.getValue() & 0x00000002) > 0) {
// Calculate MIC
// TODO correct hash should be tested
Buffer.PlainBuffer micBuffer = new Buffer.PlainBuffer(Endian.LE);
negotiateMessage.write(micBuffer); // negotiateMessage
micBuffer.putRawBytes(serverNtlmChallenge.getServerChallenge()); // challengeMessage
msg.writeAutentificateMessage(micBuffer); // authentificateMessage
negotiateMessage.write(micBuffer);
micBuffer.putRawBytes(ntlmChallengeBytes);
msg.write(micBuffer);

byte[] mic = NtlmFunctions.hmac_md5(securityProvider, sessionBaseKey, micBuffer.getCompactData());
byte[] mic = NtlmFunctions.hmac_md5(securityProvider, exportedSessionKey, micBuffer.getCompactData());
msg.setMic(mic);
}
response.setSessionKey(exportedSessionKey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_N
import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_SIGN
import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_TARGET_INFO
import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_UNICODE
import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_NEGOTIATE_VERSION
import static com.hierynomus.ntlm.messages.NtlmNegotiateFlag.NTLMSSP_REQUEST_TARGET
import static com.hierynomus.ntlm.messages.WindowsVersion.NtlmRevisionCurrent.NTLMSSP_REVISION_W2K3
import static com.hierynomus.ntlm.messages.WindowsVersion.ProductMajorVersion.WINDOWS_MAJOR_VERSION_6
Expand Down

0 comments on commit 5aba7cf

Please sign in to comment.