Python: Added tests based on security analysis

currently we do not:
- recognize the pattern
   `{'author': {"$eq": author}}` as protected
- recognize arguements to `$where` (and friends)
   as vulnerable
This commit is contained in:
Rasmus Lerchedahl Petersen
2023-08-29 16:16:38 +02:00
parent bf8bfd91cd
commit 114984bd8c
5 changed files with 93 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
from pymongo import MongoClient
client = MongoClient()
db = client.test_database
import datetime
post = {
"author": "Mike",
"text": "My first blog post!",
"tags": ["mongodb", "python", "pymongo"],
"date": datetime.datetime.now(tz=datetime.timezone.utc),
}
posts = db.posts
post_id = posts.insert_one(post).inserted_id
post_id

View File

@@ -0,0 +1,19 @@
Tutorials:
- [pymongo](https://pymongo.readthedocs.io/en/stable/tutorial.html)
- [install mongodb](https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-os-x/#std-label-install-mdb-community-macos)
I recommend creating a virtual environment with venv and then installing dependencies via
```
python -m pip --install -r requirements.txt
```
Start mongodb:
```
mongod --config /usr/local/etc/mongod.conf --fork
```
run flask app:
```
flask --app server run
```
Navigate to the root to see routes. For each route try to get the system to reveal the person in the database. If you know the name, you can just input it, but in some cases you can get to the person without knowing the name!

View File

@@ -0,0 +1,2 @@
flask
pymongo

View File

@@ -0,0 +1,55 @@
from flask import Flask, request
from pymongo import MongoClient
import json
client = MongoClient()
db = client.test_database
posts = db.posts
app = Flask(__name__)
def show_post(post, query):
if post:
return "You found " + post['author'] + "!"
else:
return "You did not find " + query
@app.route('/plain', methods=['GET'])
def plain():
author = request.args['author']
post = posts.find_one({'author': author}) # $ result=OK
return show_post(post, author)
@app.route('/dict', methods=['GET'])
def as_dict():
author_string = request.args['author']
author = json.loads(author_string)
# Use {"$ne": 1} as author
# Found by http://127.0.0.1:5000/dict?author={%22$ne%22:1}
post = posts.find_one({'author': author}) # $ result=BAD
return show_post(post, author)
@app.route('/dictHardened', methods=['GET'])
def as_dict_hardened():
author_string = request.args['author']
author = json.loads(author_string)
post = posts.find_one({'author': {"$eq": author}}) # $ SPURIOUS: result=BAD
return show_post(post, author)
@app.route('/byWhere', methods=['GET'])
def by_where():
author = request.args['author']
# Use `" | "a" === "a` as author
# making the query `this.author === "" | "a" === "a"`
# Found by http://127.0.0.1:5000/byWhere?author=%22%20|%20%22a%22%20===%20%22a
post = posts.find_one({'$where': 'this.author === "'+author+'"'}) # $ MISSING: result=BAD
return show_post(post, author)
@app.route('/', methods=['GET'])
def show_routes():
links = []
for rule in app.url_map.iter_rules():
if "GET" in rule.methods:
links.append((rule.rule, rule.endpoint))
return links

View File

@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=1 -r PoC