Generated: 2026-04-27
Executive summary
The material describes a modular bot platform built around a custom HTTP/HTTPS command-and-control protocol named CS2. The system is composed of a resident loader, a modular bot, a module execution framework, a signed configuration format, web-injection configuration logic, and a PostgreSQL-backed server platform for client management, payload distribution, command queues, telemetry, and operator APIs.
The platform is designed as a distributed bot-management system rather than a single standalone implant. Its architecture supports persistent client execution, stable host identity, periodic command polling, signed configuration updates, body replacement, module download and control, browser traffic manipulation, file/config/link distribution, targeted task assignment, client scoring, and automated follow-on command insertion from module events.
Major platform components
1. Resident loader
The resident loader is a lightweight persistent component responsible for maintaining server communication and supporting a reduced command set. It supports enrollment, command polling, file retrieval, execution reporting, key-value reporting, configuration update, body update, and executable launch behavior. The specified command set for the loader includes /0/, /1/, /5/, /10/, /14/, /23/, /25/, and /42/.
2. Modular bot
The modular bot is the broader client component. It supports a larger protocol surface, including /0/, /1/, /5/, /10/, /14/, /23/, /25/, /30-32/, /41-43/, and /62-63/. It is required to verify digital signatures. It is packaged as a single file containing both 32-bit and 64-bit bodies; on launch, it detects the system architecture, extracts the appropriate body, and runs it without saving the selected body to disk.
3. Module framework
Modules are DLLs retrieved from server-side storage through /5/. Each module has a name consisting of English letters and exists in separate 32-bit and 64-bit variants. The complete file name appends 32 or 64 to the module name. Modules export Start, Control, Release, and FreeBuffer functions using stdcall. The framework supports module autostart, automatic control calls, module configuration retrieval, process-name based module placement, zombie-process placement, and callback-based module event reporting.
4. Server-side platform
The server side accepts HTTP requests, interacts with PostgreSQL, and can be deployed as multiple server applications backed by one database. It stores client metadata, files, configurations, update links, per-client command queues, module output, module events, client activity logs, API activity logs, importance scores, idle command groups, and event-triggered automation rules.
Persistence and execution model
Windows persistence is specified through Task Scheduler. The scheduled task runs every minute rather than only at startup. If the initial execution context can create a SYSTEM task, it does so; otherwise, it creates a task under the current user. Each run checks whether another loader instance is already active by using named pipes. The naming rule for the pipe is not specified.
The modular bot is distributed as a combined 32-bit/64-bit package. On 64-bit systems, the 64-bit body is used. The design expects the correct body to be extracted and launched without writing the chosen executable body to disk.
Client identity
Each client has a stable ID that persists across restarts. The ID has two dot-separated components:
<name>_XYYYYYYY.<32-character-random-hex-string>
The first component includes a host-identifying name, an OS-family letter, and OS version/build digits. The OS-family letters are:
- W: Windows
- L: Linux
- A: Android
- M: Mac OS
The second component is 32 random characters selected from 0-9 and A-F.
Example format:
QWERTY_W617600.11223344556677889900AABBCCDDEEFF
The client also reports a devhash: a 256-bit hardware- or OS-derived hexadecimal identifier that should remain stable on the same machine.
CS2 protocol overview
The CS2 protocol uses HTTP requests. Requests may use GET or POST depending on command requirements. Servers are configured as ip:port pairs. IPv4 and IPv6 are supported. An even port indicates HTTP; an odd port indicates HTTPS. HTTPS uses self-signed certificates, and certificate validation is expected to be disabled.
Every client request begins with three URI path components:
/<group-tag>/<clientid>/<ccode>/
The group tag consists of letters a-z and digits 0-9 and is case-insensitive. The client ID is also case-insensitive. The command code is a number from 0 to 999. Additional URI components and request/response bodies depend on the command code.
The protocol distinguishes outgoing and incoming commands. An outgoing command is a command code sent in a client request URI. An incoming command is delivered by the server in response to an outgoing /1/ request. Outgoing and incoming command code ranges must not overlap.
Server communication sequence
Before using a server, the client verifies it by requesting the file spk through /5/. The spk file is a server certificate wrapped in the universal signed configuration format. If the server was checked less than four hours earlier, verification is optional.
After server verification, the client initializes the command cycle with /0/. It then performs 100 sequential /1/ command-poll requests. If 100 /1/ requests complete successfully, the client moves to the next server in the configured server list and repeats the process. If a /1/ request cannot be completed after retry handling, the client also moves to the next server and restarts verification and initialization.
HTTP responses with status codes 200, 403, and 404 are treated as valid transport results. Other status codes or send/receive failures are treated as unsuccessful. Requests are retried three times; after failed retries, retry attempts are delayed by 15 seconds.
Outgoing command map
/0/ initializes the command cycle. It sends system version, client version, external client IP address, devhash, and a token string. The response carries an extended server configuration as binary content.
/1/ polls for an incoming command. It sends a random token string that is repeated in the server response. If no command is available, the server returns /1/ as text/plain.
/5/ requests a file or config by name. It is used for server certificates, module bodies, module configs, inject configs, and other file-like server content.
/10/ reports completion of an incoming command. It sends the completed incoming command code, command ID, and result code.
/14/ stores a key-value pair in the server database for later viewing or processing.
/15/ is described server-side as reading a key-value pair from the database.
/23/ requests a new main client configuration. The client provides its current configuration version. The server returns a newer applicable configuration if one exists.
/25/ requests a link to a new client body. The linked object is expected to be a universal signed config containing a replacement executable module.
/60/ is reserved for DataPost behavior and is not used in the current version.
/63/ submits module output. It includes module name, ctl, optional result string, optional auxiliary tag, and optional binary output data. Binary output is sent via multipart/form-data using the field name noname. The maximum output size is 32 MB.
/64/ submits module events. It includes module name, event name, optional info string, optional auxiliary tag, and optional binary event data. The multipart/form-data field names are data and info.
Incoming command map
Incoming commands are returned in response to /1/. The response format includes the incoming command code, group tag, client ID, echoed token, command ID, command parameters, and server signature field.
Notable incoming command codes include:
- /30/: system restart.
- /31/: listed but not defined.
- /32/: listed but not defined.
- /41/: launch executable file.
- /42/: launch executable file through an alternative signed launch configuration.
- /62/: send command to a module.
- /70/: listed but not defined.
- /71/: listed but not defined.
- /666/: listed but not defined.
Incoming command result codes
1: successful execution.
2: invalid command parameter.
3: invalid configuration digital signature.
4: unable to download file by link.
5: damaged file or hash mismatch.
6: operating-system execution function failed.
Alternative executable launch configuration
Command /42/ carries a base64-encoded launch configuration. After decoding, the structure contains:
- DWORD flags, currently unused and set to zero.
- SHA-256 hash of the executable file.
- WORD length of the download link.
- ANSI download link string.
- Digital signature.
The client validates the decoded structure, validates the digital signature, downloads the executable, checks the downloaded file against the SHA-256 value, and launches the file if validation succeeds. Download attempts are retried three times with 15-second pauses.
Universal signed configuration format
The universal signed configuration is a container for files requiring authenticity validation. It has a 48-byte random header followed by AES-256-CBC encrypted data.
The AES key is derived from the first 32 bytes of the header by repeatedly appending SHA-256 hashes until a 4096-byte block is built, then taking SHA-256 of that block as the key. The IV is derived similarly from bytes 16 through 47 of the header; the first 16 bytes of the final SHA-256 output are used as the IV.
The encrypted portion is padded with PKCS7 to a 16-byte boundary. Before encryption, original data may be LZO-compressed. The plaintext begins with an 8-byte header containing original data size and flags and ends with an ECDSA P-384/SHA-384 signature. The signature covers the 8-byte header and prepared data. The document specifies compatibility with Microsoft CNG BCRYPT_ECDSA_P384_ALGORITHM.
Server response signatures are described separately. In the current implementation, the server response signature is not used and is replaced by the literal string 1234567890.
Main client configuration
The main client configuration is XML inside the universal signed configuration. It contains:
- Configuration version.
- Group tag.
- Server list.
- Static inject configuration name.
- Dynamic inject configuration name.
- Additional named configuration references.
The client accepts a new main configuration only if the embedded version equals the version declared in the server response and is strictly greater than the current local version.
Server certificate configuration
The server certificate is XML inside the universal signed configuration. It contains an expiration epoch timestamp. After expiration, the certificate is considered invalid for server use.
Extended server configuration
The extended server configuration is XML inside the universal signed configuration. It contains an expiration epoch timestamp and may contain plugin server addresses. Plugin servers store modules and support only /5/. If the plugins tag is absent, the bot does not support modules.
Dynamic inject configuration
The dynamic inject configuration is ANSI XML. It describes groups of URL masks and remote handlers. Each dynamic inject element can include:
- URL mask.
- Handler URL.
- Priority.
- Flag controlling whether to send no source request, request headers only, or the full source request.
- Ignore masks.
- Ignore-header conditions.
- Required-header conditions.
When a browser response is fully received and its URL matches a dynamic inject rule, the client sends a POST request to the handler. The POST body uses multipart/form-data and may include:
- sourcelink: full source URL.
- sourcequery: source HTTP request, depending on the configured flag.
- sourcehtml: full source HTTP response including headers.
The handler can return:
- 200: modified page content is returned and replaces the original browser response.
- 303: no modification and other handlers may be queried.
- 304: no modification and no other handlers should be queried.
Other handler response codes are treated like 303, and the corresponding handler is locally deactivated for 30 minutes.
Static inject configuration
The static inject configuration is ANSI XML describing lower-level request redirection and response replacement rules. Static injects contain:
- Trigger mask.
- Optional activation mask.
- Handler server ip:port.
- Replacement Host header value.
Static injects have three states: ready, enabled, and disabled. A transition from ready to enabled occurs when the activation URL is reached. While enabled, matching requests are redirected to the configured handler server. The outgoing request is copied, Host is replaced, X-Forwarded-For is added with the client's external IP, and clientinfo is added with group and client ID.
The original request is modified into a minimal GET / request with cookies and other specific headers removed. After both original and handler responses are available, the original response can be replaced with the handler response. A handler response header named sinjdsto can move the inject to disabled state for a specified number of seconds before it returns to ready. Static injects take priority over dynamic injects.
Module loading and control
Modules are requested on first use from plugin servers through /5/. They are wrapped in the universal signed configuration format. Modules are refreshed every 72 hours. If update fails, retry occurs after 30 minutes. The first module request occurs when the bot first needs to use that module.
The module framework supports one active instance per module. On 64-bit systems, only 64-bit modules are used by 64-bit upper logic. On 32-bit logic, only 32-bit modules are used.
Exported module functions
Start initializes the module and is called when ctl is start. It receives module name, decoded argument data, argument length, result buffer, parent info, event callback pointer, callback context, and reserved data. It returns a module handle or zero on failure.
Control handles ctl values other than start and release. It receives the module handle, ctl string, decoded argument data, output buffers, output-data pointer and size, auxiliary output tag buffer, and reserved data. It returns TRUE or FALSE.
Release fully terminates the module and frees module resources.
FreeBuffer frees buffers allocated by Control.
EventCallback allows the module to submit events back to the upper logic. It includes module handle, event name, event info, optional output data, output size, auxiliary tag, and context.
Module embedded configuration
Each module can contain an embedded ANSI XML configuration enclosed by <moduleconfig> and </moduleconfig>. Supported fields include:
- <autostart>: automatic module start after upper logic starts.
- <autocontrol>: ordered automatic Control calls after Start.
- <processname>: preferred process context for module execution.
- <needinfo>: parent data requested by module, such as ID, IP, or parent files.
- <autoconf>: server-provided configs passed into module controls on a schedule.
If autoconf is present, the upper logic retrieves specified config files from the server through /5/, verifies and decrypts them, and passes them to the module through Control. The maximum decrypted config size is 8 MB. If config retrieval repeatedly fails, the upper logic treats it as a signal to change servers.
Module control through /62/
Command /62/ passes module name, ctl, and base64 argument. The ctl values start and release are reserved. For start, the module is loaded if needed and Start is called. For release, Release is called and module state is cleared. For other ctl values, Control is called if the module is active.
After any ctl except release, the client reports results through /63/. If Start or Control fails, the client reports a concise failure reason through /14/.
Server storage and deployment model
The server-side application is built around PostgreSQL. The database may reside on a separate physical server, while multiple server application instances can run on other machines. Stored state includes clients, files, configs, links, events, command queues, API keys, API logs, client logs, idle command definitions, importance event definitions, and event-triggered command rules.
Client records include:
- Group tag.
- Client ID.
- Operating system.
- IP address.
- GeoIP country.
- Importance.
- Userdefined.
- Registration time.
- Last online time.
File, config, and link selection
Files, configs, and links can be filtered by:
- Group.
- Excluded group.
- Country/GeoIP.
- Operating system.
- Importance range.
- Userdefined range.
- Exact client ID.
- Priority.
Group filters can use SQL LIKE-style patterns. If multiple objects match, priority controls selection. For configs, the server returns only versions strictly greater than the client's current version. For links, each link has an expiration time.
Command queue model
Each client has an individual command queue. Commands are added by operators or server-side mechanisms. A command remains in the queue until the client reports completion through /10/. It remains queued even if the client receives it multiple times. When a /10/ report arrives, the command is removed regardless of the result code.
Each queued command has:
- Command code.
- ANSI command parameter string.
IdleCommands
IdleCommands are groups of waiting commands that are not assigned to a client until a matching client polls and has no outstanding queued command. Each IdleCommand group has:
- Command code.
- Command parameter.
- Remaining command count.
- GeoIP filters.
- Operating-system filter.
- Group filter.
- Excluded group filter.
- Importance range.
- Userdefined range.
When a matching client polls /1/, the server inserts the command into that client's queue and decrements the group count. A client can receive a command from the same IdleCommand group only once.
Server management API
The server exposes a separate HTTPS API on a configurable port. Access control uses apikey and apikeypass in the URI path. Each API key has an allowed function list and allowed CIDR ranges. The source IP is read from X-Forwarded-For; if the header is absent, the request is rejected.
API request format:
/<apikey>/<apikeypass>/<function>?param_name1=<param1>¶m_name2=<param2>
If the API function accepts binary data, the request uses POST and multipart/form-data with the binary block in a field named bdata.
API functions
GetGroupData returns activity information by group for a given time period. The response includes group name, unique client count, and first registration time.
UploadFile uploads a file and associated targeting filters. The server computes priority by taking the current maximum priority and adding one.
UploadConfig uploads a configuration and associated targeting filters.
UploadLink uploads an update link with expiration and targeting filters.
PushBack adds a command to a specific client queue.
GetFilesList lists stored files and their metadata.
DeleteFile deletes a stored file by ID.
GetLastEventData retrieves the most recent binary data for a specified client, module, and event.
GetOnline lists clients active during a specified recent period.
GetLastActivity returns the last activity timestamp for a specified client.
API activity logging
Successful API activity is logged with date/time, apikey, requested address, and function. Unsuccessful API requests are logged to the general server text log.
Client activity logging
Each client has an activity log. It records all client commands except idle /1/ polls and all commands issued to clients. The log stores:
- Date/time.
- Client ID.
- Command code.
- Additional information string.
Additional information differs by command. For /10/, it includes completed command ID and result code. For non-idle /1/, it includes issued command code. For /63/, it includes module name and ctl. For /64/, it includes module name, event name, and auxiliary tag. For /23/, it includes issued config version. For /5/, it includes issued file ID. For /25/, it includes issued link text.
Importance scoring
Importance is an integer from 0 to 100 calculated from client events. Events have three modifiers:
- preplus.
- mul.
- postplus.
The formula is:
newimportance = (oldimportance + preplus) * mul + postplus
Values are clamped to the range 0 to 100 and rounded before saving. A per-client flag can disable automatic importance changes. Event definitions are global and stored in the database rather than configuration files. The event list may be cached.
Supported event classes include:
- online: number of /0/ commands received.
- age: elapsed time since first /0/ registration.
- geo: country.
- devhash_dup: another client has the same devhash.
- command_complete: number of successfully completed outgoing commands.
- geo_change: country changed.
Event-triggered command automation
The platform can automatically add commands to a client queue when module event reports arrive through /64/. A trigger matches module name, event name, and a regular expression over the info field. Each trigger also has a per-client minimum interval to limit how often it can fire for the same client. Trigger rules are stored in a table and may be cached.
Overall system characterization
The CS2 platform combines a polling bot protocol, signed configuration distribution, modular execution, web traffic manipulation, targeted server-side content selection, persistent command queues, operator APIs, and event-driven automation. The client side emphasizes stable identity, periodic execution, server rotation, signed updates, module extensibility, and browser-response manipulation. The server side emphasizes scalable client state, precise targeting, payload/config/link staging, per-client queues, operational logging, client scoring, and automated task insertion.

No comments:
Post a Comment