Martin's Blog

12. Detecting RCS

The easiest way to write a bad RCS detection is to start with Server: nginx. The examined backend places that value on every REST response, and its error pages imitate nginx. It is a real product behavior. It is also shared by an enormous population of ordinary web servers.

Ports 80 and 443, Ruby, MongoDB, a ten-year certificate, ACME Corp, and a directory named backup have the same problem. Each belongs in the investigation. None independently identifies HackingTeam.

Reliable detection comes from combinations across boundaries. The default certificate issuer plus an RCS-specific route is useful. Either becomes much stronger when the same host exposes the expected adjacent WebSocket or MongoDB topology. A network lead becomes compelling when a seized Windows server also contains the exact RCS service names, installation root, licence schema, and per-target MongoDB collections.

I built the detection work around that layered approach. It concentrates on the operator and backend platform reconstructed in the lab. Implant-to-collector indicators appear only where static source supports them, and no implant or exploit was executed to validate a rule.

Confidence is a property of a combination

Every indicator in this research carries both provenance and confidence. Provenance says how it is known: observed in the lab, found in source, stated in a manual, or inferred from several facts. Confidence says how strongly a match supports the RCS hypothesis in the sensor position where it is used.

Layered RCS detection model combining network, host, data structure, and behavioral evidence
DETECTION SYNTHESISNo generic banner, port, or technology should name RCS alone. Confidence rises when independent product-specific observations agree across network, host, database, and behavior layers.
SignalProvenanceStandalone valueBest companion
Server: nginx on an RCS responseSOURCE + LABVery lowRCS route and nginx-styled error body from a non-nginx stack
TCP 443 followed by WSS on the next portSOURCE + LABLowDefault certificate issuer and operator API traffic
CA issuer containing both Root Certification Authority and ACME CorpSOURCEMedium to high for an unmodified defaultTen-year leaf, adjacent RCS API/WSS, host artifact
X-Auth-Frontend request headerSOURCEHighDatabase-to-collector direction and RCS topology
HTTP 444 from a collectorSOURCELowFixed-size encrypted POST behavior and repeated decoy closes
C:\RCS\DB plus the three RCS Mongo service namesSOURCEHighInstaller registry/firewall values and licence file
RCS licence YAML schemaSOURCEHighRCS directory, database, or service artifacts
evidence.<24-hex-id> plus paired target GridFS collectionsSOURCE + LABHigh in MongoDBitems, audit, signatures, and RCS evidence fields

Confidence can fall when an operator changes a default. The certificate common name is configurable. An anonymizer chooses a plausible generic CA label. The collector’s URI and cookie names are randomized by build. A later product version may change ports or field names. Detections must therefore explain both what a match means and how a deployment can evade it without changing the underlying product.

Scoring evidence without inventing certainty

A practical triage score should reward specificity and independence rather than raw match count. Three variations of the same nginx banner are one layer, not three confirmations. The following model is qualitative by design:

Evidence classExamplesSuggested interpretation
Generic environmentHTTPS, MongoDB, Ruby, port 443, nginx bannerLead only; never name RCS from this class alone
Correlated defaultAdjacent 443/444 roles, ten-year ACME/default-root certificateInteresting when several properties coexist; configuration can change them
Product-specific protocolX-Auth-Frontend, cluster of distinctive controller routesStrong network/application evidence at the required sensor position
Product-specific hostExact service names and RCS paths, launcher combinations, licence schemaStrong host evidence; distinguish operational installation from copied research material
Product-specific data modelRepeated target-scoped evidence/aggregate/GridFS namespaces plus RCS fields and global collectionsHigh-confidence backend evidence when acquired directly and read-only
Independent contextual evidenceMatching process, certificate, logs, topology, historical infrastructure recordRaises confidence and helps separate production, lab, archive, and false positive

An assessment can then use plain-language conclusions:

This is not a numeric formula because sensor quality, acquisition provenance, and environmental prevalence matter more than a universal weight. Teams can translate the classes into their case-management system, but should preserve the matched facts and reasoning rather than output only a number.

The database network surface

