← All posts

Invalid by Design: What the Softaculous BGP Hijack Actually Proves About RPKI

For 33 hours in late August, a rival network hijacked a block of Hetzner-hosted infrastructure serving Softaculous and Virtualizor, using it to issue a valid TLS certificate and push a malicious software update. The stolen route was RPKI-invalid the entire time, on file, checkable by anyone. It still won on most of the internet.

At 20:57 UTC on 28 August, a network nobody at Hetzner or Softaculous had dealt with before started announcing 162.55.80.0/24 to the internet. It was a small, unremarkable-looking slice of Hetzner’s own address space — the block hosting Softaculous’s client area, its billing pages, and the update endpoint that every Virtualizor-licensed control panel on the internet periodically calls home to. For the next 33 hours, with an 11-hour lull in the middle, a meaningful share of global traffic bound for that block went to the hijacker instead. Some of it was Let’s Encrypt’s own domain-control validation, which is how the attacker walked away with a certificate for virtualizor.com that no browser would ever flag. Some of it was update checks from real Virtualizor installations, a subset of which received a package with a systemd unit named java-jre-update.service — a name chosen to look like exactly the kind of boring maintenance job an administrator scrolls past. Virtualizor’s own incident report is admirably specific about all of it, down to the timestamps and the version number, 3.2.9.9, that shipped a mitigation tool days later.

Nothing about that paragraph required breaking cryptography, guessing a password, or exploiting a bug in Virtualizor’s code. It required one thing: announcing a route. And the part that should actually change how you think about your own network is this — Hetzner had already done the thing every BGP security checklist tells you to do. It had a Route Origin Authorization on file for this exact space, signed, correct, and strict. I checked it myself, live, while drafting this. It made no difference for a day and a half.

The problem: BGP has no opinion about who’s lying

Here’s the part worth being precise about, because it’s not quite what the press coverage implies. BGP’s own best-path process — RFC 4271’s AS-path comparisons, local preference, MED — only ever arbitrates between competing paths to the identical prefix. It never had to arbitrate here, because Hetzner’s 162.55.0.0/16 and NexonHost’s 162.55.80.0/24 aren’t the same route. Both get accepted into the RIB, both get advertised onward, no contest, no tie-break. The decision that actually mattered happens one layer down, at forwarding: when a packet’s destination matches more than one route in the table, RFC 1812 §2.2.5.2 requires the router to use “the most specific matching route (the longest matching network prefix)” — full stop, before AS-path length or local preference or anything BGP itself weighs ever gets consulted. That rule exists because it’s the only sane way to do traffic engineering at internet scale: a network that needs to carve out a specific block for a specific purpose has to be able to say so more precisely than the block it sits inside, and every router has to honour that without asking permission first. It is also, structurally, the exact same mechanism a route hijack needs. The attacker, operating as AS62390 (NexonHost Srl) and reaching the wider internet through transit provider AS6204 (Zet.net), didn’t need to win a BGP argument against Hetzner’s AS24940 at all. It needed to out-specify it. Every router that installed both routes forwarded packets for 162.55.80.0/24 to the hijacker, correctly, by the books, because that’s what the more specific route is for.

Those forwarded packets landed on a real machine, not an abstraction: for the run of the hijack, softaculous.com resolved to 162.55.80.8, and the impostor host sitting at that address carried the giveaway reverse-DNS name server.softaculous.com.

BGP selecting the more-specific /24 announced by the hijacking AS over Hetzner's legitimate /16, diverting traffic to the attacker's server

What made this particular hijack worth writing about, rather than just another entry in an incident tracker, is how it was discovered and closed. Hetzner did not catch it and did not proactively notify Softaculous — by Virtualizor’s own account, Softaculous found the anomaly and reported it, and Hetzner’s mitigation, re-announcing the /24 directly to out-compete the hijacker on its own turf, only started around 08:50 UTC on 29 August, roughly twelve hours after the first announcement. A second wave followed that evening and ran until the route was finally clean at 06:10 UTC on 30 August. Reported by a customer, fixed by out-shouting the attacker at the same protocol layer it exploited. Nothing about the fix used any of the cryptography the internet has spent the last decade building specifically for this failure mode.

The challenge: the ROA existed, and RPKI still lost

Here’s what I actually went and checked, because I didn’t want to take the press coverage’s word for it. RIPEstat’s live RPKI validation API, backed by the Routinator validator, returns exactly one Validated ROA Payload for 162.55.80.0/24: origin AS24940, covering prefix 162.55.0.0/16, max-length 16. That’s not a loose authorisation. Max-length 16 means the ROA covers the /16 and nothing narrower — no /17, no /24, from any origin, including Hetzner’s own AS24940. Query the validator against NexonHost’s /24 announcement from either side, attacker’s ASN or victim’s, and the answer is identical: invalid_length. This was never an “unauthorised origin” ambiguity, the kind of case where you can squint and imagine a legitimate customer with a permissive ROA getting flagged by accident. It was unambiguous, machine-checkable, cryptographically signed proof that this exact announcement could not be real, sitting in the global RPKI repositories the entire 33 hours the hijack ran.

