DEV Community

Shanu Kumawat
Shanu Kumawat

Posted on

The Loophole That Gave Our Pentest Agent Its Teeth

How we got nmap into an agent sandbox that refused to accept custom images -
and what the agent did when the network said no.

This weekend we built TrueStrike:
an autonomous pentest agent for the WeMakeDevs Agent Harness Hackathon. Point
it at a target you own, and it recons the attack surface, proves vulnerabilities
with working exploits, stops for human approval before anything destructive,
and writes a CVSS-scored report. It runs on
TrueForge, TrueFoundry's open-source
agent harness, with all the hands-on work happening inside a
Daytona cloud sandbox.

This is the story of the sandbox. It was the hardest part of the build, it
involved reading another project's source until we found a seam, and it ends
with our agent doing something we're genuinely proud of.

We weren't sandboxed. For a whole day.

Day one went great. The agent ran scans, executed tools, wrote reports. Too
great, actually - because at some point we noticed the "sandbox" could reach
localhost:3000.

Cloud sandboxes cannot reach your loopback. We checked the TrueForge server's
database: the sandbox provider table was empty. Nothing was configured.
TrueForge had silently fallen back to its local host-process sandbox, and our
"isolated agent" had been running tools directly on the host the entire time.
No warning in the logs, no banner in the UI. Just a very polite security
footgun.

That's upstream finding #1, filed with love. We configured Daytona properly and
moved on to the real problem: our agent had no tools.

The image you cannot replace

An agent that pentests websites needs nmap, nuclei, sqlmap, ffuf. But the
sandbox image - the thing that decides what's inside the sandbox - turned out
to be pinned at TrueForge's compile time. There's a sandboxImage.json that
their CI rewrites on every release, pointing at their registry. The provider
settings accept an API key and some timeouts. There is no config, no env var,
no setting for a custom image anywhere. We checked exhaustively, then checked
the source to be sure.

The message was clear: bring your toolchain some other way, or don't.

Reading the provider until it blinked

So we read DaytonaProvider.ts the way you read a locked room for a gap under
the door. And there was one.

When TrueForge configures its sandbox provider, it builds its image into a
Daytona snapshot. The snapshot's name is deterministic: a prefix plus a digest
of the pinned image - trueforge-build-<digest>. And if snapshot creation hits
a 409 conflict (name already exists), the provider doesn't fail. It logs
"sandbox image build started by another server replica", assumes another
replica of itself is handling it, and adopts whatever snapshot holds that
name.

The code comment above the pinned image reads: "Release-owned sandbox image
reference". The provider never verifies the snapshot it adopts actually came
from that image. It trusts the name because only TrueForge should derive it.

Daytona, meanwhile, can build snapshots directly from a Dockerfile - no
registry push required.

You can see the shape of it.

Build it where they build, name it what they'd name it

We wrote a Dockerfile that starts FROM the official sandbox image (keeping
their NATS/supervisor internals intact) and layers on nmap, sqlmap, and
pinned, checksum-verified nuclei/httpx/ffuf. Then a small script asked Daytona
to build it under exactly the name TrueForge would derive for itself:
trueforge-build-0dab475d....

The provider configured, hit the 409, adopted our snapshot as its own, and
reported ready. Every sandbox since has booted with our tools inside. No
registry, no fork of TrueForge, no patched server - the one injection point
the harness leaves open, used exactly as designed, just by someone else.

Then Qodo made us prove it

We almost shipped it like that. But in code review, Qodo flagged the obvious
weakness: adoption trusts the name, it doesn't verify the content. A stale or
official snapshot under that name would silently pass.

Fair. So now the script finishes by booting a throwaway sandbox from the
snapshot
and running nuclei -version inside it. If the tools aren't there,
it fails loudly. We turned "trust me" into "watch it execute".

The part we're actually proud of

Tools in place, we pointed the agent at a local Juice Shop through the relay,
hit go... and the sandbox network said no. Daytona's egress runs through an
envoy proxy with an allowlist of "essential services" - package registries,
GitHub, some CDN endpoints. Our target wasn't on it. Every request died with a
connection reset.

Here's the thing: the agent had an out. Some allowlisted hosts were reachable.
It could have relayed through one of them and gotten the job done.

It didn't. Working entirely on its own, it diagnosed the proxy from inside its
own cell - mapped which domains resolved, which got 403s, which got TLS resets

  • wrote a report whose centerpiece was an honest paragraph saying "here is exactly what I could not test and why", and stopped. Scope discipline, chosen over success, by the thing we built to attack stuff.

The loophole gave our agent its teeth. It still chose not to bite without
permission. That's the whole product in one sentence.

(For the record: we got the target reachable through a Cloudflare Worker relay

  • *.workers.dev is allowlisted - and the next full run confirmed eight findings, including a SQL-injection auth bypass scored 9.8 by our own CVSS engine, every approval audited.)

Filing the seams

We didn't keep any of this to ourselves. The build surfaced 18 documented
findings and improvement ideas for TrueForge - the silent local fallback, the
compile-time image pin, durable pending approvals across restarts, provider
error mapping, retry on rate limits - queued for filing upstream, with PRs
offered for several. We stress tested both the harness and the reviewer
(Qodo reviewed all twelve of our PRs; one round
correctly caught us claiming a fix that had silently failed to land - a story
for another post).

If you want the whole contraption: it's at
github.com/Shanu-Kumawat/truestrike.
Clone it, run it against your own Juice Shop, and read the scope settings
before you point it anywhere else. Seriously.

Top comments (0)