The database exposes HTTPS REST on its configured listening port and secure WebSocket on the next port. Defaults are 443 and 444. The worker uses 442, and the collector normally uses HTTP 80 plus an internal controller port 4499. MongoDB’s expected router, shard, and configuration roles use 27017, 27018, and 27019. A host presenting several members of that sequence is more interesting than a host presenting any one of them.

Port correlations are hunting filters, not signatures. MongoDB commonly uses 27017. HTTPS commonly uses 443. Port 444 has many unrelated applications. The useful question is whether the services behind them also share RCS protocol, certificate, and host evidence.

The REST path shape is /<controller>/<action>[/<id>]. Operator login is POST /auth/login with JSON fields user, pass, and version; a successful response establishes a session cookie. Subsequent routes expose unusually specific nouns such as /agent/filesystems, /evidence/commands, /backuparchive, /collector/del_logs, /injector/add_rule, /shard, and /task/create. In a TLS-terminated reverse proxy, web application firewall, or server access log, several such routes from one client session are a strong behavioral cluster.

On the original encrypted listener, a passive sensor cannot see these URIs or JSON fields. A rule that examines /auth/login must say that it requires TLS termination, endpoint instrumentation, or another decrypted sensor position. Applying an HTTP-content rule directly to port 443 and calling it tested would be misleading.

RCS adds Server: nginx itself in the REST response code, including success and error paths (lib/rcs-db/rest_response.rb:107,201,270). The HTML errors imitate nginx’s sparse 403 and 404 pages. This becomes mildly unusual when the TLS, timing, and headers show EventMachine behavior or when an authenticated API response carries the banner without an nginx proxy in the path. It still needs another product-specific fact.

The banner is assigned inside the Ruby response builder rather than supplied by a reverse proxy:

# rcs-db/lib/rcs-db/rest_response.rb:106-111
# fake server reply
@response.headers['Server'] = 'nginx'
@response.headers['Date'] = Time.now.getutc.strftime("%a, %d %b %Y %H:%M:%S GMT")

@response.headers['Content-Type'] = @content_type
@response.headers['Content-Length'] = @response.content.bytesize

That makes the header a source-backed behavior, while its generic value keeps it low-confidence when observed alone.

WebSocket traffic adds a version-specific clue. The bundled em-websocket behavior treats the Connection header value case-sensitively: Upgrade succeeds where lowercase upgrade can be dropped. This explains a reproducible proxy failure in the lab and can help fingerprint the old stack. It is fragile as a detector, however, because proxies normalize headers and other old WebSocket implementations may share the quirk.

The sensor position determines which of these observations is even possible:

Sensor positionVisible evidenceHidden or unreliable evidence
Internet perimeter without TLS terminationIPs, ports, timing, TLS handshake/certificate where not interceptedREST paths, cookies, JSON, WebSocket messages
Reverse proxy or WAFDecrypted routes, methods, headers, response codes, session-level clustersBackend process identity and internal component links unless separately logged
East-west network sensorFrontend/component headers, Worker/Mongo relationships, internal topologyHost files and data meaning; TLS may still hide content
Windows endpoint telemetryServices, command lines, registry, paths, process ancestry, network peersMongoDB document schema unless database access is collected
Backend filesystem acquisitionConfiguration, certificates, launchers, logs, licence and backup structureLive volatile sessions and traffic not preserved on disk
MongoDB acquisitionCollections, indexes, shard keys, relationships and object stateOriginal packets, deleted unbacked state, and host-only artifacts

A detection rule that requires the wrong sensor is not merely low confidence; it is inoperative. Rule documentation therefore needs the decryption point, event source, operating system, audit policy, database privilege, and retention assumptions alongside the pattern itself.

Certificates: useful defaults, dangerous conclusions

When RCS generates its database certificates, it creates a self-signed CA with subject CN=Root Certification Authority, O=ACME Corp. It then signs a database leaf whose common name is operator-configured and a collector leaf whose common name is collector. The generated certificates use a 3,650-day validity period (lib/rcs-db/config.rb:325-359 and config/certs/openssl.cnf).

