North Korean spies hid a backdoor inside a load balancer

Published

On the edge of at least two South Korean corporate networks, a HAProxy load balancer was doing exactly what a load balancer is supposed to do. Requests came in, requests went out, the health checks stayed green. What nobody noticed was that the binary had been rebuilt from source with an extra plugin compiled straight into it, and that whoever built it had left their own name for the thing sitting in the debug strings: ted.

That is the whole trick. The backdoor was not dropped next to HAProxy. It was HAProxy.

Researchers at Rapid7 Labs published their analysis of the toolkit this week, describing a previously undocumented Linux framework used against South Korea's automotive and media sectors in campaigns they believe date back to early 2025. Alongside the load balancer implant sit an SSH keylogger, a stager, and a remote access trojan the researchers call curlRAT, plus trojanized copies of five system daemons: crond, agetty, atd, sshd and polkitd. Rapid7 attributes the set with medium confidence to North Korean state-aligned operators, and says the likely goal was long-term espionage rather than theft or disruption.

A doorman who reads the mail

HAProxy sits in front of web applications and decides where each request goes. To do that it has already decrypted the traffic, which makes it one of the most valuable places on a network to stand and one of the least watched.

The ted plugin registers itself as a custom HAProxy filter and hooks the built-in HTTP parser, so it reads requests out of the same internal memory structure the load balancer uses for its own routing decisions. From there it logs high-value traffic, lifts session cookies, and runs an IP check against a list to decide whether to inject an attacker-supplied script into the page a particular visitor is about to receive. The rest of the traffic is balanced normally. A watering hole, in other words, built into the machine that everyone's browser already trusts to be in the middle.

Its configuration does not live in HAProxy's config file. The implant reads its own operating mode, its targeting regexes, its script-injection rules and its allowed operator keys from a file named haproxy-1000.cache, wrapped in two layers: a chained XOR seeded by the file's first byte, then a 67-character substitution cipher rebuilt at startup. Access control lists come from two sibling files. The same substitution alphabet turns up in the SSH keylogger, which is part of how Rapid7 tied the components together.

The command that never reaches a server

The operator talks to ted by making an ordinary-looking web request. When a request arrives for the path /favorite_list_2x_m500_ico.jpg, the plugin flips into command mode.

What it does next is the part worth pausing on. Before handling anything, it reaches into HAProxy's own statistics counters and decrements them by hand, using memory offsets hardcoded for build 2.8.12: the per-backend and global connection counts first, then cumulative connection, request and byte totals, then the peak-value gauges, each guarded so it cannot wrap around. The request is then carved out of the buffer entirely, with the forward pointers and buffer lengths zeroed so HAProxy concludes there is nothing left to pass on.

The result is that the attacker's command stops at the load balancer. No backend server ever sees it, no backend log ever records it, and the load balancer's own traffic statistics show no sign that it happened. Five operator commands run through that channel: a beacon that returns a version banner, file upload, file download, shell command execution with output returned XOR-encrypted, and a config update that is validated before it replaces the live file.

Five daemons that are no longer yours

Getting onto the box is a separate job, and Rapid7 is candid that it could not establish how the intruders got in. Both victims were running an edge web server with ports 80, 443 and 25 open, hosting a groupware login portal and a mail server. Either is a plausible entry point, and the researchers note that North Korean operators have repeatedly gone after exactly those surfaces, but they stop short of naming a vulnerability. The specific entry point and any associated CVE remain unconfirmed.

Once inside, a stager does the spreading. It decrypts its configuration with a single-byte XOR, checks it has root, then profiles the host down to distribution, version, kernel release and CPU architecture so it can pick the right payload. It only proceeds if HAProxy or cron is running. Then it overwrites the system's real crond with a trojanized build carrying curlRAT inside it, restarts the service, and copies the timestamp from /usr/bin/ssh onto the new binary so a directory listing looks undisturbed.

Then it cleans up after itself. Using a staging file named /tmp/jasper-log, chosen to read like something a Java web server would leave behind, it strips keywords such as tmp, wget, cron and crond out of the shell history and out of the system logs, including messages, audit.log, secure, syslog and auth.log. Not a wipe, which would be obvious. A selective edit.

