Python: Implement check for flask debug mode.

This commit is contained in:
Taus Brock-Nannestad
2018-11-22 15:01:02 +01:00
parent a85dfb1c4e
commit a4da245809
9 changed files with 121 additions and 7 deletions

View File

@@ -0,0 +1,9 @@
from flask import Flask
app = Flask(__name__)
@app.route('/crash')
def main():
raise Exception()
app.run(debug=True)

View File

@@ -0,0 +1,39 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Running a Flask application with debug mode enabled may allow an
attacker to gain access through the Werkzeug debugger.
</p>
</overview>
<recommendation>
<p>
Ensure that Flask applications that are run in a production
environment have debugging disabled.
</p>
</recommendation>
<example>
<p>
Running the following code starts a Flask webserver that has
debugging enabled. By visiting <code>/crash</code>, it is possible
to gain access to the debugger, and run arbitrary code through the
interactive debugger.
</p>
<sample src="FlaskDebug.py" />
</example>
<references>
<li>Flask Quickstart Documentation: <a href="http://flask.pocoo.org/docs/1.0/quickstart/#debug-mode">Debug Mode</a>.</li>
<li>Werkzeug Documentation: <a href="http://werkzeug.pocoo.org/docs/0.14/debug/">Debugging Applications</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,23 @@
/**
* @name Flask app is run in debug mode
* @description Running a Flask app in debug mode may allow an attacker to run arbitrary code through the Werkzeug debugger.
* @kind problem
* @problem.severity error
* @precision medium
* @id py/flask-debug
* @tags security
* external/cwe/cwe-215
* external/cwe/cwe-489
*/
import python
import semmle.python.web.flask.General
from CallNode call, Object isTrue
where
call = theFlaskClass().declaredAttribute("run").(FunctionObject).getACall() and
call.getArgByName("debug").refersTo(isTrue) and
isTrue.booleanValue() = true
select call, "A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger."

View File

@@ -1,15 +1,15 @@
edges
| ../lib/flask/__init__.py:14:19:14:20 | externally controlled string | ../lib/flask/__init__.py:15:19:15:20 | externally controlled string |
| ../lib/flask/__init__.py:14:19:14:20 | externally controlled string | ../lib/flask/__init__.py:16:25:16:26 | externally controlled string |
| ../lib/flask/__init__.py:15:19:15:20 | externally controlled string | ../lib/flask/__init__.py:16:19:16:20 | externally controlled string |
| ../lib/flask/__init__.py:15:19:15:20 | externally controlled string | ../lib/flask/__init__.py:17:25:17:26 | externally controlled string |
| reflected_xss.py:7:18:7:29 | dict of externally controlled string | reflected_xss.py:7:18:7:45 | externally controlled string |
| reflected_xss.py:7:18:7:45 | externally controlled string | reflected_xss.py:8:44:8:53 | externally controlled string |
| reflected_xss.py:8:26:8:53 | externally controlled string | ../lib/flask/__init__.py:14:19:14:20 | externally controlled string |
| reflected_xss.py:8:26:8:53 | externally controlled string | ../lib/flask/__init__.py:15:19:15:20 | externally controlled string |
| reflected_xss.py:8:44:8:53 | externally controlled string | reflected_xss.py:8:26:8:53 | externally controlled string |
| reflected_xss.py:12:18:12:29 | dict of externally controlled string | reflected_xss.py:12:18:12:45 | externally controlled string |
| reflected_xss.py:12:18:12:45 | externally controlled string | reflected_xss.py:13:51:13:60 | externally controlled string |
parents
| ../lib/flask/__init__.py:14:19:14:20 | externally controlled string | reflected_xss.py:8:26:8:53 | externally controlled string |
| ../lib/flask/__init__.py:15:19:15:20 | externally controlled string | reflected_xss.py:8:26:8:53 | externally controlled string |
| ../lib/flask/__init__.py:16:25:16:26 | externally controlled string | reflected_xss.py:8:26:8:53 | externally controlled string |
| ../lib/flask/__init__.py:16:19:16:20 | externally controlled string | reflected_xss.py:8:26:8:53 | externally controlled string |
| ../lib/flask/__init__.py:17:25:17:26 | externally controlled string | reflected_xss.py:8:26:8:53 | externally controlled string |
#select
| ../lib/flask/__init__.py:16:25:16:26 | flask.response.argument | reflected_xss.py:7:18:7:29 | dict of externally controlled string | ../lib/flask/__init__.py:16:25:16:26 | externally controlled string | Cross-site scripting vulnerability due to $@. | reflected_xss.py:7:18:7:29 | flask.request.args | user-provided value |
| ../lib/flask/__init__.py:17:25:17:26 | flask.response.argument | reflected_xss.py:7:18:7:29 | dict of externally controlled string | ../lib/flask/__init__.py:17:25:17:26 | externally controlled string | Cross-site scripting vulnerability due to $@. | reflected_xss.py:7:18:7:29 | flask.request.args | user-provided value |

View File

@@ -0,0 +1,3 @@
| test.py:10:1:10:19 | ControlFlowNode for Attribute() | A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger. |
| test.py:25:1:25:20 | ControlFlowNode for Attribute() | A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger. |
| test.py:29:1:29:20 | ControlFlowNode for Attribute() | A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger. |

View File

@@ -0,0 +1 @@
Security/CWE-215/FlaskDebug.ql

View File

@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=2 -p ../lib

View File

@@ -0,0 +1,37 @@
from flask import Flask
app = Flask(__name__)
@app.route('/crash')
def main():
raise Exception()
# bad
app.run(debug=True)
# okay
app.run()
app.run(debug=False)
# also okay
run(debug=True)
app.notrun(debug=True)
# a slightly more involved example using flow and truthy values
DEBUG = True
app.run(debug=DEBUG)
DEBUG = 1
app.run(debug=DEBUG)
if False:
app.run(debug=True)
# false negative
runapp = app.run
runapp(debug=True)

View File

@@ -1,7 +1,8 @@
class Flask(object):
pass
def run(self, *args, **kwargs):
pass
from .globals import request