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 thanapplication/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:
- CVE-2025-2609: https://vulncheck.com/advisories/magnusbilling-logs-xss
- CVE-2025-2610: https://vulncheck.com/advisories/magnusbilling-alarm-xss