Saturday, May 09, 2026

A Peek Inside the Conti Ransomware Gang's ContiLeaks Publicly Accessible Malware Bot Documentation - An Analysis - Part Two

Injector Module Technical Report

Generated: 2026-04-27

Executive summary

The material describes an injector subsystem designed to operate as a module within a larger bot framework. Its purpose is to monitor browser processes, inject an in-memory browser payload, process dynamic and static web-injection configurations, intercept browser HTTP/HTTPS traffic, submit selected request/response material to remote handlers, replace browser-visible responses, and forward captured HTTPS POST data and keystroke logs through a dedicated DataPost channel.

The subsystem is organized around three related areas:

- Dynamic injection rules for processing completed HTTP responses and optionally replacing them with handler-supplied content.

- Static injection rules for redirecting matching browser requests to a configured handler and replacing original responses at a lower request-processing level.

- A module DLL that runs inside an arbitrary host process, monitors browser processes, and injects a browser-specific payload without writing that payload to disk or the registry.

The design assumes that parent bot logic supplies the module with the client ID, group tag, external IP address, and injection configurations through the module interface. The module then manages browser monitoring, payload placement, and runtime configuration delivery to the injected browser component.

Major subsystem components

1. Injector module

The injector module is a DLL loaded by higher-level bot logic. It can run inside an arbitrary process and is responsible for monitoring running processes and injecting browser payload code into target browser processes.

2. Browser payload

The browser payload is a DLL-like component intended to run inside browser processes. The injector module contains or carries both 32-bit and 64-bit browser payload variants when operating on a 64-bit system, because a 64-bit host can encounter both 32-bit and 64-bit browsers.

3. Dynamic inject engine

The dynamic inject engine processes completed HTTP responses. It matches URLs and optional HTTP header conditions against XML configuration rules and can send the captured source response to a remote handler. Handler responses can replace the original browser response.

4. Static inject engine

The static inject engine operates at a lower level. It can redirect matching browser requests to a configured handler server, alter request headers, and replace the original server response with the handler response.

5. DataPost/logging channel

The DataPost channel forwards captured HTTPS POST requests, associated full URLs, and browser keystroke logs to a configured server endpoint using command code /60/.

Injector module execution model

The module is implemented as a DLL. On a 64-bit system there is a 64-bit module; on a 32-bit system there is a 32-bit module. The 64-bit module is expected to hold payloads for both 32-bit and 64-bit browsers.

The higher-level bot provides the module with:

- ClientID.

- Group tag.

- External IP address.

- Dynamic inject configuration.

- Static inject configuration.

ClientID, group, and external IP are passed through the Start function in ParentInfo. Dynamic and static inject configurations are passed through Control calls:

- Control with Ctl = dinj passes the dynamic inject configuration.

- Control with Ctl = sinj passes the static inject configuration.

The configuration bytes and length are passed through CtlArg and CtlArgLen. Other Control parameters are not used for these configuration-delivery calls.

In-memory payload constraints

The injected browser payload must not be saved to disk or the registry. The module itself also must not depend on discovering its own on-disk path because, in the intended runtime model, it may itself execute from memory without a saved file path.

A demonstration launcher is described for testing: it loads the compiled module through LoadLibrary and calls Start and Control so that configs, external IP, client ID, and group can be supplied. The module then monitors processes from a worker thread and loads the browser payload into browser processes.

Module interface

The module exports Start, Control, Release, and FreeBuffer. All exported functions use stdcall and are exported by name.

Start

Start initializes the module and is called when ctl is start.

Prototype:

PVOID Start(

LPCSTR ModuleName,

LPCBYTE Arg,

SIZE_T ArgLen,

LPSTR ResultInfo,

const ParentInfo* pParentData,

PVOID EventCallback,

PVOID EventCallbackContext,

PVOID Reserved1);

Start receives the module name, optional argument bytes, argument length, a fixed 1024-byte result string buffer, parent information, event callback pointer, callback context, and reserved data. The event callback fields are not used in this injector-specific description and are expected to be zero. On success, Start returns a module handle used by Control and Release. On failure, it returns zero.

ParentInfo

ParentInfo contains identity and context supplied by the parent logic:

typedef struct ParentInfo {

CHAR ParentID[256];

CHAR ParentGroup[64];

CHAR SelfIP[64];

LPCWSTR ParentFiles;

};

ParentID is the full parent client ID. ParentGroup is the group tag. SelfIP is the external IP address. ParentFiles is not used.

Control

Control handles runtime module commands and configuration delivery.

Prototype:

