From 6155e3718ffe4d81a22965eec6e87db72159625e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 22 Jul 2012 23:23:23 -0400 Subject: [PATCH] libwinpr-sspi: store NTLM hashes in SAM, generate NTLMv2 hashes on the fly --- include/winpr/ntlm.h | 15 ++++++++--- winpr/sspi/NTLM/ntlm_compute.c | 30 +++++++++++++++++---- winpr/tools/hash/hash.c | 36 +++++++++---------------- winpr/utils/ntlm.c | 48 +++++++++++++++++++++++++++++++++- winpr/utils/sam.c | 8 +++--- 5 files changed, 100 insertions(+), 37 deletions(-) diff --git a/include/winpr/ntlm.h b/include/winpr/ntlm.h index 6e8759ceb..d6514f92a 100644 --- a/include/winpr/ntlm.h +++ b/include/winpr/ntlm.h @@ -34,12 +34,19 @@ WINPR_API BYTE* NTOWFv2W(LPWSTR Password, UINT32 PasswordLength, LPWSTR User, WINPR_API BYTE* NTOWFv2A(LPSTR Password, UINT32 PasswordLength, LPSTR User, UINT32 UserLength, LPSTR Domain, UINT32 DomainLength, BYTE* NtHash); +WINPR_API BYTE* NTOWFv2FromHashW(BYTE* NtHashV1, LPWSTR User, UINT32 UserLength, + LPWSTR Domain, UINT32 DomainLength, BYTE* NtHash); +WINPR_API BYTE* NTOWFv2FromHashA(BYTE* NtHashV1, LPSTR User, UINT32 UserLength, + LPSTR Domain, UINT32 DomainLength, BYTE* NtHash); + #ifdef UNICODE -#define NTOWFv1 NTOWFv1W -#define NTOWFv2 NTOWFv2W +#define NTOWFv1 NTOWFv1W +#define NTOWFv2 NTOWFv2W +#define NTOWFv2FromHash NTOWFv2FromHashW #else -#define NTOWFv1 NTOWFv1A -#define NTOWFv2 NTOWFv2W +#define NTOWFv1 NTOWFv1A +#define NTOWFv2 NTOWFv2A +#define NTOWFv2FromHash NTOWFv2FromHashA #endif #endif /* WINPR_UTILS_NTLM_H */ diff --git a/winpr/sspi/NTLM/ntlm_compute.c b/winpr/sspi/NTLM/ntlm_compute.c index 18787aea7..6f4b674d0 100644 --- a/winpr/sspi/NTLM/ntlm_compute.c +++ b/winpr/sspi/NTLM/ntlm_compute.c @@ -233,16 +233,36 @@ void ntlm_fetch_ntlm_v2_hash(NTLM_CONTEXT* context, char* hash) if (entry != NULL) { - CopyMemory(hash, entry->NtHash, 16); + NTOWFv2FromHashW(entry->NtHash, + (LPWSTR) context->identity.User, context->identity.UserLength * 2, + (LPWSTR) context->identity.Domain, context->identity.DomainLength * 2, + (BYTE*) hash); + + SamFreeEntry(sam, entry); + SamClose(sam); + + return; + } + + entry = SamLookupUserW(sam, + (LPWSTR) context->identity.User, context->identity.UserLength * 2, NULL, 0); + + if (entry != NULL) + { + NTOWFv2FromHashW(entry->NtHash, + (LPWSTR) context->identity.User, context->identity.UserLength * 2, + (LPWSTR) context->identity.Domain, context->identity.DomainLength * 2, + (BYTE*) hash); + + SamFreeEntry(sam, entry); + SamClose(sam); + + return; } else { printf("Error: Could not find user in SAM database\n"); } - - SamFreeEntry(sam, entry); - - SamClose(sam); } void ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, char* hash) diff --git a/winpr/tools/hash/hash.c b/winpr/tools/hash/hash.c index e42cbd1a9..f984047a8 100644 --- a/winpr/tools/hash/hash.c +++ b/winpr/tools/hash/hash.c @@ -48,7 +48,6 @@ int main(int argc, char* argv[]) { int index = 1; BYTE NtHash[16]; - BOOL sam_entry = 0; char* User = NULL; UINT32 UserLength; char* Domain = NULL; @@ -100,10 +99,6 @@ int main(int argc, char* argv[]) printf("Usage: winpr-hash -u -p [-d ]\n"); exit(1); } - else if (strcmp("-s", argv[index]) == 0) - { - sam_entry = 1; - } index++; } @@ -118,29 +113,22 @@ int main(int argc, char* argv[]) PasswordLength = strlen(Password); DomainLength = (Domain) ? strlen(Domain) : 0; - NTOWFv2A(Password, PasswordLength, User, UserLength, Domain, DomainLength, NtHash); + NTOWFv1A(Password, PasswordLength, NtHash); - if (sam_entry) - { - printf("%s:", User); - - if (DomainLength > 0) - printf("%s:", Domain); - else - printf(":"); + printf("%s:", User); + if (DomainLength > 0) + printf("%s:", Domain); + else printf(":"); - for (index = 0; index < 16; index++) - printf("%02x", NtHash[index]); - printf("\n"); - } - else - { - for (index = 0; index < 16; index++) - printf("%02x", NtHash[index]); - printf("\n"); - } + printf(":"); + + for (index = 0; index < 16; index++) + printf("%02x", NtHash[index]); + + printf(":::"); + printf("\n"); return 0; } diff --git a/winpr/utils/ntlm.c b/winpr/utils/ntlm.c index be2851705..f345bc391 100644 --- a/winpr/utils/ntlm.c +++ b/winpr/utils/ntlm.c @@ -61,7 +61,7 @@ BYTE* NTOWFv1A(LPSTR Password, UINT32 PasswordLength, BYTE* NtHash) PasswordW = (LPWSTR) malloc(PasswordLength * 2); MultiByteToWideChar(CP_ACP, 0, Password, PasswordLength, PasswordW, PasswordLength); - NtHash = NTOWFv1W(PasswordW, PasswordLength, NtHash); + NtHash = NTOWFv1W(PasswordW, PasswordLength * 2, NtHash); free(PasswordW); @@ -129,3 +129,49 @@ BYTE* NTOWFv2A(LPSTR Password, UINT32 PasswordLength, LPSTR User, return NtHash; } +BYTE* NTOWFv2FromHashW(BYTE* NtHashV1, LPWSTR User, UINT32 UserLength, LPWSTR Domain, UINT32 DomainLength, BYTE* NtHash) +{ + BYTE* buffer; + + if (!User) + return NULL; + + if (!NtHash) + NtHash = (BYTE*) malloc(16); + + buffer = (BYTE*) malloc(UserLength + DomainLength); + + /* Concatenate(UpperCase(User), Domain) */ + + CopyMemory(buffer, User, UserLength); + CharUpperBuffW((LPWSTR) buffer, UserLength / 2); + CopyMemory(&buffer[UserLength], Domain, DomainLength); + + /* Compute the HMAC-MD5 hash of the above value using the NTLMv1 hash as the key, the result is the NTLMv2 hash */ + HMAC(EVP_md5(), (void*) NtHashV1, 16, buffer, UserLength + DomainLength, (void*) NtHash, NULL); + + free(buffer); + + return NtHash; +} + +BYTE* NTOWFv2FromHashA(BYTE* NtHashV1, LPSTR User, UINT32 UserLength, LPSTR Domain, UINT32 DomainLength, BYTE* NtHash) +{ + LPWSTR UserW = NULL; + LPWSTR DomainW = NULL; + LPWSTR PasswordW = NULL; + + UserW = (LPWSTR) malloc(UserLength * 2); + DomainW = (LPWSTR) malloc(DomainLength * 2); + + MultiByteToWideChar(CP_ACP, 0, User, UserLength, UserW, UserLength); + MultiByteToWideChar(CP_ACP, 0, Domain, DomainLength, DomainW, DomainLength); + + NtHash = NTOWFv2FromHashW(NtHashV1, UserW, UserLength * 2, DomainW, DomainLength * 2, NtHash); + + free(UserW); + free(DomainW); + free(PasswordW); + + return NtHash; +} diff --git a/winpr/utils/sam.c b/winpr/utils/sam.c index 14ca4ab01..f93ed0e3e 100644 --- a/winpr/utils/sam.c +++ b/winpr/utils/sam.c @@ -112,7 +112,7 @@ void HexStrToBin(char* str, BYTE* bin, int length) WINPR_SAM_ENTRY* SamReadEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry) { - char* p[5]; + char* p[7]; int LmHashLength; int NtHashLength; @@ -120,13 +120,15 @@ WINPR_SAM_ENTRY* SamReadEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry) p[1] = strchr(p[0], ':') + 1; p[2] = strchr(p[1], ':') + 1; p[3] = strchr(p[2], ':') + 1; - p[4] = p[0] + strlen(p[0]); + p[4] = strchr(p[3], ':') + 1; + p[5] = strchr(p[4], ':') + 1; + p[6] = p[0] + strlen(p[0]); entry->UserLength = p[1] - p[0] - 1; entry->DomainLength = p[2] - p[1] - 1; LmHashLength = p[3] - p[2] - 1; - NtHashLength = p[4] - p[3]; + NtHashLength = p[4] - p[3] - 1; entry->User = (LPSTR) malloc(entry->UserLength + 1); memcpy(entry->User, p[0], entry->UserLength);