Reporting a vulnerability found by iCR
The bug detection tool created the following message:
In file: routes_dashboard.py, there is a method that is vulnerable to XSS which can compromise any cookies, session tokens and other sensitive information used with the website and browser. iCR identified the Cross-Site Scripting sink where data has flowed without being validated properly. It suggested proper sanitization to prevent this XSS attack.
Notes from the Triage Team
Links
https://github.com/kizniche/Mycodo/blob/master/mycodo/mycodo_flask/routes_dashboard.py#L260
Problem
In file: routes_dashboard.py
, the method page_dashboard
uses an user provided parameter to render an HTML file. This is a potential XSS vulnerability.
See above link.
Process
This demonstration is only an approximate representation of the original project. I have copied the vulnerable parts of the method based on the following observations
-
The app.route
decorator above the mehod definition includes a route parameter dashboard_id
whose value is passed to the method.
@blueprint.route('/dashboard/<dashboard_id>', methods=('GET', 'POST'))
@flask_login.login_required
def page_dashboard(dashboard_id):
Now flask automatically URL decodes the route parameter before passing it onto the method. So an user can pass a URL encoded JS code as the parameter value.
-
At the end the method passes on dashboard_id
to a tempalte HTML file for rendering.
return render_template('pages/dashboard.html',
# other parameters
dashboard_id=dashboard_id,
# other parameters
)
In the template HTML file, I notice that dashboard_id is used in the value
attribute of an input
tag which could be exploited.
<input type="hidden" name="dashboard_id" value="{{ dashboard_id }}">
In all other places dashboard_id
is used to filter object instances (e.g. Dashboard.query.filter( Dashboard.unique_id == dashboard_id).first()
). The effects of these on the vulnerability is not clear without actually running the project.
Based on the above observations I created a simple Flask project app.py
with the following code.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello, Flask!"
@app.route('/dashboard/<dashboard_id>')
def dashboard(dashboard_id):
return f"""
<!DOCTYPE html>
<html>
<head>
<title>Dashboard</title>
</head>
<body>
<h1>Dashboard ID: {dashboard_id}</h1>
<form>
<input type="hidden" name="input_value" value="{dashboard_id}">
<input type="submit" value="Submit">
</form>
</body>
</html>
"""
if __name__ == '__main__':
app.run(debug=True)
Now to exploit it the dashboard_id
parameter must be set to an appropriate payload so that JS code executes when browser renders the HTML file. The process by which I arrived at the payload is as follows
-
Setting dashboard_id = alert(1)
will not work since the value is passed to the method as a string and the method passes it on to the HTML file as a string. So the HTML file will have the following code which will not execute.
<input type="hidden" name="input_value" value="alert(1)">
-
Next I tried dashboard_id = 1" onclick="alert(1)
. This would make the input field function like the following where now it has an onclick event associated with it. Now it should execute but it doesn’t. That’s because the field is hidden, so the onclick event doesn’t register.
<input type="hidden" name="input_value" value="1" onclick="alert(1)">
-
Only way to circumvent the hidden problem I found was to include the accesskey attribute. By including this attribute if one presses the correct key combination the onclick event will trigger and the code will execute.
So the payload is now dashboard_id = 1" accesskey= "X" onclick="alert(1)
. Now if somehow an user was convinced to press the key combination Alt+Shift+X the code will execute.
<input type="hidden" name="input_value" value="1" accesskey="X" onclick="alert(1)">
Finally ran the flask app in my local machine and typed the following URL (after necessary URL encoding of the payload)
http://127.0.0.1:5000/dashboard/1%22%20accesskey%3D%22X%22%20onclick%3D%22alert(1)
Then I pressed Alt+Shift+X and the alert box popped up.
CLA Requirements:
This section is only relevant if your project requires contributors to sign a Contributor License Agreement (CLA) for external contributions.
All contributed commits are already automatically signed off.
The meaning of a signoff depends on the project, but it typically certifies that committer has the rights to submit this work under the same license and agrees to a Developer Certificate of Origin (see https://developercertificate.org/ for more information).
Sponsorship and Support:
This work is done by the security researchers from OpenRefactory and is supported by the Open Source Security Foundation (OpenSSF): Project Alpha-Omega. Alpha-Omega is a project partnering with open source software project maintainers to systematically find new, as-yet-undiscovered vulnerabilities in open source code - and get them fixed - to improve global software supply chain security.
The bug is found by running the iCR tool by OpenRefactory, Inc. and then manually triaging the results.
Reporting a vulnerability found by iCR
The bug detection tool created the following message:
In file: routes_dashboard.py, there is a method that is vulnerable to XSS which can compromise any cookies, session tokens and other sensitive information used with the website and browser. iCR identified the Cross-Site Scripting sink where data has flowed without being validated properly. It suggested proper sanitization to prevent this XSS attack.
Notes from the Triage Team
Links
https://github.com/kizniche/Mycodo/blob/master/mycodo/mycodo_flask/routes_dashboard.py#L260
Problem
In file:
routes_dashboard.py
, the methodpage_dashboard
uses an user provided parameter to render an HTML file. This is a potential XSS vulnerability.See above link.
Process
This demonstration is only an approximate representation of the original project. I have copied the vulnerable parts of the method based on the following observations
The
app.route
decorator above the mehod definition includes a route parameterdashboard_id
whose value is passed to the method.Now flask automatically URL decodes the route parameter before passing it onto the method. So an user can pass a URL encoded JS code as the parameter value.
At the end the method passes on
dashboard_id
to a tempalte HTML file for rendering.In the template HTML file, I notice that dashboard_id is used in the
value
attribute of aninput
tag which could be exploited.In all other places
dashboard_id
is used to filter object instances (e.g.Dashboard.query.filter( Dashboard.unique_id == dashboard_id).first()
). The effects of these on the vulnerability is not clear without actually running the project.Based on the above observations I created a simple Flask project
app.py
with the following code.Now to exploit it the
dashboard_id
parameter must be set to an appropriate payload so that JS code executes when browser renders the HTML file. The process by which I arrived at the payload is as followsSetting
dashboard_id = alert(1)
will not work since the value is passed to the method as a string and the method passes it on to the HTML file as a string. So the HTML file will have the following code which will not execute.Next I tried
dashboard_id = 1" onclick="alert(1)
. This would make the input field function like the following where now it has an onclick event associated with it. Now it should execute but it doesn’t. That’s because the field is hidden, so the onclick event doesn’t register.Only way to circumvent the hidden problem I found was to include the accesskey attribute. By including this attribute if one presses the correct key combination the onclick event will trigger and the code will execute.
So the payload is now
dashboard_id = 1" accesskey= "X" onclick="alert(1)
. Now if somehow an user was convinced to press the key combination Alt+Shift+X the code will execute.Finally ran the flask app in my local machine and typed the following URL (after necessary URL encoding of the payload)
Then I pressed Alt+Shift+X and the alert box popped up.
CLA Requirements:
This section is only relevant if your project requires contributors to sign a Contributor License Agreement (CLA) for external contributions.
All contributed commits are already automatically signed off.
The meaning of a signoff depends on the project, but it typically certifies that committer has the rights to submit this work under the same license and agrees to a Developer Certificate of Origin (see https://developercertificate.org/ for more information).
Sponsorship and Support:
This work is done by the security researchers from OpenRefactory and is supported by the Open Source Security Foundation (OpenSSF): Project Alpha-Omega. Alpha-Omega is a project partnering with open source software project maintainers to systematically find new, as-yet-undiscovered vulnerabilities in open source code - and get them fixed - to improve global software supply chain security.
The bug is found by running the iCR tool by OpenRefactory, Inc. and then manually triaging the results.