The combination of both default issuer attributes, long validity, and an RCS port relationship is useful for historical hunting. Each component alone is weak. Root Certification Authority is intentionally generic. ACME is a widely used certificate-management acronym and company name. Long-lived self-signed certificates are common in appliances. The database common name can simply be an IP address.

The CA also may not be default. RCS can import other certificate material, and anonymizer construction samples generic names such as server, test, apache, nginx, development, web, and www. Those names should never be blocklisted. Their defensive value lies in a broader certificate cluster: shared key or issuer relationships, matching validity and generation times, co-located RCS services, and supporting historical infrastructure evidence.

The initial Suricata rule therefore requires both default issuer strings in the certificate issuer buffer. It will miss custom certificates by design. A miss is not evidence that a candidate is benign, and a hit still warrants confirmation.

Certificate-hunting recipes

In a certificate inventory, the safest query keeps the individual properties visible rather than hashing them into one opaque rule. A platform-neutral schema might use:

SELECT observed_ip, port, subject_dn, issuer_dn, not_before, not_after,
       sha256_fingerprint
FROM tls_certificates
WHERE issuer_dn LIKE '%CN=Root Certification Authority%'
  AND issuer_dn LIKE '%O=ACME Corp%'
  AND not_after >= not_before + INTERVAL '3500 days';

Syntax will vary by data lake. The 3,500-day threshold tolerates small date representation differences around the source’s 3,650-day setting; it is a hunt, not an exact product assertion. Results should be joined with service relationships on the same IP or nearby hosts, especially adjacent HTTPS/WSS ports and RCS-specific application routes.

For an acquired certificate file, a read-only local check can preserve the fields and fingerprint without exposing private keys:

openssl x509 -in candidate.crt -noout \
  -subject -issuer -serial -dates -fingerprint -sha256

Do not upload an unknown private certificate or key to a public search service. Search infrastructure should use the public certificate fingerprint or public transparency data where legally and operationally appropriate. A match to the default issuer still needs host, protocol, or database corroboration.

Database-to-collector traffic

One network artifact is much more specific. The backend sends the shared server signature to a frontend in the header X-Auth-Frontend. In the examined source this path uses ordinary HTTP on port 80 (lib/rcs-db/frontend.rb:14-127). The header name describes an internal RCS concept and is not a standard web authentication scheme.

A sensor that sees this header should preserve the transaction and examine both endpoints. The value is a credential and should be handled as sensitive; it does not need to be logged to raise the alert. Detection should capture the header name, flow direction, server addresses, timing, and nearby status or topology traffic while redacting the header value from routine output.

Cleartext reachability is a deployment condition. A reverse proxy, VPN, or later change may hide it. Conversely, security scanners or researchers can replay the header name. Host and database corroboration remain important.

The collector’s public listener uses deliberate decoy behavior. Invalid or unauthenticated requests can receive nginx’s non-standard status 444 after a delay or simply lose the connection. Unsupported non-public methods can enter a HACK ALERT log path and return 405. Status 444 alone is low-confidence: ordinary nginx configurations use it. Repeated 444 or delayed-close behavior on a candidate that also has RCS certificate, port, or header evidence is much more useful.

Implant-side traffic: what source can and cannot provide

The static capability audit improves the inventory of possible endpoint behaviors, but it does not manufacture network observations. Module factories and evidence writers support platform-specific collection claims; they do not show which build was shipped, whether a module ran, or what its traffic looked like on a historical network. Scout, Soldier, full cores, and companion layers also have different scope, so a detection must name the source snapshot and stage it is meant to cover. Appendices D–E record those distinctions.

Static source describes an authentication POST whose encrypted body is normally 112 bytes with up to 16 bytes of trailing variation. It uses AES-128-CBC under a customer signature, and build-side URI and cookie names can be randomized. User-Agent strings influence which platform payload the collector selects.

This suggests a hunting pattern: repeated short POSTs of a narrow size range to an otherwise static-looking HTTP site, followed by collector-specific response and file-transfer behavior. It is not sufficient for a production signature. Many telemetry beacons use short fixed-size posts; packetization can obscure body length; HTTPS or anonymizers can remove visibility; and the lab did not execute an implant-to-collector session.