BOOL Control (

PVOID ModuleHandle,

LPCSTR Ctl,

LPCBYTE CtlArg,

SIZE_T CtlArgLen,

LPSTR CtlResultInfo,

PVOID* ppOutData,

PDWORD pOutDataSize,

LPCSTR pOutDataTag,

PVOID Reserved1);

Control receives the module handle returned by Start, the ctl string, argument bytes, argument length, a fixed 1024-byte result string buffer, output-data pointers, a fixed 128-byte auxiliary tag buffer, and reserved data. It returns TRUE on success and FALSE on failure. If Control allocates output data and returns it through ppOutData, that buffer must later be freed through FreeBuffer.

Release

Release fully terminates the module and frees resources used by the module.

Prototype:

VOID Release (

PVOID ModuleHandle);

FreeBuffer

FreeBuffer releases memory allocated inside Control for output data.

Prototype:

VOID FreeBuffer (

PVOID pMemory);

Dynamic inject configuration

The dynamic inject configuration is an ANSI XML text file. It describes URL masks, remote handlers, and optional flags controlling what request material is submitted to handlers. The configuration is organized as a collection of <igroup> elements. Each <igroup> can contain any number of <dinj> elements.

Each <dinj> describes a rule for processing an HTTP response and can contain:

- <lm>: link mask.

- <hl>: handler URL.

- <sq>: source-query submission mode.

- <pri>: priority.

- Optional ignore and require conditions inside <lm>.

The link mask is specified either as the value attribute of <lm> or as the text content of <lm>. The handler is specified through <hl>. The priority is specified by <pri>. If priority is absent, the default is the maximum positive signed 32-bit integer, 2147483647. Lower numeric priority values are handled first.

Source-query mode

The <sq> value controls whether the original request is submitted to the handler:

- 0: do not send the source request.

- 1: send only source request headers.

- 2: send the full source request.

If <sq> is absent, it is treated as zero.

URL mask syntax

URL masks omit the http:// or https:// prefix. Matching is performed against the URL without that prefix. The mask syntax supports:

- * for any sequence of characters, including an empty sequence.

- ? for any single character.

- [] for a set of acceptable single characters.

Examples of mask intent include matching login paths, specific domains, user profile paths, and site-specific URL structures.

Dynamic inject filtering

The <lm> element can contain additional conditions:

- ignore_mask: ignore the URL if it matches the configured mask.

- ignore_header: ignore the response if a header name/value pair matches configured masks.

- require_header: require a matching header name/value pair before processing.

If multiple ignore_mask or ignore_header entries exist, a match against any one of them causes the response to be ignored. If multiple require_header entries exist, all required header checks must match for the response to be eligible.

Dynamic inject processing flow

Dynamic processing begins only after the full HTTP response is received by the browser interception layer. The described callback point is AfterReceiveResponse.

If a URL matches rules in more than one <igroup>, the URL is skipped. If multiple matching <dinj> elements exist inside the same <igroup>, handlers are queried in ascending priority order. Equal priority behavior is left to the implementation.

The handler request is sent as POST using multipart/form-data. The body can include:

- sourcelink: full source URL with http:// or https:// prefix, encoded as ANSI. If the scheme is unknown, https:// is used.

- sourcequery: original HTTP request data depending on the <sq> mode.

- sourcehtml: full original HTTP response from beginning to end, including all headers and body.

The handler URI also includes URL-encoded parameters:

- cid: client ID.

- group: group tag.

Dynamic handler responses

Handler response behavior is controlled by HTTP status:

- 200: handler modified the page; the response body contains the replacement browser response including headers. No additional handlers are queried.

- 303: handler did not modify the page and permits additional handlers to be queried.

- 304: handler did not modify the page and blocks additional handlers from being queried.

Other response codes are treated like 303. After such a result, the corresponding rule is locally deactivated for 30 minutes in the current process.

Static inject configuration

The static inject configuration is an ANSI XML text file describing request redirection and full response replacement rules. Static injects operate below dynamic injects and take priority over them.

The configuration uses a <slist> element containing <sinj> entries. Each <sinj> can contain:

- <mm>: match mask.

- <sm>: activation mask, optional.

- <srv>: handler server as ip:port.

- <nh>: replacement Host header.

If <sm> is absent, its value is treated as equal to <mm>.

Static inject states

Each static inject can be in one of three states:

- ready.

- enabled.

- disabled.

The initial state is ready. When navigation reaches the activation mask, the static inject transitions from ready to enabled. The ready-to-enabled transition is associated with the CheckURL callback. The enabled-to-disabled transition is associated with AfterReceiveResponse. A disabled inject can later return to ready.

Static matching and redirection

When a request is evaluated in BeforeSendRequest and matches an enabled static inject, the request is redirected to the configured handler server. The outgoing request is copied and modified:

