Two Stored XSS in MagnusBilling: From CTF Curiosity to CVEs


What is MagnusBilling?

MagnusBilling is a VoIP billing platform launched in 2006, originally a commercial product. Since 2015, it has been released as open source, with the goal of involving the community to “improve even more” the product and provide a complete billing system for the telecom world.

It relies on a classic stack: Asterisk for VoIP handling, ExtJS for the frontend, and Yii Framework for the backend. According to their site, the project boasts:

  • Over 11,600 installs
  • 380 production deployments
  • 695,000+ lines of code
  • A 4-person team maintaining it

Installation is… straightforward:

wget https://raw.githubusercontent.com/magnussolution/magnusbilling7/source/script/install.sh
bash install.sh

Yes, it reboots your server, and yes, the default credentials are root / magnus.

There’s also a Telegram support group, a YouTube channel full of tutorials, and commercial support options from Magnus Solutionยฉ for those who prefer guided help.


Backstory: TryHackMe, Curiosity, and a Backdoor

I first encountered MagnusBilling during a TryHackMe CTF, where it was used in a vulnerable machine. That particular version contained a backdoor from 2023, which raised my interest.

After the challenge, I downloaded the latest version to take a quick look. No reverse engineering, no fuzzing, no automated tools - just curiosity and a browser. Within less than an hour, I came across two Stored XSS vulnerabilities, including one that doesn’t even require authentication.

This wasn’t a formal audit. I stopped after those two findings. But it suggests the surface might be larger than expected.


CVE-2025-2609: Unauthenticated Stored XSS in Login Logs

When a user fails to log in, MagnusBilling logs their username - which is expected. But the application doesn’t sanitize or encode that field. So if someone sends a JavaScript payload as their username, it gets stored directly in the database.

Later, when an administrator checks the login logs, that payload is executed in the admin’s browser context.

Proof of Concept

POST /mbilling/index.php/authentication/login
Content-Type: application/x-www-form-urlencoded

user=<img src=x onerror=alert(1337)>&password=random

Then, when an admin accesses:

GET /mbilling/index.php/logUsers/read

โ€ฆthe script executes immediately.

Impact

  • Arbitrary JavaScript execution in the admin session
  • Potential session hijacking
  • CSRF
  • Admin panel compromise

And all this is possible without being authenticated.


CVE-2025-2610: Stored XSS in Alarm Module

The second issue affects the Alarm module, which allows users to store alerts or internal notifications. The message field is stored as-is and displayed without encoding - resulting in another persistent XSS opportunity.

Proof of Concept

POST /mbilling/index.php/alarm/save
Content-Type: application/x-www-form-urlencoded

rows={"email":"test@test.com","message":"<img src=x onerror=alert(document.cookie)>","id":2}

Accessing:

GET /mbilling/index.php/alarm/read

…will trigger the payload in the browser of any admin viewing the alarms.


Why It Matters

These are not complex vulnerabilities, but their presence has significant implications:

  • The first issue works without authentication, which increases its risk profile significantly.
  • User input is rendered directly into the frontend without any escaping or filtering.
  • API responses are served as text/html rather than application/json, making frontend exploitation easier.
  • These modules are part of the core admin workflow - meaning they are frequently accessed.

What makes this even more concerning is that there are public reports of suspicious activity involving MagnusBilling. In particular, a user opened a GitHub issue (#696) reporting that they suspected real-world attacks against their instance. While not confirmed, the presence of an unauthenticated XSS certainly fits the pattern.


Vendor Response & Patch

I reported both vulnerabilities via email on March 19, 2025, but received no reply. I followed up via the official Telegram support group on March 21, and the MagnusBilling team responded promptly. The patch was published publicly that same day.

Patch commit:
github.com/magnussolution/magnusbilling7/commit/f0f083c76157e31149ae58342342fb1bf1629e22

Their response time, once the message reached them, was commendable.


Timeline

Date Event
March 19, 2025 Vulnerabilities identified and reported via email
March 19, 2025 No response received
March 21, 2025 Contact made via Telegram
March 21, 2025 Patch released on GitHub
March 21, 2025 Full disclosure published (wasn’t planning to wait anyway)

To be clear, it’s not that the vulnerability was too simple to justify a grace period. I just didn’t see the point of waiting longer. Given the state of the project-and the fact that someone already suspected attacks-I figured it was more useful to disclose than to pretend this needed a coordinated process.


Final Thoughts

These vulnerabilities weren’t the result of a deep audit or hours of fuzzing - they surfaced during casual browsing. That’s not necessarily a criticism, but it does raise the question of how much else might be hiding in plain sight.

I chose not to continue the review - not because there’s nothing left to find, but because I had already made my point. That said, if you’re comfortable working with PHP and ExtJS and you enjoy web vulnerability research, MagnusBilling might be worth exploring further.

It’s an active project, widely deployed, and clearly used in production settings. That makes security all the more important.

Use with care, and keep it updated.

Special Thanks

A big thank you to VulnCheck for their prompt collaboration and support in indexing these CVEs. Their team responded quickly and helped ensure proper visibility and tracking for both vulnerabilities.

You can find their advisories here: