Ticket to Shell - Exploiting PHP Filters and CNEXT in osTicket (CVE-2026-22200)
We discovered an interesting CTF-inspired vulnerability, CVE-2026-22200, in osTicket, a popular open source helpdesk system. This flaw allows anonymous attackers to read arbitrary files from the server by injecting malicious PHP filter chain expressions into a ticket and then exporting it to PDF. This can be exploited to exfiltrate sensitive files, embedded as bitmap images within the PDF, or achieve remote code execution when chained with CVE-2024-2961 (CNEXT). This issue is patched in osTicket 1.18.3 / 1.17.7, and we strongly encourage all users to upgrade to the latest version. osTicket is a widely used open-source helpdesk system, favored by organizations seeking a lightweight, self-hosted support solution. With thousands of instances exposed to the Internet and many more deployed internally, the attack surface is significant. Ticketing systems tend to be high value targets for attackers, typically containing sensitive information such as tokens or credentials, and may act as a breach point to pivot into internal networks.
We began by analyzing mPDF, the third-party PHP library osTicket uses to generate PDF documents from support tickets. This functionality is accessible to any user authorized to view a ticket, including unauthenticated guests if the helpdesk is configured for guest ticket access (the default). To bypass mPDF’s image validation, PHP filter chains are used to prepend a valid Bitmap (BMP) header to the contents of an arbitrary file. This tricks mPDF into rendering arbitrary files (e.g., /etc/passwd) as a valid image within the PDF. The sensitive data can then be extracted back from the resulting bitmap in the PDF file. In applying this solution to osTicket, we found osTicket was using a really old version of mPDF from circa 2019. However, we discovered a different normalization bypass involving URL encoding. A URL encoded stream wrapper like php%3a// could bypass the blacklisted stream wrapper check. This is due to logic in the older version of mPDF that URL decodes local resources after checking them against the stream wrapper blacklist but before accessing them.
Even with a bypass for mPDF, we still had to deliver our payload through osTicket’s input validation layer. All rich-text HTML content in tickets is cleaned and then processed by htmLawed, a third-party library used to purify input by neutralizing suspicious tags and attributes. While testing style attributes, we noticed a subtle parsing differential in htmLawed. If we included whitespace between the url keyword and the opening parenthesis, the URI escaped the sanitizer’s whitelist check. Additionally, osTicket registers a custom post-sanitization callback __html_cleanup with htmLawed that performed additional string manipulation on style attributes. We came up with this payload: <ul><li style="list-style-image:url"(php%3a//myurl)">listitem</li></ul>. This uses the HTML entity " for “ to bypass htmLawed.
To trigger the PDF export, an attacker must first be able to view a submitted ticket. In the default osTicket configuration, there are two paths for an anonymous attacker. If self-registration is enabled (the default), an attacker can simply register an account, log in, and open a ticket. If self-registration is disabled, an attacker can submit a ticket as a guest and then brute-force access via the “Check Ticket Status” form. The brute force path is aided by the following: The Check Ticket Status form acts as an oracle and will confirm when the combination of an email address and ticket number is valid. By default the ticket number space is limited to 6 digits, starting with 100000 and ending with 999999. Per-user rate limiting protection can be bypassed by simply opening a new session before each request. In our testing, brute forcing is something that can be easily be accomplished in less than an hour over a standard Internet connection.
To read the complete article see: Horizon3.ai Article
Upcoming Events:
- RISE-USA: February 18-19, register at RISE-USA Registration using the password “d7Jf#ga5Pr”.
- RISE-IRELAND: April 14-15, register at RISE-IRELAND Registration (no password).