Operation CameraSwarm: Over 14,000 Dahua cameras compromised across Ukraine and Russia

Operation CameraSwarm: Over 14,000 Dahua cameras compromised across Ukraine and Russia

Published on

Operation CameraSwarm:  Over 14,000 Dahua cameras compromised across Ukraine and Russia

Disclosure note: The relevant national CERTs were notified on 10 August 2026, and Dahua's PSIRT was notified regarding the issues that affect devices in all countries observed in this campaign. We appreciate Dahua PSIRT's engagement in reviewing parts of this research ahead of publication. Publication was held until 18 August 2026 under TLP:AMBER.

Between 17 June and 22 July 2026, a single operator compromised over 14,000 Dahua IP cameras. The scanning behind it was global: masscan sweeps ran against Russian address space first, then across the full IPv4 range, and the largest single haul actually landed in Mexican and Vietnamese ISP ranges before the operator's focus settled on Russian and CIS telecom netblocks. Where the confirmed, geolocated compromises concentrated was Ukraine and Russia, with Ukraine holding the largest share.

This is the second Dahua-related camera compromise operation we've traced back to an exposed operator directory in as many weeks. Where last week's investigation centered on a Russian-speaking operator running a purpose-built platform against 58 cameras, this one is a different scale entirely.

On 23 July, Hunt.io AttackCapture crawled the operator's own server and recovered their entire working environment, 2,616 files across 234 subdirectories, 407 MB, from an open HTTP directory they had exposed themselves. This report is built from that corpus and re-verified against additional Hunt.io telemetry. It covers three exploitation paths running in parallel, a persistent backdoor left on nearly 1,900 cameras, and a separate Windows stealer staged on the same host.

Key Findings

  • 14,530+ devices compromised in 35 days. The brute-force engine alone reached 12,324 unique addresses.

  • 1,923 cameras carry a persistent backdoor account (p2pwn / p2password), installed over RPC after either bypass. It is stored independently of the admin password and survives a password change and, on most firmware, a factory reset.

  • 283 cameras were reached by serial number alone through Dahua's cloud relay, with no device address and, in most cases, no credentials. The operator's own code records that 89.4 percent of live serials required no authentication.

  • Offline recovery codes grant cloud-level administrative access by serial number, independently of device credentials. This transferable-access design is the strongest indicator the toolkit was built to hand access to a third party.

  • A second, unrelated capability was staged on the same host: a UPX-packed Windows binary classified as SalatStealer, with a five-method Defender bypass script.

  • The toolkit is assembled, not authored. Its components are credited to at least six upstream developers. Running these tools establishes use, not authorship.

Two of the CVE identifiers running through the operator's tooling do not hold up on inspection. Both techniques work as described, but the labels are wrong, and both recur below, so they are worth clearing up first.

A Note on CVE Identifiers

Two CVE identifiers used throughout the operator's tooling do not survive verification. We state both here because they recur below.

CVE-2024-39943 is the label p2pwn attaches to its persistent-backdoor technique. That identifier belongs to an unrelated command-injection flaw in Rejetto HTTP File Server, a different vendor and product family. The technique is real and verified from source; the identifier is wrong. We could not determine whether a correct one has been assigned.

CVE-2025-31702 is inherited from the upstream researchers whose proof-of-concept underlies the relay component. Dahua's advisory for that identifier describes something far narrower: a post-authentication privilege escalation (CWE-732, CVSS 6.8, not in CISA KEV) requiring valid credentials. That is not the unauthenticated relay abuse documented under P2P Relay Exploitation below.

With the CVE labels cleared up, the report starts where our visibility did: the server that exposed itself.

Discovery

