Skip to content

[Python] Add Unicode Bypass Validation query tests and help #749

Description

@Sim4n6

Query PR

github/codeql#12991

Language

Python

CVE(s) ID list

CWE

CWE-176

Report

According to GoSecure's presentation [PDF] "Unicode vulnerabilities that could byte you", the recommendation states that "If you need to do normalization, normalized prior to a security validation". This is due to the fact that a Post-Unicode Normalization may cause serious security issues. Why ? A late Unicode normalization may introduce back an omitted or an escaped character.

When the Unicode normalization is applied to U+FF20 (@), the resulting character will be U+0040 (@). If ever a security check is performed against the latter U+0040 (@), before the normalization, that would bring it back using the other U+FF20 (@).

Breaking URL parser

Take for instance, the URL parser bases its splitting of the host part and user:password part on the identification of @ (U+0040).
The URL parser would deny-list this URL https://@evil.com

  • host: evil.com
    However, a malicious URL may include the Unicode character﹫ (U+FE6B) as https://@evil.com. No @ is found. The host is @evil.com different from evil.com which leads to the bypass of the deny-list.

Another vulnerability rXSS

Take for instance this useful Python Flask snippet:

import unicodedata
from flask import Flask, request, escape, render_template

app = Flask(__name__)

@app.route("/")
def escape_nd_normalize():
    ui_escaped = escape(request.args.get('ui'))
    norm_escaped_ui = unicodedata.normalize("NFKC", ui_escaped)
    return render_template('result.html', norm_escaped_ui=norm_escaped_ui) # $result=BAD

with the result.html html template:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Results</title>
</head>
<body>
    <h1>Results</h1>
    <p>
        {{ norm_escaped_ui | safe }}
    </p>
  
</body>
</html>

Run the web server using:

FLASK_APP=snippet.py flask run --reload

Now, hit the server using the following payloads:

# 1. No XSS triggered 
http://127.0.0.1:5000/?ui=%3Cimg%20src=x%20onerror=print()%3E

# 2. A Reflected XSS is triggered 
http://127.0.0.1:5000/?ui=%EF%B9%A4img%20src=x%20onerror=print()%EF%B9%A5

Using the first payload, the flask.escape() function has successfully escaped the symbols < and > making the first payload benign. While the escape function is considered the symbol ﹤ (U+FE64) and ﹥ (U+FE65) as harmless. Thus, no escaping is being performed. But, when the late Unicode normalization with the algorithm NKFC is applied, it leads to the conversion of the symbol ﹤ (U+FE64) back into the regular <, resulting in an rXSS triggered.

Source-Sink flow representation

I modeled the identification of a Post-Unicode Normalization vulnerability the following way.

  1. The source is any remote user-controlled data.
  2. When the user-controlled data flows through either a string validation, regex verification, or an escaping function.
  3. The sink would be valid for a late post-Unicode normalization using one of the forms NKFC or NFC.

vulnerability-flow

Pull Requests

I modeled the QL query for the following languages:

Impact

A post-Unicode normalization may lead to:

  • Breaking the URL parser and Credentials and information leakage, for instance:
    https://www.evil.c℀.ms.com would become https://www.evil.ca/c.ms.com.
  • Bypass-escaping mechanisms lead to various vulnerabilities in Command injections, XSS, SQLi, etc.
  • Account takeover due to character collision.
  • Bypass deny lists.

References:

Are you planning to discuss this vulnerability submission publicly? (Blog Post, social networks, etc).

  • Yes
  • No

Blog post link

https://sim4n6.beehiiv.com

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

All For OneSubmissions to the All for One, One for All bounty

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions