27th Sep 19 3:23 pm

Jetbrains TeamCity Reflected XSS

September 27, 2019 | By: Jeremy Buis

How we came about the TeamCity XSS: CVE-2019-15848

On a recent client engagement, we were challenged to gain access to their private CI server. The CI server suffered from a security misconfiguration, and we were able to gain access. This was already a success, but we wanted to show more impact.

We started looking around the client’s TeamCity instance to see how we could increase the impact of the issue. While manually crawling the site, with Burp open in the background, Burp popped a DOM-based XSS issue. This is generally a false positive, but we had a look anyway.

This is where the fun began.

The analysis BurpSuite produced was the following:

Data is read from location.href and passed to jQuery.html

The following value was injected into the source: 



The previous value reached the sink as: 

<div class=”selected project”data-projectId=”cb_Root” data-parentId=”_Root”><span class=”contentWrapper “><span class=”iWrapper”><span class=”icon_45c SvgIcon__icon–3t ProjectOrBuildTypeIcon__icon–2o projectOrBuildTypeIcon hasSiblings”><svg xmlns=”http://www.w3.org/2000/svg” width=”20″ height=”20″ viewBox=”0 0 20 20″ class=”glyph_1f4″><path d=”M2 9h7V2H2zm2-5h3v3H4zm7-2v7h7V2zm5 5h-3V4h3zM2 18h7v-7H2zm2-5h3v3H4zm7 5h7v-7h-7zm2-5h3v3h-3z”></path></svg></span><span class=”icon_45c SvgIcon__icon–3t ProjectOrBuildTypeIcon__icon–2o projectOrBuildTypeIcon hasSiblings ProjectOrBuildTypeIcon__arrow–26 projectOrBuildTypeIcon_arrow”><svg xmlns=”http://www.w3.org/2000/svg” width=”20″ height=”20″ viewBox=”0 0 20 20″ class=”glyph_1f4″><path d=”M2 9h7V2H2zm2-5h3v3H4zm6 7l5 8 5-8zm8-9h-7v7h7zm-2 5h-3V4h3zM2 18h7v-7H2zm2-5h3v3H4z”></path></svg></span></span><a href=’https://teamcity.jetbrains.com/project.html?projectId=ja21ikln4q%27%22`'”/ja21ikln4q/><ja21ikln4q/\>eqoajxnqdk&cb_Root&fromExperimentalUI=ja21ikln4q%27%22`'”/ja21ikln4q/><ja21ikln4q/\>eqoajxnqdk&true&stats&projectId=cb_Root’>teamcity.codebetter.com </a></span></div> 




The following proof of concept was generated for this issue: 

https://teamcity.jetbrains.com/project.html?projectId='”><img src=1 onerror=alert(1)>cb_Root&fromExperimentalUI='”><img src=1 onerror=alert(1)>true&tab='”><img src=1 onerror=alert(1)>stats

I tried the POC, it didn’t work, and it returned a 404.

Let us try and get a working URL, and see if any injections are reflected. A working URL looks like:


I tried injecting payloads I could easily search for in the DOM, like “xINJECTx”, into each query string parameter.

For the “tab” parameter, I found that my payload was reflected in the page inside a JavaScript context. This means my “xINJECTx” was inside script tags when the page was rendered. I sent the request “https://teamcity.jetbrains.com/overview.html?tab=xINJECTx” and was returned:



    ReactUI.renderConnected(‘open_in_experimental_ui’, ReactUI.OpenInExperimentalUI, {


      tab: ‘xINJECTx’




This has the potential of a reflected XSS!

I tried the URL “https://teamcity.jetbrains.com/overview.html?tab=’-alert(document.cookie)-‘” and the payload:




And was met with the following page:


This means our injection is returned to the browser, where the payload executes. This can lead to many outcomes, some of which have high impact, including the potential to steal CSRF tokens, which can lead to account takeover. The injection was found to work on 4 pages in total.

Another researcher found the same XSS independently of us, and was able to show more impact by proving they were able to gain remote command execution.

The issue was also valid on the open source TeamCity instance, so the examples above are all based on that instead of the private client data.

We disclosed the issue privately to Jetbrains, and they promptly created a fix. The XSS affects versions 2019.1 and 2019.1.1 of TeamCity and is fixed in version 2019.1.2.

The fixed script snippet looks like:

ReactUI.renderConnected(‘open_in_experimental_ui’, ReactUI.OpenInExperimentalUI, {
    favoriteBuilds: true,
    tab: ReactUI.queryToObject(location.search).tab

This safely accepts input from the tab query string parameter.

How to Fix

Always dig a little deeper to see how we can increase the impact of our findings.

In general, the key to fixing XSS in any form, is to apply the proper encoding to the output before being rendered in a browser. In this case, the fix was to remove the reflected payload, and access the query string value in a safe way. This is a better approach in this case, as user input is no longer reflected onto the page. For detailed XSS mitigation techniques visit our post on practical mitigation techniques.


July 16, 2019 – Found reflected XSS issue

July 17, 2019 – Reported reflected XSS issue

July 18, 2019 – Issue updated to fixed

July 19, 2019 – Issue verified as fixed

July 31, 2019 – TeamCity 2019.1.2 released

September 26, 2019 – Quarterly Security Bulletin released describing security issues


Another researcher found the same XSS independently of us, and was able to show more impact by proving they were able to gain remote command execution. https://twitter.com/JLLeitschuh/status/1169332316612644864?s=20

We help DevOps teams at SaaS companies to build confidence in their application security.
Discover PTaaS

Was this article helpful?

Share This Post

Leave a Reply

Your email address will not be published.

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Related Post
7 July 2021 | By: Jeremy Buis
Exploiting Less.js to Achieve RCE
24 June 2021 | By: Alex Hewko
The 6- Step Guide to Reviewing Your PenTesting Results
17 June 2021 | By: Alex Hewko
Mobile Pentesting vs. MDM: 3 Reasons Why MDM Is Not the Best Solution