AttackCapture flagged 154.86[.]119.60 for activity on TCP/37777 (Dahua's Easy4IP management protocol) and crawled the host's open HTTP server on 23 July 2026 at 11:45 UTC, recovering 2,616 files across 234 subdirectories.

The exposure traces to one command, preserved in shell history and executed from /root:

cd /root && sudo python3 -m http.server 80 --bind 0.0.0.0

                
Copy

It appears twice in succession. Immediately before it, the history records three escalating full-internet masscan sweeps against port 37777 (70,000, then 120,000, then 150,000 packets per second), confirming the operator was still tuning scan rate when the filesystem was exposed.

File timestamps across much of the corpus cluster around 23 July at 17:06 UTC, close to the crawl. We cannot determine whether this reflects an environment reset or an artefact of collection, and note the timing without attributing a cause.

SSH fingerprint telemetry establishes that the server predates the campaign. We recorded three operating-system generations on this address, the earliest from April 2025, over a year before the operation began. The infrastructure was repurposed, not provisioned.

Knowing when and where the server was crawled sets up the sequence the operator ran before it.

Campaign Timeline and Scope

Shell history and file timestamps together reconstruct the operation's sequencing, which network telemetry alone would not reveal.

Figure 1Figure 1. Campaign timeline: VPS activation through discovery, 17 June - 23 July 2026.

Setup and reconnaissance opened the campaign on 17 June. The brute-force engine deployed the next day and stayed active throughout. The operator first tested the relay workflow on 25 June, nearly three weeks before deploying the CVE chain on 12 July. The largest wave ran on 22 July; within a day the host underwent an apparent environment change and its working directory was exposed.

Exploitation pathDevices reached
Port 37777 credential brute-force (asyncio engine)12,324 unique IPs / 13,229 total records
CVE-2021-33044 / CVE-2021-33045 bypass chain1,923 cameras, each backdoored
P2P relay, no device address required283 cameras
Total unique devices14,530+
Table 1. Campaign scope by exploitation path.

Thirteen p2pwn executions produced campaign folders named RU, RU2, RU3, RUNODIN, UA, UA1, UA2, UA4, UA5, dom, 123, and a final consolidated run. Targeting weighted toward Ukraine: the Ukrainian runs hold the largest share of results, and UA5 output shows the DH-IPC-K35P as the most frequently compromised model.

The timeline shows the order of operations; the mechanics of how each device was reached come next.

Attack Chain

Two independent paths run in parallel. The IP-layer path reaches cameras with routable addresses. The cloud path reaches cameras behind NAT using only a serial number, invisible to any address-based scanner.

Figure 2Figure 2. Attack chain: the four-stage IP-layer path and the three tracks branching from it.

The upper sequence runs once. The operator provisions the VPS, scans for port 37777 with masscan while filtering by local time of day, brute-forces credentials against responsive addresses, then applies the two bypass CVEs to survivors. Three tracks then run independently: Track A abuses the cloud relay by serial number, Track B enumerates serials and generates recovery codes, Track C is the Windows pivot, a separate capability unrelated to camera exploitation.

The orchestrator feeds the serial tracks, sn/_pipeline_runner.py, hard-codes TARGET_HITS = 5 and MAX_ROUNDS = 20, importing the prefix-ranking and Shodan-harvest modules directly. Each round widens the candidate window by 2,000 from a base offset of 8,000 until five live devices are found or twenty rounds elapse.

Each stage in that chain starts from the same place, deciding which devices to hit at all.

Reconnaissance and Target Selection

Internet-Wide Scanning

masscan sweeps TCP/37777 in two phases: Russian address space at ten million packets per second, then a global IPv4 sweep at a lower rate. The target list cidrs.txt is IPDeny's ru-aggregated.zone renamed. The checkpoint file records the parameters:

seed = 9870541109226383968
rate = 10000000.000000
adapter-ip = 154.86.119.60
adapter-port = 57280-57296
ports = 37777
range = 0.0.0.0-255.255.255.254

                
Copy

This confirms the host was the live scanning source, not a passive collection point.

Time-of-Day Filtering

Before scanning any block, a companion module samples an address in it and skips the block entirely if local time falls outside business hours.

Figure 3Figure 3. geolocation.py: CIDR blocks are skipped unless local time falls within 09:00-16:59.

A separate post-capture filter discards dark or featureless frames after a snapshot is taken. Together the two most plausibly serve one goal: avoiding scan and storage cost on scenes unlikely to yield a usable image. The behaviour is equally consistent with suppressing an off-hours traffic signature. The code does not distinguish the motives.

Serial-Number Harvesting

shodan_collect.py harvests serial numbers without contacting a camera, issuing twelve Shodan queries and reading DDNS subdomains from dahuaddns[.]com, where cloud-registered cameras auto-register as {serial}.dahuaddns.com:

SHODAN_QUERIES = [
    'hostname:".dahuaddns.com" "Serial Number"',
    'hostname:".quickddns.com" "Serial Number"',
    'hostname:".dahuaddns.com" port:37777',
    'hostname:".quickddns.com" port:37777',
]
SHODAN_P2P_QUERIES = [
    'product:"Dahua" "Serial Number"',
    '"Dahua Technology" port:37777',
    'http.html:"Serial Number" "Dahua"',
    'http.title:"WEB SERVICE" "Dahua"',
    # + 4 further Dahua/P2P banner variants
]

                
Copy

The ranked output at crawl time is modest. Only two prefixes carried live-probe scores: AH1058FPAG led at 0.0055, carried from an earlier scan that had already found 2,246 live devices, while 5E00107PAJ scored zero across a 32-sample probe. This is an operational snapshot, not a mature target list.

A Third Discovery Channel

CCTV-Scanner-main/, publicly attributed to github.com/ipxobd/CCTV-Scanner, contains no exploitation code. It fingerprints Dahua and Hikvision devices over HTTP by page title and probes RTSP, port 37777, and Hikvision's port 8000. It sits earlier in the pipeline than any of the operator's own tools.

Once a target list existed, the first path against it was the oldest one, guessing credentials at scale.

Credential Brute-Force Engine

The engine is a purpose-built asyncio framework, publicly available as asleep_scanner under the handle d34db33f-1007; the operator's copy links to that repository from its bundled README. Russian developer comments run throughout, in the register of someone documenting fixes to their own production failures.

Concurrency and System Tuning

Before opening a single connection, the tool raises Linux file-descriptor and connection-tracking limits directly.

Figure 4Figure 4. async_brute.py: kernel limits raised before scanning. The Russian comment reads "without this, thousands of connections are silently dropped by the kernel."

The comment is diagnostic rather than explanatory: the author is recording a failure they already hit in production. Concurrency is adaptive: a default of 300 workers auto-scales to a hard ceiling of 4,000 for lists exceeding one million hosts.

Figure 5Figure 5. async_brute.py: bounded queue and sentinel shutdown, giving O(concurrency) memory rather than O(hosts).

The bounded queue is the design decision that makes internet-scale lists viable: memory stays constant regardless of target count, and one sentinel per worker guarantees race-free shutdown. This is engineering discipline, not script-kiddie assembly.

Protocol Implementation

dahua.py implements Dahua's Easy4IP binary protocol on TCP/37777 from a reverse-engineered packet structure: opcode 0xA0 for login, 0xA8 for channel enumeration, 0x11 for snapshot capture, and a shared 0xA4 query opcode distinguished by a sub-code byte.

Figure 6Figure 6. dahua.py: hand-built Easy4IP login packet and response parsing, including lockout detection after five attempts.

Lockout detection matters operationally. By reading the 0x01/0x04 response pair the engine abandons a host after five failures rather than burning the account, preserving the device for a later CVE pass. The synchronous snapshot controller closes sockets with SO_LINGER(1,0) to force an immediate RST; the async path does not.

Snapshot Quality Filtering

Captured frames pass a two-stage quality filter before delivery.

Figure 7Figure 7. snapshot.py: mean-brightness and Canny edge-density filtering discard blank or offline camera views.

A frame is discarded only when it is both dark and featureless; dark-but-detailed frames are retained. The operator is filtering for scenes with usable content, not merely for working cameras.

Exfiltration

Every credential hit that survives filtering is posted immediately to a Telegram channel.

Figure 8Figure 8. bot.py: Telegram notification template with a hardcoded VKontakte community link.

Two details carry weight. The message embeds a hardcoded VKontakte community link, and delete=False at line 147 means the rmtree() cleanup never executes: every captured JPEG is retained on disk alongside the credentials.

Enterprise-Format Export

A separate module batches credential hits into SMART PSS-compatible XML, Dahua's own enterprise camera-management platform.

Figure 9Figure 9. export.py: credential records serialised into SMART PSS import batches; 13,229 records produce 52 XML files.

The 255-device split is SMART PSS's own per-import ceiling, so the output is built to load directly into the vendor platform. This indicates intent to manage the device population at scale through enterprise tooling. It does not by itself distinguish personal use from handoff, since either would benefit from the format.

Operational Results

Recovered logs cover 24 runs between 17 June and 13 July. More than a third returned nothing: nine cracked zero devices, five within a single forty-minute session against the same list. Across the twelve productive runs we estimate roughly 13,000 device credentials were recovered. Hit rate is volatile even against an identical list: one evening's three successive attempts against the same ~55,000-host list returned 0, then 0.044, then 0.38 percent.

Targeting shifted over the campaign. The largest single haul, on 18 June, skewed toward Mexican and Vietnamese consumer ISP ranges; by late June the dominant geography was Russian and CIS telecom netblocks. The device population is broader than DVRs: door-entry intercoms, a PC-based NVR software client fingerprinted identically to hardware, and OEM-rebranded lines (RVi, ST-XVR, QVC, AC-D, SNR) all speaking the same protocol.

Evidence of a Private Fork

The operator's build has diverged from the public project. Its README documents an inverted default for snapshot capture and omits two undocumented performance flags present in the code. Three of four defined Dahua opcodes are dead code the pipeline never calls, superseded by a leaner async reimplementation. The largest early hauls are missing their SMART PSS exports, most plausibly because that step had no error handling and failed silently on large result sets before an early-July fix.

Brute force only reaches devices with a weak password, so the operator carried a second path that needed no password at all.

Authentication-Bypass Chain (p2pwn)

p2pwn is a compiled Go binary chaining three authentication vulnerabilities. Unlike the brute-force engine it requires no working password, only an unpatched camera.

Figure 10Figure 10. p2pwn config.toml: the embedded default configuration, matching the operator's campaign settings.

The configuration is worth reading closely. p2pwn toggles only the two bypasses and the backdoor installation. The relay tooling is not one of its methods: that capability lives entirely in the separate relay toolkit, with its own launcher and development history. The two paths are structurally distinct, not options within one tool.

The Two Bypasses

CVE-2021-33044 exploits unconditional trust in clients identifying as NetKeyboard hardware controllers: when clientType is NetKeyboard, the password field is never evaluated. CVE-2021-33045 exploits the firmware reading the claimed source address from the request body rather than the TCP connection.

Figure 11Figure 11. Both payloads side by side: the NetKeyboard hardware-trust bypass and the loopback source-address spoof.

Each returns a full administrator session unauthenticated. The first sends the literal string Not Used as the password; the second claims to originate from 127.0.0.1. Both execute in under a second against unpatched firmware.

Persistent Backdoor Installation

After either bypass, p2pwn installs the account p2pwn / p2password over RPC. Because it is stored independently of the admin password, it survives a password change and, on most firmware, a factory reset. This is the technique the tool mislabels CVE-2024-39943, crediting the exploit to 0x5477.

Source Analysis

A later crawl recovered the Go source. p2pwn is not a single exploit but three attack surfaces multiplexed over one tunnel: a raw SDK channel for the bypasses and backdoor; an HTTP CGI channel proxying to the device's embedded web server; and a JSON-RPC console channel mimicking Dahua's internal NetKeyboard protocol.

That third channel reaches beyond the device under attack. After login it runs OnvifUser -u to dump ONVIF credentials and retrieve passwords the camera has cached for talking to an NVR, a lateral credential-harvesting step. The SDK credentials hardcoded in this source are byte-identical to those recovered from the relay toolkit, confirming both codebases draw on the same recovered Dahua material.

Shell history shows the tool built from two locations: an unzipped archive in /root, and a git clone inside /var/xdwd/ created immediately before the Windows binary was renamed. That second directory sits outside the served web root and is invisible to the crawl.

Campaign Results

Folder names are applied after the fact, based on where compromised devices geolocated; the scan itself varies only by serial prefix. Of roughly thirteen invocations, eleven produced folders that persisted to the crawl.

FolderDevicesDominant modelMechanism
RU31DH-IPC-K15PCVE-2021-33044
RU244DH-IPC-K35PCVE-2021-33044 + backdoor re-entry
RU330IPC-K35CVE-2021-33044
RUNODIN77DH-IPC-HDW1531SP-0280BCVE-2021-33044
UA301DH-IPC-K35APCVE-2021-33044 + backdoor re-entry
UA120DH-IPC-A35PCVE-2021-33044
UA2~640 (partial)DH-IPC-K35PCVE-2021-33044 + backdoor re-entry
UA410IPC-C46CVE-2021-33044 + CGI credential brute
UA5~690 (partial)DH-IPC-K35PCVE-2021-33044 + backdoor re-entry
dom0n/aDry run, no hits
12379DH-IPC-K35APCVE-2021-33044
Table 2. p2pwn campaign folders recovered from the open directory.

Roughly 1,900 compromises across eleven runs in a nineteen-day window. No folder named UA3 or a combined run exists on disk despite both appearing in shell history; whether through failure or cleanup, those runs left no recoverable trace. UA4 carries a tag seen nowhere else, CGI Brute, confirming a plain HTTP fallback alongside the CVE path.

One pattern matters for defenders. The non-dictionary password I0949488055 recurs across eleven distinct devices and addresses in the UA run alone; VIT0662082161 and 01131418s recur ten times each. This is consistent with a single installer setting the same custom default across many independent client deployments: the operator's wordlist already held it and matched immediately rather than iterating.

Upstream Attribution

p2pwn's THANKS.md credits four authors: the P2P protocol implementation (khoanguyen-3fc), a PTCP tunnel variant (MaximilianoFelice), the serial-generator algorithm (itres-labs), and the backdoor exploit (0x5477). The tool's author assembled it from four other people's research, a distinction worth preserving when weighing what running it implies about writing it.

Both paths so far assume a routable address, and the third abandons that assumption entirely.

P2P Relay Exploitation

Dahua's cloud relay, easy4ipcloud[.]com:8800, lets any application reach a camera behind NAT using only its serial number. Authentication to the relay depends on credentials hardcoded identically into every Dahua client ever shipped. ITRES separately documented this unauthenticated serial-enumeration path as a non-CVE issue, distinct from CVE-2025-31702, which Dahua's advisory covers as an authenticated recovery-secret flaw.

Figure 12Figure 12. The fixed Dahua SDK relay identity and WSSE authentication scheme, embedded in every shipped Dahua client.

Presenting these values authenticates the caller as a trusted Dahua application. The same AES material reappears independently in dh-p2p-test/, an unrelated protocol-research project on the same server; its presence in two independently-obtained codebases confirms this is Dahua's own recovered cryptographic material, not something the operator generated.

Figure 13Figure 13. Session-key derivation: PBKDF2-HMAC-SHA256 over device credentials, 20,000 iterations, feeding AES-256-OFB.

This second layer engages only where device-level authentication is required. The distinction matters: the fixed key above reaches the relay, while this derived key encrypts connection metadata for the device itself.

The Three-Phase Serial Attack

The serial brute-forcer probes each candidate before spending credentials on it. The recorded branch statistics are the single most consequential result in this component.

Figure 14Figure 14. brute_serial(): device-state probe, per-device salt retrieval, then the credential loop. The operator's own comment records that 89.4 percent of live serials return an open, no-authentication channel.

Phase 1 alone resolves the overwhelming majority of targets: nearly nine in ten live serials return an open channel requiring no credentials at all. Only the remainder proceed to salt retrieval and the credential loop. The scale of this campaign rests less on cracking passwords than on the fact that most exposed devices never demand one.

Observed Relay Behaviour

Live testing is recorded in brute_p2p.log. Against eight serials under one prefix, seven distinct backend addresses answered rather than a single shared server:

[5E00107PAJ01FFF] P2P SERVER - 165.154.164[.]157:8803
[5E00107PAJ02C6E] P2P SERVER - 165.154.165[.]25:8803
[5E00107PAJ06CD2] P2P SERVER - 165.154.165[.]24:8803
[5E00107PAJ0892A] P2P SERVER - 128.14.224[.]137:8803

                
Copy

Across sampled runs the lookup touched at least eight backend addresses: 165.154.164[.]157, 165.154.165[.]25, 165.154.165[.]24, 165.154.164[.]186, 165.154.164[.]52, 128.14.224[.]137, 165.154.165[.]174, and 165.154.198[.]11. Port 8803 is known only from the operator's own logs; our passive scanning has never observed it open on any of these addresses, so this function rests on the operator's traffic rather than external corroboration.

Why These Addresses Are Not Indicators

We deliberately exclude these nine addresses from the indicator tables. Seven, plus 45.192.10[.]33, sit on AS135377 (UCLOUD). The subdomain naming on easy4ipcloud.com (p2pauth-fk, devaccess-fk) maps directly onto these hosts' Frankfurt geolocation, consistent with genuine shared Dahua relay capacity.

Three of them share byte-identical response fingerprints across the same unusual port set: an Uptime Kuma dashboard, a Basic-authenticated proxy, and a bare SOCKS4 listener. That is not the signature of independent customers sharing a provider; it is one monitoring-and-proxy image deployed across several rented hosts. 45.192.10[.]33 is the genuine outlier, showing a rotating cast of unrelated third-party domains, the actual profile of multi-tenant hosting the other eight only superficially resemble. A ninth, 165.154.198[.]11, sits on AS142002 and has shown almost no activity since March 2025.

Publishing this pool as adversary infrastructure would cause defenders to block Dahua's own legitimate relay traffic and break camera connectivity for real customers. The absence of any independent malicious tag across all nine is the finding, not a gap in coverage.

The scannerdahua Toolkit

scannerdahua chains three techniques. It first attempts the bypasses over HTTP; on success it shells out to a locally-held copy of the public Ingram DahuaConsole toolkit; only if both fail does it drop to a sixteen-pair default-credential brute. Its keyspace arithmetic is explicit: seven ten-character prefixes, each brute-forced across a five-hex-digit suffix: 1,048,576 candidates per prefix, roughly 7.3 million serials.

A shell script alongside it, run_auth_brute.sh, carries an explicit instruction in its own comment: run this only on a rented VDS, never on the local machine. This is the clearest evidence in the corpus that the operator separates their personal machine from the execution host. That separation did not hold.

Figure 15Figure 15. A Windows-side test log exposing the operator's development username and local path, recorded while the Linux VPS had been scanning the same prefix for hours.

The log leaks both the Windows username SystemX and the full development path. It was produced by a single-serial test run from the operator's own laptop, precisely the machine run_auth_brute.sh warns against using.

An Actively Running Directory

A third copy of the serial brute-forcer sits in /cve-project/, wired to a live launcher rather than merely stored.

Figure 16Figure 16. start_all.sh: eleven hardcoded prefixes launched in parallel, 220 concurrent serial slots against the Dahua relay.

The script kills any stale run, clears prior output, and launches eleven parallel processes staggered by half a second to avoid a connection burst. Both result files were empty at crawl time: AttackCapture captured this directory mid-run.

A Development Lineage in Three Languages

Comparing the three copies reveals a progression invisible from any single file. The original is entirely Spanish-commented, with a hardcoded thread count and no lock around its output writes, a genuine race condition. The /cve-project/ copy is a hand-edited refactor: configurable threads, a lock around the writes, faster timeouts, and Russian runtime strings inserted into still-Spanish code:

print(f"[{num:06d}] Probando {candidate_serial}... | Найдено: {found_count[0]}")

                
Copy

Run without arguments, the same copy prompts entirely in Russian: "How many threads? (default 30)". A third copy is fully Russified. These are three stages of one person progressively rewriting a borrowed tool, one language layer at a time.

The same toolkit also recovers stored device passwords outright, through a routine that derives its decryption key entirely from values the attacker already holds, device class prefix and serial number, so no device secret is needed. A residual Spanish comment in that code confirms it came from the same upstream source as the original brute-forcer.

A Fourth Lineage and the Orchestrator

A previously undocumented fourth lineage exists in English, citing khoanguyen-3fc/dh-p2p in its docstring. Its MIT licence credits a publisher calling themselves Purple Ghosts, a name appearing nowhere else in the operator's code, suggesting it belongs to the upstream author.

scannerdahua/scanner.py is the complete automated pipeline for a given prefix: it enumerates candidate serials, probes each against the relay, and for every live serial opens a local tunnel and runs the bypass chain, attempting a no-auth login and a short credential list before full exploitation. It maintains resume state per prefix; one log shows a scan resuming after more than 9,000 serials had already been probed.

Deep Dive: The PTCP Tunnel Protocol

The independently-sourced dh-p2p-test/ project on the same server allows the relay exploitation to be described at protocol level rather than merely as "it uses hardcoded credentials."

Packet Format and Handshake

Every PTCP packet carries a fixed 24-byte header: the ASCII magic PTCP followed by four big-endian 32-bit fields tracking bytes sent, bytes received, a packet identifier, and a message-ID pair used for acknowledgment. A one-byte type field selects purpose: 0x00 SYN, 0x10 tunnelled data, 0x11 bind request, 0x12 connect notification, 0x13 heartbeat, and 0x19/0x1a/0x1b forming an authentication exchange.

A client first contacts Dahua's cloud broker to resolve a serial to a dedicated P2P server, then a relay, then an agent server that issues a session token. Only then does it attempt to reach the device, using a technique the recovered Wireshark dissector labels Inverted STUN: a STUN-shaped NAT-traversal packet with every byte bit-flipped before transmission.

What "Bind" Means

A 0x11 frame always targets 127.0.0.1 on the device itself, instructing the camera to open a connection to itself on a given port and return the bytes. This is why one implementation can tunnel RTSP on port 554 or Dahua's management port with a one-line change. The operator's tooling is exactly this: a fork binding port 37777 instead of 554, pushing a hand-built login packet through the tunnel, reaching a camera's management interface with no routable path to the device.

The Trust Model

The device never authenticates the connecting party. It authenticates the session, via a token the cloud issued before the device was contacted. Obtaining that token requires only the fixed SDK credentials shared by every legitimate Dahua application. The only real barrier to reaching any camera through this path is knowing its serial number, precisely what the operator's harvesting pipeline exists to produce at scale. Reaching a device this way is not the same as controlling it: the relay opens a path to the device's management interface, but taking control from there still requires either valid credentials or one of the authentication-bypass flaws (CVE-2021-33044 / CVE-2021-33045) documented earlier. Dahua confirmed this distinction directly. What makes the relay path matter at scale is that, per the operator's own logging, most exposed devices returned an open channel that demanded neither.

Cryptography, Precisely

Three distinct mechanisms are in play, and conflating them would misstate the protocol. A fixed AES-256-OFB key and IV, the same pair embedded in every Dahua client, decrypts exactly one thing: a JSON blob returned by the cloud containing a per-device salt. A session-specific AES-256-OFB key, derived via PBKDF2 from device credentials, encrypts connection metadata only where device authentication is required. Every request to the cloud broker is signed rather than encrypted, using WSSE HMAC over the same fixed credentials.

None of the three protects the tunnelled application data once the connection is established; that traffic flows as plain TCP bytes wrapped in PTCP frames. One gap remains: p2pwn's own Go implementation could not be retrieved, so its wire-level behaviour is documented from its README and from independent reference implementations rather than the binary.

Reaching a camera by serial is one thing; the same serial also unlocks a more durable form of access.

Offline Recovery-Code Generation

seria2/asfefwq.py replicates part of Dahua's own account-recovery flow offline. It generates candidate serials, tests each against the relay, and derives a recovery code for every live device.

Figure 17Figure 17. The liveness check: a serial is resolved against Dahua's cloud broker, then probed on the returned P2P server.

A code is generated only for a serial that passes this check. Dead candidates are logged as scanned and discarded, never assigned a code: the operator is building a verified inventory, not a speculative list. A near-complete recovery of the file (96.9 percent) shows it is not a script but a full terminal-UI application with a six-item menu.

The derivation is entirely offline and deterministic. Given a serial, the code follows without any interaction with the device, which is what makes the output transferable.

Figure 18Figure 18. auth_codes.json: generated recovery codes, each granting an administrative reset through Dahua's cloud.

Each entry is redeemable through Dahua's standard password-recovery flow without knowledge of the current credentials. Runs write to a dated per-prefix directory holding an append-only serial list, a JSON code map, and XML credential files batched sixty-four at a time.

We looked specifically for a Telegram or Discord exfiltration step comparable to the brute-force engine's and found none. Every network call is a read-only liveness check; all output stays local. The hardcoded console title CCTV Scanner | discord.gg/cctv indicates the tool was sourced from a Dahua-exploitation Discord community rather than written from scratch, and functions purely as a static banner.

A companion Russian-language target list maps real Dahua and OEM-rebranded models to serial prefixes, tiered by observed hit rate. Its comments note that the same scan works regardless of brand: Amcrest, Lorex, Annke and Swann cameras all sit on Dahua's cloud backend beneath the rebrand. It also maintains a disabled tier of zero-yield prefixes, content byte-identical to a previously unexplained stray file elsewhere on the host, resolving an open question about its purpose.

Everything to this point concerns the cameras, but the same host held a capability that has nothing to do with them.

Windows Pivot (Unrelated Capability)

Shell history records mv xeno.exe 1.exe, executed immediately after building a second copy of p2pwn outside the served web root, and immediately after that build was used for a final combined run against eight validated prefixes.

Raw header bytes confirm a genuine UPX-packed 32-bit Windows executable rather than merely a sandbox label: MZ/PE signature, sections named UPX0, UPX1 and UPX2, the packer's version marker, and a compile timestamp zeroed to 0x00000000. The shell history is unbroken but contains no download, build, or transfer step for this file before the renaming.

A Second, Independent Sighting

The identical file (same SHA-256, same byte size) appeared on 185.132[.]53.56 two days later, still under its original name. That host's entire recorded shell history is 153 bytes:

apt install screen
kill -9 968]
kill -9 968
apt install screen
kill -9 2736
apt install screen
dpkg --configure -a
apo
apt install screen
screen -R scan

                
Copy

The final line matters. This host's screen session is named scan, the same non-default name used on the primary host. Two unrelated parties independently choosing an identical custom session name is unlikely.

Set against that, the host's longer-term profile shows genuine personal hosting on a different provider (AS211507): YunoHost then HestiaCP running an ordinary mail and web stack for oxycod[.]one since September 2025, plus a .bashrc.original backup implying a pre-existing configuration was preserved. Port 37777 appears for a single four-hour window on 22 July 2026.

Assessment (moderate confidence): a pre-existing server, likely belonging to someone else, briefly taken over on 25 July to restage the same payload, neither a fully independent victim nor a host built from scratch. The filename xeno.exe most plausibly reflects how the file is distributed upstream of both incidents.

What that binary actually is turns out to be less certain than its filename suggests.

Malware Family Assessment

The pre-rename filename implies XenoRAT. The only sandbox classification attached to the file, on both hosts, is SalatStealer. In our indexed reporting these are materially different families: every XenoRAT sighting ties to nation-state espionage against named government and financial targets, never to opportunistic IoT-adjacent activity. SalatStealer is a Go-based, UPX-packed commodity credential and cryptocurrency stealer sold as a service.

The balance of evidence favours SalatStealer, though this is an inference from technique fit rather than direct confirmation: no sandbox report, extracted configuration, or independent corroboration beyond the original tag exists for this hash. A third tagged technique, root certificate installation (T1553.004), is explained by neither family and remains unresolved.

Its presence is weaker evidence of an actor profile than it first appears. Our reporting shows SalatStealer resold across at least five unrelated campaigns: a Russian-aligned actor phishing Ukrainian government targets, a loader operation, a fake-videocall credential-theft crew, an AI-generated cryptominer campaign, and distribution through pirated software. None shares infrastructure with this one. This binary is a second, distinct capability staged on the same host, and organisations tracking this operator should treat it as a separate incident rather than folding it into the Dahua findings.

Whatever the family, it did not arrive alone, and the script staged beside it says something about the intended target.

Defender Evasion

A PowerShell script staged alongside the binary disables Defender coverage across an entire volume.

Figure 19Figure 19. add-defender-exclusions.ps1: five independent Defender exclusion methods, including Group Policy persistence.

The five methods are deliberately redundant: user-context PowerShell, a full-list override, CIM/WMI to bypass constrained language mode, a SYSTEM-context scheduled task, and a Group Policy registry key surviving reboots and Defender updates. Targeting C:\ wholesale, and reaching for Group Policy at all, indicates an intended enterprise victim rather than a home machine.

Infrastructure Analysis

Every finding above rests on the recovered corpus. This section asks a different question: what does the server itself reveal, independently of anything the operator wrote? We worked through the server data two ways: through Hunt.io's Threat Enrichment API and SQL layer, and through the portal interface. The two reconcile: total capture size is identical (407 MB) either way, with differences in file counts reflecting how each view handles duplicates and previewable items. We cite the API figures throughout, since they're independently reproducible.

Figure 20Figure 20. Hunt.io Info view for 154.86[.]119.60: port-level timestamps and automated risk classification.

The portal confirms current hosting attribution (Cloud Innovation Ltd, AS197574, Amsterdam) and supplies per-port dates the API does not surface. Port 80 has been open since 12 May 2022, over four years before this operator's activity. Port 443, carrying the certificate discussed below, first appeared on 8 January 2024. The "Telemt Panel" on port 8080 ran between 28 July and 1 August 2026. Port 443's banner reads QRATOR, so the camouflage extends to the HTTP layer, not the certificate alone.

Automated reputation scoring reads 0 High Risk, no active malware signals, with a single warning: Potential Malicious Open Directory, dated 23 July. Stated plainly: every substantive finding in this report comes from reading the corpus. The platform's automated classification is conservative next to what that reading produced.

Ownership and Provenance

ASN attribution resolves two ways depending on the subsystem. The host carries a paired attribution: ExpressHost Ltd (AS197574) as the original RIR record, and Ferdinand Zink trading as Tube-Hosting (AS49581) as a newer routing-level re-announcement of the same prefix. Every other active address sampled across this /24 reports only the older label, indicating registration churn plus a data-freshness gap rather than contested ownership.

Tube-Hosting is a real, operating VPS reseller with dozens of live client subdomains. A third name, Cloud Innovation Ltd, appears in the portal for the same ASN; we found no technical link to this host. The reverse-DNS hostname has changed between observations, but the identical {adjective}-{noun}-{hash}.expresshost.cloud convention appears elsewhere in the same /24, a provider-wide scheme, not operator-specific.

SSH history shows three OS generations: Ubuntu 22.04 (April-May 2025), Ubuntu 24.04 (briefly, June 2026), and Debian 13 since 18 June 2026. None of these host keys, nor the current JARM, is distinctive enough to pivot: the April 2025 key matches over 67,000 unrelated hosts and the JARM over 2.5 million. The timeline holds; it simply cannot find sibling hosts.

The first recorded action was a speedtest at 17 June 2026, 11:24:08 UTC, measuring uplink capacity before configuring masscan's rate. The surrounding /24 is not exclusive to this operator: spot checks found a Russian-language Confluence phishing page with a Telegram-linked VPN app, a Kubernetes node, and a developer's portfolio site on neighbouring addresses.

Figure 21Figure 21. AttackCapture File Manager: corpus accounting and the platform's own unread Analysis Report.

This view counts 875 files across 125 subdirectories against the 2,616 items across 234 subdirectories cited from the API. The exact byte match either way (407 MB, 427,142,652 bytes) is what confirms these are the same crawl counted differently, previewable files against every captured item including duplicates.

Two scale details are worth keeping: the brute-force engine's log directory alone accounts for 371 of 407 MB, roughly 91 percent, and the file-level scanner tags 177 of the 875 files as malicious. The portal also advertises a platform-generated Analysis Report visible only in the interface; no API endpoint available to us returns its contents, so we have neither verified nor relied on it.

The rbc.ru Certificate

Certificate history on port 443 shows three unrelated states: an empty-subject certificate from early 2024, a self-signed CUPS default on port 631 in May 2025, and from 28 July 2026 a live certificate with subject rbc.ru (a major Russian financial media group), byte-for-byte identical to the one on RBC's genuine Qrator-fronted edge.

Figure 22Figure 22. SSL History for 154.86[.]119.60: the certificate's observation window and claimed issuer.

The recorded issuer, Let's Encrypt, cannot reflect genuine issuance to this host: Let's Encrypt issues only after validating domain control, and nothing indicates this server controls rbc.ru. The consistent reading is that the issuer field was copied wholesale alongside the subject and the certificate bytes.

A direct query against our certificate database found this exact hash on more than 11,000 separate address observations worldwide, beginning 28 July and still accumulating, across a long tail of proxy-typical ports: 443 and 8443 dominate, but 1080 (SOCKS5), 9443, 2053 and 2083 each appear.

Figure 23Figure 23. Pivots for 154.86[.]119.60: the cloned certificate's address-specific count, and a second unidentified hash.

This host alone accounts for roughly 2,000 observations of the cloned certificate, last seen 3 August 2026. The same list surfaces one item we cannot explain: a second x.509 hash, DC2C5491AFDC25A2A5588C2BA2D1F054B0E2BDF4C925304FA0825F25F9D57DC8, with roughly 11,000 observations last seen 22 April 2024, over two years before this campaign. We flag it as an open item rather than speculate.

Decisively, dozens of unrelated addresses including this one presented the certificate a full eighteen hours before RBC's genuine edge appears in the same dataset. That timing and scale rule out targeted private-key theft.

Comparison Against the Genuine Edge

The genuine RBC edge provides a control against which the clone can be measured.

Figure 24Figure 24. Info view for 178.248.238[.]247, RBC's genuine Qrator-fronted edge.

It shows a completely clean reputation (0 High Risk, 0 Warnings), with ports 80 and 443 open continuously since December 2022 under the same QRATOR banner: long-standing, actively maintained infrastructure.

Figure 25Figure 25. Domains resolving to the genuine edge: exactly two, both legitimate RBC properties.

Only rbc.ru and app.rbc.ru resolve here, neither carrying a malware or IOC Hunter tag, consistent with a dedicated edge rather than shared hosting, even though the underlying block is anycast.

Figure 26Figure 26. Pivots for the genuine edge: an entirely different JARM fingerprint despite the identical certificate.

The genuine edge's JARM (114,000-plus observations) shares no relationship with any value recorded on the operator's host. Identical certificate bytes with unrelated TLS fingerprints is exactly the signature of a certificate captured and replayed as a static file, rather than a server stack being cloned. The same list shows the chain running through Let's Encrypt production intermediates, and surfaces a curator.pro subject on the same anycast address: Qrator Labs' own product domain sharing infrastructure with its customer.

SELECT
    *
FROM
    ip
WHERE
    tls.cert.subject.common_name = 'curator.pro'

                
Copy
Figure 27Figure 27. SQL pivot on the curator.pro certificate subject: two results, both unrelated to this campaign.

The pivot returns exactly two addresses (the anycast address itself and one unrelated host at Leaseweb Germany), confirming ordinary shared-anycast behaviour rather than a new lead.

The Telemt Panel

On the day the certificate first appeared, a second service surfaced: a React single-page application on port 8080 titled Telemt Panel. Searching our database for that exact title produced corroboration from two unconnected directions. The identical title is fingerprinted on an unrelated address elsewhere on the internet. Separately, DevOps logs from a different operator refer repeatedly to telemt as a Rust-based proxy component underlying a Telegram MTProto circumvention stack called MTProxyMax, binding port 443, with a documented memory leak in version 3.3.28.

Read together with the certificate evidence, the coherent explanation is that Telemt Panel is the management interface for exactly this kind of tool, and the rbc.ru certificate is that tool's default masquerade identity, not something this operator captured or built, and not a sign of compromise at RBC or Qrator (moderate to high confidence). The same pattern appears on another address in this /24, presenting a byte-plausible clone of Yahoo's production certificate.

Searching for Sibling Infrastructure

We searched for other hosts running this toolkit: by file hash, by distinctive source strings, and by malware tag. The p2pwn binary and the SalatStealer-tagged binary both remain confined to the two hosts documented here, and no other address in the primary host's /24 has ever been crawled into AttackCapture.

Two other hosts run asleep_scanner, but neither belongs to this campaign. One, first seen a year earlier, hosts the public source tree inside a broader red-team toolbox (Meterpreter, GitHack, a Heartbleed scanner), consistent with the tool author's own development host. The other is a larger multi-CVE exploitation and DDoS platform reusing the Dahua protocol module for a single snapshot script, with result folders named for Ukrainian and Moldovan cities. It shares no credentials, CVE chain, or file hash with this toolkit.

Both neighbouring netblocks host unrelated malicious activity: a beaconing Sliver C2 implant with CRM credential-dumping scripts on the second host's provider, and an open directory of Chinese-origin red-team tooling (fscan, Vshell) on the relay pool's provider, on a different netblock from any relay address. Both confirm ordinary multi-tenant clouds with unconnected customers.

A Rare Fingerprint, Checked and Ruled Out

One JARM value recorded during an earlier certificate era has only two observations across all of our data, a genuinely low-cardinality pivot, unlike the generic value dismissed above.

SELECT
    *
FROM
    ip
WHERE
    jarm.fingerprint = '20d14d20d21d20d00020d14d20d20d164ecea2ac2b4ce60f02137d3925f7d0'

                
Copy

Output example:

Figure 28Figure 28. SQL query for a rare, two-observation JARM fingerprint, matching exactly one other address worldwide.

The only other match is 159.65.136[.]18, a DigitalOcean host in Singapore. A two-count match is meaningful enough to warrant direct investigation rather than dismissal.

Figure 29Figure 29. Info view for 159.65.136[.]18: an ordinary, clean DigitalOcean host.

It shows the same clean reputation as the genuine RBC edge, with SSH and HTTP open continuously since December 2022 and no warnings of any kind.

Figure 30Figure 30. Domains hosted on 159.65.136[.]18: unrelated Vietnamese small-business sites, none tagged.

Seven domains resolve here, all Vietnamese-language small-business sites (a pet and ornamental-plant retailer, a gardening-tools shop), none carrying a malware or IOC Hunter tag.

Figure 31Figure 31. Code Search: the address surfaces only in unrelated mass-scanner output captured from other operators' open directories.

The clearest evidence against a connection: the address appears as an ordinary open tcp 22 result inside two internet-wide scans sweeping DigitalOcean's 159.65.0.0/16 range, and in a target list for CVE-2024-4577, a PHP-CGI flaw unrelated to Dahua. All four files were captured from open directories belonging to other, unconnected operators.

Assessment: a live, frequently-scanned host that appears in whatever mass-scanning toolkit sweeps its subnet, not a sibling of this operator's infrastructure. We assess the shared 2024-era JARM as a coincidental artefact of a common base image, and are not adding this address to the indicator tables.

With the corpus and the host both accounted for, the observable indicators can be listed for defenders.

Indicators of Compromise

Network Infrastructure

IndicatorTypeContext
154.86[.]119.60IPv4Operator server, open directory, payload staging
185.132[.]53.56IPv4Second sighting of the identical payload; second operator touchpoint (see Windows Pivot)
easy4ipcloud[.]com:8800Domain:portDahua P2P relay endpoint; abused legitimate infrastructure, not itself malicious
vk[.]com/wall-163997495URLVKontakte community link hardcoded in the operator's Telegram notifications
dahuaddns[.]com / quickddns[.]comDomainDahua DDNS, serial enumeration targets; abused legitimate infrastructure
37777/TCPPortDahua Easy4IP binary management protocol
rbc[.]ruTLS subjectPresented on 154.86[.]119.60:443 with a spoofed QRATOR banner; a shared proxy-tool default certificate on 11,000+ unrelated addresses, not operator-specific
discord.gg/cctvDiscordCommunity handle in the recovery-code tool's console title
Table 3. Network indicators. The nine P2P relay backend addresses are described under P2P Relay Exploitation and deliberately excluded.

Host Artefacts

IndicatorTypeContext
p2pwn / p2passwordCredentialRPC-installed backdoor account on 1,923+ cameras. Primary victim-side artefact.
admin:admin, 666666:666666, 888888:888888, admin:admin123, default:tluafedCredentialsp2pwn's default brute-force list
AH1058FPAG, 3E02C9DPAA, 7K01F36PCA, 5D03CF3PAJ, 3K04B95PAG, 4J00809PAJ, 3L05AF4PAG, 5E00106PAJ, 5F009F7PAJ, 5K09CA1PAJ, 6E0933FPAZ, 3J03016PAG, 5H01308PAJSerial prefixesThirteen prefixes brute-forced, 1,048,576 candidates each
de03a0ae5c7aa0c237ae36a649875f986fd9701ac06857dd214054367ce5090cSHA-2561.exe / xeno.exe: SalatStealer (T1112, T1553.004, T1614.001); confirmed on two hosts
694bfbe44bcd9b4844e15294be74dafe86ff8ae40b8b1067f4dae70a6ef75da8SHA-256p2pwn compiled Go ELF
be2738c8a2beb55ac484d71c329381f3caabac80664283ad9a24dbab82b5b590SHA-256Operator's copy of the p2pwn source archive
cidrs.txtSource fileIPDeny Russia CIDR list, renamed, driving masscan targeting
Port 8080 "Telemt Panel"Live serviceVite/React SPA, observed 28 July - 1 August 2026
C:\Users\SystemX\Downloads\seriaWindows pathOperator development machine
rogue-orbit-ryzhuuxu.expresshost.cloudPTRProvider auto-naming convention, confirmed elsewhere in the same /24
oxycod[.]oneDomainLegitimate personal domain on 185.132[.]53.56; part of that host's pre-existing history
Table 4. Host-based artefacts.

SSH Fingerprint History

Fingerprint (SHA-256)OS / BannerObserved windowPort
570963ee5a1253832c80e95ebb34dca5fe0f6bf7437857dcae511fbb023ed109Ubuntu 22.04, OpenSSH 8.9p12025-04-15 - 2025-05-1022
e2d20dbe2b4f7762313acf42c278ef99d7d2ec8906b0a5280798ad4ca779266eUbuntu 24.04, OpenSSH 9.6p12026-06-07 - 2026-06-1720001
cba28f2b4f12cd3f0a222a3885bef70518872c0ca62798dc810b8caa3035cca7Debian 13, OpenSSH 10.0p22026-06-18 - present22
Table 5. SSH host key history for 154.86[.]119.60.

Serial Brute-Forcer Development Lineage

LocationSHA-256Stage
/CVE-2025-31702-main/dahua-sn-brute2.py7a963211a052a78899a8881d36b42b55690c02e95c1485f5d78d3cfdb3d2842aOriginal: all-Spanish, hardcoded threads, unlocked writes
/cve-project/dahua-sn-brute2.py083c9ee06ed2ee1bc0051358b3eeb3242f474eacea4c88b3b10b06d15389b753Mid-rewrite: mixed Spanish/Russian, configurable threads, locked writes
/cve-2025-31702/dahua-sn-brute2.py5f60e5b45ecd67a987bdb1173d6a8063aa20a7946377da4698aaba3ebe72d088Fully Russified
/cve-project/start_all.sh4a4a346df72c839272ea835e4717cd88f843333b3563e745e657895347ea3dbeActive launcher, eleven hardcoded prefixes
Table 6. Three stages of one operator progressively rewriting a borrowed tool.

Dahua P2P SDK Relay Material

These values are Dahua's own, embedded in every shipped client. Withheld here; available for verification or detection matching on request.

  • SDK relay application identity (USERNAME)

  • SDK relay application secret (USERKEY)

  • Global relay RANDSALT

  • AES-256-OFB key/IV decrypting the cloud's per-device salt blob

  • Static OFB IV for the session/credential-derived layer

  • Second SDK credential pair, found only in the operator's recovery-code tool

Upstream Attribution Handles

HandleRepositoryRole
thebadintegergithub.com/thebadinteger/p2pwnp2pwn: active use confirmed; authorship not established
d34db33f-1007github.com/d34db33f-1007/asleep_scannerBrute-force engine, linked from the operator's own copy
ipxobdgithub.com/ipxobd/CCTV-ScannerFingerprinting scanner, confirmed unrelated authorship
khoanguyen-3fc, MaximilianoFelice, itres-labs, 0x5477VariousCredited in p2pwn's THANKS.md as upstream research sources
"Purple Ghosts"MIT copyrightPublisher name on the upstream relay tool's licence, not the operator's identifier
t.me/asleep_cgTelegramChannel printed in the brute-force engine's startup banner
Table 7. Handles credited across the toolkit. Attribution of use, not of authorship.

MITRE ATT&CK Mapping

TacticTechniqueEvidence
ReconnaissanceT1595.001: IP Block Scanningmasscan at 10M pps against Russian CIDRs, then a global sweep, port 37777
ReconnaissanceT1596.005: Scan DatabasesDahua-specific Shodan queries and DDNS enumeration
Initial AccessT1190: Exploit Public-Facing ApplicationCVE-2021-33044, CVE-2021-33045, and the relay technique
Credential AccessT1110.003: Password Sprayingasyncio engine, adaptive concurrency to 4,000, port 37777
PersistenceT1136.001: Create Local AccountRPC-installed p2pwn account on 1,923 cameras
CollectionT1113: Screen CaptureSnapshot opcode 0x11 with brightness and edge-density filtering
ExfiltrationT1567.004: Exfiltration Over WebhookTelegram Bot API, credential and image delivery per victim
Defense EvasionT1027.002: Software PackingUPX-packed Windows binary staged before use
Defense EvasionT1562.001: Disable or Modify ToolsFive-method PowerShell Defender exclusion framework
Lateral MovementT1210: Exploit Remote ServicesRelay tunnel providing NAT-equivalent access; ONVIF credential dumping
(Unrelated capability)T1112, T1553.004, T1614.001Sandbox tags on the SalatStealer binary specifically, not host-wide
Table 8. ATT&CK mapping. The final row is scoped to the Windows binary alone.

Mitigations

  1. Audit Dahua camera account lists. Any camera reachable on port 37777 between June and July 2026 should be treated as a potential compromise. Look for an account named p2pwn and remove it. Because this chain performs a nine-call credential drain, assume every stored credential was exfiltrated and rotate accordingly.

  2. Disable P2P where not actively required. Cameras with P2P enabled are reachable by serial number alone, and the relay routes across a pool of backend addresses, so address-based blocking is not a reliable control. Disable P2P under Settings → Network unless in use.

  3. Apply Dahua SA-2021-0130. CVE-2021-33044 and CVE-2021-33045 have been patched since October 2021. Unpatched firmware is exploitable with no credentials in under a second. Port 37777 should not be reachable from outside the perimeter.

  4. Assume recovery codes remain valid. Codes generated in this campaign grant cloud-level administrative reset by serial number and remain usable until Dahua alters the derivation server-side. Removing the backdoor account does not invalidate them.

  5. Do not escalate the rbc.ru certificate as a bank-specific incident. More than 11,000 other addresses present the same certificate, on ports typical of proxy tooling. RBC and Qrator infrastructure shows no sign of compromise.

  6. Detection guidance. Login requests carrying "clientType": "NetKeyboard", or "loginType": "Loopback" with "ipAddr": "127.0.0.1", do not occur in legitimate Dahua client traffic. On Windows estates, monitor for Defender exclusion paths set to C:, CIM method invocations against MSFT_MpPreference, and unscheduled policy refreshes.

Summary

CameraSwarm was not a single clean build. The infrastructure predates the campaign by more than a year, and the toolkit is a patchwork of the operator's own code alongside at least four other researchers' published work, accumulated and rewritten over time. Two findings matter most for defenders: the 1,923 cameras carrying the p2pwn backdoor stay accessible until an administrator finds and removes the account, and the offline recovery codes stay valid until Dahua changes the derivation on its side. Removing the backdoor does not invalidate the codes.

We assess with moderate confidence that the toolkit was built to hand access to a third party, based on the transferable recovery-code design and the enterprise-format export pipeline. That is narrower than a confirmed commercial operation, which the evidence does not support. Worth remembering too: this operator is not the only party running these exploits or SalatStealer. Our own reporting ties the same NetKeyboard bypass to a separate Iran-aligned cluster, a Telnet-based Dahua DVR campaign with no CVE at all, and SalatStealer resold across at least five unconnected operations. Dahua exploitation is common ground, not one actor's signature.

→ The entire picture above came from a single open directory the operators exposed themselves, recovered and re-verified through AttackCapture. If you want to see how we surface this kind of attacker infrastructure before it goes dark, book a demo and we'll walk you through the pivots behind this campaign.

Disclosure note: The relevant national CERTs were notified on 10 August 2026, and Dahua's PSIRT was notified regarding the issues that affect devices in all countries observed in this campaign. We appreciate Dahua PSIRT's engagement in reviewing parts of this research ahead of publication. Publication was held until 18 August 2026 under TLP:AMBER.

Between 17 June and 22 July 2026, a single operator compromised over 14,000 Dahua IP cameras. The scanning behind it was global: masscan sweeps ran against Russian address space first, then across the full IPv4 range, and the largest single haul actually landed in Mexican and Vietnamese ISP ranges before the operator's focus settled on Russian and CIS telecom netblocks. Where the confirmed, geolocated compromises concentrated was Ukraine and Russia, with Ukraine holding the largest share.

This is the second Dahua-related camera compromise operation we've traced back to an exposed operator directory in as many weeks. Where last week's investigation centered on a Russian-speaking operator running a purpose-built platform against 58 cameras, this one is a different scale entirely.

On 23 July, Hunt.io AttackCapture crawled the operator's own server and recovered their entire working environment, 2,616 files across 234 subdirectories, 407 MB, from an open HTTP directory they had exposed themselves. This report is built from that corpus and re-verified against additional Hunt.io telemetry. It covers three exploitation paths running in parallel, a persistent backdoor left on nearly 1,900 cameras, and a separate Windows stealer staged on the same host.

Key Findings

  • 14,530+ devices compromised in 35 days. The brute-force engine alone reached 12,324 unique addresses.

  • 1,923 cameras carry a persistent backdoor account (p2pwn / p2password), installed over RPC after either bypass. It is stored independently of the admin password and survives a password change and, on most firmware, a factory reset.

  • 283 cameras were reached by serial number alone through Dahua's cloud relay, with no device address and, in most cases, no credentials. The operator's own code records that 89.4 percent of live serials required no authentication.

  • Offline recovery codes grant cloud-level administrative access by serial number, independently of device credentials. This transferable-access design is the strongest indicator the toolkit was built to hand access to a third party.

  • A second, unrelated capability was staged on the same host: a UPX-packed Windows binary classified as SalatStealer, with a five-method Defender bypass script.

  • The toolkit is assembled, not authored. Its components are credited to at least six upstream developers. Running these tools establishes use, not authorship.

Two of the CVE identifiers running through the operator's tooling do not hold up on inspection. Both techniques work as described, but the labels are wrong, and both recur below, so they are worth clearing up first.

A Note on CVE Identifiers

Two CVE identifiers used throughout the operator's tooling do not survive verification. We state both here because they recur below.

CVE-2024-39943 is the label p2pwn attaches to its persistent-backdoor technique. That identifier belongs to an unrelated command-injection flaw in Rejetto HTTP File Server, a different vendor and product family. The technique is real and verified from source; the identifier is wrong. We could not determine whether a correct one has been assigned.

CVE-2025-31702 is inherited from the upstream researchers whose proof-of-concept underlies the relay component. Dahua's advisory for that identifier describes something far narrower: a post-authentication privilege escalation (CWE-732, CVSS 6.8, not in CISA KEV) requiring valid credentials. That is not the unauthenticated relay abuse documented under P2P Relay Exploitation below.

With the CVE labels cleared up, the report starts where our visibility did: the server that exposed itself.

Discovery

AttackCapture flagged 154.86[.]119.60 for activity on TCP/37777 (Dahua's Easy4IP management protocol) and crawled the host's open HTTP server on 23 July 2026 at 11:45 UTC, recovering 2,616 files across 234 subdirectories.

The exposure traces to one command, preserved in shell history and executed from /root:

cd /root && sudo python3 -m http.server 80 --bind 0.0.0.0

                
Copy

It appears twice in succession. Immediately before it, the history records three escalating full-internet masscan sweeps against port 37777 (70,000, then 120,000, then 150,000 packets per second), confirming the operator was still tuning scan rate when the filesystem was exposed.

File timestamps across much of the corpus cluster around 23 July at 17:06 UTC, close to the crawl. We cannot determine whether this reflects an environment reset or an artefact of collection, and note the timing without attributing a cause.

SSH fingerprint telemetry establishes that the server predates the campaign. We recorded three operating-system generations on this address, the earliest from April 2025, over a year before the operation began. The infrastructure was repurposed, not provisioned.

Knowing when and where the server was crawled sets up the sequence the operator ran before it.

Campaign Timeline and Scope

Shell history and file timestamps together reconstruct the operation's sequencing, which network telemetry alone would not reveal.

Figure 1Figure 1. Campaign timeline: VPS activation through discovery, 17 June - 23 July 2026.

Setup and reconnaissance opened the campaign on 17 June. The brute-force engine deployed the next day and stayed active throughout. The operator first tested the relay workflow on 25 June, nearly three weeks before deploying the CVE chain on 12 July. The largest wave ran on 22 July; within a day the host underwent an apparent environment change and its working directory was exposed.

Exploitation pathDevices reached
Port 37777 credential brute-force (asyncio engine)12,324 unique IPs / 13,229 total records
CVE-2021-33044 / CVE-2021-33045 bypass chain1,923 cameras, each backdoored
P2P relay, no device address required283 cameras
Total unique devices14,530+
Table 1. Campaign scope by exploitation path.

Thirteen p2pwn executions produced campaign folders named RU, RU2, RU3, RUNODIN, UA, UA1, UA2, UA4, UA5, dom, 123, and a final consolidated run. Targeting weighted toward Ukraine: the Ukrainian runs hold the largest share of results, and UA5 output shows the DH-IPC-K35P as the most frequently compromised model.

The timeline shows the order of operations; the mechanics of how each device was reached come next.

Attack Chain

Two independent paths run in parallel. The IP-layer path reaches cameras with routable addresses. The cloud path reaches cameras behind NAT using only a serial number, invisible to any address-based scanner.

Figure 2Figure 2. Attack chain: the four-stage IP-layer path and the three tracks branching from it.

The upper sequence runs once. The operator provisions the VPS, scans for port 37777 with masscan while filtering by local time of day, brute-forces credentials against responsive addresses, then applies the two bypass CVEs to survivors. Three tracks then run independently: Track A abuses the cloud relay by serial number, Track B enumerates serials and generates recovery codes, Track C is the Windows pivot, a separate capability unrelated to camera exploitation.

The orchestrator feeds the serial tracks, sn/_pipeline_runner.py, hard-codes TARGET_HITS = 5 and MAX_ROUNDS = 20, importing the prefix-ranking and Shodan-harvest modules directly. Each round widens the candidate window by 2,000 from a base offset of 8,000 until five live devices are found or twenty rounds elapse.

Each stage in that chain starts from the same place, deciding which devices to hit at all.

Reconnaissance and Target Selection

Internet-Wide Scanning

masscan sweeps TCP/37777 in two phases: Russian address space at ten million packets per second, then a global IPv4 sweep at a lower rate. The target list cidrs.txt is IPDeny's ru-aggregated.zone renamed. The checkpoint file records the parameters:

seed = 9870541109226383968
rate = 10000000.000000
adapter-ip = 154.86.119.60
adapter-port = 57280-57296
ports = 37777
range = 0.0.0.0-255.255.255.254

                
Copy

This confirms the host was the live scanning source, not a passive collection point.

Time-of-Day Filtering

Before scanning any block, a companion module samples an address in it and skips the block entirely if local time falls outside business hours.

Figure 3Figure 3. geolocation.py: CIDR blocks are skipped unless local time falls within 09:00-16:59.

A separate post-capture filter discards dark or featureless frames after a snapshot is taken. Together the two most plausibly serve one goal: avoiding scan and storage cost on scenes unlikely to yield a usable image. The behaviour is equally consistent with suppressing an off-hours traffic signature. The code does not distinguish the motives.

Serial-Number Harvesting

shodan_collect.py harvests serial numbers without contacting a camera, issuing twelve Shodan queries and reading DDNS subdomains from dahuaddns[.]com, where cloud-registered cameras auto-register as {serial}.dahuaddns.com:

SHODAN_QUERIES = [
    'hostname:".dahuaddns.com" "Serial Number"',
    'hostname:".quickddns.com" "Serial Number"',
    'hostname:".dahuaddns.com" port:37777',
    'hostname:".quickddns.com" port:37777',
]
SHODAN_P2P_QUERIES = [
    'product:"Dahua" "Serial Number"',
    '"Dahua Technology" port:37777',
    'http.html:"Serial Number" "Dahua"',
    'http.title:"WEB SERVICE" "Dahua"',
    # + 4 further Dahua/P2P banner variants
]

                
Copy

The ranked output at crawl time is modest. Only two prefixes carried live-probe scores: AH1058FPAG led at 0.0055, carried from an earlier scan that had already found 2,246 live devices, while 5E00107PAJ scored zero across a 32-sample probe. This is an operational snapshot, not a mature target list.

A Third Discovery Channel

CCTV-Scanner-main/, publicly attributed to github.com/ipxobd/CCTV-Scanner, contains no exploitation code. It fingerprints Dahua and Hikvision devices over HTTP by page title and probes RTSP, port 37777, and Hikvision's port 8000. It sits earlier in the pipeline than any of the operator's own tools.

Once a target list existed, the first path against it was the oldest one, guessing credentials at scale.

Credential Brute-Force Engine

The engine is a purpose-built asyncio framework, publicly available as asleep_scanner under the handle d34db33f-1007; the operator's copy links to that repository from its bundled README. Russian developer comments run throughout, in the register of someone documenting fixes to their own production failures.

Concurrency and System Tuning

Before opening a single connection, the tool raises Linux file-descriptor and connection-tracking limits directly.

Figure 4Figure 4. async_brute.py: kernel limits raised before scanning. The Russian comment reads "without this, thousands of connections are silently dropped by the kernel."

The comment is diagnostic rather than explanatory: the author is recording a failure they already hit in production. Concurrency is adaptive: a default of 300 workers auto-scales to a hard ceiling of 4,000 for lists exceeding one million hosts.

Figure 5Figure 5. async_brute.py: bounded queue and sentinel shutdown, giving O(concurrency) memory rather than O(hosts).

The bounded queue is the design decision that makes internet-scale lists viable: memory stays constant regardless of target count, and one sentinel per worker guarantees race-free shutdown. This is engineering discipline, not script-kiddie assembly.

Protocol Implementation

dahua.py implements Dahua's Easy4IP binary protocol on TCP/37777 from a reverse-engineered packet structure: opcode 0xA0 for login, 0xA8 for channel enumeration, 0x11 for snapshot capture, and a shared 0xA4 query opcode distinguished by a sub-code byte.

Figure 6Figure 6. dahua.py: hand-built Easy4IP login packet and response parsing, including lockout detection after five attempts.

Lockout detection matters operationally. By reading the 0x01/0x04 response pair the engine abandons a host after five failures rather than burning the account, preserving the device for a later CVE pass. The synchronous snapshot controller closes sockets with SO_LINGER(1,0) to force an immediate RST; the async path does not.

Snapshot Quality Filtering

Captured frames pass a two-stage quality filter before delivery.

Figure 7Figure 7. snapshot.py: mean-brightness and Canny edge-density filtering discard blank or offline camera views.

A frame is discarded only when it is both dark and featureless; dark-but-detailed frames are retained. The operator is filtering for scenes with usable content, not merely for working cameras.

Exfiltration

Every credential hit that survives filtering is posted immediately to a Telegram channel.

Figure 8Figure 8. bot.py: Telegram notification template with a hardcoded VKontakte community link.

Two details carry weight. The message embeds a hardcoded VKontakte community link, and delete=False at line 147 means the rmtree() cleanup never executes: every captured JPEG is retained on disk alongside the credentials.

Enterprise-Format Export

A separate module batches credential hits into SMART PSS-compatible XML, Dahua's own enterprise camera-management platform.

Figure 9Figure 9. export.py: credential records serialised into SMART PSS import batches; 13,229 records produce 52 XML files.

The 255-device split is SMART PSS's own per-import ceiling, so the output is built to load directly into the vendor platform. This indicates intent to manage the device population at scale through enterprise tooling. It does not by itself distinguish personal use from handoff, since either would benefit from the format.

Operational Results

Recovered logs cover 24 runs between 17 June and 13 July. More than a third returned nothing: nine cracked zero devices, five within a single forty-minute session against the same list. Across the twelve productive runs we estimate roughly 13,000 device credentials were recovered. Hit rate is volatile even against an identical list: one evening's three successive attempts against the same ~55,000-host list returned 0, then 0.044, then 0.38 percent.

Targeting shifted over the campaign. The largest single haul, on 18 June, skewed toward Mexican and Vietnamese consumer ISP ranges; by late June the dominant geography was Russian and CIS telecom netblocks. The device population is broader than DVRs: door-entry intercoms, a PC-based NVR software client fingerprinted identically to hardware, and OEM-rebranded lines (RVi, ST-XVR, QVC, AC-D, SNR) all speaking the same protocol.

Evidence of a Private Fork

The operator's build has diverged from the public project. Its README documents an inverted default for snapshot capture and omits two undocumented performance flags present in the code. Three of four defined Dahua opcodes are dead code the pipeline never calls, superseded by a leaner async reimplementation. The largest early hauls are missing their SMART PSS exports, most plausibly because that step had no error handling and failed silently on large result sets before an early-July fix.

Brute force only reaches devices with a weak password, so the operator carried a second path that needed no password at all.

Authentication-Bypass Chain (p2pwn)

p2pwn is a compiled Go binary chaining three authentication vulnerabilities. Unlike the brute-force engine it requires no working password, only an unpatched camera.

Figure 10Figure 10. p2pwn config.toml: the embedded default configuration, matching the operator's campaign settings.

The configuration is worth reading closely. p2pwn toggles only the two bypasses and the backdoor installation. The relay tooling is not one of its methods: that capability lives entirely in the separate relay toolkit, with its own launcher and development history. The two paths are structurally distinct, not options within one tool.

The Two Bypasses

CVE-2021-33044 exploits unconditional trust in clients identifying as NetKeyboard hardware controllers: when clientType is NetKeyboard, the password field is never evaluated. CVE-2021-33045 exploits the firmware reading the claimed source address from the request body rather than the TCP connection.

Figure 11Figure 11. Both payloads side by side: the NetKeyboard hardware-trust bypass and the loopback source-address spoof.

Each returns a full administrator session unauthenticated. The first sends the literal string Not Used as the password; the second claims to originate from 127.0.0.1. Both execute in under a second against unpatched firmware.

Persistent Backdoor Installation

After either bypass, p2pwn installs the account p2pwn / p2password over RPC. Because it is stored independently of the admin password, it survives a password change and, on most firmware, a factory reset. This is the technique the tool mislabels CVE-2024-39943, crediting the exploit to 0x5477.

Source Analysis

A later crawl recovered the Go source. p2pwn is not a single exploit but three attack surfaces multiplexed over one tunnel: a raw SDK channel for the bypasses and backdoor; an HTTP CGI channel proxying to the device's embedded web server; and a JSON-RPC console channel mimicking Dahua's internal NetKeyboard protocol.

That third channel reaches beyond the device under attack. After login it runs OnvifUser -u to dump ONVIF credentials and retrieve passwords the camera has cached for talking to an NVR, a lateral credential-harvesting step. The SDK credentials hardcoded in this source are byte-identical to those recovered from the relay toolkit, confirming both codebases draw on the same recovered Dahua material.

Shell history shows the tool built from two locations: an unzipped archive in /root, and a git clone inside /var/xdwd/ created immediately before the Windows binary was renamed. That second directory sits outside the served web root and is invisible to the crawl.

Campaign Results

Folder names are applied after the fact, based on where compromised devices geolocated; the scan itself varies only by serial prefix. Of roughly thirteen invocations, eleven produced folders that persisted to the crawl.

FolderDevicesDominant modelMechanism
RU31DH-IPC-K15PCVE-2021-33044
RU244DH-IPC-K35PCVE-2021-33044 + backdoor re-entry
RU330IPC-K35CVE-2021-33044
RUNODIN77DH-IPC-HDW1531SP-0280BCVE-2021-33044
UA301DH-IPC-K35APCVE-2021-33044 + backdoor re-entry
UA120DH-IPC-A35PCVE-2021-33044
UA2~640 (partial)DH-IPC-K35PCVE-2021-33044 + backdoor re-entry
UA410IPC-C46CVE-2021-33044 + CGI credential brute
UA5~690 (partial)DH-IPC-K35PCVE-2021-33044 + backdoor re-entry
dom0n/aDry run, no hits
12379DH-IPC-K35APCVE-2021-33044
Table 2. p2pwn campaign folders recovered from the open directory.

Roughly 1,900 compromises across eleven runs in a nineteen-day window. No folder named UA3 or a combined run exists on disk despite both appearing in shell history; whether through failure or cleanup, those runs left no recoverable trace. UA4 carries a tag seen nowhere else, CGI Brute, confirming a plain HTTP fallback alongside the CVE path.

One pattern matters for defenders. The non-dictionary password I0949488055 recurs across eleven distinct devices and addresses in the UA run alone; VIT0662082161 and 01131418s recur ten times each. This is consistent with a single installer setting the same custom default across many independent client deployments: the operator's wordlist already held it and matched immediately rather than iterating.

Upstream Attribution

p2pwn's THANKS.md credits four authors: the P2P protocol implementation (khoanguyen-3fc), a PTCP tunnel variant (MaximilianoFelice), the serial-generator algorithm (itres-labs), and the backdoor exploit (0x5477). The tool's author assembled it from four other people's research, a distinction worth preserving when weighing what running it implies about writing it.

Both paths so far assume a routable address, and the third abandons that assumption entirely.

P2P Relay Exploitation

Dahua's cloud relay, easy4ipcloud[.]com:8800, lets any application reach a camera behind NAT using only its serial number. Authentication to the relay depends on credentials hardcoded identically into every Dahua client ever shipped. ITRES separately documented this unauthenticated serial-enumeration path as a non-CVE issue, distinct from CVE-2025-31702, which Dahua's advisory covers as an authenticated recovery-secret flaw.

Figure 12Figure 12. The fixed Dahua SDK relay identity and WSSE authentication scheme, embedded in every shipped Dahua client.

Presenting these values authenticates the caller as a trusted Dahua application. The same AES material reappears independently in dh-p2p-test/, an unrelated protocol-research project on the same server; its presence in two independently-obtained codebases confirms this is Dahua's own recovered cryptographic material, not something the operator generated.

Figure 13Figure 13. Session-key derivation: PBKDF2-HMAC-SHA256 over device credentials, 20,000 iterations, feeding AES-256-OFB.

This second layer engages only where device-level authentication is required. The distinction matters: the fixed key above reaches the relay, while this derived key encrypts connection metadata for the device itself.

The Three-Phase Serial Attack

The serial brute-forcer probes each candidate before spending credentials on it. The recorded branch statistics are the single most consequential result in this component.

Figure 14Figure 14. brute_serial(): device-state probe, per-device salt retrieval, then the credential loop. The operator's own comment records that 89.4 percent of live serials return an open, no-authentication channel.

Phase 1 alone resolves the overwhelming majority of targets: nearly nine in ten live serials return an open channel requiring no credentials at all. Only the remainder proceed to salt retrieval and the credential loop. The scale of this campaign rests less on cracking passwords than on the fact that most exposed devices never demand one.

Observed Relay Behaviour

Live testing is recorded in brute_p2p.log. Against eight serials under one prefix, seven distinct backend addresses answered rather than a single shared server:

[5E00107PAJ01FFF] P2P SERVER - 165.154.164[.]157:8803
[5E00107PAJ02C6E] P2P SERVER - 165.154.165[.]25:8803
[5E00107PAJ06CD2] P2P SERVER - 165.154.165[.]24:8803
[5E00107PAJ0892A] P2P SERVER - 128.14.224[.]137:8803

                
Copy

Across sampled runs the lookup touched at least eight backend addresses: 165.154.164[.]157, 165.154.165[.]25, 165.154.165[.]24, 165.154.164[.]186, 165.154.164[.]52, 128.14.224[.]137, 165.154.165[.]174, and 165.154.198[.]11. Port 8803 is known only from the operator's own logs; our passive scanning has never observed it open on any of these addresses, so this function rests on the operator's traffic rather than external corroboration.

Why These Addresses Are Not Indicators

We deliberately exclude these nine addresses from the indicator tables. Seven, plus 45.192.10[.]33, sit on AS135377 (UCLOUD). The subdomain naming on easy4ipcloud.com (p2pauth-fk, devaccess-fk) maps directly onto these hosts' Frankfurt geolocation, consistent with genuine shared Dahua relay capacity.

Three of them share byte-identical response fingerprints across the same unusual port set: an Uptime Kuma dashboard, a Basic-authenticated proxy, and a bare SOCKS4 listener. That is not the signature of independent customers sharing a provider; it is one monitoring-and-proxy image deployed across several rented hosts. 45.192.10[.]33 is the genuine outlier, showing a rotating cast of unrelated third-party domains, the actual profile of multi-tenant hosting the other eight only superficially resemble. A ninth, 165.154.198[.]11, sits on AS142002 and has shown almost no activity since March 2025.

Publishing this pool as adversary infrastructure would cause defenders to block Dahua's own legitimate relay traffic and break camera connectivity for real customers. The absence of any independent malicious tag across all nine is the finding, not a gap in coverage.

The scannerdahua Toolkit

scannerdahua chains three techniques. It first attempts the bypasses over HTTP; on success it shells out to a locally-held copy of the public Ingram DahuaConsole toolkit; only if both fail does it drop to a sixteen-pair default-credential brute. Its keyspace arithmetic is explicit: seven ten-character prefixes, each brute-forced across a five-hex-digit suffix: 1,048,576 candidates per prefix, roughly 7.3 million serials.

A shell script alongside it, run_auth_brute.sh, carries an explicit instruction in its own comment: run this only on a rented VDS, never on the local machine. This is the clearest evidence in the corpus that the operator separates their personal machine from the execution host. That separation did not hold.

Figure 15Figure 15. A Windows-side test log exposing the operator's development username and local path, recorded while the Linux VPS had been scanning the same prefix for hours.

The log leaks both the Windows username SystemX and the full development path. It was produced by a single-serial test run from the operator's own laptop, precisely the machine run_auth_brute.sh warns against using.

An Actively Running Directory

A third copy of the serial brute-forcer sits in /cve-project/, wired to a live launcher rather than merely stored.

Figure 16Figure 16. start_all.sh: eleven hardcoded prefixes launched in parallel, 220 concurrent serial slots against the Dahua relay.

The script kills any stale run, clears prior output, and launches eleven parallel processes staggered by half a second to avoid a connection burst. Both result files were empty at crawl time: AttackCapture captured this directory mid-run.

A Development Lineage in Three Languages

Comparing the three copies reveals a progression invisible from any single file. The original is entirely Spanish-commented, with a hardcoded thread count and no lock around its output writes, a genuine race condition. The /cve-project/ copy is a hand-edited refactor: configurable threads, a lock around the writes, faster timeouts, and Russian runtime strings inserted into still-Spanish code:

print(f"[{num:06d}] Probando {candidate_serial}... | Найдено: {found_count[0]}")

                
Copy

Run without arguments, the same copy prompts entirely in Russian: "How many threads? (default 30)". A third copy is fully Russified. These are three stages of one person progressively rewriting a borrowed tool, one language layer at a time.

The same toolkit also recovers stored device passwords outright, through a routine that derives its decryption key entirely from values the attacker already holds, device class prefix and serial number, so no device secret is needed. A residual Spanish comment in that code confirms it came from the same upstream source as the original brute-forcer.

A Fourth Lineage and the Orchestrator

A previously undocumented fourth lineage exists in English, citing khoanguyen-3fc/dh-p2p in its docstring. Its MIT licence credits a publisher calling themselves Purple Ghosts, a name appearing nowhere else in the operator's code, suggesting it belongs to the upstream author.

scannerdahua/scanner.py is the complete automated pipeline for a given prefix: it enumerates candidate serials, probes each against the relay, and for every live serial opens a local tunnel and runs the bypass chain, attempting a no-auth login and a short credential list before full exploitation. It maintains resume state per prefix; one log shows a scan resuming after more than 9,000 serials had already been probed.

Deep Dive: The PTCP Tunnel Protocol

The independently-sourced dh-p2p-test/ project on the same server allows the relay exploitation to be described at protocol level rather than merely as "it uses hardcoded credentials."

Packet Format and Handshake

Every PTCP packet carries a fixed 24-byte header: the ASCII magic PTCP followed by four big-endian 32-bit fields tracking bytes sent, bytes received, a packet identifier, and a message-ID pair used for acknowledgment. A one-byte type field selects purpose: 0x00 SYN, 0x10 tunnelled data, 0x11 bind request, 0x12 connect notification, 0x13 heartbeat, and 0x19/0x1a/0x1b forming an authentication exchange.

A client first contacts Dahua's cloud broker to resolve a serial to a dedicated P2P server, then a relay, then an agent server that issues a session token. Only then does it attempt to reach the device, using a technique the recovered Wireshark dissector labels Inverted STUN: a STUN-shaped NAT-traversal packet with every byte bit-flipped before transmission.

What "Bind" Means

A 0x11 frame always targets 127.0.0.1 on the device itself, instructing the camera to open a connection to itself on a given port and return the bytes. This is why one implementation can tunnel RTSP on port 554 or Dahua's management port with a one-line change. The operator's tooling is exactly this: a fork binding port 37777 instead of 554, pushing a hand-built login packet through the tunnel, reaching a camera's management interface with no routable path to the device.

The Trust Model

The device never authenticates the connecting party. It authenticates the session, via a token the cloud issued before the device was contacted. Obtaining that token requires only the fixed SDK credentials shared by every legitimate Dahua application. The only real barrier to reaching any camera through this path is knowing its serial number, precisely what the operator's harvesting pipeline exists to produce at scale. Reaching a device this way is not the same as controlling it: the relay opens a path to the device's management interface, but taking control from there still requires either valid credentials or one of the authentication-bypass flaws (CVE-2021-33044 / CVE-2021-33045) documented earlier. Dahua confirmed this distinction directly. What makes the relay path matter at scale is that, per the operator's own logging, most exposed devices returned an open channel that demanded neither.

Cryptography, Precisely

Three distinct mechanisms are in play, and conflating them would misstate the protocol. A fixed AES-256-OFB key and IV, the same pair embedded in every Dahua client, decrypts exactly one thing: a JSON blob returned by the cloud containing a per-device salt. A session-specific AES-256-OFB key, derived via PBKDF2 from device credentials, encrypts connection metadata only where device authentication is required. Every request to the cloud broker is signed rather than encrypted, using WSSE HMAC over the same fixed credentials.

None of the three protects the tunnelled application data once the connection is established; that traffic flows as plain TCP bytes wrapped in PTCP frames. One gap remains: p2pwn's own Go implementation could not be retrieved, so its wire-level behaviour is documented from its README and from independent reference implementations rather than the binary.

Reaching a camera by serial is one thing; the same serial also unlocks a more durable form of access.

Offline Recovery-Code Generation

seria2/asfefwq.py replicates part of Dahua's own account-recovery flow offline. It generates candidate serials, tests each against the relay, and derives a recovery code for every live device.

Figure 17Figure 17. The liveness check: a serial is resolved against Dahua's cloud broker, then probed on the returned P2P server.

A code is generated only for a serial that passes this check. Dead candidates are logged as scanned and discarded, never assigned a code: the operator is building a verified inventory, not a speculative list. A near-complete recovery of the file (96.9 percent) shows it is not a script but a full terminal-UI application with a six-item menu.

The derivation is entirely offline and deterministic. Given a serial, the code follows without any interaction with the device, which is what makes the output transferable.

Figure 18Figure 18. auth_codes.json: generated recovery codes, each granting an administrative reset through Dahua's cloud.

Each entry is redeemable through Dahua's standard password-recovery flow without knowledge of the current credentials. Runs write to a dated per-prefix directory holding an append-only serial list, a JSON code map, and XML credential files batched sixty-four at a time.

We looked specifically for a Telegram or Discord exfiltration step comparable to the brute-force engine's and found none. Every network call is a read-only liveness check; all output stays local. The hardcoded console title CCTV Scanner | discord.gg/cctv indicates the tool was sourced from a Dahua-exploitation Discord community rather than written from scratch, and functions purely as a static banner.

A companion Russian-language target list maps real Dahua and OEM-rebranded models to serial prefixes, tiered by observed hit rate. Its comments note that the same scan works regardless of brand: Amcrest, Lorex, Annke and Swann cameras all sit on Dahua's cloud backend beneath the rebrand. It also maintains a disabled tier of zero-yield prefixes, content byte-identical to a previously unexplained stray file elsewhere on the host, resolving an open question about its purpose.

Everything to this point concerns the cameras, but the same host held a capability that has nothing to do with them.

Windows Pivot (Unrelated Capability)

Shell history records mv xeno.exe 1.exe, executed immediately after building a second copy of p2pwn outside the served web root, and immediately after that build was used for a final combined run against eight validated prefixes.

Raw header bytes confirm a genuine UPX-packed 32-bit Windows executable rather than merely a sandbox label: MZ/PE signature, sections named UPX0, UPX1 and UPX2, the packer's version marker, and a compile timestamp zeroed to 0x00000000. The shell history is unbroken but contains no download, build, or transfer step for this file before the renaming.

A Second, Independent Sighting

The identical file (same SHA-256, same byte size) appeared on 185.132[.]53.56 two days later, still under its original name. That host's entire recorded shell history is 153 bytes:

apt install screen
kill -9 968]
kill -9 968
apt install screen
kill -9 2736
apt install screen
dpkg --configure -a
apo
apt install screen
screen -R scan

                
Copy

The final line matters. This host's screen session is named scan, the same non-default name used on the primary host. Two unrelated parties independently choosing an identical custom session name is unlikely.

Set against that, the host's longer-term profile shows genuine personal hosting on a different provider (AS211507): YunoHost then HestiaCP running an ordinary mail and web stack for oxycod[.]one since September 2025, plus a .bashrc.original backup implying a pre-existing configuration was preserved. Port 37777 appears for a single four-hour window on 22 July 2026.

Assessment (moderate confidence): a pre-existing server, likely belonging to someone else, briefly taken over on 25 July to restage the same payload, neither a fully independent victim nor a host built from scratch. The filename xeno.exe most plausibly reflects how the file is distributed upstream of both incidents.

What that binary actually is turns out to be less certain than its filename suggests.

Malware Family Assessment

The pre-rename filename implies XenoRAT. The only sandbox classification attached to the file, on both hosts, is SalatStealer. In our indexed reporting these are materially different families: every XenoRAT sighting ties to nation-state espionage against named government and financial targets, never to opportunistic IoT-adjacent activity. SalatStealer is a Go-based, UPX-packed commodity credential and cryptocurrency stealer sold as a service.

The balance of evidence favours SalatStealer, though this is an inference from technique fit rather than direct confirmation: no sandbox report, extracted configuration, or independent corroboration beyond the original tag exists for this hash. A third tagged technique, root certificate installation (T1553.004), is explained by neither family and remains unresolved.

Its presence is weaker evidence of an actor profile than it first appears. Our reporting shows SalatStealer resold across at least five unrelated campaigns: a Russian-aligned actor phishing Ukrainian government targets, a loader operation, a fake-videocall credential-theft crew, an AI-generated cryptominer campaign, and distribution through pirated software. None shares infrastructure with this one. This binary is a second, distinct capability staged on the same host, and organisations tracking this operator should treat it as a separate incident rather than folding it into the Dahua findings.

Whatever the family, it did not arrive alone, and the script staged beside it says something about the intended target.

Defender Evasion

A PowerShell script staged alongside the binary disables Defender coverage across an entire volume.

Figure 19Figure 19. add-defender-exclusions.ps1: five independent Defender exclusion methods, including Group Policy persistence.

The five methods are deliberately redundant: user-context PowerShell, a full-list override, CIM/WMI to bypass constrained language mode, a SYSTEM-context scheduled task, and a Group Policy registry key surviving reboots and Defender updates. Targeting C:\ wholesale, and reaching for Group Policy at all, indicates an intended enterprise victim rather than a home machine.

Infrastructure Analysis

Every finding above rests on the recovered corpus. This section asks a different question: what does the server itself reveal, independently of anything the operator wrote? We worked through the server data two ways: through Hunt.io's Threat Enrichment API and SQL layer, and through the portal interface. The two reconcile: total capture size is identical (407 MB) either way, with differences in file counts reflecting how each view handles duplicates and previewable items. We cite the API figures throughout, since they're independently reproducible.

Figure 20Figure 20. Hunt.io Info view for 154.86[.]119.60: port-level timestamps and automated risk classification.

The portal confirms current hosting attribution (Cloud Innovation Ltd, AS197574, Amsterdam) and supplies per-port dates the API does not surface. Port 80 has been open since 12 May 2022, over four years before this operator's activity. Port 443, carrying the certificate discussed below, first appeared on 8 January 2024. The "Telemt Panel" on port 8080 ran between 28 July and 1 August 2026. Port 443's banner reads QRATOR, so the camouflage extends to the HTTP layer, not the certificate alone.

Automated reputation scoring reads 0 High Risk, no active malware signals, with a single warning: Potential Malicious Open Directory, dated 23 July. Stated plainly: every substantive finding in this report comes from reading the corpus. The platform's automated classification is conservative next to what that reading produced.

Ownership and Provenance

ASN attribution resolves two ways depending on the subsystem. The host carries a paired attribution: ExpressHost Ltd (AS197574) as the original RIR record, and Ferdinand Zink trading as Tube-Hosting (AS49581) as a newer routing-level re-announcement of the same prefix. Every other active address sampled across this /24 reports only the older label, indicating registration churn plus a data-freshness gap rather than contested ownership.

Tube-Hosting is a real, operating VPS reseller with dozens of live client subdomains. A third name, Cloud Innovation Ltd, appears in the portal for the same ASN; we found no technical link to this host. The reverse-DNS hostname has changed between observations, but the identical {adjective}-{noun}-{hash}.expresshost.cloud convention appears elsewhere in the same /24, a provider-wide scheme, not operator-specific.

SSH history shows three OS generations: Ubuntu 22.04 (April-May 2025), Ubuntu 24.04 (briefly, June 2026), and Debian 13 since 18 June 2026. None of these host keys, nor the current JARM, is distinctive enough to pivot: the April 2025 key matches over 67,000 unrelated hosts and the JARM over 2.5 million. The timeline holds; it simply cannot find sibling hosts.

The first recorded action was a speedtest at 17 June 2026, 11:24:08 UTC, measuring uplink capacity before configuring masscan's rate. The surrounding /24 is not exclusive to this operator: spot checks found a Russian-language Confluence phishing page with a Telegram-linked VPN app, a Kubernetes node, and a developer's portfolio site on neighbouring addresses.

Figure 21Figure 21. AttackCapture File Manager: corpus accounting and the platform's own unread Analysis Report.

This view counts 875 files across 125 subdirectories against the 2,616 items across 234 subdirectories cited from the API. The exact byte match either way (407 MB, 427,142,652 bytes) is what confirms these are the same crawl counted differently, previewable files against every captured item including duplicates.

Two scale details are worth keeping: the brute-force engine's log directory alone accounts for 371 of 407 MB, roughly 91 percent, and the file-level scanner tags 177 of the 875 files as malicious. The portal also advertises a platform-generated Analysis Report visible only in the interface; no API endpoint available to us returns its contents, so we have neither verified nor relied on it.

The rbc.ru Certificate

Certificate history on port 443 shows three unrelated states: an empty-subject certificate from early 2024, a self-signed CUPS default on port 631 in May 2025, and from 28 July 2026 a live certificate with subject rbc.ru (a major Russian financial media group), byte-for-byte identical to the one on RBC's genuine Qrator-fronted edge.

Figure 22Figure 22. SSL History for 154.86[.]119.60: the certificate's observation window and claimed issuer.

The recorded issuer, Let's Encrypt, cannot reflect genuine issuance to this host: Let's Encrypt issues only after validating domain control, and nothing indicates this server controls rbc.ru. The consistent reading is that the issuer field was copied wholesale alongside the subject and the certificate bytes.

A direct query against our certificate database found this exact hash on more than 11,000 separate address observations worldwide, beginning 28 July and still accumulating, across a long tail of proxy-typical ports: 443 and 8443 dominate, but 1080 (SOCKS5), 9443, 2053 and 2083 each appear.

Figure 23Figure 23. Pivots for 154.86[.]119.60: the cloned certificate's address-specific count, and a second unidentified hash.

This host alone accounts for roughly 2,000 observations of the cloned certificate, last seen 3 August 2026. The same list surfaces one item we cannot explain: a second x.509 hash, DC2C5491AFDC25A2A5588C2BA2D1F054B0E2BDF4C925304FA0825F25F9D57DC8, with roughly 11,000 observations last seen 22 April 2024, over two years before this campaign. We flag it as an open item rather than speculate.

Decisively, dozens of unrelated addresses including this one presented the certificate a full eighteen hours before RBC's genuine edge appears in the same dataset. That timing and scale rule out targeted private-key theft.

Comparison Against the Genuine Edge

The genuine RBC edge provides a control against which the clone can be measured.

Figure 24Figure 24. Info view for 178.248.238[.]247, RBC's genuine Qrator-fronted edge.

It shows a completely clean reputation (0 High Risk, 0 Warnings), with ports 80 and 443 open continuously since December 2022 under the same QRATOR banner: long-standing, actively maintained infrastructure.

Figure 25Figure 25. Domains resolving to the genuine edge: exactly two, both legitimate RBC properties.

Only rbc.ru and app.rbc.ru resolve here, neither carrying a malware or IOC Hunter tag, consistent with a dedicated edge rather than shared hosting, even though the underlying block is anycast.

Figure 26Figure 26. Pivots for the genuine edge: an entirely different JARM fingerprint despite the identical certificate.

The genuine edge's JARM (114,000-plus observations) shares no relationship with any value recorded on the operator's host. Identical certificate bytes with unrelated TLS fingerprints is exactly the signature of a certificate captured and replayed as a static file, rather than a server stack being cloned. The same list shows the chain running through Let's Encrypt production intermediates, and surfaces a curator.pro subject on the same anycast address: Qrator Labs' own product domain sharing infrastructure with its customer.

SELECT
    *
FROM
    ip
WHERE
    tls.cert.subject.common_name = 'curator.pro'

                
Copy
Figure 27Figure 27. SQL pivot on the curator.pro certificate subject: two results, both unrelated to this campaign.

The pivot returns exactly two addresses (the anycast address itself and one unrelated host at Leaseweb Germany), confirming ordinary shared-anycast behaviour rather than a new lead.

The Telemt Panel

On the day the certificate first appeared, a second service surfaced: a React single-page application on port 8080 titled Telemt Panel. Searching our database for that exact title produced corroboration from two unconnected directions. The identical title is fingerprinted on an unrelated address elsewhere on the internet. Separately, DevOps logs from a different operator refer repeatedly to telemt as a Rust-based proxy component underlying a Telegram MTProto circumvention stack called MTProxyMax, binding port 443, with a documented memory leak in version 3.3.28.

Read together with the certificate evidence, the coherent explanation is that Telemt Panel is the management interface for exactly this kind of tool, and the rbc.ru certificate is that tool's default masquerade identity, not something this operator captured or built, and not a sign of compromise at RBC or Qrator (moderate to high confidence). The same pattern appears on another address in this /24, presenting a byte-plausible clone of Yahoo's production certificate.

Searching for Sibling Infrastructure

We searched for other hosts running this toolkit: by file hash, by distinctive source strings, and by malware tag. The p2pwn binary and the SalatStealer-tagged binary both remain confined to the two hosts documented here, and no other address in the primary host's /24 has ever been crawled into AttackCapture.

Two other hosts run asleep_scanner, but neither belongs to this campaign. One, first seen a year earlier, hosts the public source tree inside a broader red-team toolbox (Meterpreter, GitHack, a Heartbleed scanner), consistent with the tool author's own development host. The other is a larger multi-CVE exploitation and DDoS platform reusing the Dahua protocol module for a single snapshot script, with result folders named for Ukrainian and Moldovan cities. It shares no credentials, CVE chain, or file hash with this toolkit.

Both neighbouring netblocks host unrelated malicious activity: a beaconing Sliver C2 implant with CRM credential-dumping scripts on the second host's provider, and an open directory of Chinese-origin red-team tooling (fscan, Vshell) on the relay pool's provider, on a different netblock from any relay address. Both confirm ordinary multi-tenant clouds with unconnected customers.

A Rare Fingerprint, Checked and Ruled Out

One JARM value recorded during an earlier certificate era has only two observations across all of our data, a genuinely low-cardinality pivot, unlike the generic value dismissed above.

SELECT
    *
FROM
    ip
WHERE
    jarm.fingerprint = '20d14d20d21d20d00020d14d20d20d164ecea2ac2b4ce60f02137d3925f7d0'

                
Copy

Output example:

Figure 28Figure 28. SQL query for a rare, two-observation JARM fingerprint, matching exactly one other address worldwide.

The only other match is 159.65.136[.]18, a DigitalOcean host in Singapore. A two-count match is meaningful enough to warrant direct investigation rather than dismissal.

Figure 29Figure 29. Info view for 159.65.136[.]18: an ordinary, clean DigitalOcean host.

It shows the same clean reputation as the genuine RBC edge, with SSH and HTTP open continuously since December 2022 and no warnings of any kind.

Figure 30Figure 30. Domains hosted on 159.65.136[.]18: unrelated Vietnamese small-business sites, none tagged.

Seven domains resolve here, all Vietnamese-language small-business sites (a pet and ornamental-plant retailer, a gardening-tools shop), none carrying a malware or IOC Hunter tag.

Figure 31Figure 31. Code Search: the address surfaces only in unrelated mass-scanner output captured from other operators' open directories.

The clearest evidence against a connection: the address appears as an ordinary open tcp 22 result inside two internet-wide scans sweeping DigitalOcean's 159.65.0.0/16 range, and in a target list for CVE-2024-4577, a PHP-CGI flaw unrelated to Dahua. All four files were captured from open directories belonging to other, unconnected operators.

Assessment: a live, frequently-scanned host that appears in whatever mass-scanning toolkit sweeps its subnet, not a sibling of this operator's infrastructure. We assess the shared 2024-era JARM as a coincidental artefact of a common base image, and are not adding this address to the indicator tables.

With the corpus and the host both accounted for, the observable indicators can be listed for defenders.

Indicators of Compromise

Network Infrastructure

IndicatorTypeContext
154.86[.]119.60IPv4Operator server, open directory, payload staging
185.132[.]53.56IPv4Second sighting of the identical payload; second operator touchpoint (see Windows Pivot)
easy4ipcloud[.]com:8800Domain:portDahua P2P relay endpoint; abused legitimate infrastructure, not itself malicious
vk[.]com/wall-163997495URLVKontakte community link hardcoded in the operator's Telegram notifications
dahuaddns[.]com / quickddns[.]comDomainDahua DDNS, serial enumeration targets; abused legitimate infrastructure
37777/TCPPortDahua Easy4IP binary management protocol
rbc[.]ruTLS subjectPresented on 154.86[.]119.60:443 with a spoofed QRATOR banner; a shared proxy-tool default certificate on 11,000+ unrelated addresses, not operator-specific
discord.gg/cctvDiscordCommunity handle in the recovery-code tool's console title
Table 3. Network indicators. The nine P2P relay backend addresses are described under P2P Relay Exploitation and deliberately excluded.

Host Artefacts

IndicatorTypeContext
p2pwn / p2passwordCredentialRPC-installed backdoor account on 1,923+ cameras. Primary victim-side artefact.
admin:admin, 666666:666666, 888888:888888, admin:admin123, default:tluafedCredentialsp2pwn's default brute-force list
AH1058FPAG, 3E02C9DPAA, 7K01F36PCA, 5D03CF3PAJ, 3K04B95PAG, 4J00809PAJ, 3L05AF4PAG, 5E00106PAJ, 5F009F7PAJ, 5K09CA1PAJ, 6E0933FPAZ, 3J03016PAG, 5H01308PAJSerial prefixesThirteen prefixes brute-forced, 1,048,576 candidates each
de03a0ae5c7aa0c237ae36a649875f986fd9701ac06857dd214054367ce5090cSHA-2561.exe / xeno.exe: SalatStealer (T1112, T1553.004, T1614.001); confirmed on two hosts
694bfbe44bcd9b4844e15294be74dafe86ff8ae40b8b1067f4dae70a6ef75da8SHA-256p2pwn compiled Go ELF
be2738c8a2beb55ac484d71c329381f3caabac80664283ad9a24dbab82b5b590SHA-256Operator's copy of the p2pwn source archive
cidrs.txtSource fileIPDeny Russia CIDR list, renamed, driving masscan targeting
Port 8080 "Telemt Panel"Live serviceVite/React SPA, observed 28 July - 1 August 2026
C:\Users\SystemX\Downloads\seriaWindows pathOperator development machine
rogue-orbit-ryzhuuxu.expresshost.cloudPTRProvider auto-naming convention, confirmed elsewhere in the same /24
oxycod[.]oneDomainLegitimate personal domain on 185.132[.]53.56; part of that host's pre-existing history
Table 4. Host-based artefacts.

SSH Fingerprint History

Fingerprint (SHA-256)OS / BannerObserved windowPort
570963ee5a1253832c80e95ebb34dca5fe0f6bf7437857dcae511fbb023ed109Ubuntu 22.04, OpenSSH 8.9p12025-04-15 - 2025-05-1022
e2d20dbe2b4f7762313acf42c278ef99d7d2ec8906b0a5280798ad4ca779266eUbuntu 24.04, OpenSSH 9.6p12026-06-07 - 2026-06-1720001
cba28f2b4f12cd3f0a222a3885bef70518872c0ca62798dc810b8caa3035cca7Debian 13, OpenSSH 10.0p22026-06-18 - present22
Table 5. SSH host key history for 154.86[.]119.60.

Serial Brute-Forcer Development Lineage

LocationSHA-256Stage
/CVE-2025-31702-main/dahua-sn-brute2.py7a963211a052a78899a8881d36b42b55690c02e95c1485f5d78d3cfdb3d2842aOriginal: all-Spanish, hardcoded threads, unlocked writes
/cve-project/dahua-sn-brute2.py083c9ee06ed2ee1bc0051358b3eeb3242f474eacea4c88b3b10b06d15389b753Mid-rewrite: mixed Spanish/Russian, configurable threads, locked writes
/cve-2025-31702/dahua-sn-brute2.py5f60e5b45ecd67a987bdb1173d6a8063aa20a7946377da4698aaba3ebe72d088Fully Russified
/cve-project/start_all.sh4a4a346df72c839272ea835e4717cd88f843333b3563e745e657895347ea3dbeActive launcher, eleven hardcoded prefixes
Table 6. Three stages of one operator progressively rewriting a borrowed tool.

Dahua P2P SDK Relay Material

These values are Dahua's own, embedded in every shipped client. Withheld here; available for verification or detection matching on request.

  • SDK relay application identity (USERNAME)

  • SDK relay application secret (USERKEY)

  • Global relay RANDSALT

  • AES-256-OFB key/IV decrypting the cloud's per-device salt blob

  • Static OFB IV for the session/credential-derived layer

  • Second SDK credential pair, found only in the operator's recovery-code tool

Upstream Attribution Handles

HandleRepositoryRole
thebadintegergithub.com/thebadinteger/p2pwnp2pwn: active use confirmed; authorship not established
d34db33f-1007github.com/d34db33f-1007/asleep_scannerBrute-force engine, linked from the operator's own copy
ipxobdgithub.com/ipxobd/CCTV-ScannerFingerprinting scanner, confirmed unrelated authorship
khoanguyen-3fc, MaximilianoFelice, itres-labs, 0x5477VariousCredited in p2pwn's THANKS.md as upstream research sources
"Purple Ghosts"MIT copyrightPublisher name on the upstream relay tool's licence, not the operator's identifier
t.me/asleep_cgTelegramChannel printed in the brute-force engine's startup banner
Table 7. Handles credited across the toolkit. Attribution of use, not of authorship.

MITRE ATT&CK Mapping

TacticTechniqueEvidence
ReconnaissanceT1595.001: IP Block Scanningmasscan at 10M pps against Russian CIDRs, then a global sweep, port 37777
ReconnaissanceT1596.005: Scan DatabasesDahua-specific Shodan queries and DDNS enumeration
Initial AccessT1190: Exploit Public-Facing ApplicationCVE-2021-33044, CVE-2021-33045, and the relay technique
Credential AccessT1110.003: Password Sprayingasyncio engine, adaptive concurrency to 4,000, port 37777
PersistenceT1136.001: Create Local AccountRPC-installed p2pwn account on 1,923 cameras
CollectionT1113: Screen CaptureSnapshot opcode 0x11 with brightness and edge-density filtering
ExfiltrationT1567.004: Exfiltration Over WebhookTelegram Bot API, credential and image delivery per victim
Defense EvasionT1027.002: Software PackingUPX-packed Windows binary staged before use
Defense EvasionT1562.001: Disable or Modify ToolsFive-method PowerShell Defender exclusion framework
Lateral MovementT1210: Exploit Remote ServicesRelay tunnel providing NAT-equivalent access; ONVIF credential dumping
(Unrelated capability)T1112, T1553.004, T1614.001Sandbox tags on the SalatStealer binary specifically, not host-wide
Table 8. ATT&CK mapping. The final row is scoped to the Windows binary alone.

Mitigations

  1. Audit Dahua camera account lists. Any camera reachable on port 37777 between June and July 2026 should be treated as a potential compromise. Look for an account named p2pwn and remove it. Because this chain performs a nine-call credential drain, assume every stored credential was exfiltrated and rotate accordingly.

  2. Disable P2P where not actively required. Cameras with P2P enabled are reachable by serial number alone, and the relay routes across a pool of backend addresses, so address-based blocking is not a reliable control. Disable P2P under Settings → Network unless in use.

  3. Apply Dahua SA-2021-0130. CVE-2021-33044 and CVE-2021-33045 have been patched since October 2021. Unpatched firmware is exploitable with no credentials in under a second. Port 37777 should not be reachable from outside the perimeter.

  4. Assume recovery codes remain valid. Codes generated in this campaign grant cloud-level administrative reset by serial number and remain usable until Dahua alters the derivation server-side. Removing the backdoor account does not invalidate them.

  5. Do not escalate the rbc.ru certificate as a bank-specific incident. More than 11,000 other addresses present the same certificate, on ports typical of proxy tooling. RBC and Qrator infrastructure shows no sign of compromise.

  6. Detection guidance. Login requests carrying "clientType": "NetKeyboard", or "loginType": "Loopback" with "ipAddr": "127.0.0.1", do not occur in legitimate Dahua client traffic. On Windows estates, monitor for Defender exclusion paths set to C:, CIM method invocations against MSFT_MpPreference, and unscheduled policy refreshes.

Summary

CameraSwarm was not a single clean build. The infrastructure predates the campaign by more than a year, and the toolkit is a patchwork of the operator's own code alongside at least four other researchers' published work, accumulated and rewritten over time. Two findings matter most for defenders: the 1,923 cameras carrying the p2pwn backdoor stay accessible until an administrator finds and removes the account, and the offline recovery codes stay valid until Dahua changes the derivation on its side. Removing the backdoor does not invalidate the codes.

We assess with moderate confidence that the toolkit was built to hand access to a third party, based on the transferable recovery-code design and the enterprise-format export pipeline. That is narrower than a confirmed commercial operation, which the evidence does not support. Worth remembering too: this operator is not the only party running these exploits or SalatStealer. Our own reporting ties the same NetKeyboard bypass to a separate Iran-aligned cluster, a Telnet-based Dahua DVR campaign with no CVE at all, and SalatStealer resold across at least five unconnected operations. Dahua exploitation is common ground, not one actor's signature.

→ The entire picture above came from a single open directory the operators exposed themselves, recovered and re-verified through AttackCapture. If you want to see how we surface this kind of attacker infrastructure before it goes dark, book a demo and we'll walk you through the pivots behind this campaign.