Using Openssl to implement Crypto Operations in Netlogon Remote Protocol
Background
The Netlogon Remote Protocol remote procedure call (RPC) interface is used primarily by Microsoft Windows to maintain the relationship between a machine and its domain. In the protocol, a client delivers a logon request to the domain controller over an established secure channel between a DC and its clients. Before a secure channel is established and any method can utilize it, the authentication process must be completed, as described in 3.1 of MS-NRPC. The authentication process consists of the following steps:
· A session key is negotiated to establish the secure channel (as specified in section 3.1.4.1) over non-protected RPC, which is a RPC connection without any underlying security support. At the end of this process, if successful, client and server will mutually verify each other’s credentials and agree on a session key to be used to secure further RPC communications. The two calculations involved with cryptographic operations are:
o Session Key Computation (3.1.4.3 MS-NRPC)
The client and the server use the shared secret (password), client challenge and server challenge to compute the session key. (For more information, see one of my previous blog on this topic).
o Netlogon Credential Computation (3.1.4.4 MS-NRPC)
The client and the server compute a credential using the challenge received and session key to compare it with the credential received.
· Once the secure channel has been established, almost all RPC calls will use Netlogon Authenticators that are calculated using session key and stored nelogon credential. The algorithm in 3.1.4.4 is used.
Therefore, we can see that session key computation and Netlogon credential computation are very important operations in Netlogon remote protocol implementation. In the latest Windows operating systems, a stronger encryption (AES) and a hashing algorithm (SHA) are added. As a valuable supplement to the protocol documentation, it could be very helpful to implement the algorithm using existing cryptographic libraries and provide sample data for implementers to verify their implementation.
In this blog, we will show how to implement session key and Netlogon credential computation algorithms using OpenSSL, which is a general purpose cryptography open source library. Our focus will be on the implementation when AES and strong key support are negotiated between the client and the server.
The code shown can be compiled using gcc compiler with openssl library installed.
Session Key Computation
#include <openssl/crypto.h>
#include <openssl/aes.h>
#include <openssl/md4.h>
#include <openssl/md5.h>
#include <openssl/des.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
/*******************************************************************
*The function to compute theMD4 of a unicode string
*******************************************************************/
int ComputeM4ss(unsigned char *m4ss)
{
int retVal = 0;
int pswd_l;
char *passwdPtr = globals.password;
wchar_t pswd_u[2 * MAX_PASSWORD_LEN + 2];
mbstate_t ps;
MD4_CTX ctx;
/* Convert the ASCII password to UNICODE */
bzero(pswd_u, 2*MAX_PASSWORD_LEN+2);
pswd_l = mbsrtowcs( pswd_u, (const char **)&passwdPtr, strlen(globals.password), &ps);
if (pswd_l > 0)
{
MD4_Init(&ctx);
MD4_Update(&ctx, pswd_u, pswd_l * sizeof(wchar_t));
MD4_Final(m4ss, &ctx);
retVal = 1;
}
return retVal;
}
/*******************************************************************
*The function to compute session key.
*******************************************************************/
int NRPCComputeSessionKey(unsigned char *sessionKey)
{
int retVal = 0;
int zeros = 0;
int sts;
long long *temp1;
long long *temp2;
long long sum;
unsigned char m4ss[16];
unsigned char outBuf[16];
unsigned char k1[8];
unsigned char k2[8];
unsigned char output[8];
unsigned char output2[8];
MD5_CTX md5Ctx;
SHA256_CTX shaCtx;
DES_cblock keyStruct;
DES_key_schedule keySch;
HMAC_CTX hmacCtx;
int hmacLen = 16;
int pswd_l;
char *passwdPtr = globals.password;
wchar_t pswd_u[2 * MAX_PASSWORD_LEN + 2];
mbstate_t ps;
bzero(sessionKey, 16);
/*
* If AES support is negotiaited, then SHA will be used to calculate the session
* key.
*/
if (globals.aes & globals.strongkey)
{
/*
* Convert the ASCII password to UNICODE
* The password is actually OWF hash of the shared secret
*/
bzero(pswd_u, 2*MAX_PASSWORD_LEN+2);
pswd_l = mbsrtowcs( pswd_u, (const char **)&passwdPtr, strlen(globals.password), &ps);
SHA256_Init(&shaCtx);
SHA256_Update(&shaCtx, pswd_u, pswd_l*2);
SHA256_Update(&shaCtx, globals.clientChallenge, MAX_CHALLENGE_LEN);
SHA256_Update(&shaCtx, globals.serverChallenge, MAX_CHALLENGE_LEN);
SHA256_Final(sessionKey, &shaCtx);
retVal = 1;
}
/*
* If strong key support is negotiaited, then MD5 will be used to calculate the session
* key.
*/
else if (globals.strongkey)
{
printf("\nSession Key: Strong key computation\n");
if (ComputeM4ss(m4ss))
{
sts = MD5_Init(&md5Ctx);
sts = MD5_Update(&md5Ctx, &zeros, 4);
sts = MD5_Update(&md5Ctx, globals.clientChallenge, MAX_CHALLENGE_LEN);
sts = MD5_Update(&md5Ctx, globals.serverChallenge, MAX_CHALLENGE_LEN);
sts = MD5_Final(outBuf, &md5Ctx);
HMAC_CTX_init(&hmacCtx);
HMAC_Init_ex(&hmacCtx, m4ss, 16, EVP_md5(), NULL);
HMAC_Update(&hmacCtx, outBuf, 16);
HMAC_Final(&hmacCtx, sessionKey, &hmacLen);
HMAC_CTX_cleanup(&hmacCtx);
retVal = 1;
}
}
/*
* If strong key support is not negotiaited, then MD4 will be used to calculate the session
* key.
*/
else if (!globals.strongkey & !globals.aes)
{
if (ComputeM4ss(m4ss))
{
// SET sum to ClientChallenge + ServerChallenge
temp1 = (long long *)&globals.clientChallenge;
temp2 = (long long *)&globals.serverChallenge;
sum = *temp1 + *temp2; // TODO: make sure overflow is ignored.
// SET k1 to lower 7 bytes of the M4SS
bzero(k1, 8);
bcopy(m4ss, k1, 7);
// SET k2 to upper 7 bytes of the M4SS
bzero(k2, 8);
bcopy(&m4ss[8], k2, 7);
bcopy(k1, keyStruct, sizeof(keyStruct));
DES_key_sched(&keyStruct, &keySch);
DES_ecb_encrypt((DES_cblock*)&sum, (DES_cblock*)output, &keySch, DES_ENCRYPT);
bcopy(k2, keyStruct, sizeof(keyStruct));
DES_key_sched(&keyStruct, &keySch);
DES_ecb_encrypt((DES_cblock*)output, (DES_cblock*)output2, &keySch, DES_ENCRYPT);
bzero(sessionKey, 16);
bcopy(output2, sessionKey, 8);
retVal = 1;
}
}
else /* Invalid to have just the AES flag set */
{
printf("ERROR - invalid to have just the AES flag set without the Strong Key flag.\n");
}
return retVal;
}
Examples of Session Key Calculations
For AES support :
OWF Password: 13 c0 b0 4b 66 25 0d 08-b8 a3 90 4d cc 8b 34 e3
Client Challenge: 25 63 e3 5f 69 e1 5a 24
Server Challenge: 9C 66 5F 90 D9 83 DF 43
Session Key calculated: c9 c7 f7 2f c6 b9 13 e3-67 ae a9 1d 0a e3 a7 70
For Strong Key support:
OWF Password: 31 a5 90 17 0a 35 1f d5-11 48 b2 a1 0a f2 c3 05
Client Challenge: 3a 03 90 a4 6d 0c 3d 4f
Server Challenge: 0c 4c 13 d1 60 41 c8 60
Session Key calculated: ee fe 8f 40 00 7a 2e eb 68 43 d0 d3 0a 5b e2 e3
Netlogon Credential Computation
/*********************************************************************************
* Arguments
* input: Is the client challenge when computing the credential for the client.
* It is the server challenge when computing the credential for the server.
*
* Return
* 1 = success
* 0 = error
**********************************************************************************/
int NRPCComputeNetlogonCredentials( unsigned char *challenge, unsigned char *sessionKey, unsigned char *output )
{
int retVal = 0;
int blockUsed = 0;
unsigned char k1[8];
unsigned char k2[8];
unsigned char k3[8];
unsigned char k4[8];
unsigned char iv[16];
unsigned char temp[16];
AES_KEY aesKey;
DES_cblock keyStruct;
DES_key_schedule keySch;
/*
* When AES is negotiated, simply AES encrypt the input with the session key
* Note: Currently using an IV of all zeros to cover the statement:
* "AES128 is the AES128 algorithm in CFB-8 mode without an initialization vector"
*/
if (globals.aes & globals.strongkey)
{
if (globals.debug)
{
printf("AES Computation\n");
printf("Challenge:\n");
HexDump(challenge, 8);
printf("Session key:\n");
HexDump(sessionKey, 16);
}
bzero(iv, 16);
bzero(temp, 16);
bcopy(challenge, temp, 8);
AES_set_encrypt_key(sessionKey, 128, &aesKey);
AES_cfb8_encrypt(temp, output, 16, &aesKey, iv, &blockUsed, AES_ENCRYPT);
retVal = 1;
}
else
{
/* use DES */
bzero(k1, 8);
bzero(k2, 8);
bzero(k3, 8);
bzero(k4, 8);
bcopy(sessionKey, k1, 7); // SET k1 to bytes(0, 6, Sk)
InitLMKey(k1, k3);
bcopy(&sessionKey[7], k2, 7); // SET k2 to bytes(7, 13, Sk)
InitLMKey(k2, k4);
if (globals.debug)
{
printf("\nNRPCComputeNetlogonCredentials()");
printf("\nsessionKey");
HexDump(sessionKey, 16);
printf("\nk1");
HexDump(k1, 8);
printf("\nk2");
HexDump(k2, 8);
printf("\nk3");
HexDump(k3, 8);
printf("\nk4");
HexDump(k4, 8);
}
// CALL DES_ECB(Input, k3, &output1)
bcopy(k3, keyStruct, sizeof(keyStruct));
DES_key_sched(&keyStruct, &keySch);
DES_ecb_encrypt((DES_cblock*)challenge, (DES_cblock*)temp, &keySch, DES_ENCRYPT);
// CALL DES_ECB(temp, k4, output)
bcopy(k4, keyStruct, sizeof(keyStruct));
DES_key_sched(&keyStruct, &keySch);
DES_ecb_encrypt((DES_cblock*)temp, (DES_cblock*)output, &keySch, DES_ENCRYPT);
}
return retVal;
}
Examples of Session Key Calculations
For AES support :
Client Challenge: 25 63 e3 5f 69 e1 5a 24
Session Key used: c9 c7 f7 2f c6 b9 13 e3-67 ae a9 1d 0a e3 a7 70
Computed Client Netlogon Credential: 58 6a df 53 ef 72 78 d9
For Strong Key support:
Client Challenge: 3a 03 90 a4 6d 0c 3d 4f
Session Key used: ee fe 8f 40 00 7a 2e eb 68 43 d0 d3 0a 5b e2 e3
Computed Client Netlogon Credential: b6 38 95 82 44 fc ea cd
With the code above, we demonstrated the implementation of NRPC cryptographic operations using the OpenSSL library. Also included are examples of input and output data. This will assist those in the implementation of interoperability solutions against Windows Netlogon RPC interface.
Thanks to Nick Meier for providing the source code for this blog.