An RPKI validator comparing the hijacked /24 announcement to Hetzner's strict /16 ROA and returning invalid, forking into networks that enforce Route Origin Validation and networks that don't

The reason that proof didn’t stop anything is that a ROA is a publication, not an enforcement mechanism. Signing one tells the internet what’s true. Whether any given network actually rejects an announcement that contradicts it — Route Origin Validation, ROV — is a separate operational decision every AS in the path has to make and keep making. I wrote about AWS’s own push to automate ROA hygiene for BYOIP customers a few weeks ago, and flagged then that signing your own prefix “protects it only on networks that bother to check.” This incident is what that sentence looks like when it stops being a caveat and starts being the whole story: measurement cited by RIPE Labs’ analysis and Hurricane Electric’s RPKI/ASPA adoption report put global ROA coverage at a record 67.43% of announced prefixes on 29 June 2026 — and, in the same breath, only around 12.3% of autonomous systems fully enforce ROV on what they accept, while roughly 36.2% don’t validate at all. Signing has scaled. Checking hasn’t kept pace, and the gap between those two numbers is precisely the width of the window NexonHost’s route walked through.

It gets more uncomfortable once you follow the certificate. Certificate authorities are now required, under CA/Browser Forum ballot SC-067, to corroborate domain-control validation from multiple, geographically separated network vantage points before issuing — Multi-Perspective Issuance Corroboration, a mechanism Let’s Encrypt itself pioneered in 2020 specifically against routing attacks on certificate issuance, working with the same Princeton researchers who first demonstrated the attack class. Let’s Encrypt’s own description of why it works is direct: it “makes the kind of attack described earlier more difficult because an attacker must successfully compromise three different network paths at the same time.” That threat model is built for an attacker with a foothold — someone who can poison the view from one or two vantage points while others still see the truth. A more-specific-prefix hijack doesn’t have a foothold in that sense. It doesn’t win in one place; by the same longest-match arithmetic that moved the traffic, it tends to win almost everywhere simultaneously, because every validator doing that lookup reaches the identical, correct- by-its-own-rules, wrong conclusion independently. Multi-perspective validation assumes the lie is local. This lie wasn’t.

The solution: sign it, enforce it, and don’t let a padlock be the only gate

None of this is an argument against RPKI — Hetzner did the right thing, and a hijacker facing a network that actually enforces ROV loses instantly and invisibly, no incident report required. It’s an argument for treating a signed ROA as the first control in a chain, not the whole chain:

  • Publish strict ROAs, and mean strict. Max-length matching the prefix exactly, the way Hetzner’s already does, is what turned this from an “unauthorised announcement” grey area into an unambiguous invalid the moment anyone checked. A permissive max-length would have left room for argument; this one didn’t.
  • If you speak BGP yourself — Direct Connect, a Transit Gateway peered to on-premises routers, colocation transit — enforce ROV on what you accept, not just what you publish. Every network that dropped NexonHost’s /24 on sight did it because it was checking, not because the ROA existed. The ROA is necessary. It is not sufficient without a validator in your own path set to reject, not just log.
  • Monitor your own announced space for competing or more-specific announcements. Softaculous found out from a customer, not from automated detection. Route-monitoring services exist precisely to close that gap between “a hijack starts” and “a human notices,” and twelve hours is a long time to be the last to know about your own prefix.
  • Don’t let domain-control validation be the only gate on anything supply-chain-critical. A DV certificate proves whoever answered an HTTP or DNS challenge controlled the address at that moment — precisely the assumption a routing attack breaks. Virtualizor’s fix, going forward, is cryptographic signing of update packages independent of TLS. That’s the right lesson, arrived at the expensive way.
  • Have an escalation path to your upstream before you need one. Twelve hours to first mitigation, against a hijack that RPKI had already labelled invalid, is a process gap as much as a protocol one.

The footprint

The uncomfortable read of this incident isn’t that RPKI failed. It’s that RPKI did exactly its job — it told the truth, precisely, the entire time — and truth-telling was never the part of internet routing that was actually scarce. Enforcement is, and enforcement doesn’t arrive as a side effect of more prefixes getting signed. It arrives one operator at a time, deciding that an invalid route gets dropped rather than logged, and that decision still isn’t the industry default eight years after the 2018 Amazon Route 53 hijack made the same point with different numbers. Longest-match routing isn’t a bug some future protocol revision quietly patches out, either — it is the mechanism traffic engineering depends on every day, which means the fix has to sit a layer above the routing decision, not inside it. Sign your prefix strictly. Then go check whether your own edge actually rejects what it says it should — because for 33 hours in August, a cryptographically provable lie moved real traffic anyway, and the only thing standing between that and every other Direct Connect customer’s own space is whether somebody, somewhere in the path, bothered to look.

Cover of Cloud Networking and Resilience
Apress Media, LLC

Order the book

Cloud Networking and Resilience

Designing Scalable, Fault-Tolerant, and Highly-Available Cloud Network Architectures

ISBN · Print 979-8-8688-2435-7
ISBN · eBook 979-8-8688-2436-4

Dedicated to Ade (2017–2022). All personal proceeds donated to charities supporting cats in distress.

Affiliate links — proceeds go to charities supporting cats in distress.