Skip to content

Commit

Permalink
Validate urls in element and window endpoints
Browse files Browse the repository at this point in the history
Domains were previously not validated before being handled, leading to a
potential scenario where someone could pass something like
"element_url=127.0.0.1:<port>/<resource>" to access other resources on a
machine running Whoogle. This change ensures that the resource used in
both endpoints is a valid domain.

This also includes validation of config names to prevent names from
including path values such as "../../(etc)".
  • Loading branch information
benbusby committed Sep 13, 2023
1 parent 8830615 commit 3a2e0b2
Showing 1 changed file with 26 additions and 7 deletions.
33 changes: 26 additions & 7 deletions app/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import json
import os
import pickle
import re
import urllib.parse as urlparse
import uuid
import validators
from datetime import datetime, timedelta
from functools import wraps

Expand Down Expand Up @@ -420,13 +422,18 @@ def config():
config_disabled = (
app.config['CONFIG_DISABLE'] or
not valid_user_session(session))

name = ''
if 'name' in request.args:
name = os.path.normpath(request.args.get('name'))
if not re.match(r'^[A-Za-z0-9_.+-]+$', name):
return make_response('Invalid config name', 400)

if request.method == 'GET':
return json.dumps(g.user_config.__dict__)
elif request.method == 'PUT' and not config_disabled:
if 'name' in request.args:
config_pkl = os.path.join(
app.config['CONFIG_PATH'],
request.args.get('name'))
if name:
config_pkl = os.path.join(app.config['CONFIG_PATH'], name)
session['config'] = (pickle.load(open(config_pkl, 'rb'))
if os.path.exists(config_pkl)
else session['config'])
Expand All @@ -444,7 +451,7 @@ def config():
config_data,
open(os.path.join(
app.config['CONFIG_PATH'],
request.args.get('name')), 'wb'))
name), 'wb'))

session['config'] = config_data
return redirect(config_data['url'])
Expand All @@ -463,6 +470,8 @@ def imgres():
@session_required
@auth_required
def element():
empty_gif = base64.b64decode(
'R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==')
element_url = src_url = request.args.get('url')
if element_url.startswith('gAAAAA'):
try:
Expand All @@ -475,6 +484,11 @@ def element():

src_type = request.args.get('type')

# Ensure requested element is from a valid domain
domain = urlparse.urlparse(src_url).netloc
if not validators.domain(domain):
return send_file(io.BytesIO(empty_gif), mimetype='image/gif')

try:
file_data = g.user_request.send(base_url=src_url).content
tmp_mem = io.BytesIO()
Expand All @@ -485,8 +499,6 @@ def element():
except exceptions.RequestException:
pass

empty_gif = base64.b64decode(
'R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==')
return send_file(io.BytesIO(empty_gif), mimetype='image/gif')


Expand All @@ -504,6 +516,13 @@ def window():
root_url=request.url_root,
config=g.user_config)
target = urlparse.urlparse(target_url)

# Ensure requested URL has a valid domain
if not validators.domain(target.netloc):
return render_template(
'error.html',
error_message='Invalid location'), 400

host_url = f'{target.scheme}://{target.netloc}'

get_body = g.user_request.send(base_url=target_url).text
Expand Down

0 comments on commit 3a2e0b2

Please sign in to comment.