The SSH keylogger, meanwhile, sits inside the password authentication path. It compares each password typed by a real user against a set of hardcoded master passwords the operators kept for themselves, then encrypts the rest with that same substitution cipher and files them away under /var/lib/sshd/.

A trojan that sleeps twelve hours at a time

curlRAT is deliberately slow. By default it wakes once every 43,200 seconds, which is twice a day, fetches a configuration file from its command server over HTTPS using libcurl, and goes back to sleep. Operators who need to move faster can set a flag that drops the interval to 30 seconds. A single byte in the fetched config selects one of six handlers: run a command list and return the output, write a new config, drop a staged payload, open a reverse shell as root, send a system beacon, or open a full interactive terminal.

Every victim gets a stable identity: the RAT concatenates a fixed version string, the hostname, the IPv4 address and the machine's hardware UUID, then hashes the result and sends it as an HTTP header on every check-in. A second thread does nothing but watch HAProxy, reading its PID file once an hour and reporting one of four states back to the operators. Started, stopped, restarted, reloaded. Before it starts, it checks for a file that only exists on virtualized hosts and quietly gives up if it is missing, which is a cheap way of avoiding an analyst's bare-metal sandbox.

What the attribution does and does not prove

Rapid7's confidence is medium, and the reasons it gives are worth separating. The targeting fits: South Korean media and automotive companies, held for long-term collection. The cryptography is simple and reused across components in a way that suggests one development shop. And the hardcoded command servers overlap with infrastructure that the ThreatFox and maltrail trackers associate with APT37.

None of that is a confession. Infrastructure attribution rests on third-party tracker labels rather than Rapid7's own telemetry, and the comparison drawn to Kimsuky's habit of attacking exposed mail and groupware servers is a statement about tradecraft, not about who typed the commands. What the report does establish firmly is capability: a Linux implant compiled into a production load balancer, tuned to a specific HAProxy build, with the operators' own log-cleaning and statistics-fudging routines written in. The earliest samples appeared on VirusTotal in mid-2025, and the HAProxy build involved dates the earliest possible compilation to late 2024. This was patient work.

It also fits a pattern in the region. Shortcut files have become the dominant lure against South Korean targets, and campaigns against South Korea have leaned steadily toward quiet, long-dwell access rather than smash-and-grab theft.

Treat the load balancer like a server, not plumbing

Rapid7's advice is short and follows directly from the design of the implant: any edge component that terminates user traffic, handles SSL, or loads runtime modules deserves the same scrutiny as the application servers behind it. Concretely, that means three things. Verify the integrity of your system daemons rather than trusting their timestamps, because these ones were timestomped and their installation scrubbed from the logs. Correlate network activity independently of the component's own logs and counters, because ted edits both. And look at process memory, since the malicious code is inside a legitimate, correctly functioning HAProxy process rather than beside it.

Defenders can also hunt for the artifacts directly: cache files named haproxy-1000.cache through haproxy-1002.cache in a home directory, a staging directory called snapd under /var/lib on a host that does not use snap, FIFO pipes matching /tmp/t*_w.pipe, and any inbound request for /favorite_list_2x_m500_ico.jpg.

Indicators of compromise

Command servers (defanged): img.monderhouse[.]space, img.smartnords[.]site, img.darklights[.]store, img.socialteams[.]store, img.worksongo[.]store, img.responsive.pstatic[.]autos.

The full hash set, along with the mapped ATT&CK techniques, is in Rapid7's report. The uncomfortable part of this one is not the sophistication of any single component. It is the choice of ground. A load balancer is treated by most security teams as plumbing, monitored for uptime and little else, and it is the one box on the network that sees every request in plaintext before anyone else does. Somebody worked that out and spent a year quietly living there.

This briefing is provided by IntelFusions for informational and defensive purposes only. It is based on sources assessed to be reliable at the time of writing, and analytic judgments carry the confidence levels indicated. Indicators of compromise are defanged; re-arm them only in controlled environments. IntelFusions is not affiliated with the organizations named and makes no warranty as to completeness or accuracy.

Read the full analysis on IntelFusions