For that reason the initial ruleset does not publish a “fixed-size POST equals RCS” alert. The behavior belongs in threat hunting with strict scoping and companion signals until a safe, representative PCAP corpus can measure both coverage and false positives. The capability audit may guide defensive host artifact review or evidence-type triage, but it is not itself an IOC set.

Windows server artifacts

The original Windows installer is less coy than the network protocol. It uses C:\RCS\ as the root and places the database under C:\RCS\DB, alongside bundled Ruby, MongoDB, Java, Python, collector, configuration, certificate, log, backup, core, and exploit material according to the selected installation roles (nsis/RCS.nsi).

It installs MongoDB roles under exact service names:

The corresponding service descriptions explicitly mention “Remote Control System,” and image paths point into C:\RCS\DB\mongodb\win\ with router, configuration-server, or shard arguments (RCS.nsi:505-516). The combination of an exact service name and exact RCS path is high-confidence. A service name by itself is slightly weaker because short abbreviations can collide.

The installer writes a distinctive compatibility value:

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers\C:\RCS\Ruby\bin\ruby.exe = DisableNXShowUI

The complete path and exact value make this a useful registry detection (RCS.nsi:296). It also adds Windows Firewall rules under the group RCS Firewall Rules with names beginning RCS_FWD, including Database, Database Websocket, Mongo Router, Mongo Shard, and Mongo Config on the expected ports (RCS.nsi:598-602).

Batch launchers repeatedly change directory to C:\RCS\DB and invoke names such as ruby bin\rcs-db-status or ruby bin\rcs-worker-stats. The backend may contain bin\ruby_x64.dll for HASP dongle support and haspdinst.exe. Those filenames have other legitimate uses; their RCS directory and companion files supply the confidence.

The initial Sigma set detects the registry value, service-install event, and MongoDB service command line. They are marked experimental because the source side is confirmed but representative Windows telemetry has not yet been replayed through a Sigma converter and target SIEM pipeline.

Read-only Windows host triage

On an acquired or safely isolated Windows host, defenders can test the exact service and registry evidence without starting any RCS component:

Get-CimInstance Win32_Service |
  Where-Object Name -in 'RCSMasterConfig','RCSMasterRouter','RCSShard' |
  Select-Object Name, DisplayName, State, StartMode, PathName

