At 20:57:30 UTC on August 28, 2026, a Romanian hosting network, AS62390, started announcing 162.55.80.0/24. The block belongs to Hetzner (AS24940) and holds the servers behind Virtualizor's update endpoint and the Softaculous client area. The announcement stayed up, with one interruption, until about 06:10 UTC on August 30: a little over 33 hours. In that window the attacker obtained a real Let's Encrypt certificate for 26 Softaculous domains and served a backdoored Virtualizor update to hypervisors that happened to check in. Every router running route origin validation looked at the route and marked it Valid.
That last detail is the story. Hetzner had a ROA. The hijacker did not need to defeat it, only to read it. What follows is built on the routing data published by BGPKIT's Mingwei Zhang and Virtualizor's own disclosure, plus what our ASN data says about the two networks that carried the route.
Two waves and an eleven-hour lull
RIPE RIS collector rrc00 logged the forged /24 at 20:57:30 UTC. Route Views saw it at 21:01:04. Within its first minute the route reached 584 collector peers, and about three hours in it peaked at 615 peers simultaneously diverted. Against the 368 RIS peers Virtualizor's disclosure counts, about 72 percent had a best path through AS62390 while a wave was active, and every one of the 368 carried the route at some point.
Softaculous says it reported the hijack to Hetzner and escalated repeatedly. At about 08:50 UTC on August 29, roughly twelve hours after the first announcement, Hetzner applied the standard fix: announce your own 162.55.80.0/24 so the forged /24 no longer wins on prefix length. With two /24s in the table, routers fall back to AS path length, and Hetzner's path (852 3320 3320 3320 24940 from one collector's view) was shorter than anything routed through Romania. Diversion dropped to near zero within minutes.
Then, at about 14:30 UTC, Hetzner withdrew the countermeasure. For five and a half hours no /24 existed anywhere in the public view. At 19:55:25 the impostor came back with the identical path signature, and the second wave ran until the last forged record at 05:45:16 on August 30. It ended the same way the first one had: the disclosure's snapshots show Hetzner's own /24 back at 348 of 349 peers by 05:50, and this time it stayed. BGPKIT counts about 10,600 withdrawals across the whole window. This route flapped hard.
The withdrawal is the decision that is hardest to defend in hindsight. Hetzner presumably saw the forged route disappear, took that as the end of it, and went back to the tidy /16. The attacker was watching too. If you ever fight a hijack with a more-specific, leave it up until the offending network has explained itself, not until the route goes quiet. Hetzner seems to have drawn the same conclusion the second time around: its /24 stayed in the table after wave two, and RIPEstat last saw it on September 2. Softaculous adds, "for transparency", that Hetzner never notified them of the hijack and only acknowledged it on August 31, after being contacted again.
A /24 beats a /16 on every router, no exceptions
BGP has no notion of who owns a block. A router picks the most specific matching prefix first and only then compares paths, local preference, and everything else. Hetzner announces 162.55.0.0/16 as one route (the block is a 1992 legacy allocation in the RIPE database). Anyone who injects a /24 inside it wins on every router that accepts the announcement, because prefix length is checked before any tie-break runs. Deaggregation is routine traffic engineering, so a new /24 from a network that already originates a few dozen of them raises no alarm. The 584 peers in the first minute are BGP doing its job.
The origin was real, so validation said Valid
Here is the AS path most collectors recorded for the forged route, in the usual observer-to-origin order:
29504 15935 6204 62390 24940 AS24940 is Hetzner, at the origin position, exactly where a legitimate Hetzner route would put it. AS62390 sits one hop to the left, posing as Hetzner's transit provider. AS6204 is the real transit that accepted the announcement and passed it to the world. Hetzner's routers were never involved. The attacker wrote Hetzner's ASN into the path and let everyone else's routers do the rest.
Route origin validation checks two things against the ROA: the rightmost ASN in the path and the prefix length. Hetzner's ROA at the time read 162.55.0.0/16, origin AS24940, maxLength 24, and BGPKIT's ROA history shows it unchanged through the whole window. A /24 with origin 24940 is inside that envelope. Valid. Every validating router on the internet accepted this route on the same grounds it accepts Hetzner's own.
The technique is not new. We covered it in July's post on RPKI blind spots, with the 2022 Celer Bridge hijack of Amazon space as the example. What is new is the reach, the duration, and a payload that was a software update rather than a phishing page.
The maxLength 24 in Hetzner's ROA did the attacker a favor
A ROA can cover exactly the prefix you announce, or it can carry a maxLength that pre-authorizes every more-specific down to that length. Hetzner signed 162.55.0.0/16 with maxLength 24, which authorizes the /16 and all 256 possible /24s inside it for AS24940 in one object. Convenient if you ever want to announce a /24 for traffic engineering. Also, as RFC 9319 spells out, an invitation: anyone who can forge the origin gets the more-specific for free.
Had the ROA covered the /16 alone, the forged /24 would have been RPKI Invalid and every network that drops invalids, which today includes most large transit providers and exchange route servers, would have refused it. The attacker could have fallen back to a forged /16, which loses on path length instead of winning on prefix length, and Hetzner would still have been exposed on networks that do not validate. The footprint would have been a fraction of what it was. Minimal ROAs are the cheapest fix in this story and the one most operators skip.
Hetzner has since stopped skipping it. As of September 3, both the RIPE NCC and Cloudflare validators list the ROA as 162.55.0.0/16, AS24940, maxLength 16. Replay the same announcement today and it comes back Invalid. Nobody at Hetzner has said so publicly, but the object changed between BGPKIT's snapshot on August 31 and the time of writing, and it changed in exactly the direction RFC 9319 asks for.
ASPA closes the hole ROV leaves open. With an Autonomous System Provider Authorization, Hetzner signs the list of its transit providers. A validator walking the forged path reaches the hop from AS24940 to AS62390, finds AS62390 absent, and stops. In BGPKIT's words, the "up-ramp from the origin collapses to zero hops and the algorithm halts at Invalid." RIPE NCC has accepted ASPA objects since December 2025, with ARIN and APNIC following this year, and Hurricane Electric's count on September 2 puts 2,543 routed ASNs behind one: 2.89 percent. Hetzner was not among them, no network on the forged path was, and the set of operators verifying ASPA in production is smaller still.
| Control | Verdict on the forged /24 | Status |
|---|---|---|
| ROA 162.55.0.0/16, AS24940, maxLength 24 what Hetzner had signed | Valid, installed everywhere | In place on August 28. Reissued without the maxLength by September 3. |
| ROA 162.55.0.0/16, AS24940, no maxLength minimal ROA per RFC 9319 | Invalid, dropped by every validating network | Not in use then. In place now. |
| ASPA record for AS24940 signed list of Hetzner's providers | Invalid at the hop from AS24940 to AS62390 | Not published. Few networks verify ASPA yet. |
| Customer prefix filter at AS6204 accept only what AS62390 may originate | Rejected before it left Romania | Absent or bypassed. |
The fourth row deserves a word. AS6204 accepted a Hetzner /24 from a customer that does not own it. A prefix filter built from that customer's IRR objects or ROAs, which MANRS has asked transit providers to run for a decade, rejects the announcement at the session. No cryptography, no new RFC. It requires the transit provider to bother, and this one did not.
Domain validation rode the same route to a real certificate
The certificate is what turned a routing incident into a supply chain attack. Virtualizor's update client talks HTTPS to files.virtualizor.com, so a hijacker with a self-signed certificate gets a TLS error and nothing else. The attacker instead asked Let's Encrypt for a certificate covering virtualizor.com, api.virtualizor.com, files.virtualizor.com, softaculous.com, and 22 more names, and answered the domain validation challenge from the impostor server at 162.55.80.8, which even carried the reverse DNS name server.softaculous.com. Let's Encrypt looked up the name, the route led to the attacker, the challenge response was there. The certificate appeared in the CT logs at 21:30:29 UTC, 33 minutes after the first forged announcement. It has since been revoked, which helps nobody who already installed the update.
Let's Encrypt was the first CA to deploy multi-perspective validation, in 2020. Today it checks from five perspectives, one primary and four remote, and requires a quorum of them to agree; the CA/Browser Forum made multi-perspective checks mandatory for every CA in 2025, with BGP attacks in mind. It did not help, and to be fair to Let's Encrypt, it could not have. Multiple perspectives catch a hijack that only part of the internet sees, which is what an equally-specific announcement produces. When 72 percent of RIS peers follow a more-specific, a quorum of vantage points landing on the impostor is the expected outcome. Domain validation cannot tell a global hijack from a real change of hosting.
What the update client did with the diverted traffic
Hypervisors that polled for updates during a wave got a package advertising version 3.2.9.8. Installing it left the host reporting 3.2.9.7, which BGPKIT points out is an indicator on its own. AlbaHost, a provider that found the modifications on 5 of its 34 hypervisor nodes, described the rest of the chain on LowEndTalk: the injected code installed a Java 17 runtime if none was present, fetched a payload named widdow.jar into /tmp, registered a systemd unit called java-jre-update.service for persistence, added an ed25519 key to root's authorized keys, created a user named proxyuser, and called home to 31.77.220.138 on port 2025 and the domains cdn.nerat.cc and connect.ne-rat.xyz. Root, in other words, on the machine that runs everyone's VPSes.
The update client did not verify package signatures. Virtualizor says so plainly: "Our product update clients did not yet cryptographically verify update packages." That is the second avoidable failure in this chain. A signed package survives a hijack, a rogue CA, and a compromised mirror all at once, because the client checks a key it already holds instead of trusting whatever the network delivered. Virtualizor 3.2.9.9, released September 1, ships a scanner for the indicators. Signing is still listed as future work.
How many hosts were hit is not known. Virtualizor says "a handful of servers" and admits it cannot produce a definitive list, since the malicious responses were served by the attacker's system and never reached its own logs. AlbaHost's 5 of 34, reported by The Hacker News, is the only hard number from a victim so far.
What our data says about the two networks
An autonomous system number is just an identifier; the registry data behind it is what tells you who runs the network and what kind of business it is. The Ipregistry page for AS62390 lists it as Nexonhost SRL, registered through RIPE NCC in Romania, type hosting, 38 IPv4 prefixes covering 9,728 addresses, and five upstreams: Cogent, Vodafone, Arelion, Orange, and AS6204. Hurricane Electric shows 30 of its prefixes as RPKI-valid and none invalid, so this is a network that has signed its own ROAs. One more detail from our peer list: AS24940, Hetzner itself, is among the networks AS62390 exchanges traffic with directly, most likely across an exchange point. The hijacker and the victim were already neighbors.
AS6204 is registered to INTERKVM HOST SRL, also Romania, also RIPE NCC, also hosting. It originates only five IPv4 prefixes of its own but sits in front of 210 peers and 126 downstream networks, AS62390 among them, and its nine upstreams are the usual tier 1 set: Cogent, Arelion, NTT, GTT, Orange, Tata, Sparkle, Telefónica. Its RPKI record is less tidy than its customer's: Hurricane Electric counts 45 prefixes with AS6204 at the origin that fail validation against 6 that pass. That is how a route from a small Romanian hosting company reached 615 collector peers in three hours. BGPKIT saw the forged route leave AS6204 through sixteen different neighboring networks, Orange and GTT most often, and none of them filter what a customer's customer sends.
Who actually sent the announcement is unknown. A hijack "from AS62390" means an announcement carrying that ASN entered AS6204's session, which fits an operator acting deliberately, a compromised router, or a NexonHost customer with BGP access. Neither company has published a statement as of this writing, and Virtualizor's disclosure avoids attributing intent. The same-path second wave, five hours after Hetzner's countermeasure came down, argues for someone watching the table rather than an accident.
An RPKI Valid badge on a route says one thing: the origin ASN in the path is allowed to originate that prefix. It does not say the origin ASN sent the route, or that the network next to it is a real provider. The network that sent this route has valid ROAs of its own and a clean-looking registry record. If validation state feeds a trust decision anywhere in your stack, this is your counterexample.
What to change on your own network this week
If you originate prefixes, reissue your ROAs without maxLength unless you announce the more-specifics today, and add one ROA per more-specific if you do. Publish an ASPA object for your ASN: RIPE NCC, ARIN and APNIC accept them today, the other two RIRs are expected to by year end, and it takes an afternoon. Then monitor your own space for more-specifics and unexpected paths, because a forged-origin hijack trips no validity alarm and you will otherwise learn about it from your customers. Twelve hours from first announcement to countermeasure is what that looks like.
If you provide transit, filter customer sessions against their ROAs and IRR objects, and validate ASPA if your vendor supports it. If you ship software that updates itself, sign the packages and pin the key in the client, so the next 33-hour window delivers nothing but a verification error.
Routing data is one of the raw inputs behind Ipregistry: every lookup returns the originating AS, its prefix and registry, and whether the block belongs to a hosting provider, drawn from the same global tables this incident played out in. That tells you who a route claims to come from, not whether the claim is true. You can try the API with 20,000 free lookups to get started.
Keep users in the loop Ship release notes that get read. Try Noticeable