Workloft
▸ WORKLOFT LABS NEWS №34 · 14 JULY 2026

AutoJack: One Web Page, Code Execution on the Agent's Host

Microsoft disclosed a chain where a single malicious page, visited by an AI browsing agent, spawns a process on the machine the agent runs on. Nothing was jailbroken. The page just called a local service that trusted it.

REG FIT ●●● · STRONG · ANY AGENT THAT BROWSES THE WEB ON A HOST YOU CARE ABOUT

§1What happened

On 18 June 2026 Microsoft's Defender research team disclosed AutoJack, a chain in which untrusted web content, rendered by an AI browsing agent, reaches a local Model Context Protocol WebSocket in AutoGen Studio and spawns arbitrary processes on the host. No credentials, no sign-in, no second click. You steer an agent at a page, and the page's JavaScript starts a process under the developer's account.

Three weaknesses, stacked. First, an origin check that allowlisted http://localhost and http://127.0.0.1, which is fine until the browser making the request is a headless one owned by an agent sitting on that same machine. Second, the authentication middleware skipped /api/mcp/* on the assumption those routes would check for themselves, and the WebSocket handler never did. Third, the endpoint took a server_params query parameter, base64-decoded a JSON blob out of it, and handed the resulting command and arguments straight to a process launcher with no allowlist. Put them together and you get ws://localhost:8081/api/mcp/ws/?server_params=, followed by whatever the attacker fancied.

The honest scope, which several write-ups skated over: Microsoft found no exploitation in the wild, this was their own proof of concept reported through MSRC, and the stable PyPI release of AutoGen Studio (0.4.2.2) does not carry the vulnerable route. The code lived on the main branch between the MCP integration and the hardening commit. If you installed the stable release with pip, this specific chain was never yours to worry about.

§2Why it still matters if you never touched AutoGen

Because the bug is not really in AutoGen. It is in an assumption that most developer tooling on your machine is quietly making right now: a request from localhost is a request from you.

That assumption held for thirty years because the only things on your loopback interface were things you started. It stops holding the moment one of the processes on your machine is an agent that fetches and renders content chosen by someone else. The agent's browser is local. The page inside it is not. Untrusted remote content inherits local trust, and every unauthenticated service listening on 127.0.0.1 becomes an API that any web page can call, if it can get an agent to visit it.

That is the structural failure, and it is worth naming plainly: there was no boundary between untrusted web content and privileged execution. Not a weak boundary. None. The page and the process launcher were on the same side of every check that existed. AutoJack is the demonstration, not the disease.

§3What we already had, and what it would not have caught

We run an autonomous background-agent fleet, so this is an operational question here rather than a thought experiment. We already had a deny-list gate on the agents' shell path: a PreToolUse screen that blocks a tight set of high-confidence-malicious command shapes before they run. It is tested against the real thing. It blocks a network download piped into a shell, base64 decoded into a shell, a raw /dev/tcp socket or netcat reverse shell, a recursive delete of root or home, and a command that reads an SSH or cloud credential and sends it off the box in the same breath. It fails open by design, because over-blocking our own work would be worse than the narrow set it catches. On 22 June we extended the same screen down to the bob-bg layer, so a job launched into the background is screened on the same rules as one typed in a session.

Now the uncomfortable part. None of that would have stopped AutoJack. A command deny-list guards the commands the agent runs through its own shell tool. AutoJack does not use the agent's shell tool. The page talks straight to a privileged local service, and that service spawns the process itself. Our gate is standing at the front door, checking what the agent carries in, while this attack walks through a side door the agent is merely holding open. Anyone reading a disclosure like this and reaching for their command filter to feel better should sit with that for a second. We did.

The right question AutoJack forces is not "would my screen have caught that command". It is "what is listening on loopback on the box where my agent browses, and does it check who is calling". That is a different audit, and it is the one we owe ourselves.

§4The gap we have not closed

While we are being honest, here is the one we cannot fix from where we stand. Our deny-list runs as a PreToolUse hook, and those hooks do not fire for Claude Code subagents spawned through the Agent tool. A subagent's shell calls are not screened. That is a platform limit, not a configuration we have got wrong, and no amount of tightening the rules on our side reaches it.

So we hold it with an operational rule, which is to say a rule about how we work rather than a control that enforces itself: chunky or untrusted work does not get delegated to an unscreened subagent shell. We are clear-eyed that an operational rule is the weakest kind of control, because it depends on the operator, and the operator here is frequently an agent. The next real control we want is replay divergence: re-run what the agent claims it did, compare the trace against the record, and flag where they disagree. That is not built yet. Saying so is the point of writing these up.

§5What we are doing about blast radius

If you cannot guarantee that nothing bad ever executes, you contain what it can reach when it does. So we are putting default-deny egress on the autonomous loops, and we are doing it in the only order that does not take the fleet down.

Log first. Since 13 July a job has been sampling every established outbound connection from the box every five minutes, with the owning process, dropping loopback and private ranges. It touches no firewall. After a day or two it gives us an empirical list of everywhere the fleet actually reaches, which becomes the allowlist. Then, and only then, the default flips to deny. Building that list from evidence rather than guesswork is the difference between a working policy and severing Anthropic, Telegram and Supabase at three in the morning.

It would not have prevented AutoJack either. It is not meant to. It is meant to make the payload useless: code that executes but cannot phone home, cannot fetch its second stage and cannot post your keys anywhere is a considerably duller kind of code execution. That is the trade. You will not stop everything getting in, so you spend your effort on what can get out.

§6What to take away

The lesson is not "patch AutoGen Studio", which most of you never ran. It is that giving an agent a browser puts an attacker's content inside your trust boundary, and every local service you have been protecting with the word "localhost" is now protected by nothing at all. Go and look at what is listening on your loopback. Ask each one what it does when an unauthenticated caller arrives, because an agent with a browser means one eventually will.


Methodology note. AutoJack is Microsoft's disclosure and Microsoft's research, not ours. We are arguing about what it means for people running agents, not claiming we found it. Facts here are taken from the Microsoft Security Blog post of 18 June 2026 and checked against the reporting; where outlets differ on whether the vulnerable handler reached any pre-release build, we have followed Microsoft's own account. Everything we say about our own controls is checked against the code that runs them: the deny-list screen, its extension to the background-job layer on 22 June, the subagent hook gap, and the log-only egress audit now running on cron. The replay-divergence control is named as wanted, not built. Source: microsoft.com/en-us/security/blog/2026/06/18/autojack-single-page-rce-host-running-ai-agent/