- Host is replaced with the value from <nh>.

- X-Forwarded-For is added with the client's external IP address.

- clientinfo is added with the group tag and full client ID.

The handler server uses HTTP when its configured port is even and SSL/TLS when its configured port is odd. The request to the handler can be sent asynchronously or from another thread.

Original request modification

After the copied request is sent to the handler, the original request to the real server is transformed into a minimal GET request with URI /. Cookies and other request-specific headers are removed before sending it to the original server.

After the original response is received, the logic waits for the handler response. The original response can then be replaced completely, including both headers and body, with the handler response.

Static inject timeout header

The handler response can contain a special header named sinjdsto. Its numeric value specifies how many seconds the static inject should remain disabled before returning to ready. When replacing the original response with the handler response, the sinjdsto header is removed.

Static and dynamic precedence

If a request or response has been handled by a static inject, it is not evaluated by dynamic inject logic. Static injects therefore have priority over dynamic injects.

DataPost channel

The DataPost component submits captured HTTPS POST activity to a server. It is centered on command code /60/. The client side sends each intercepted POST request that was sent by a browser over SSL/TLS. Along with the complete intercepted POST request, it also sends the full request URL and a log of browser keystrokes.

The full URL is built from the Host header and URI from the original request. The https:// prefix is omitted in the link field. Keystroke logging is associated with POST submission by collecting all keystrokes since the previous POST submission.

DataPost request format

DataPost requests are HTTP POST requests. The URI format is:

/<group-tag>/<clientid>/60/

The group tag and client ID follow the same structure as the parent bot platform. The group tag contains letters a-z and digits 0-9 and is case-insensitive. The client ID has two dot-separated components:

<name>_XYYYYYYY.<32-character-random-hex-string>

The OS-family marker uses:

- W for Windows.

- L for Linux.

- A for Android.

- M for Mac OS.

Example client ID shape:

QWERTY_W617600.11223344556677889900AABBCCDDEEFF

DataPost response behavior

The server response can be:

- HTTP 200 with body /1/ and content-type text/plain.

- HTTP 403 if the request is not POST, the POST body has an unknown format or fields, the URI format is unknown, or the client ID format is invalid.

DataPost body format

The POST body uses multipart/form-data and contains:

- data: binary field up to 32 KB containing the intercepted POST request headers and body.

- keys: UTF-8 string up to 1024 characters containing captured keystrokes.

- link: UTF-8 string up to 4096 characters containing Host plus URI for the original POST destination.

- image: binary field, currently unused.

DataPost server selection

The DataPost configuration contains one or more handler entries:

<dpost>

<handler>11.22.33.44:port</handler>

<handler>...</handler>

</dpost>

Handler selection is random initially. If the selected handler is unavailable, the next handler is tried. If the end of the list is reached, selection wraps to the beginning. If no handler is working, sending stops for five minutes, after which random selection and fallback attempts resume.

Client identity and parent context

The injector module relies on the parent bot logic for identity and network context. The parent supplies:

- Full client ID.

- Group tag.

- External IP address.

These values are then used by browser-injection logic and DataPost activity. Dynamic inject handler requests include cid and group URI parameters. Static inject handler requests include clientinfo and X-Forwarded-For headers. DataPost uses the group and client ID in the /60/ URI path.

Configuration delivery model

The parent logic starts the module and then delivers dynamic and static configuration data through Control calls. The dynamic configuration is passed with ctl dinj. The static configuration is passed with ctl sinj. Both configurations are passed as raw byte buffers with explicit lengths.

This creates a separation between:

- Parent bot responsibilities: configuration retrieval, signature validation, module loading, identity context, and server communication.

- Injector module responsibilities: browser process monitoring, payload injection, configuration application, request/response interception, and browser-level data submission.

Overall system characterization

The injector subsystem is a browser-focused module that extends the parent bot platform with web traffic interception and response manipulation capabilities. It uses XML-based dynamic and static injection configurations, an in-memory browser payload delivery model, and a dedicated /60/ DataPost path for captured HTTPS POST submissions and associated keystrokes.

Dynamic injects are response-oriented and handler-driven: they submit completed source responses to remote handlers and can replace the browser response based on handler status codes. Static injects are redirection-oriented and stateful: they activate on configured navigation patterns, route matching requests through handler servers, alter request headers, and replace original responses. DataPost adds a separate collection path for intercepted HTTPS POST requests and keystroke context.

Together, the components describe a modular browser-interception system that receives identity and configuration from parent bot logic, places browser payload code in memory, applies rule-based traffic handling, and communicates selected browser data and modified traffic flows through configured remote endpoints.

No comments:

Post a Comment