spotless, we will find some functions that take
spotlessas a string argument and one of those functions is
CredIsMarshaledCredentialWas shown below:
ADVAPI32!CredIsMarshaledCredentialWand print out its first and only argument (stored in RCX register per x64 calling convention), we will see
ws01(our hostname) as a string argument. Although RdpThief hooks
SSPICLI!SspiPrepareForCredRead(hostname supplied as a second argument), another function that could be considered for hooking is
CredReadW(hostname a the first argument) as seen below:
CredReadWand attempt to RDP to our host
ws01, we get a hit:
SSPICLI!SspiPrepareForCredReadand once it's hit, print out the second argument supplied to the function, which is stored in the RDX register:
CryptProtectMemorycalls manually in API Monitor showed no plaintext password either, although there were multiple calls to the function and I would see the password already encrypted:
CryptProtectMemoryin WinDBG and print out a unicode string (this should be the plaintext password passed to the function for encryption) starting 4 bytes into the address (first 4 bytes indicate the size of the encrypted data) pointed by the RCX register:
CryptProtectMemoryfunction call (in API Monitor) and also mentioned the 4 byte offset into RCX that holds the size of the encrypted blob - below shows that - first 4 bytes found at RCX (during the
CryptProtectMemorybreak) are 0x20 or 32 in decimal:
ws01- we see the credentials getting intercepted and written to a file:
CredReadWfor intercepting the hostname was possible, so I made some quick changes to the RdpThief's project to test it.
_SspiPrepareForCredReadsignature and hooked
CreadReadWwith a new function called
HookedCredReadWwhich will pop a message box each time
CredReadWis called and print its first argument as the message box text.
lpServervariable which is later written to the file creds.txt together with the username and password.
HookedCredReadWand unregister the old hook
CredReadWcan be used to intercept the the hostname: