Unleashing the power of CSS injection: The access key to an internal API
In this write-up, we will be explaining a vulnerability that was discovered in an online accounting application. The vulnerability was a CSS injection flaw that could be exploited in the application’s PDF generator. We will explain to you how we discovered the vulnerability and how we were able to exploit it to get internal API access.
The first step was to identify entry points where we could inject malicious code. We focused on the application’s invoice generation feature, which allows users to create and download invoices in PDF format. We hoped for finding injection points within the PDF generation feature, allowing us to inject malicious payloads leading to code execution.
Useless CSS injection
We tried injecting our payloads in different user input fields for invoices which got included in the generated PDF. All these input fields did not lead to execution of our payloads. This led us to the global styling configuration for the invoice documents. Most of the configuration options had validation and sanitization in place, except for one configuration option: the color of the text being used in the documents.
color: #7c878e; /* USER-SETTING */
font-family: Arial, Helvetica, sans-serif; /* USER-SETTING */
color: #f3f3f3;}Hi there! I am some new css but I can not escape the style tag to become xss : (
; /* USER-SETTING */
As the application allowed us to preview the styling options being configured, we quickly found out that the injection point wasn’t as exciting as we hoped for. The characters
<>'" were escaped making it impossible to close the
<style> tag and inject our own HTML (e.g.
<iframe>). At first we thought this was a dead end, but something interesting caught our attention. The CSS injection got rendered by the PDF generator. Fiddling a bit with the CSS, we found that SSRF was possible by using the url() directive without encapsulating the URL in quotes
[c6029550qf512863dfOgcgynjfayyyyyn] Received DNS interaction (AAAA) from xx.xxx.xx.xx at 2021-11-01 17:32:35
[c6029550qf512863dfOgcgynjfayyyyyn] Received DNS interaction (A) from xx.xxx.xx.xxx at 2021-11-01 17:32:35
[c6029550qf512863dfOgcgynjfayyyyyn] Received HTTP interaction from xx.xxx.xx.xxx at 2021-11-01 17:32:35
But again, excitement was short-lived as we were not able to extract any interesting data. Images did not render as they were not valid images and timing attacks by polling for alive internal IPs and hosts were not possible due the PDF generation time not being consistent enough.
At this point we started looking into the PDF generation software. When viewing the metadata of the generated PDFs, we found that Prince 13.2 was being used.
PrinceXML’s documentation is very extensive, so we ran a local copy to start testing the possibilities. The things we looked at were custom HTML-tags and custom CSS directives, which looked promising to abuse. Unfortunately, nothing seemed to work as we were limited by the escaped characters in the CSS injection point.
When we tested the
prince-pdf-script property, we thought it didn’t work at first.
Nothing happened. No print box popped up. No custom code got executed during the generation of the PDF. Nothing. But then it hit!
We tried to include a local file with the
4 0 obj
Decoding the UTF16 string, showed us the hostname of the generator service:
03f827eaea5c. Retrieving other files e.g.
/etc/passwd, worked perfectly fine. Apparently, PrinceXML does not validate the content type of the included file and includes the content as is.
The final destination
Unfortunately, a list with common UNIX files did not expose a lot of interesting stuff. We did move on and started looking at requesting internal hosts. Using securitytrails.com we got us an accurate list of subdomains being available to enumerate.
After enumerating a few domains, we found out we had access to:
- A NuGet gallery, exposing other applications available to downloading
- An Elastic Search instance, which allowed us to query all public and internal network requests
- The internal API, making it possible to query all available data of other customers
Being able to access the above listed hosts, the company decided that this was enough to assess the severity as a critical vulnerability.
PrinceXML decided to change the functionality of the
prince-pdf-script property in version 15.