h3llh0und

pwnloop, fifty-one machines

A month after the first root, the autonomous loop has taken fifty-one lab machines from an IP to root and cleared a Pro Lab the platform itself certified. The count is the proof. The methodology the runs wrote is the point — and it is why the loop breaks business logic, the one thing it is supposed to be unable to break.

Disclaimer. This is a personal open-source project, built and run on my own equipment and my own accounts. It has no connection to any current or former employer, and no client, employer or production environment was involved at any point. Every single-host target was a Hack The Box machine, and the campaign target was a Hack The Box Pro Lab, all engaged over the platform’s own VPN and within its terms of service. No flag values appear in this post or in the repository. No chain is published for any machine not confirmed retired, and no Pro Lab chain, hostname or credential appears anywhere — flag sharing is a platform violation regardless of a machine’s status, and Pro Labs never retire.

When I wrote the one-week post, pwnloop had rooted seventeen machines and I said the completion count was the least interesting row in the table. I still think that. Here is the plain number anyway, because it is the thing people argue about:

In thirty-six days the loop has taken fifty-one machines from an IP address to root, and cleared a Pro Lab the platform certified at 100%.

The earlier posts cover what the tool is — the single-host loop, lab mode against a Pro Lab, and where it stood after a week. This one does not repeat them. It is about the part I care about and undersold before: the fifty-one machines are proof, not product. The product is the methodology the runs wrote, and the methodology is the reason the loop does the thing an agent is supposed to be unable to do — reason about a bespoke system and break its business logic. That is most of this post. The machine count is the receipt stapled to the front.

Where it is

  day 8 day 36
Machines 17 rooted 51 rooted
Difficulty up the ladder Easy → Insane
Surfaces Linux · Windows / AD Linux · Windows · AD · Kubernetes · OT · 802.11 wireless
Methodology 26 files, ~4,000 lines 25 files, ~6,200 lines
Pro Lab 1 · 3/3 hosts 1 · 3/3 hosts · platform-certified
Releases v1.9.0 v1.19.0, 29 tags

The bold row is the one to read. Everything else is an outcome; that row is the machine that produces outcomes. Six thousand lines of methodology now exist that did not a month ago, and not one line of it was written up front by me. It is entirely the residue of engagements — what each run had to work out on target, distilled into something the next run reads before it starts.

And before anyone reads the thirty-six days as slow: those are calendar days, not working days. I run this in spare hours — evenings, weekends, and whenever there are leftover tokens sitting around doing nothing. No sprint, no cleared calendar, no team. Fifty-one machines is what a side project did in the gaps of a normal month. The wall-clock is a measure of my free time, not the loop’s throughput; point it at a queue and walk away and the limiter is tokens and target availability, not attention.

The method is the whole thing

Strip away the container and the tooling and pwnloop is one idea: a loop that is not allowed to close until it has made itself better at the next one.

The inner loop is an engagement — recon, enumeration, foothold, privilege escalation, cleanup, report, without a human between the phases. That part is table stakes; plenty of things can drive a scanner. The outer loop is the part that matters. When a box falls, the run is required to write back into its own shared reference set whatever generalised: a check that paid off, a primitive worth trying early, an environment gotcha, a mistake that a rule would have prevented. It cannot mark the engagement done until it has. So every run leaves the tool different from how it found it, and the difference is always the same shape — something the run had to reason out on target is now on the page, and the next run reads it instead of reasoning it out again.

That is why the reference set is the number I watch. It is not documentation I maintain; it is compiled experience, and it compounds. The seventeenth box paid for a lesson the thirtieth read for free. The Active Directory reference grew a whole section on delegation-definition rights because one box turned on a low-privileged group holding the right to mark an account trusted for delegation, and the run that untangled it wrote the section so no future run has to. The wireless reference exists at all because one target could only be reached over the air, there was nothing written, and the run that solved it left the section behind. Nobody sat down and authored six thousand lines of tradecraft. Fifty-one engagements did, one hard-won paragraph at a time.

Three rules make this more than a scraper that got lucky, and they are worth stating exactly, because they are also — not coincidentally — the rules that make the loop able to break business logic:

There is a second, quieter reason I care about the methodology over the machines. A completion count is a claim about a month, and a good result is easy to fake with a good month. Numbers about the method carry the signal a flag does not: a re-run of the same target on a later version of the reference set is a controlled experiment — same box, heavier methodology — and the delta is the only honest measure of whether the loop is actually getting better or just getting more attempts. The reference set is the thing under test. The flags are just how you score it.

The hard case: breaking business logic

Here is the objection worth the most, because it comes from people who do this for a living: sure, an agent can pin a version and fire a public exploit — but that is not the job. The job is understanding a system well enough to abuse the assumptions its designers never wrote down, and a model doesn’t “understand,” it pattern-matches. It can’t break a business rule it was never shown.

I want to take that seriously rather than wave the machine count at it, because it is pointing at something real. So first, precisely what business logic is, and why it is the exact case that should defeat an agent.

A business-logic flaw is a gap between the invariants a system’s designers assumed and the invariants its code actually enforces. No memory corruption, no injection of a foreign language — just a rule the application takes for granted that turns out to be only a convention. “Only the person with the emailed token can reset this password.” “A user can only see their own records.” “This directory column is confined to /srv.” “Setup can only be completed once, by us.” Every one of those is a sentence in the designer’s head. Whether it is true depends entirely on what the code does, and the code is bespoke — written once, for this system, nowhere on the internet to have been read before.

That is why it is the double-hard case. It defeats the two objections at once:

The loop does this, and it does it the same way a good operator does — not by recognising an exploit, but by tracing intent to enforcement and probing the seam. A few worked reasonings, each the same move under different clothes:

None of that is memorised, and none of it is a scanner finding. Every one is the loop building a small model of what the system meant to guarantee and locating the sentence where meaning and mechanism diverge. That is what “breaking business logic” is, and the same three rules that make the loop honest make it good at this: it reasons forward from evidence it collected (so it reads the real contract, not a remembered one), it works the actual artifacts (so the model is of this system), and it treats every assumed guarantee as a question rather than a fact. The methodology was not tuned for logic bugs as a special case. Logic bugs are just what falls out of enumerating exhaustively and refusing to trust anything you have not proven.

The spread

Breadth is its own answer to “it can only do the easy, mechanical ones.” The difficulty picture:

tier machines
Easy 21
Medium 22
Hard 5
Insane 1

More Medium than Easy, with Hard and Insane both taken end to end. Difficulty on these platforms is not a length rating — a Hard box is one where the path is non-obvious, the step you have to reason your way to rather than scan your way to. And it is not one trick on repeat:

surface machines
Linux 35
Windows / Active Directory 12
Kubernetes · OT/ICS · 802.11 wireless 3

Active Directory takedowns through delegation and DCSync. A box reachable only over 802.11, cracked and its traffic decrypted. An OT box where the privilege escalation was writing a calibration offset a root safety process trusted. A Kubernetes escape through a service-account token. Different worlds, different primitives — and for each, the loop reads the reference section that world’s earlier boxes wrote, and works the problem there.

The smaller worry, addressed

There is a narrower doubt I should close, though it is secondary to everything above: maybe it only reproduces retired-box write-ups it absorbed in training. Fair for a retired box in isolation, and the discipline already forbids it — you cannot satisfy “trace every action to evidence you collected” with a remembered path, and the name that would trigger the memory is withheld until after root. But the cleaner answer is target selection. I deliberately pointed the loop at freshly-released machines, rooted at or near the moment they went live, before any walkthrough existed to have memorised — you cannot recite a path nobody has written yet. And at a Pro Lab, a multi-host network with no per-box solution to lean on, which it cleared three hosts of three, four flags of four, in about four and a half hours — and which I do not ask you to take on faith, because the platform issues a completion certificate at 100% and nowhere else. The proof of completion is issued by the target, not asserted by the attacker. The lab, its host count and its certificate are in the repository’s labs.md index; the chain never leaves my machine. Memorisation is not an available explanation for a box with no write-up or a network with no per-host answer, and both fell.

The point

To the friends who doubted this could work: the doubt was correct to have, and it is no longer correct to hold. But do not fixate on fifty-one — that is the scoreboard, and scoreboards are a claim about a month. Fixate on the six thousand lines the runs wrote, because that is a claim about every month after this one.

Handed nothing but IP addresses, forbidden from looking up the box, and required to justify every move from evidence it gathered itself, an autonomous loop climbed the whole difficulty ladder, crossed six kinds of attack surface, and — box after box — reconstructed what a bespoke system assumed and stepped through the place its code did not enforce it. That last part is the one that was supposed to be impossible. It is not. It is just method: model the contract, question every guarantee, and never trust what you have not proven. The loop writes that method down a little more completely every time it runs, which means the honest headline is not that it rooted fifty-one machines.

It is that fifty-two will be cheaper than fifty-one was.

The name still goes in last. The root still comes first.

Eurico Nicacio — @h3llh0und

← all posts Eurico Nicacio · h3llh0und