[HackerNotes Ep. 198] Halloween Bug Stories: Shameful Sandboxes, Null-Origin Frames, and SQLi to ASPX RCE

Justin, Joseph, Ads, and XSS Doctor swap the bugs that still bother them, from exposed patient records to AI sandboxes full of other users' secrets and an agent that rewrote production on its way to RCE.

Hacker TL;DR

  • Sandbox RCE is only a gadget until you prove what document, tenant, or service you can reach afterward.

  • An AI sandbox exposed other users' conversations, a Slack token, proxied model credentials, uploaded files, and internal network services.

  • A forged LAN device bound an attacker-created ticket to a victim account, then returned cookies and a 90-day refresh token.

  • The simplest bugs won: an unauthenticated camera panel, an empty mobile path, a global agent registration integer, and a migration endpoint returning admin sessions.

Critical Thinking Podcast

Check out the CTBB Discord!

We do subscriptions at $25, $10, and $5. Premium subscribers get access to:

Hackalongs: live bug bounty hacking on real programs, with VODs available

Live data streams, exploits, tools, scripts, and unredacted bug reports

You can also find some hacker swag here.

Need a pentest? Check out CTBB Pentests.

Hack full time? Check out the Full-Time Hunter's Guild.

A Halloween campfire

This episode is the follow-up to last year's hacking horror stories format. Justin, Joseph, Ads, and JD trade bugs and vote on the scariest one.

The migration endpoint that returned admin cookies

Joseph found /api/migrate in the JavaScript of a heavily hardened cloud product. The endpoint requested two identifiers that looked like a nonce and another random ID. Both accepted arbitrary values. Two random numbers produced complete admin session cookies.

The fix reportedly landed within a week or two. The payout took roughly two months. The lesson is practical: search JavaScript for migration, legacy, impersonation, switch, upgrade, and SSO routes. These endpoints often sit between authentication systems, which makes weak parameter validation especially dangerous.

A beautiful chain that went nowhere

JD spent days chaining a chatbot sandbox into what looked like main-page XSS:

  1. An RPC call allowed an arbitrary write into a special sandbox directory.

  2. JavaScript triggered from DevTools produced a reverse shell in the sandbox.

  3. A deprecated artifact feature supplied a delivery mechanism and an iframe.

  4. A missing origin check registered a MessagePort.

  5. A null-origin popup bypassed a source restriction and won a postMessage race.

  6. The port carried proxied fetch requests and attacker-controlled responses.

The catch was the execution context. JD's script ran in a blob inside another null-origin iframe. The fetches were meaningless, and there was no impact. The chain was technically impressive, but it paid nothing.

When testing postMessage, record event.origin, event.source, location.href, and window.opener. “I have XSS” only means something after you identify the exact document and privileges involved.

The shameful AI sandbox

Justin's chatbot target rejected a curl | bash install script. He switched to a harmless script with a syntax error, let the agent inspect it, then replaced the server-side file before the agent ran it. Rotating benign and malicious responses made the replacement look like a normal debugging flow. A realistic “install this skill” request worked even better.

Inside the sandbox he found:

  • Redis data containing other users' conversations;

  • the Slack token used by the bot;

  • a way to inject bot responses into other conversations;

  • an unauthenticated proxy that added model-provider credentials; and

  • a file endpoint exposing uploads from the environment.

The sandbox could also reach several internal network devices, one of which returned company SQL data. The scary part was not an escape. The application had placed sensitive material inside the boundary and trusted the model not to reveal it.

Before looking for an escape, inventory the sandbox: environment variables, mounted files, caches, sidecars, credentials, and reachable network services.

One integer, every tenant

Ads found an agent-registration function in a multi-tenant CRM. A custom header contained a bare integer. Changing it did not register an agent in another tenant, but enumerating further reached a global value. The resulting agent appeared in every account in the ecosystem.

The agent could recommend documents. Ads used that trust relationship to point victims toward attacker-controlled content on an in-scope blog platform where XSS led to account takeover. Markdown image rendering also suggested a path to exfiltrate conversations.

The boundary was not the model's system prompt. It was the registration authorization. For agents, bots, webhooks, and connectors, ask which tenants the object appears in and what a victim can click because it recommended something.

A 3D printer that minted cookies

Joseph created a service ticket, then advertised a fake printer on the local network. A victim's desktop application discovered the device and clicked connect. That request carried the victim's JWT and associated the attacker-created ticket with the victim account.

Later, POST /user/ticket/{ticketId} returned the victim's session cookies and a refresh token valid for 90 days. No credential was stolen. The victim only connected to a device that looked legitimate.

Local discovery is an authentication surface. Test LAN broadcasts, mDNS, BLE, recovery modes, pairing flows, and any identifier that the cloud API treats as proof of ownership.

Mobile API, empty path, entire patient database

JD found nothing interesting on the web application, then proxied the Android app. Its scheduling route used /api/scheduling/{md5}. Traversal payloads produced strange responses. Removing the hash entirely returned gigabytes of patient data: names, SSNs, phone numbers, diagnoses, and doctors.

The program first tried to classify it as a high because there was no availability or code-execution impact. JD pushed back, and the healthcare provider paid the maximum. The web scope looked clean; the mobile endpoint did not. Always test missing values, not only malformed ones.

Scope can be the bug

Justin found an informant database while enumerating an in-scope government IP range. The JavaScript exposed an API, and numeric iteration returned the records. Joseph found an unauthenticated camera admin panel on an in-scope internal IP reachable through a VPN. The target is deliberately not named because the episode explicitly asks for a bleep.

Treat scope as an attack-surface map. IP ranges, internal hosts, cameras, and forgotten admin panels can matter more than the main application.

Justin also described a war-drivable IoT leak: identify a device, deauthenticate it, connect to its recovery access point, read a Bluetooth device ID, then submit that ID to the cloud API. The cloud returned household names, addresses, email addresses, and phone numbers. Other devices spoke over 802.15.4, Zigbee, or Thread, and firmware review exposed additional attack surfaces.

The load test that reached the dispatcher

Ads found type confusion in a load-test assertion. The platform expected an integer in a click step, but failed to validate it before compiling the script as Node.js. That produced RCE on a worker.

The worker was segmented, so the first impact was limited. From there, AWS metadata credentials led to the dispatcher, where Ads could manipulate queues and read other customers' test source code. Shared infrastructure turns a small assertion bug into a fleet-level problem.

Anything called an assertion, filter, formula, template, or expression deserves eval-level scrutiny. Test types, runtime objects, metadata access, and internal control-plane credentials.

The AI that rewrote production on its way to RCE

Justin's final story closes the episode with one last warning about autonomous agents. He asked an agent to obtain RCE from a known primitive while he left for dinner. The agent found SQL injection, used it to overwrite the production configuration that controlled where reports were stored, redirected that path to a writable location, uploaded an ASPX file through another primitive, then hit the route that mapped to the uploaded file and triggered code execution.

Most importantly, it cleaned up after itself by reverting the configuration in a Python script. The result was a beautiful chain and a terrible reminder: an autonomous agent can cross several boundaries while pursuing a goal, and cleanup does not make an unauthorized production change safe. Keep destructive operations out of agent scopes, require approval before writes, and make the test environment real.

The spooky pattern

The bugs crossed boundaries the product assumed were safe: migration versus authentication, blob versus main window, agent versus tenant, sandbox versus secret, LAN device versus account, mobile API versus web API, worker versus dispatcher. The useful pattern is not "AI finds bugs". It tests stranger hypotheses, reads more source, and sometimes takes actions a human would have stopped before validating.

Test the weaker side of every boundary. Before celebrating RCE, prove where the code runs and what it can touch.

That's it for the week, keep hacking!