python.django.security.nan-injection.nan-injection

Author
unknown
Download Count*
License
Found user input going directly into typecast for bool(), float(), or complex(). This allows an attacker to inject Python's not-a-number (NaN) into the typecast. This results in undefind behavior, particularly when doing comparisons. Either cast to a different type, or add a guard checking for all capitalizations of the string 'nan'.
Run Locally
Run in CI
Defintion
rules:
- id: nan-injection
message: Found user input going directly into typecast for bool(), float(), or
complex(). This allows an attacker to inject Python's not-a-number (NaN)
into the typecast. This results in undefind behavior, particularly when
doing comparisons. Either cast to a different type, or add a guard
checking for all capitalizations of the string 'nan'.
languages:
- python
severity: ERROR
mode: taint
pattern-sources:
- patterns:
- pattern-inside: |
def $FUNC(request, ...):
...
- pattern-either:
- pattern: request.$PROPERTY.get(...)
- pattern: request.$PROPERTY[...]
pattern-sinks:
- patterns:
- pattern-either:
- pattern: float(...)
- pattern: bool(...)
- pattern: complex(...)
- pattern-not-inside: |
if $COND:
...
...
pattern-sanitizers:
- pattern: $ANYTHING(...)
not_conflicting: true
metadata:
references:
- https://discuss.python.org/t/nan-breaks-min-max-and-sorting-functions-a-solution/2868
- https://blog.bitdiscovery.com/2021/12/python-nan-injection/
category: security
cwe:
- "CWE-704: Incorrect Type Conversion or Cast"
technology:
- django
subcategory:
- vuln
impact: MEDIUM
likelihood: MEDIUM
confidence: MEDIUM
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
Examples
nan-injection.py
import models
from django.http import HttpResponse
from app import get_price, deny, buy, fetch_obj
class Person(models.Model):
first_name = models.CharField(...)
last_name = models.CharField(...)
birth_date = models.DateField(...)
##### True Positives #########
def test1(request):
tid = request.POST.get("tid")
price = get_price()
# ruleid: nan-injection
x = float(tid)
if x < price:
return deny()
return buy()
def test2(request):
tid = request.POST.get("tid")
# ruleid: nan-injection
bool(tid)
# ruleid: nan-injection
complex(tid)
def test3(request, something_else):
tid = request.GET['tid']
# ruleid: nan-injection
float(tid)
# ruleid: nan-injection
bool(tid)
# ruleid: nan-injection
complex(tid)
def ok1(request, something_else):
tid = request.POST.get("tid")
obj = fetch_obj(tid)
# ok: nan-injection
float(obj.num)
def ok2(request, something_else):
tid = request.POST.get("tid")
# ok: nan-injection
int(float(tid))
# ok: nan-injection
float(int(tid))
# ok: nan-injection
int(bool(tid))
def ok3(request):
tid = request.POST.get("tid")
if tid.lower() == "nan":
raise ValueError
# ok: nan-injection
num = float(tid)
if num > get_price():
buy()
deny()
Short Link: https://sg.run/Og7L