Get-ItemProperty -LiteralPath `
  'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers' |
  Select-Object -Property 'C:\RCS\Ruby\bin\ruby.exe'

Get-NetFirewallRule -Group 'RCS Firewall Rules' -ErrorAction SilentlyContinue |
  Select-Object DisplayName, Enabled, Direction, Action

These queries read configuration; they do not stop services or open files. Investigators should capture full service paths and firewall port filters before concluding that a name collision is benign. If C:\RCS exists, inventory names, sizes, timestamps, owners, alternate data streams, and cryptographic hashes before previewing content. Licence files, configuration, logs, backups, certificates, and MongoDB data can contain credentials or victim information and need restricted evidence handling.

A filesystem hunt should favor combinations such as an RCS database launcher, the exact root, MongoDB role arguments, and the licence/configuration layout. Searching enterprise-wide for the three-letter directory name alone will produce unrelated matches and may collect sensitive content unnecessarily.

File-content detection

The RCS licence is a YAML document with a distinctive combination of product dimensions: serial, maintenance, users, desktop and mobile agent counts, individual historical platforms, collectors and anonymizers, shards, alerting, correlation, deletion, archive, OCR, translation, digest, signature, and integrity fields. Several names are generic, but the complete schema is unusual.

The first YARA rule requires a multi-field subset rather than a historical digest or signature value. This preserves coverage across customer licences while avoiding a rule that merely recognizes the leaked sample. It is bounded to small files and requires the YAML document marker at offset zero.

A second rule detects Windows batch launchers only when the exact RCS database root occurs with an rcs-db or rcs-worker Ruby command. A third recognizes the original certificate-generator source through the joint presence of the default CA, database and collector certificate names, collector subject, and anonymizer CA filename. The source rule is principally useful on seized development or server trees; it is not an implant signature.

YARA 4.5.0 parsed the rules and matched the intended original licence, launcher, and configuration-source files. It produced no match on the initial three-file sanity set or an expanded 603-file, 27.1 MB local corpus containing executables, libraries, structured files, documents, and three rule-specific near misses. That proves syntax and expected positives against a materially better local negative set, not operational false-positive performance. An authorized representative Windows enterprise corpus is still needed.

What the experimental rules cover

The appendices preserve full rule text; the useful chapter-level question is where each family can see and what its tests establish.

Rule familyRulesIntended sensorPositive basisRemaining gate
Suricata4TLS metadata or decrypted/internal HTTPSuricata 7.0.3 parse; four intended positive flows and two near-miss flows replayedRepresentative benign PCAP corpus, sensor tuning, and performance
Sigma3Windows registry, service-install, and process telemetrySigma CLI check, pySigma Splunk conversion, three positive and eight near-miss fixture eventsTarget-SIEM field mapping and ingestion-normalization replay
YARA3Acquired server/source filesFrozen positives plus 603-file local negative corpusRepresentative Windows/enterprise corpus and operational-image calibration

Rules stay separate because combining them inside one engine can hide sensor assumptions. A Suricata alert cannot require a Windows registry key it cannot see; correlation belongs in the SIEM or investigation layer. Conversely, a YARA hit on a researcher’s copy of leaked source should not be promoted into an operational incident merely because the file content is exact.

Every rule record therefore carries provenance, configurable fields, expected coverage, companion signals, false positives, and validation status. That metadata is part of the detection. Removing it to obtain a shorter rule file would make deployment easier while making the resulting conclusion less safe.

MongoDB as a high-confidence discovery surface

On a seized or exposed database, collection topology is one of the strongest backend indicators. RCS expects the database rcs and creates target-specific namespaces:

The evidence collections use a compound shard key {type: 1, da: 1, aid: 1}. Documents contain acquisition and receive times, relevance, report flag, notes, an agent ID stored as a string, keyword arrays, and type-specific data. GridFS references use _grid and _grid_size. Related global collections include items, users, groups, signatures, audit, alerts, backups, status, queue, collector, injector, connector, and topology data.

MongoDB applications commonly use GridFS and ObjectIds. The confidence comes from the repeated target ID across evidence, aggregate, files, and chunks, plus RCS-specific fields and global collections. Investigators should collect names and schema read-only before running application cleanup or restore. An unprotected database may itself be the compromise path described in Chapter 10.

A practical read-only hunt can begin with collection names matching the four patterns, then verify the shard key and sample only metadata necessary to establish schema. Sensitive evidence content should not be copied into a detection system merely to prove the product identity.

The following MongoDB 2.6-compatible discovery fragment lists names and index metadata without returning evidence bodies or changing state:

var rcsDb = db.getSiblingDB('rcs');
var targetPattern = /^(evidence|aggregate|grid)\.[0-9a-f]{24}(\.(files|chunks))?$/;
var scoped = rcsDb.getCollectionNames().filter(function (name) {
  return targetPattern.test(name);
}).sort();

printjson(scoped);

scoped.filter(function (name) {
  return name.indexOf('evidence.') === 0;
}).forEach(function (name) {
  print(name);
  printjson(rcsDb.getCollection(name).getIndexes());
});

db.getSiblingDB('config').collections.find(
  { _id: /^rcs\.(evidence|aggregate|grid)\./ },
  { _id: 1, key: 1, unique: 1, dropped: 1 }
).forEach(printjson);

The fragment should run only against an authorized acquisition or forensic clone. A production query still changes access logs, cache state, and possibly timing, even when it performs no writes. Appendix H maps the full schema, and Chapter 13 supplies the preservation-first query catalogue. Discovery output should retain BSON types and collection names; JSON-only export can erase the string-versus-ObjectId distinction that matters to aid interpretation.

Logs and behavioral signals

Backend logs contain component and workflow language that can corroborate a candidate:

Log strings can be copied, and many fragments are generic. Their value rises when directory, process, certificate, and database evidence independently agree.

Two behaviors are useful for identification but poor as automatic alerts. First, logging in as one user invalidates the earlier session and queues a “used on another machine” logout push. Second, the reconstructed database can mark its own heartbeat stale after several minutes while REST and WebSocket traffic remain healthy. Both can explain operator reports and captured state; neither should trigger an RCS incident without stronger artifacts.

Playbooks for four common starting leads

Different leads require different confirmation paths. The initial fact should determine what is collected next, not predetermine the conclusion.

A certificate or adjacent-port lead

Preserve the full certificate chain, fingerprints, handshake metadata, IP/port, observation time, and sensor position. Determine whether the certificate came from the origin or a TLS-intercepting proxy. Look for the 443/444 relationship and other historical component ports, then seek decrypted route clusters or host evidence through authorized means.

The default issuer and ten-year validity make the lead interesting; they do not identify the operator, customer, implant, or victim. If the origin uses a custom certificate, route and host evidence may still confirm RCS. If only the default strings match, classify the lead as possible and keep hunting.

An exact Windows service or path lead

Acquire service configuration, binary and interpreter paths, registry values, firewall rules, scheduled tasks, process ancestry, listening sockets, and file hashes. Check whether the host is an operational server, a forensic image, a research archive, or a reconstruction lab. The presence of source code or a licence-schema sample on an analyst workstation has a different meaning from running RCSMasterRouter beneath the expected installation root.

Do not start a stopped service to test it. Preserve configuration, logs, certificates, MongoDB paths, and volatile state first. If the service was running, relate its command line and network peers to the RCS role model rather than relying on the name alone.

A MongoDB namespace lead

Begin with database name, collection names, index definitions, shard metadata, and counts. Confirm the repeated target ID across evidence, aggregate, and GridFS namespaces, then look for the RCS global collection cluster and model fields. Preserve BSON types. Sample evidence content only when legally authorized and necessary; schema and relationships are normally enough for platform identification.

An exposed database is simultaneously a detection lead and a security incident. Chapter 10 shows that bundled all-in-one deployment can lack database authorization. Record access conditions and network placement, but do not use a default application login or run cleanup. Platform identification, database exposure, data access, and historical exploitation are four separate findings.

A collector-decoy or internal-header lead

Status 444 or delayed close begins as low confidence. Preserve timing, request method, response bytes, TLS state, redirects, and repeatability without aggressive probing. Seek the fixed component relationships, certificate cluster, or host identity. The X-Auth-Frontend header is much more specific; alert on its name, redact its credential value, and investigate both endpoints as potentially sensitive components.

Because collector URIs and cookies can be randomized, absence of a known path is weak negative evidence. Because scanners can copy public signatures, a header-name hit without the surrounding RCS topology can also be simulated. Independent endpoint or database evidence resolves that ambiguity.

Starting leadStrongest next layerAvoid concluding
Certificate/portsDecrypted routes or host artifactsCustomer, operation, or victim identity
Windows service/pathProcess, configuration, certificate, and MongoDB structureThat copied research files were operational
MongoDB namespacesGlobal RCS schema, shard metadata, host roleThat exposure was exploited or records are authentic
Collector/header behaviorBoth endpoint identities and component topologyThat generic 444 behavior alone is RCS

Negative controls and configuration tests

A detector should be tested against both ordinary software and modified RCS defaults. The first measures false positives; the second measures how much of the rule is only configuration recognition.

Useful negative sets include ordinary nginx 444 configurations, self-generated ACME-labelled certificates, unrelated applications under C:\RCS, standard MongoDB/GridFS applications, Ruby service launchers, and generic YAML licences. The corpus should be documented and legally shareable; a tiny set of operating- system text files cannot represent enterprise software prevalence.

Configuration-variation tests should change one dimension at a time: certificate issuer, leaf subject, listener ports, installation root, service display names, proxy normalization, and TLS termination. Expected misses should be recorded as coverage limits, not patched by adding broad strings that raise false positives. Product-specific relationships—route vocabulary, collection families, model fields, component header, or exact multi-artifact combinations—should carry the detector when defaults change.

A staged hunting workflow

Detection should minimize both false accusation and unnecessary exposure of surveillance data.

  1. Collect a lead. Identify a certificate, header, service, file, database namespace, or route cluster. Record its provenance and sensor limitations.
  2. Seek an independent layer. Corroborate network evidence on the host, host evidence in MongoDB, or database evidence in logs and certificates.
  3. Test configuration alternatives. Determine whether certificates, ports, paths, and listener addresses were customized before interpreting a miss.
  4. Preserve before interaction. Do not log in with a default account, restore a backup, compact MongoDB, or use the UI to “look around” before acquiring state. Login changes sessions and audit; restore can replace collections and signatures.
  5. Separate platform from payload. A backend finding does not identify which implant version, exploit, customer, or targets were involved.
  6. Score the conclusion. Report confirmed RCS only when multiple specific artifacts agree. Label weaker cases possible or probable and state what would resolve them.

One reasonable high-confidence combination is the default issuer, the 443/444 service pair, and any exact Windows service or licence artifact. Another is the X-Auth-Frontend header plus RCS-specific REST routes and the target-scoped MongoDB layout. The precise scoring should be adapted to the environment, but the principle is stable: prefer independent, difficult-to-fake layers.

False positives and negative evidence

Every published rule needs an explicit false-positive story.

Rule familyLikely false positiveTuning or confirmation
nginx banner or 444 statusOrdinary nginx and custom web applicationsRequire RCS route, certificate, or host evidence
Default CA stringsLab appliances and locally generated certificatesRequire both issuer attributes, validity context, and adjacent services
Login routeUnrelated APIs using /auth/login and common JSON namesRequire additional RCS routes in the same session
Port topologyMongoDB and custom admin servicesFingerprint protocols and corroborate on host
Service namesInternal abbreviations or reconstruction labsRequire RCS path or service description
C:\RCS pathUnrelated application using the acronymRequire database layout, services, licence, or registry value
License-schema YARAA copied research artifactDetermine acquisition path; distinguish operational host from source archive
MongoDB collection patternAnother target-scoped analytics productVerify compound schema and RCS global collections

Negative evidence also has limits. No default certificate may mean a custom certificate. No C:\RCS may mean a non-Windows installation. No worker port may mean a distributed topology or firewall. No exploit package says nothing about whether the backend existed. Rules describe expected coverage; they are not universal definitions of RCS.

Validation status

I deliberately label the research rules experimental.

YARA has executable validation against frozen positive files and a 603-file local negative corpus. Sigma CLI reports no rule, condition, or validation issues; all rules convert through pySigma’s Splunk backend, and their portable selections pass three positive and eight one-field near-miss events. Suricata 7.0.3 loads all four rules; each SID fires exactly once on its intended synthetic or certificate-only flow, while HTTP and ordinary-certificate TLS near misses produce no alert.

The one live lab interaction was a certificate-only loopback TLS handshake; it did not log in or send an application request. The cleartext HTTP traffic was generated from inert packet fixtures. The 444 fixture validates the rule, not the collector behavior: no collector ran. The detection ledger records which artifact is source-backed, previously observed, or newly validated.

The remaining production gate is narrower: repeat Suricata tests on the target sensor version and representative benign traffic; convert and execute Sigma in each intended SIEM with its real field mappings; scan an authorized Windows enterprise corpus with YARA; measure performance; and test how useful signal combinations remain after default certificates, ports, and paths are changed.

Validation material must preserve the same safety boundary as the research. Synthetic backend captures can test REST, WebSocket, certificate, and Worker rules. Collector coverage should use sanitized, lawfully obtained defensive captures or a separately approved non-operational fixture—not a rebuilt implant session. Negative corpora must exclude credentials and victim content. The validation ledger should record engine version, command, input hashes, expected alerts, actual alerts, misses, false positives, and sensor assumptions so a later rule revision can be compared rather than merely declared improved. Without that provenance, a successful alert is a demonstration, not validation.

Sources and evidence

↑ HackingTeam's RCS: Bringing a Commercial Spyware Platform Back to Life