CVE-2024-22417 (Python, published 2024-01-23T18:15Z)
Description: value: Whoogle Search is a self-hosted metasearch engine. In versions 0.8.3 and prior, the `element` method in `app/routes.py` does not validate the user-controlled `src_type` and `element_url` variables and passes them to the `send` method which sends a `GET` request on lines 339-343 in `requests.py`. The returned contents of the URL are then passed to and reflected back to the user in the `send_file` function on line 484, together with the user-controlled `src_type`, which allows the attacker to control the HTTP response content type leading to a cross-site scripting vulnerability. An attacker could craft a special URL to point to a malicious website and send the link to a victim. The fact that the link would contain a trusted domain (e.g. from one of public Whoogle instances) could be used to trick the user into clicking the link. The malicious website could, for example, be a copy of a real website, meant to steal a person’s credentials to the website, or trick that person in another way. Version 0.8.4 contains a patch for this issue.
Severity: nan (CVSS v3 base score: 6.1)
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Repository: https://github.com/benbusby/whoogle-search
Files changed:
app/routes.py
Commit message: Validate urls in `element` and `window` endpoints

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)".


Vulnerable code:
        if 'name' in request.args:
            config_pkl = os.path.join(
                app.config['CONFIG_PATH'],
                request.args.get('name'))
                    request.args.get('name')), 'wb'))
    empty_gif = base64.b64decode(
        'R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==')

Fixed code:
import re
import validators

    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 name:
            config_pkl = os.path.join(app.config['CONFIG_PATH'], name)
                    name), 'wb'))
    empty_gif = base64.b64decode(
        'R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==')
    # 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')


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