Unveiling Ctrl‑Inject: A Stealthy Console‑Based Process Injection Technique

This article explains the novel Ctrl‑Inject method that leverages the console Ctrl‑signal handling mechanism to inject code into Windows console processes, detailing its inner workings, pointer‑encoding and CFG bypasses, and the steps required to trigger and clean up the injection.

ITPUB
ITPUB
ITPUB
Unveiling Ctrl‑Inject: A Stealthy Console‑Based Process Injection Technique

Overview

The article introduces a new process‑injection technique called Ctrl‑Inject , which exploits the way Windows console applications handle Ctrl signals (e.g., Ctrl+C) via SetConsoleCtrlHandler. When a signal is sent, the system spawns a new thread in the target process to run the registered handler, providing a stealthy injection vector.

Control Signal Handling

When a console process (such as cmd.exe or powershell.exe) receives a Ctrl+C or Break signal, the system process csrss.exe creates a new thread that calls CtrlRoutine. This routine iterates over a global HandlerList until a handler returns TRUE, indicating the signal was processed. Handlers are stored as encoded pointers using RtlEncodePointer and must point to valid CFG (Control Flow Guard) targets.

The SetConsoleCtrlHandler function encodes each handler pointer before adding it to HandlerList. The list and its length are stored in kernelbase.dll, which is mapped at the same address in all processes, allowing an attacker to locate and modify them via WriteProcessMemory.

Bypassing Pointer Encoding

Older Windows versions require understanding the encoding algorithm:

EncodedPointer = (OriginalPointer ^ SecretCookie) >> (SecretCookie & 0x1F)

. By calling RtlEncodePointer in the remote process with a NULL argument, the attacker can obtain a predictable encoded value and brute‑force the secret cookie.

NTSTATUS WINAPI NtQueryInformationProcess(
    _In_HANDLE ProcessHandle,
    _In_PROCESSINFOCLASS ProcessInformationClass,
    _Out_ PVOIDProcessInformation,
    _In_ULONG ProcessInformationLength,
    _Out_opt_ PULONG ReturnLength
);

On Windows 10 and later, Microsoft provides RtlEncodeRemotePointer and RtlDecodeRemotePointer, which directly return a valid encoded pointer for a remote process, simplifying the bypass.

Bypassing CFG

After injecting the payload and updating HandlerList, sending Ctrl+C would normally cause a CFG violation because the jump target is not a registered call target. Microsoft’s SetProcessValidCallTargets API can mark the remote address as a valid target, preventing the exception.

WINAPI SetProcessValidCallTargets(
    _In_HANDLE hProcess,
    _In_PVOID VirtualAddress,
    _In_SIZE_T RegionSize,
    _In_ULONG NumberOfOffsets,
    _Inout_ PCFG_CALL_TARGET_INFO OffsetInformation
);

Undocumented APIs such as those described in the EnSilo blog can also be used for the same purpose.

Triggering the Ctrl+C Event

To activate the injected code, the attacker sends a Ctrl+C signal to the target console process. This can be done by synthesizing a global Ctrl key press with SendInput combined with a PostMessage that sends the ‘C’ key, which works even for hidden or invisible console windows.

Underlying Mechanism

The injection never directly creates a remote thread or manipulates the thread context. Instead, it relies on the legitimate behavior of csrss.exe, which, upon receiving the Ctrl signal, calls CsrClientCallServer with a unique index (0x30401). This eventually leads to SrvEndTask and finally to RtlCreateUserThread that runs the attacker’s payload.

Summary and Step‑by‑Step Procedure

Ctrl‑Inject offers a stealthier alternative to classic remote‑thread injection because the created thread originates from the trusted csrss.exe. However, it is limited to console applications.

The required steps are:

Open the target console process with OpenProcess.

Allocate a buffer in the remote process using VirtualAllocEx and write the payload with WriteProcessMemory.

Encode the payload pointer either by calling RtlEncodePointer with a NULL argument (brute‑forcing the cookie) or by using RtlEncodeRemotePointer.

Mark the encoded pointer as a valid call target via SetProcessValidCallTargets.

Trigger the Ctrl+C signal using a combination of SendInput and PostMessage.

After execution, restore the original handler list to clean up.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Windows securityCFG bypasscsrssCtrl-Injectprocess injection
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.