Abusing dynamic placeholders in helpdesks to extract user data
By exploiting dynamic placeholders in popular helpdesks like Zendesk, we were able to extract sensitive customer data.
By exploiting dynamic placeholders in popular helpdesks like Zendesk, we were able to extract sensitive customer data.
We identified that several companies were creating custom support portals on top of common support systems like Zendesk. In doing so, companies created misconfigured API integration allowing us to abuse a common feature, placeholder, to extract sensitive user information. Abusing this vulnerability would allow attackers to extract sensitive information such as billing information, purchase metadata, internal notes, plaintext passwords (where possible) and more through usage of placeholders.
Note: These abuse/exploit cases are not due to vulnerabilities in Zendesk. Affected organizations patched the vulnerability on their own by switching to the correct API endpoint and further sanitizing user inputs.
It is common practice for companies to build custom customer support portals. A customized portal allows companies to enforce their form of authentication, such as OAuth, to retrieve needed information about a user. For example, a user authenticating with the company’s main application into a support portal can share their account information, making it easier for customer support agents to interact with them. However, designing a customer support portal from scratch is difficult. This is why companies like Zendesk, Freshdesk, and more exist. When investigating, we noticed that most custom support portals used off-the-shelf products such as Zendesk through API integrations. So, while the custom portal would have its own authentication and authorization schemes, the cases created still went to the internal Zendesk portals of the company.
While this is an efficient way to 1) provide company-specific authentication and 2) not store user cases in one place, creating a custom application also introduces the common OWASP security risks and business logic vulnerabilities that products like Zendesk have in-built mitigations for. Not only the OWASP vulnerabilities, using API integrations as “backend” to store, extract and operate on user-given inputs also introduces vulnerabilities due to misuse of the API, lack of documentation, and more. For example, in our research, we noticed that many companies who used custom support portal with Zendesk as a case storage misconfigured their API integration. This allowed us to specifically abuse one Zendesk feature, placeholders, to extract user information of other users.
Placeholders are generic terms or words that are later replaced with actual information. Support systems act in similar ways with placeholders. For example, a ticket’s data is returned to the end-user based on the requested information. As a result, a placeholder can request information such as information on the case creator, agent(s) handling the case, case status, and associated (secondary) users to the case. Placeholders can be designed in various ways and depend on the product itself. This blog looks into Zendesk’s placeholder design and an exploitation case study.
Zendesk has built-in placeholder support allowing for agents and automated mailers to auto-fill user information when responding to cases. A classic example of Zendesk’s placeholder is the automated mail trigger that kicks-in when a case has been created:
Zendesk has built-in suppression and activation rules to prevent abuse that configure when a placeholder is used. In situations where a placeholder suppression is active, a placeholder will be treated as a regular string, while if a placeholder suppression is deactivated, then the placeholder will return the appropriate value. In Zendesk, placeholders are activated in three specific situations:
Zendesk has two distinct API endpoints: Requests and Tickets API. We noticed in our research that most custom portals used the Tickets API endpoint instead of the Requests API endpoint. This raised concern because the Tickets API endpoint (/v2/tickets.json) is an admin API endpoint and requires authenticating either with an agent or an admin session key. As we covered earlier, admin API endpoints can invoke placeholders.
To check how the admin API differed from the requests API, we created two tickets in our sample instance and invoked a placeholder through the ticket’s subject.
Ticket API endpoint request
When sending a request to the Tickets API endpoint, we noticed that the placeholder would be invoked and auto-filled in the subject value. The ticket will still retain the original string in the raw_subject value. In our test case, {{ current_user.email }} was auto-filled to the authenticated user’s email address.
Request API endpoint request
In comparison, when sending a request to the Requests API endpoint, the user input in subject value was properly sanitized. The placeholder was not invoked and the curly-brackets were also removed to prevent any second-level injection later on.
After confirming that the Ticket API endpoints could access/invoke placeholders in Zendesk, we reviewed what specific placeholders we could access and any potential impact of those placeholders.
Zendesk’s placeholders can access three objects: user, ticket, and organization. Objects are like classes and can be referenced by different variables. In addition, each object contains fields/sub-fields that can store information related to the ticket.
A user object contains information associated with the Zendesk user that is requested via a placeholder. Zendesk users are agents and end-user with an account in the portal. The user object has the following accessible fields:
Out of all the accessible fields, two fields stood out to us: custom_fields and notes. Custom fields are organization dependent and contain information the respective organization sets to assist their customer support agents. For example, an e-commerce application may have custom fields storing a user’s most recent order ID, billing address, and account number. In contrast, a telephone company may store the user’s account number, # of phone numbers in the user accounts, and the last billing date.
Notes are an array of strings containing notes about the user account. These can be notes added by support agents for future references/cases to explain the customer’s status better. For example, an enterprise customer for company A may have specific notes about them stored in the support portal so agents can help them debug future problems.
A ticket object contains information associated with the Zendesk ticket that is requested via a placeholder. The ticket object has the following accessible fields:
Out of all the accessible fields, one field stood out to us: ccs. ccs field is similar to cc_names because they store information regarding secondary users added to a ticket. In Zendesk, end-users can add CC secondary users to their tickets. This allows the CCed user to view and respond to the case. cc_names field contains an array of strings of the CCed users. This field only contains the first and last names of the registered users. If they are not registered, the email address CCed to the ticket will instead be included.
In comparison, ccs field contains an array of User objects of users that CCed to the ticket. For example, if an email address is CCed to the ticket and the email address links to a valid user, the user information is then linked via the ccs field. Since each value in the ccs field link to a user object, it allows calling for specific user fields like custom_fields and notes.
Zendesk uses Shopify’s Liquid template language for their placeholder. This makes it easier to program placeholders to access specific information in certain cases. Here are some of the examples of how placeholders can be invoked:
{{ ticket.id }} - Ticket ID of the current ticket{{ ticket.ccs }} - List of all Users CCed to the ticket{{ ticket.comments }} - List of all comments (internal & external) on the ticket{{ current_user.custom_fields }} - Custom Fields for the current user performing action on the ticket
Since some placeholders contain arrays and objects, Liquid’s for loops can be used to clean the returned information. For example, if we want information on the first user in ticket.ccs placeholder, a quick for loop will help.
After we had a high-level understanding of how Zendesk’s placeholder worked, we started testing for vulnerabilities in custom portals that were using Zendesk as the backend. To identify such systems, we looked through our internal asset tracker to highlight specific patterns:
The first example of a vulnerable system and Patient Zero was GitHub’s custom support portal.
GitHub has recently moved away from using vanilla Zendesk to their custom support portal at https://support.github.com. This custom portal helps users to create support tickets for organizations they are part of in addition to their own account.
To identify what GitHub was doing in the backend, we downloaded GitHub Enterprise Server (GHES). GHES shares most of its codebase with github.com except for beta features not yet available for public beta. When looking through the codebase, we noticed that GitHub called the tickets API endpoint instead of the requests API endpoint.
We then performed a series of test to confirm and exploit the vulnerability.
Confirming the vulnerability To confirm the vulnerability, we created an initial ticket with {{ ticket.requester.email }}. If vulnerable, this would create a ticket with the subject header pointing to the requester user’s email address. The request user would have been the account rojan-rijal
on GitHub. After the ticket creation, we confirmed the subject was reflecting personal email associated with rojan-rijal
account.
We then extracted {{ current_user }}
object to identify what user was creating ticket in Zendesk. In this case, it was mutwin+ticketbot[]github[.]com
. We could also confirm that that user was an Admin user from the returned role information.
During our test we noticed that GitHub’s support portal allowed adding secondary user. Having the ability to CC other users allowed us to pull their user specific information via the ticket.ccs field. The exploit scenario would look like:
{{ ticket.ccs[0].custom_fields }}
. This would then create a ticket with the subject containing all stored custom fields about the user.At the end of the test, we identified several companies to be vulnerable for this type of injection. Some of the information this allowed extracting:
In order to mitigate similar vulnerabilities, following actions can be taken:
Zendesk is not the only support system used as backend by companies. We have also noticed an increase in usage of systems like Salesforce’s Service Cloud. That system also has certain exploitable conditions to be wary about. We recently gave a talk at BSides San Francisco that also covers example cases for Salesforce. We highly recommend viewing the talk to learn more. If you want to detect similar vulnerabilities and more in your systems, schedule a free 1:1 consult today to learn how we can help.