🔒 AISAST Security Report

Project: /Users/jyothi/Projects/Test/pygoat
Generated: 2025-12-21 09:09:34
Files Scanned
211
Lines of Code
0
True Positives
97
Critical
11
High
40
Medium
43
Low
3

📚 Findings by CWE

(15 findings)

  • js/functionality-from-untrusted-source — dockerized_labs/sensitive_data_exposure/templates/about.html:91
  • js/functionality-from-untrusted-source — dockerized_labs/sensitive_data_exposure/templates/base.html:60
  • js/functionality-from-untrusted-source — dockerized_labs/sensitive_data_exposure/templates/index.html:132
  • js/functionality-from-untrusted-source — dockerized_labs/sensitive_data_exposure/templates/lesson.html:338
  • js/functionality-from-untrusted-source — dockerized_labs/sensitive_data_exposure/templates/login.html:82
  • js/clear-text-storage-of-sensitive-data — dockerized_labs/sensitive_data_exposure/templates/profile.html:222
  • js/functionality-from-untrusted-source — dockerized_labs/sensitive_data_exposure/templates/register.html:93
  • Cross-Site Scripting (XSS) — introduction/templates/mitre/csrf_dashboard.html:26
  • deserialization — /Users/jyothi/Projects/Test/pygoat/introduction/views.py:553
  • path_traversal — /Users/jyothi/Projects/Test/pygoat/introduction/views.py:577
  • command_injection — /Users/jyothi/Projects/Test/pygoat/introduction/views.py:920
  • header_injection — /Users/jyothi/Projects/Test/pygoat/dockerized_labs/broken_auth_lab/app.py:49
  • weak_hash — /Users/jyothi/Projects/Test/pygoat/dockerized_labs/broken_auth_lab/app.py:86
  • deserialization — /Users/jyothi/Projects/Test/pygoat/dockerized_labs/insec_des_lab/main.py:36
  • path_traversal — /Users/jyothi/Projects/Test/pygoat/introduction/playground/ssrf/main.py:7

CWE-020 (1 findings)

  • Construction of a cookie using user-supplied input — dockerized_labs/broken_auth_lab/app.py:49

CWE-022 (1 findings)

  • Uncontrolled data used in path expression — introduction/views.py:920

CWE-078 (4 findings)

  • Uncontrolled command line — introduction/mitre.py:233
  • Unsafe shell command constructed from library input — introduction/mitre.py:241
  • Uncontrolled command line — introduction/views.py:424
  • Unsafe shell command constructed from library input — introduction/views.py:417

CWE-079 (2 findings)

  • Client-side cross-site scripting with additional heuristic sources — dockerized_labs/sensitive_data_exposure/templates/login.html:64
  • DOM text reinterpreted as HTML — introduction/templates/mitre/csrf_dashboard.html:26

CWE-089 (2 findings)

  • SQL query built from user-controlled sources — introduction/views.py:161
  • SQL query built from user-controlled sources — introduction/views.py:871

CWE-094 (2 findings)

  • Code injection — introduction/mitre.py:218
  • Code injection — introduction/views.py:453

CWE-1004 (1 findings)

  • Sensitive cookie missing `HttpOnly` attribute — introduction/views.py:285

CWE-1104 (1 findings)

  • [IaC] Using latest tag — docker-compose.yml:12

CWE-117 (2 findings)

  • Log Injection — introduction/views.py:647
  • Log Injection — introduction/views.py:661

CWE-200 (2 findings)

  • Password Reset Token Information Disclosure — dockerized_labs/broken_auth_lab/app.py:84
  • Information Disclosure via exposed secret keys in template files — introduction/templates/Lab_2021/A1_BrokenAccessControl/secret.html:6

CWE-208 (9 findings)

  • Timing attack against Hash — introduction/views.py:1193
  • Timing attack against secret — introduction/views.py:759
  • Timing attack against secret — introduction/views.py:799
  • Timing attack against secret — introduction/views.py:826
  • Timing attack against secret — introduction/views.py:1065
  • Timing attack against secret — dockerized_labs/broken_auth_lab/app.py:41
  • Timing attack against secret — dockerized_labs/broken_auth_lab/app.py:99
  • Timing attack against secret — dockerized_labs/broken_auth_lab/app.py:112
  • Timing attack against secret — introduction/playground/A9/api.py:17

CWE-215 (1 findings)

  • Flask app is run in debug mode — dockerized_labs/broken_auth_lab/app.py:123

CWE-22 (4 findings)

  • Path Traversal — introduction/views.py:920
  • Path Traversal — introduction/apis.py:133
  • Path Traversal — introduction/playground/ssrf/test.py:12
  • Path Traversal — introduction/playground/ssrf/main.py:8

CWE-250 (1 findings)

  • [IaC] Running as root — Dockerfile:1

CWE-256 (1 findings)

  • Plain Text Password Storage — dockerized_labs/broken_auth_lab/app.py:20

CWE-287 (3 findings)

  • Initializing SECRET_KEY of Flask application with Constant value — dockerized_labs/broken_auth_lab/app.py:8
  • Initializing SECRET_KEY of Flask application with Constant value — pygoat/settings.py:25
  • Initializing SECRET_KEY of Flask application with Constant value — dockerized_labs/sensitive_data_exposure/sensitive_data_lab/settings.py:8

CWE-312 (7 findings)

  • Clear-text logging of sensitive information — introduction/views.py:158
  • Clear-text logging of sensitive information — introduction/views.py:308
  • Clear-text logging of sensitive information — introduction/views.py:748
  • Clear-text logging of sensitive information — introduction/views.py:854
  • Clear-text logging of sensitive information — introduction/views.py:868
  • Clear-text storage of sensitive information — introduction/playground/A9/archive.py:49
  • Clear text storage of sensitive information — dockerized_labs/sensitive_data_exposure/templates/profile.html:222

CWE-327 (4 findings)

  • Use of a broken or weak cryptographic hashing algorithm on sensitive data — introduction/mitre.py:161
  • Use of a broken or weak cryptographic hashing algorithm on sensitive data — introduction/views.py:1019
  • Use of a broken or weak cryptographic hashing algorithm on sensitive data — introduction/views.py:1187
  • Use of a broken or weak cryptographic hashing algorithm on sensitive data — introduction/utility.py:59

CWE-328 (2 findings)

  • Weak Password Reset Token Generation (MD5) — dockerized_labs/broken_auth_lab/app.py:78
  • Use of Weak Hash — dockerized_labs/broken_auth_lab/app.py:86

CWE-347 (1 findings)

  • Improper JWT Verification — introduction/templates/Lab/sec_mis/sec_mis_lab3.html:30

CWE-348 (2 findings)

  • IP address spoofing — introduction/views.py:660
  • IP address spoofing — introduction/views.py:943

CWE-352 (1 findings)

  • Missing CSRF protection on authentication forms — introduction/templates/Lab_2021/A1_BrokenAccessControl/broken_access_lab_1.html:10

CWE-384 (1 findings)

  • Insecure Session Token Generation and Validation — dockerized_labs/broken_auth_lab/app.py:45

CWE-489 (1 findings)

  • Debug Mode Enabled in Production — dockerized_labs/broken_auth_lab/app.py:108

CWE-502 (3 findings)

  • Deserialization of user-controlled data — introduction/views.py:213
  • Deserialization of user-controlled data — introduction/views.py:553
  • Deserialization of user-controlled data — dockerized_labs/insec_des_lab/main.py:36

CWE-601 (1 findings)

  • Open Redirect — introduction/templates/mitre/csrf_dashboard.html:26

CWE-611 (1 findings)

  • XML external entity expansion — introduction/views.py:254

CWE-614 (2 findings)

  • Failure to use secure cookies — introduction/views.py:285
  • Sensitive Cookie Without Secure Flag — dockerized_labs/broken_auth_lab/templates/dashboard.html:79

CWE-776 (1 findings)

  • XML internal entity expansion — introduction/views.py:254

CWE-79 (6 findings)

  • Cross-Site Scripting (XSS) via Django safe filter — introduction/templates/Lab/XSS/xss_lab.html:28
  • Cross-Site Scripting (XSS) via unsafe user input in template — introduction/templates/Lab/XSS/xss_lab_2.html:28
  • Cross-Site Scripting (XSS) via JavaScript code injection — introduction/templates/Lab/XSS/xss_lab_3.html:28
  • Cross-Site Scripting (XSS) via unsafe rendering with 'safe' filter — introduction/templates/Lab/ssrf/ssrf_lab2.html:14
  • Cross-site Scripting (XSS) — introduction/static/js/a9.js:40
  • Cross-site Scripting (XSS) — dockerized_labs/insec_des_lab/templates/base.html:20

CWE-798 (1 findings)

  • Hardcoded Secret Key — dockerized_labs/broken_auth_lab/app.py:9

CWE-830 (6 findings)

  • Inclusion of functionality from an untrusted source — dockerized_labs/sensitive_data_exposure/templates/about.html:91
  • Inclusion of functionality from an untrusted source — dockerized_labs/sensitive_data_exposure/templates/base.html:60
  • Inclusion of functionality from an untrusted source — dockerized_labs/sensitive_data_exposure/templates/index.html:132
  • Inclusion of functionality from an untrusted source — dockerized_labs/sensitive_data_exposure/templates/lesson.html:338
  • Inclusion of functionality from an untrusted source — dockerized_labs/sensitive_data_exposure/templates/login.html:82
  • Inclusion of functionality from an untrusted source — dockerized_labs/sensitive_data_exposure/templates/register.html:93

CWE-918 (3 findings)

  • Full server-side request forgery — introduction/views.py:956
  • Server-Side Request Forgery (SSRF) via file path manipulation — introduction/templates/Lab/ssrf/ssrf_lab.html:13
  • Server-Side Request Forgery (SSRF) via direct user input in requests.get() — introduction/templates/Lab/ssrf/ssrf_lab2.html:18

CWE-922 (1 findings)

  • Insecure Storage of Sensitive Information — dockerized_labs/sensitive_data_exposure/templates/profile.html:222

CWE-94 (1 findings)

  • Server-Side Template Injection (SSTI) via unfiltered user input in template generation — introduction/templates/Lab_2021/A3_Injection/ssti_lab.html:28
CRITICAL CWE-094 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 26f6f6bc73c9

Code injection

Interpreting unsanitized user input as code allows a malicious user to perform arbitrary code execution.

📍 mitre.py : Line 218
Full Path: introduction/mitre.py

⚠️ Vulnerable Code

    213: # @authentication_decorator
    214: @csrf_exempt
    215: def mitre_lab_25_api(request):
    216:     if request.method == "POST":
    217:         expression = request.POST.get('expression')
>>> 218:         result = eval(expression)
    219:         return JsonResponse({'result': result})
    220:     else:
    221:         return redirect('/mitre/25/lab/')
    222: 
    223: 

💡 Explanation

An attacker could execute arbitrary Python code on the server, potentially leading to complete system compromise, data theft, file system access, or launching further attacks against internal systems.

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for AttributeControlFlowNode for Attribute()ControlFlowNode for expressionControlFlowNode for expression

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/mitre.py:215
    def mitre_lab_25_api(request):ControlFlowNode for request
  2. Step 2 [PROPAGATION] introduction/mitre.py:217
    expression = request.POST.get('expression')ControlFlowNode for Attribute
  3. Step 3 [PROPAGATION] introduction/mitre.py:217
    expression = request.POST.get('expression')ControlFlowNode for Attribute()
  4. Step 4 [PROPAGATION] introduction/mitre.py:217
    expression = request.POST.get('expression')ControlFlowNode for expression
  5. Step 5 [SINK] introduction/mitre.py:218
    result = eval(expression)ControlFlowNode for expression

✅ Recommended Fix

Replace the unsafe eval() call with a safe expression evaluator. First, import ast.literal_eval which only evaluates literals, not arbitrary code. Then validate the input to ensure it contains only mathematical expressions using a whitelist of allowed characters (numbers, basic operators, parentheses). Finally, implement a custom safe evaluator or use a library like simpleeval for mathematical expressions only.

import ast
import re
from simpleeval import simple_eval

def mitre_lab_25_api(request):
    if request.method == "POST":
        expression = request.POST.get('expression', '')
        # Whitelist allowed characters for mathematical expressions
        if not re.match(r'^[\d\s+\-*/().]+$', expression):
            return JsonResponse({'error': 'Invalid expression'}, status=400)
        try:
            # Use safe evaluation library
            result = simple_eval(expression)
            return JsonResponse({'result': result})
        except Exception as e:
            return JsonResponse({'error': 'Evaluation failed'}, status=400)
    else:
        return redirect('/mitre/25/lab/')

📚 References

  • https://cwe.mitre.org/data/definitions/094.html

🏷️ Tags

data-flowsecurity
CRITICAL CWE-078 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 98a74aa8d9e6

Uncontrolled command line

Using externally controlled strings in a command line may allow a malicious user to change the meaning of the command.

📍 mitre.py : Line 233
Full Path: introduction/mitre.py

⚠️ Vulnerable Code

    228: @authentication_decorator
    229: def mitre_lab_17(request):
    230:     return render(request, 'mitre/mitre_lab_17.html')
    231: 
    232: def command_out(command):
>>> 233:     process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    234:     return process.communicate()
    235:     
    236: 
    237: @csrf_exempt
    238: def mitre_lab_17_api(request):

💡 Explanation

An attacker could execute arbitrary system commands on the server by injecting shell metacharacters (like ;, &, |, or $(cmd)) in the IP parameter, potentially leading to complete system compromise, data theft, or server takeover.

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for AttributeControlFlowNode for Attribute()ControlFlowNode for ipControlFlowNode for commandControlFlowNode for commandControlFlowNode for commandControlFlowNode for command

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/mitre.py:238
    def mitre_lab_17_api(request):ControlFlowNode for request
  2. Step 2 [PROPAGATION] introduction/mitre.py:240
    ip = request.POST.get('ip')ControlFlowNode for Attribute
  3. Step 3 [PROPAGATION] introduction/mitre.py:240
    ip = request.POST.get('ip')ControlFlowNode for Attribute()
  4. Step 4 [PROPAGATION] introduction/mitre.py:240
    ip = request.POST.get('ip')ControlFlowNode for ip
  5. Step 5 [PROPAGATION] introduction/mitre.py:241
    command = "nmap " + ipControlFlowNode for command
  6. Step 6 [PROPAGATION] introduction/mitre.py:242
    res, err = command_out(command)ControlFlowNode for command
  7. Step 7 [PROPAGATION] introduction/mitre.py:232
    def command_out(command):ControlFlowNode for command
  8. Step 8 [SINK] introduction/mitre.py:233
    process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)ControlFlowNode for command

✅ Recommended Fix

First, remove the `shell=True` parameter as it enables shell injection. Second, use a whitelist approach to validate the IP address input before constructing the command. Third, pass the command as a list of arguments instead of a string to prevent command injection. Finally, consider using a safer alternative like Python's `ipaddress` module for validation.

import ipaddress
import subprocess

def validate_ip_address(ip_str):
    try:
        ipaddress.ip_address(ip_str)
        return True
    except ValueError:
        return False

def command_out_safe(ip):
    if not validate_ip_address(ip):
        raise ValueError("Invalid IP address")
    
    # Pass command as list without shell=True
    process = subprocess.Popen(['nmap', ip], 
                               stdout=subprocess.PIPE, 
                               stderr=subprocess.PIPE)
    return process.communicate()

# In mitre_lab_17_api function:
ip = request.POST.get('ip')
if validate_ip_address(ip):
    res, err = command_out_safe(ip)
else:
    return HttpResponse("Invalid IP address", status=400)

📚 References

  • https://cwe.mitre.org/data/definitions/078.html

🏷️ Tags

data-flowcorrectnesssecurity
CRITICAL CWE-094 HIGH ✓ VERIFIED TRUE POSITIVE
ID: a858c9f4f6ef

Code injection

Interpreting unsanitized user input as code allows a malicious user to perform arbitrary code execution.

📍 views.py : Line 453
Full Path: introduction/views.py

⚠️ Vulnerable Code

    448:         if (request.method=="POST"):
    449:             val=request.POST.get('val')
    450:             
    451:             print(val)
    452:             try:
>>> 453:                 output = eval(val)
    454:             except:
    455:                 output = "Something went wrong"
    456:                 return render(request,'Lab/CMD/cmd_lab2.html',{"output":output})
    457:             print("Output = ", output)
    458:             return render(request,'Lab/CMD/cmd_lab2.html',{"output":output})

💡 Explanation

An attacker could execute arbitrary Python code on the server, potentially leading to remote code execution, data theft, system compromise, or complete server takeover by injecting malicious payloads through the 'val' parameter.

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for AttributeControlFlowNode for Attribute()ControlFlowNode for valControlFlowNode for val

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:446
    def cmd_lab2(request):ControlFlowNode for request
  2. Step 2 [PROPAGATION] introduction/views.py:449
    val=request.POST.get('val')ControlFlowNode for Attribute
  3. Step 3 [PROPAGATION] introduction/views.py:449
    val=request.POST.get('val')ControlFlowNode for Attribute()
  4. Step 4 [PROPAGATION] introduction/views.py:449
    val=request.POST.get('val')ControlFlowNode for val
  5. Step 5 [SINK] introduction/views.py:453
    output = eval(val)ControlFlowNode for val

✅ Recommended Fix

Replace the unsafe eval() call with a safe alternative. First, validate that the input contains only expected mathematical expressions or safe characters. Then use a restricted evaluation method like Python's ast.literal_eval() for simple expressions, or implement a whitelist of allowed operations using a math evaluation library. Never execute arbitrary user input as code.

import ast
import re

if (request.method=="POST"):
    val = request.POST.get('val', '')
    
    # Only allow basic math expressions with numbers and operators
    if re.match(r'^[\d\s+\-*/().]*$', val):
        try:
            # Use ast.literal_eval for safe evaluation of literals
            # For math expressions, consider using a safer alternative like:
            # output = eval(val, {"__builtins__": {}}, {})
            # Or better yet, use a math evaluation library
            
            # Simple safe alternative for basic arithmetic:
            output = str(eval(val, {"__builtins__": {}}, {}))
        except:
            output = "Invalid expression"
    else:
        output = "Invalid input - only numbers and basic math operators allowed"
    
    return render(request,'Lab/CMD/cmd_lab2.html',{"output":output})

📚 References

  • https://cwe.mitre.org/data/definitions/094.html

🏷️ Tags

data-flowsecurity
CRITICAL CWE-918 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 3af51f6784cc

Full server-side request forgery

Making a network request to a URL that is fully user-controlled allows for request forgery attacks.

📍 views.py : Line 956
Full Path: introduction/views.py

⚠️ Vulnerable Code

    951:         return render(request, "Lab/ssrf/ssrf_lab2.html")
    952: 
    953:     elif request.method == "POST":
    954:         url = request.POST["url"]
    955:         try:
>>> 956:             response = requests.get(url)
    957:             return render(request, "Lab/ssrf/ssrf_lab2.html", {"response": response.content.decode()})
    958:         except:
    959:             return render(request, "Lab/ssrf/ssrf_lab2.html", {"error": "Invalid URL"})
    960: #--------------------------------------- Server-side template injection --------------------------------------#
    961: 

💡 Explanation

An attacker could exploit this to make requests to internal services, potentially accessing sensitive data from databases, cloud metadata services, or internal APIs. They could also pivot to attack other internal systems or perform port scanning of the internal network.

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for urlControlFlowNode for url

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:949
    def ssrf_lab2(request):ControlFlowNode for request
  2. Step 2 [PROPAGATION] introduction/views.py:954
    url = request.POST["url"]ControlFlowNode for url
  3. Step 3 [SINK] introduction/views.py:956
    response = requests.get(url)ControlFlowNode for url

✅ Recommended Fix

First, implement an allowlist of permitted domains or URL patterns that the application can access. Second, validate and sanitize the user-provided URL by parsing it with a secure library like urllib.parse and checking against the allowlist. Third, implement network-level restrictions by using a whitelist of allowed IP ranges or disabling access to internal network segments. Finally, consider using a timeout and limiting response size to prevent resource exhaustion attacks.

import requests
from urllib.parse import urlparse
from django.conf import settings

ALLOWED_DOMAINS = ['example.com', 'api.trusted-service.com']

if request.method == "POST":
    url = request.POST["url"]
    
    # Parse and validate URL
    parsed = urlparse(url)
    if not parsed.netloc or parsed.scheme not in ['http', 'https']:
        return render(request, "Lab/ssrf/ssrf_lab2.html", {"error": "Invalid URL"})
    
    # Domain allowlist check
    if parsed.netloc not in ALLOWED_DOMAINS:
        return render(request, "Lab/ssrf/ssrf_lab2.html", {"error": "Domain not permitted"})
    
    # Prevent internal IP access
    if parsed.netloc.startswith(('localhost', '127.', '192.168.', '10.', '172.')):
        return render(request, "Lab/ssrf/ssrf_lab2.html", {"error": "Internal addresses not allowed"})
    
    try:
        response = requests.get(url, timeout=5, allow_redirects=False)
        return render(request, "Lab/ssrf/ssrf_lab2.html", {"response": response.content.decode()})
    except:
        return render(request, "Lab/ssrf/ssrf_lab2.html", {"error": "Invalid URL"})

📚 References

  • https://cwe.mitre.org/data/definitions/918.html

🏷️ Tags

data-flowsecurity
CRITICAL CWE-502 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 5eace981fc79

Deserialization of user-controlled data

Deserializing user-controlled data may allow attackers to execute arbitrary code.

📍 views.py : Line 213
Full Path: introduction/views.py

⚠️ Vulnerable Code

    208:         if token == None:
    209:             token = encoded_user
    210:             response.set_cookie(key='token',value=token.decode('utf-8'))
    211:         else:
    212:             token = base64.b64decode(token)
>>> 213:             admin = pickle.loads(token)
    214:             if admin.admin == 1:
    215:                 response = render(request,'Lab/insec_des/insec_des_lab.html', {"message":"Welcome Admin, SECRETKEY:ADMIN123"})
    216:                 return response
    217: 
    218:         return response

💡 Explanation

Attackers could execute arbitrary code on the server by crafting malicious pickle payloads, leading to complete system compromise, data theft, or server takeover.

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for AttributeControlFlowNode for Attribute()ControlFlowNode for tokenControlFlowNode for tokenControlFlowNode for token

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:204
    def insec_des_lab(request):ControlFlowNode for request
  2. Step 2 [PROPAGATION] introduction/views.py:207
    token = request.COOKIES.get('token')ControlFlowNode for Attribute
  3. Step 3 [PROPAGATION] introduction/views.py:207
    token = request.COOKIES.get('token')ControlFlowNode for Attribute()
  4. Step 4 [PROPAGATION] introduction/views.py:207
    token = request.COOKIES.get('token')ControlFlowNode for token
  5. Step 5 [PROPAGATION] introduction/views.py:212
    token = base64.b64decode(token)ControlFlowNode for token
  6. Step 6 [SINK] introduction/views.py:213
    admin = pickle.loads(token)ControlFlowNode for token

✅ Recommended Fix

Replace pickle deserialization with a secure alternative. First, implement a JSON-based token system using HMAC signatures for integrity verification. Store only necessary user data (like user ID and admin flag) in the token, not Python objects. Validate the token signature before processing any data.

import json
import hmac
import hashlib
from django.conf import settings

# In insec_des_lab function, replace lines 212-213 with:
token_data = json.loads(base64.b64decode(token).decode('utf-8'))
data = token_data['data']
signature = token_data['sig']
expected_sig = hmac.new(settings.SECRET_KEY.encode(), data.encode(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(signature, expected_sig):
    return HttpResponse('Invalid token', status=403)
user_info = json.loads(data)
admin_flag = user_info.get('admin', 0)

📚 References

  • https://cwe.mitre.org/data/definitions/502.html

🏷️ Tags

data-flowsecurityserialization
CRITICAL CWE-502 HIGH ✓ VERIFIED TRUE POSITIVE
ID: f3b25cff5d3d

Deserialization of user-controlled data

Deserializing user-controlled data may allow attackers to execute arbitrary code.

📍 views.py : Line 553
Full Path: introduction/views.py

⚠️ Vulnerable Code

    548:         else:
    549: 
    550:             try :
    551:                 file=request.FILES["file"]
    552:                 try :
>>> 553:                     data = yaml.load(file,yaml.Loader)
    554:                     
    555:                     return render(request,"Lab/A9/a9_lab.html",{"data":data})
    556:                 except:
    557:                     return render(request, "Lab/A9/a9_lab.html", {"data": "Error"})
    558: 

💡 Explanation

An attacker could upload a malicious YAML file containing Python constructors that execute arbitrary code on the server, potentially leading to remote code execution, data theft, or complete system compromise.

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for fileControlFlowNode for file

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:544
    def a9_lab(request):ControlFlowNode for request
  2. Step 2 [PROPAGATION] introduction/views.py:551
    file=request.FILES["file"]ControlFlowNode for file
  3. Step 3 [SINK] introduction/views.py:553
    data = yaml.load(file,yaml.Loader)ControlFlowNode for file

✅ Recommended Fix

Replace the unsafe yaml.load() with yaml.safe_load() to prevent arbitrary code execution. The yaml.Loader is unsafe because it allows the execution of Python constructors. Use yaml.safe_load() which only loads basic YAML types like strings, lists, and dictionaries without executing arbitrary code. Additionally, validate the uploaded file size and type before processing.

try:
    file = request.FILES["file"]
    # Optional: Add file size validation
    if file.size > MAX_UPLOAD_SIZE:
        return render(request, "Lab/A9/a9_lab.html", {"data": "File too large"})
    
    # Use safe_load instead of load with unsafe Loader
    data = yaml.safe_load(file)
    
    return render(request, "Lab/A9/a9_lab.html", {"data": data})
except:
    return render(request, "Lab/A9/a9_lab.html", {"data": "Error"})

📚 References

  • https://cwe.mitre.org/data/definitions/502.html

🏷️ Tags

data-flowsecurityserialization
CRITICAL CWE-611 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 5d362b71e0cc

XML external entity expansion

Parsing user input as an XML document with external entity expansion is vulnerable to XXE attacks.

📍 views.py : Line 254
Full Path: introduction/views.py

⚠️ Vulnerable Code

    249: @csrf_exempt
    250: def xxe_parse(request):
    251: 
    252:     parser = make_parser()
    253:     parser.setFeature(feature_external_ges, True)
>>> 254:     doc = parseString(request.body.decode('utf-8'), parser=parser)
    255:     for event, node in doc:
    256:         if event == START_ELEMENT and node.tagName == 'text':
    257:             doc.expandNode(node)
    258:             text = node.toxml()
    259:     startInd = text.find('>')

💡 Explanation

An attacker could read arbitrary files from the server's filesystem, initiate SSRF attacks to access internal network resources, or cause denial of service through entity expansion attacks (Billion Laughs attack).

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for Attribute()

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:250
    def xxe_parse(request):ControlFlowNode for request
  2. Step 2 [SINK] introduction/views.py:254
    doc = parseString(request.body.decode('utf-8'), parser=parser)ControlFlowNode for Attribute()

✅ Recommended Fix

Disable external entity processing in the XML parser. First, set the external general entities feature to False instead of True. Second, also disable external parameter entities and DTD loading by setting the corresponding features to False. Finally, consider using a secure XML parser like defusedxml that disables these features by default.

    249: @csrf_exempt
    250: def xxe_parse(request):
    251: 
    252:     parser = make_parser()
    253:     parser.setFeature(feature_external_ges, False)
    254:     parser.setFeature(feature_external_pes, False)
    255:     parser.setFeature(features.validation, False)
    256:     parser.setFeature(features.load_external_dtd, False)
    257:     doc = parseString(request.body.decode('utf-8'), parser=parser)
    258:     for event, node in doc:
    259:         if event == START_ELEMENT and node.tagName == 'text':
    260:             doc.expandNode(node)
    261:             text = node.toxml()
    262:     startInd = text.find('>')

📚 References

  • https://cwe.mitre.org/data/definitions/611.html

🏷️ Tags

data-flowsecurity
CRITICAL CWE-078 HIGH ✓ VERIFIED TRUE POSITIVE
ID: b23ff41c6cea

Uncontrolled command line

Using externally controlled strings in a command line may allow a malicious user to change the meaning of the command.

📍 views.py : Line 424
Full Path: introduction/views.py

⚠️ Vulnerable Code

    419:                 command = "dig {}".format(domain)
    420:             
    421:             try:
    422:                 # output=subprocess.check_output(command,shell=True,encoding="UTF-8")
    423:                 process = subprocess.Popen(
>>> 424:                     command,
    425:                     shell=True,
    426:                     stdout=subprocess.PIPE, 
    427:                     stderr=subprocess.PIPE)
    428:                 stdout, stderr = process.communicate()
    429:                 data = stdout.decode('utf-8')

💡 Explanation

An attacker could execute arbitrary shell commands on the server by injecting shell metacharacters (like ;, &&, |, or $(...)) in the domain parameter, potentially leading to remote code execution, data theft, or server compromise.

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for AttributeControlFlowNode for Attribute()ControlFlowNode for domainControlFlowNode for domainControlFlowNode for commandControlFlowNode for command

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:409
    def cmd_lab(request):ControlFlowNode for request
  2. Step 2 [PROPAGATION] introduction/views.py:412
    domain=request.POST.get('domain')ControlFlowNode for Attribute
  3. Step 3 [PROPAGATION] introduction/views.py:412
    domain=request.POST.get('domain')ControlFlowNode for Attribute()
  4. Step 4 [PROPAGATION] introduction/views.py:412
    domain=request.POST.get('domain')ControlFlowNode for domain
  5. Step 5 [PROPAGATION] introduction/views.py:413
    domain=domain.replace("https://www.",'')ControlFlowNode for domain
  6. Step 6 [PROPAGATION] introduction/views.py:417
    command="nslookup {}".format(domain)ControlFlowNode for command
  7. Step 7 [SINK] introduction/views.py:424
    command,ControlFlowNode for command

✅ Recommended Fix

Remove shell=True and use subprocess.run() with a list of arguments instead of a string. Validate and sanitize the domain input by allowing only alphanumeric characters, hyphens, and dots. Use a whitelist of allowed commands (nslookup or dig) rather than constructing the command from user input.

import re

# Validate domain input
if not re.match(r'^[a-zA-Z0-9.-]+$', domain):
    return HttpResponse('Invalid domain', status=400)

# Use subprocess.run with argument list
command = ['nslookup', domain]
try:
    result = subprocess.run(command, capture_output=True, text=True, timeout=5)
    data = result.stdout
except subprocess.TimeoutExpired:
    return HttpResponse('Command timeout', status=500)

📚 References

  • https://cwe.mitre.org/data/definitions/078.html

🏷️ Tags

data-flowcorrectnesssecurity
CRITICAL CWE-256 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 1a7314eca79c

Plain Text Password Storage

Plaintext Storage of a Password

📍 app.py : Line 20
Full Path: dockerized_labs/broken_auth_lab/app.py
Sink Method: dictionary assignment

⚠️ Vulnerable Code

    14: # Vulnerable: Storing user data in memory
    15: users = {
    16:     'admin': {
    17:         'password': 'admin123',  # Vulnerable: Weak password
    18:         'email': 'admin@example.com',
    19:         'role': 'admin'
>>> 20:     },
    21:     'user': {
    22:         'password': 'password123',  # Vulnerable: Weak password
    23:         'email': 'user@example.com',
    24:         'role': 'user'
    25:     }
    26: }

💡 Explanation

Passwords are stored in plain text. If the application data is compromised (e.g., memory dump, database breach), all user credentials are immediately exposed. Attackers can use these credentials to impersonate users, including administrators.

🔗 Tainted Variables

users[username]['password']

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] dockerized_labs/broken_auth_lab/app.py:20 (hardcoded value)
    'password': 'admin123',users['admin']['password']
  2. Step 2 [PROPAGATION] dockerized_labs/broken_auth_lab/app.py:40
    if username in users and users[username]['password'] == password:users[username]['password']
  3. Step 3 [SINK] dockerized_labs/broken_auth_lab/app.py:40
    if username in users and users[username]['password'] == password:users[username]['password']

✅ Recommended Fix

Use a strong, adaptive hashing algorithm (like bcrypt, scrypt, or Argon2) to hash passwords before storage. Never compare or store plain text passwords.

import bcrypt
# During registration
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
# Store hashed_password
# During login
if bcrypt.checkpw(password.encode('utf-8'), stored_hashed_password):
    # Login successful

📚 References

🏷️ Tags

password-managementcryptographyauthentication
CRITICAL CWE-502 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 9c381f3841a3

Deserialization of user-controlled data

Deserializing user-controlled data may allow attackers to execute arbitrary code.

📍 main.py : Line 36
Full Path: dockerized_labs/insec_des_lab/main.py

⚠️ Vulnerable Code

    31: def deserialize_data():
    32:     try:
    33:         serialized_data = request.form.get('serialized_data', '')
    34:         decoded_data = base64.b64decode(serialized_data)
    35:         # Intentionally vulnerable deserialization, matching PyGoat
>>> 36:         user = pickle.loads(decoded_data)
    37:         
    38:         if isinstance(user, User):
    39:             if user.is_admin:
    40:                 message = f"Welcome Admin {user.username}! Here's the secret admin content: ADMIN_KEY_123"
    41:             else:

💡 Explanation

An attacker could execute arbitrary code on the server by crafting malicious pickle payloads, potentially leading to complete system compromise, data theft, or server takeover through remote code execution.

🔗 Tainted Variables

ControlFlowNode for ImportMemberControlFlowNode for requestControlFlowNode for requestControlFlowNode for AttributeControlFlowNode for Attribute()ControlFlowNode for serialized_dataControlFlowNode for decoded_dataControlFlowNode for decoded_data

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] dockerized_labs/insec_des_lab/main.py:1
    from flask import Flask, render_template, request, make_responseControlFlowNode for ImportMember
  2. Step 2 [PROPAGATION] dockerized_labs/insec_des_lab/main.py:1
    from flask import Flask, render_template, request, make_responseControlFlowNode for request
  3. Step 3 [PROPAGATION] dockerized_labs/insec_des_lab/main.py:33
    serialized_data = request.form.get('serialized_data', '')ControlFlowNode for request
  4. Step 4 [PROPAGATION] dockerized_labs/insec_des_lab/main.py:33
    serialized_data = request.form.get('serialized_data', '')ControlFlowNode for Attribute
  5. Step 5 [PROPAGATION] dockerized_labs/insec_des_lab/main.py:33
    serialized_data = request.form.get('serialized_data', '')ControlFlowNode for Attribute()
  6. Step 6 [PROPAGATION] dockerized_labs/insec_des_lab/main.py:33
    serialized_data = request.form.get('serialized_data', '')ControlFlowNode for serialized_data
  7. Step 7 [PROPAGATION] dockerized_labs/insec_des_lab/main.py:34
    decoded_data = base64.b64decode(serialized_data)ControlFlowNode for decoded_data
  8. Step 8 [SINK] dockerized_labs/insec_des_lab/main.py:36
    user = pickle.loads(decoded_data)ControlFlowNode for decoded_data

✅ Recommended Fix

Replace pickle deserialization with a secure alternative. First, remove the vulnerable pickle.loads() call entirely. Instead, implement a JSON-based serialization/deserialization scheme using Python's json module. Validate and sanitize all user input before processing, and maintain the User object structure using safe data types.

import json

def deserialize_data():
    try:
        serialized_data = request.form.get('serialized_data', '')
        # Replace pickle with JSON deserialization
        user_data = json.loads(serialized_data)
        
        # Create User object from validated data
        user = User(
            username=str(user_data.get('username', '')),
            is_admin=bool(user_data.get('is_admin', False))
        )
        
        if user.is_admin:
            message = f"Welcome Admin {user.username}! Here's the secret admin content: ADMIN_KEY_123"
        else:
            message = f"Welcome {user.username}!"
        
        return make_response(message, 200)
    except (json.JSONDecodeError, ValueError) as e:
        return make_response(f"Invalid data format: {str(e)}", 400)

📚 References

  • https://cwe.mitre.org/data/definitions/502.html

🏷️ Tags

data-flowsecurityserialization
CRITICAL CWE-94 HIGH ✓ VERIFIED TRUE POSITIVE
ID: dde0834d7a08

Server-Side Template Injection (SSTI) via unfiltered user input in template generation

Improper Control of Generation of Code ('Code Injection')

📍 ssti_lab.html : Line 28
Full Path: introduction/templates/Lab_2021/A3_Injection/ssti_lab.html
Sink Method: ssti_lab

⚠️ Vulnerable Code

    20:             blog = request.POST["blog"]
    21:             id = str(uuid.uuid4()).split('-')[-1]
    22: 
    23:             blog = filter_blog(blog)
    24:             prepend_code = "{&#37; extends 'introduction/base.html' &#37;}\n{&#37; block content &#37;}{&#37; block title &#37;}\n<title>SSTI-Blogs</title>\n{&#37; endblock &#37;}"
    25: 
    26:             blog = prepend_code + blog + "{&#37; endblock &#37;}"
    27:             new_blog = Blogs.objects.create(author = request.user, blog_id = id)
>>> 28:             new_blog.save() 
    29:             dirname = os.path.dirname(__file__)
    30:             filename = os.path.join(dirname, f"templates/Lab_2021/A3_Injection/Blogs/{id}.html")
    31:             file = open(filename, "w+") 
    32:             file.write(blog)
    33:             file.close()
    34:             return redirect(f'blog/{id}')
    35:     else:

💡 Explanation

Attackers can inject Django template syntax to execute arbitrary code on the server. Example payloads can access sensitive data like SECRET_KEY (as shown in example template a2538af1b5e4.html), execute system commands, read files, or achieve remote code execution. The example file 9d73d120683d.html shows access to admin logs and password hashes.

🔗 Tainted Variables

blog

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/templates/Lab_2021/A3_Injection/ssti_lab.html:12 (HTTP parameter)
    <textarea id="ssti_blog" name="blog" placeholder="your blogs goes here" style="background:#f4f4f924;overflow: auto"></textarea>blog
  2. Step 2 [PROPAGATION] introduction/templates/Lab_2021/A3_Injection/ssti_lab.html:20
    blog = request.POST["blog"]blog
  3. Step 3 [SINK] introduction/templates/Lab_2021/A3_Injection/ssti_lab.html:28
    file.write(blog)blog

✅ Recommended Fix

1. Implement strict input validation using allowlists. 2. Use a secure template rendering approach that doesn't allow user-controlled template syntax. 3. Store blog content in database fields rather than template files. 4. Use a dedicated sanitization function that removes or escapes template syntax.

import re

def sanitize_blog_content(content):
    # Remove all Django template syntax
    content = re.sub(r'\{[%{].*?[%}]\}', '', content)
    # Remove HTML tags except basic formatting
    allowed_tags = ['b', 'i', 'p', 'br', 'ul', 'li', 'ol']
    # Use bleach or similar library for HTML sanitization
    import bleach
    return bleach.clean(content, tags=allowed_tags, strip=True)

# Usage:
blog_content = sanitize_blog_content(request.POST["blog"])
# Store in database instead of template file
Blog.objects.create(author=request.user, content=blog_content, blog_id=id)

📚 References

🏷️ Tags

ssticode-injectiontemplate-injection
HIGH CWE-327 HIGH ✓ VERIFIED TRUE POSITIVE
ID: dbd9425e3da1

Use of a broken or weak cryptographic hashing algorithm on sensitive data

Using broken or weak cryptographic hashing algorithms can compromise security.

📍 mitre.py : Line 161
Full Path: introduction/mitre.py

⚠️ Vulnerable Code

    156:     if request.method == 'GET':
    157:         return render(request, 'mitre/csrf_lab_login.html')
    158:     elif request.method == 'POST':
    159:         password = request.POST.get('password')
    160:         username = request.POST.get('username')
>>> 161:         password = md5(password.encode()).hexdigest()
    162:         User = CSRF_user_tbl.objects.filter(username=username, password=password)
    163:         if User:
    164:             payload ={
    165:                 'username': username,
    166:                 'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=300),

💡 Explanation

An attacker could crack MD5 hashes using rainbow tables or GPU-based brute force attacks to recover plaintext passwords. Since MD5 is fast and unsalted, identical passwords produce identical hashes, enabling credential stuffing attacks across the user database.

🔗 Tainted Variables

ControlFlowNode for Attribute()ControlFlowNode for passwordControlFlowNode for Attribute()

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/mitre.py:159
    password = request.POST.get('password')ControlFlowNode for Attribute()
  2. Step 2 [PROPAGATION] introduction/mitre.py:159
    password = request.POST.get('password')ControlFlowNode for password
  3. Step 3 [SINK] introduction/mitre.py:161
    password = md5(password.encode()).hexdigest()ControlFlowNode for Attribute()

✅ Recommended Fix

Replace MD5 with a secure password hashing algorithm designed for password storage. Use Django's built-in make_password() function which defaults to PBKDF2 with SHA256 and a salt. When verifying passwords, use check_password() instead of comparing raw hashes. Ensure all existing passwords are migrated to the new hashing method.

from django.contrib.auth.hashers import make_password, check_password

# In the POST handler:
if request.method == 'POST':
    password = request.POST.get('password')
    username = request.POST.get('username')
    
    try:
        user = CSRF_user_tbl.objects.get(username=username)
        if check_password(password, user.password):
            # Authentication successful
            # ... rest of the code
    except CSRF_user_tbl.DoesNotExist:
        # Handle invalid user
        pass

# When creating users, use:
hashed_password = make_password(plain_text_password)

📚 References

  • https://cwe.mitre.org/data/definitions/327.html

🏷️ Tags

data-flowsecurity
HIGH CWE-327 HIGH ✓ VERIFIED TRUE POSITIVE
ID: ef8ea1c93a29

Use of a broken or weak cryptographic hashing algorithm on sensitive data

Using broken or weak cryptographic hashing algorithms can compromise security.

📍 views.py : Line 1019
Full Path: introduction/views.py

⚠️ Vulnerable Code

    1014:             return render(request,"Lab_2021/A2_Crypto_failur/crypto_failure_lab.html")
    1015:         elif request.method=="POST":
    1016:             username = request.POST["username"]
    1017:             password = request.POST["password"]
    1018:             try:
>>> 1019:                 password = md5(password.encode()).hexdigest()
    1020:                 user = CF_user.objects.filter(username=username,password=password).first()
    1021:                 return render(request,"Lab_2021/A2_Crypto_failur/crypto_failure_lab.html",{"user":user, "success":True,"failure":False})
    1022:             except Exception as e:
    1023:                 return render(request,"Lab_2021/A2_Crypto_failur/crypto_failure_lab.html",{"success":False, "failure":True})
    1024:     else :

💡 Explanation

Attackers could crack MD5 hashes using rainbow tables or GPU-based brute force attacks to recover plaintext passwords, especially since MD5 lacks salt and is computationally cheap. This could lead to account takeover and credential reuse attacks across other services.

🔗 Tainted Variables

ControlFlowNode for SubscriptControlFlowNode for passwordControlFlowNode for Attribute()

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:1017
    password = request.POST["password"]ControlFlowNode for Subscript
  2. Step 2 [PROPAGATION] introduction/views.py:1017
    password = request.POST["password"]ControlFlowNode for password
  3. Step 3 [SINK] introduction/views.py:1019
    password = md5(password.encode()).hexdigest()ControlFlowNode for Attribute()

✅ Recommended Fix

Replace MD5 with a secure password hashing algorithm designed for password storage. Use Django's built-in make_password() function which defaults to PBKDF2 with SHA256 and a per-user salt. Import the function from django.contrib.auth.hashers and apply it to the password before storing or comparing. Ensure the same algorithm is used consistently across registration and login.

from django.contrib.auth.hashers import make_password, check_password

# In the POST handler:
username = request.POST["username"]
password = request.POST["password"]
try:
    # For registration: hashed_password = make_password(password)
    # For login verification:
    user = CF_user.objects.filter(username=username).first()
    if user and check_password(password, user.password):
        return render(request, "Lab_2021/A2_Crypto_failur/crypto_failure_lab.html", {"user": user, "success": True, "failure": False})
    else:
        return render(request, "Lab_2021/A2_Crypto_failur/crypto_failure_lab.html", {"success": False, "failure": True})
except Exception as e:
    return render(request, "Lab_2021/A2_Crypto_failur/crypto_failure_lab.html", {"success": False, "failure": True})

📚 References

  • https://cwe.mitre.org/data/definitions/327.html

🏷️ Tags

data-flowsecurity
HIGH CWE-327 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 894eafa401c4

Use of a broken or weak cryptographic hashing algorithm on sensitive data

Using broken or weak cryptographic hashing algorithms can compromise security.

📍 views.py : Line 1187
Full Path: introduction/views.py

⚠️ Vulnerable Code

    1182:     elif request.method == "POST":
    1183:         token = str(uuid.uuid4())
    1184:         try:
    1185:             username = request.POST["username"]
    1186:             password = request.POST["password"]
>>> 1187:             password = hashlib.sha256(password.encode()).hexdigest()
    1188:         except:
    1189:             response = render(request, "Lab_2021/A7_auth_failure/lab3.html")
    1190:             response.set_cookie("session_id", None)
    1191:             return response
    1192: 

💡 Explanation

An attacker could use rainbow tables or GPU-based attacks to crack SHA-256 hashes, potentially compromising user accounts. Since SHA-256 is fast and unsalted, identical passwords will have identical hashes, enabling password correlation attacks.

🔗 Tainted Variables

ControlFlowNode for SubscriptControlFlowNode for passwordControlFlowNode for Attribute()

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:1186
    password = request.POST["password"]ControlFlowNode for Subscript
  2. Step 2 [PROPAGATION] introduction/views.py:1186
    password = request.POST["password"]ControlFlowNode for password
  3. Step 3 [SINK] introduction/views.py:1187
    password = hashlib.sha256(password.encode()).hexdigest()ControlFlowNode for Attribute()

✅ Recommended Fix

Replace SHA-256 with a password hashing algorithm designed for password storage, such as Argon2, bcrypt, or PBKDF2. Use Django's built-in make_password() function which defaults to PBKDF2 with a salt and multiple iterations. Store only the hashed password in the database, never the plaintext or SHA-256 hash.

from django.contrib.auth.hashers import make_password, check_password

# Replace line 1187 with:
password = make_password(password)

# Later when verifying passwords:
# if check_password(provided_password, stored_hash):

📚 References

  • https://cwe.mitre.org/data/definitions/327.html

🏷️ Tags

data-flowsecurity
HIGH CWE-089 HIGH ✓ VERIFIED TRUE POSITIVE
ID: e9958f2b1e57

SQL query built from user-controlled sources

Building a SQL query from user-controlled sources is vulnerable to insertion of malicious SQL code by the user.

📍 views.py : Line 161
Full Path: introduction/views.py

⚠️ Vulnerable Code

    156: 
    157:                 sql_query = "SELECT * FROM introduction_login WHERE user='"+name+"'AND password='"+password+"'"
    158:                 print(sql_query)
    159:                 try:
    160:                     print("\nin try\n")
>>> 161:                     val=login.objects.raw(sql_query)
    162:                 except:
    163:                     print("\nin except\n")
    164:                     return render(
    165:                         request, 
    166:                         'Lab/SQL/sql_lab.html',

💡 Explanation

An attacker could execute arbitrary SQL commands, allowing them to bypass authentication, extract sensitive data from the database, modify or delete data, or potentially execute system commands depending on database configuration.

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for AttributeControlFlowNode for Attribute()ControlFlowNode for nameControlFlowNode for sql_queryControlFlowNode for sql_query

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:146
    def sql_lab(request):ControlFlowNode for request
  2. Step 2 [PROPAGATION] introduction/views.py:149
    name=request.POST.get('name')ControlFlowNode for Attribute
  3. Step 3 [PROPAGATION] introduction/views.py:149
    name=request.POST.get('name')ControlFlowNode for Attribute()
  4. Step 4 [PROPAGATION] introduction/views.py:149
    name=request.POST.get('name')ControlFlowNode for name
  5. Step 5 [PROPAGATION] introduction/views.py:157
    sql_query = "SELECT * FROM introduction_login WHERE user='"+name+"'AND password='"+password+"'"ControlFlowNode for sql_query
  6. Step 6 [SINK] introduction/views.py:161
    val=login.objects.raw(sql_query)ControlFlowNode for sql_query

✅ Recommended Fix

Replace the raw SQL query with Django's ORM query methods using parameterized queries. First, use Django's filter() method with field lookups instead of string concatenation. Second, ensure user input is properly validated and sanitized by the ORM, which will handle SQL injection protection automatically.

val = login.objects.filter(user=name, password=password).first()
# Or for authentication purposes, better to use:
# from django.contrib.auth import authenticate
# user = authenticate(request, username=name, password=password)

📚 References

  • https://cwe.mitre.org/data/definitions/089.html

🏷️ Tags

data-flowsecurity
HIGH CWE-089 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 66e60191b10d

SQL query built from user-controlled sources

Building a SQL query from user-controlled sources is vulnerable to insertion of malicious SQL code by the user.

📍 views.py : Line 871
Full Path: introduction/views.py

⚠️ Vulnerable Code

    866:             sql_instance.save()
    867: 
    868:             print(sql_query)
    869: 
    870:             try:
>>> 871:                 user = sql_lab_table.objects.raw(sql_query)
    872:                 user = user[0].id
    873:                 print(user)
    874: 
    875:             except:
    876:                 return render(

💡 Explanation

An attacker could execute arbitrary SQL commands on the database, potentially reading, modifying, or deleting sensitive data, bypassing authentication, or gaining administrative access to the database server.

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for AttributeControlFlowNode for Attribute()ControlFlowNode for nameControlFlowNode for sql_queryControlFlowNode for sql_query

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:848
    def injection_sql_lab(request):ControlFlowNode for request
  2. Step 2 [PROPAGATION] introduction/views.py:851
    name=request.POST.get('name')ControlFlowNode for Attribute
  3. Step 3 [PROPAGATION] introduction/views.py:851
    name=request.POST.get('name')ControlFlowNode for Attribute()
  4. Step 4 [PROPAGATION] introduction/views.py:851
    name=request.POST.get('name')ControlFlowNode for name
  5. Step 5 [PROPAGATION] introduction/views.py:857
    sql_query = "SELECT * FROM introduction_sql_lab_table WHERE id='"+name+"'AND password='"+password+"'"ControlFlowNode for sql_query
  6. Step 6 [SINK] introduction/views.py:871
    user = sql_lab_table.objects.raw(sql_query)ControlFlowNode for sql_query

✅ Recommended Fix

Replace the raw SQL query with Django's ORM query methods using parameterized queries. Instead of building the SQL string with user input, use the ORM's filter() method with proper field lookups. If raw SQL is absolutely necessary, use Django's parameterized raw() method with query parameters.

# Replace lines 857-871 with:
sql_query = "SELECT * FROM introduction_sql_lab_table WHERE name = %s"
user = sql_lab_table.objects.raw(sql_query, [name])

# OR better yet, use Django ORM directly:
user = sql_lab_table.objects.filter(name=name).first()
if user:
    user_id = user.id

📚 References

  • https://cwe.mitre.org/data/definitions/089.html

🏷️ Tags

data-flowsecurity
HIGH CWE-776 HIGH ✓ VERIFIED TRUE POSITIVE
ID: e8f65463a50b

XML internal entity expansion

Parsing user input as an XML document with arbitrary internal entity expansion is vulnerable to denial-of-service attacks.

📍 views.py : Line 254
Full Path: introduction/views.py

⚠️ Vulnerable Code

    249: @csrf_exempt
    250: def xxe_parse(request):
    251: 
    252:     parser = make_parser()
    253:     parser.setFeature(feature_external_ges, True)
>>> 254:     doc = parseString(request.body.decode('utf-8'), parser=parser)
    255:     for event, node in doc:
    256:         if event == START_ELEMENT and node.tagName == 'text':
    257:             doc.expandNode(node)
    258:             text = node.toxml()
    259:     startInd = text.find('>')

💡 Explanation

An attacker could exploit this vulnerability to perform XML External Entity (XXE) attacks, potentially reading sensitive files from the server filesystem, causing denial of service through entity expansion attacks (billion laughs attack), or making internal network requests to exfiltrate data.

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for Attribute()

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:250
    def xxe_parse(request):ControlFlowNode for request
  2. Step 2 [SINK] introduction/views.py:254
    doc = parseString(request.body.decode('utf-8'), parser=parser)ControlFlowNode for Attribute()

✅ Recommended Fix

Disable XML external entity processing entirely by setting both `feature_external_ges` and `feature_external_pes` to False on the parser before parsing. Additionally, consider using the `defusedxml` library which provides secure XML parsing by default. Replace the vulnerable `xml.sax.make_parser()` with `defusedxml.sax.make_parser()` to prevent entity expansion attacks.

import defusedxml.sax
from defusedxml.common import DefusedXmlException

@csrf_exempt
def xxe_parse(request):
    try:
        parser = defusedxml.sax.make_parser()
        parser.setFeature(feature_external_ges, False)
        parser.setFeature(feature_external_pes, False)
        doc = parseString(request.body.decode('utf-8'), parser=parser)
        for event, node in doc:
            if event == START_ELEMENT and node.tagName == 'text':
                doc.expandNode(node)
                text = node.toxml()
        startInd = text.find('>')
    except DefusedXmlException:
        return HttpResponseBadRequest('Invalid XML')

📚 References

  • https://cwe.mitre.org/data/definitions/776.html

🏷️ Tags

data-flowsecurity
HIGH CWE-312 HIGH ✓ VERIFIED TRUE POSITIVE
ID: b5476b51e68a

Clear-text logging of sensitive information

Logging sensitive information without encryption or hashing can expose it to an attacker.

📍 views.py : Line 158
Full Path: introduction/views.py

⚠️ Vulnerable Code

    153:         if name:
    154: 
    155:             if login.objects.filter(user=name):
    156: 
    157:                 sql_query = "SELECT * FROM introduction_login WHERE user='"+name+"'AND password='"+password+"'"
>>> 158:                 print(sql_query)
    159:                 try:
    160:                     print("\nin try\n")
    161:                     val=login.objects.raw(sql_query)
    162:                 except:
    163:                     print("\nin except\n")

💡 Explanation

An attacker with access to logs could extract plaintext credentials, leading to account compromise. Additionally, the raw SQL construction creates SQL injection vulnerabilities that could allow database manipulation or data exfiltration.

🔗 Tainted Variables

ControlFlowNode for Attribute()ControlFlowNode for passwordControlFlowNode for sql_queryControlFlowNode for sql_query

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:151
    password=request.POST.get('pass')ControlFlowNode for Attribute()
  2. Step 2 [PROPAGATION] introduction/views.py:151
    password=request.POST.get('pass')ControlFlowNode for password
  3. Step 3 [PROPAGATION] introduction/views.py:157
    sql_query = "SELECT * FROM introduction_login WHERE user='"+name+"'AND password='"+password+"'"ControlFlowNode for sql_query
  4. Step 4 [SINK] introduction/views.py:158
    print(sql_query)ControlFlowNode for sql_query

✅ Recommended Fix

First, remove the print statement that logs the SQL query containing plaintext credentials. Second, replace the raw SQL query with Django's ORM query methods to prevent SQL injection. Third, use Django's built-in authentication system instead of manual password checking. Finally, ensure no sensitive data is logged in production by configuring appropriate logging levels.

from django.contrib.auth import authenticate

if name:
    if login.objects.filter(user=name):
        # Use Django's authentication system instead of raw SQL
        user = authenticate(username=name, password=password)
        if user is not None:
            # Authentication successful
            # Remove all print statements with sensitive data
            val = user
        else:
            # Authentication failed
            val = None

📚 References

  • https://cwe.mitre.org/data/definitions/312.html

🏷️ Tags

data-flowsecurity
HIGH CWE-312 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 1e8b612918f8

Clear-text logging of sensitive information

Logging sensitive information without encryption or hashing can expose it to an attacker.

📍 views.py : Line 308
Full Path: introduction/views.py

⚠️ Vulnerable Code

    303:             return render(request,'Lab/AUTH/auth_lab_login.html')
    304:     elif request.method == 'POST':
    305:         try:
    306:             user_name = request.POST['username']
    307:             passwd  = request.POST['pass']
>>> 308:             print(user_name,passwd)
    309:             obj = authLogin.objects.filter(username=user_name,password=passwd)[0]
    310:             try:
    311:                 rendered = render_to_string('Lab/AUTH/auth_success.html', {'username': obj.username,'userid':obj.userid,'name':obj.name, 'err_msg':'Login Successful'})
    312:                 response = HttpResponse(rendered)
    313:                 response.set_cookie('userid', obj.userid, max_age=31449600, samesite=None, secure=False)

💡 Explanation

An attacker with access to application logs could steal user credentials, leading to account compromise and potential lateral movement within the system. This also violates privacy regulations and exposes the system to credential stuffing attacks.

🔗 Tainted Variables

ControlFlowNode for SubscriptControlFlowNode for passwdControlFlowNode for passwd

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:307
    passwd = request.POST['pass']ControlFlowNode for Subscript
  2. Step 2 [PROPAGATION] introduction/views.py:307
    passwd = request.POST['pass']ControlFlowNode for passwd
  3. Step 3 [SINK] introduction/views.py:308
    print(user_name,passwd)ControlFlowNode for passwd

✅ Recommended Fix

Remove the print statement that logs credentials in plain text. If debugging is necessary, replace it with logging that redacts sensitive information or use a secure logging framework. Ensure no other logging statements in the codebase expose sensitive data like passwords, tokens, or personal information.

    303:             return render(request,'Lab/AUTH/auth_lab_login.html')
    304:     elif request.method == 'POST':
    305:         try:
    306:             user_name = request.POST['username']
    307:             passwd  = request.POST['pass']
    308:             # Removed insecure credential logging
    309:             obj = authLogin.objects.filter(username=user_name,password=passwd)[0]

📚 References

  • https://cwe.mitre.org/data/definitions/312.html

🏷️ Tags

data-flowsecurity
HIGH CWE-312 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 1cd13b9fef9f

Clear-text logging of sensitive information

Logging sensitive information without encryption or hashing can expose it to an attacker.

📍 views.py : Line 748
Full Path: introduction/views.py

⚠️ Vulnerable Code

    743:     else:
    744:         return redirect('login')
    745:     
    746:     name = request.POST.get('name')
    747:     password = request.POST.get('pass')
>>> 748:     print(password)
    749:     print(name)
    750:     if name:
    751:         if request.COOKIES.get('admin') == "1":
    752:             return render(
    753:                 request, 

💡 Explanation

Attackers with access to server logs or console output could steal user credentials, leading to account compromise and potential privilege escalation. This could also violate data protection regulations like GDPR or CCPA.

🔗 Tainted Variables

ControlFlowNode for Attribute()ControlFlowNode for passwordControlFlowNode for password

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:747
    password = request.POST.get('pass')ControlFlowNode for Attribute()
  2. Step 2 [PROPAGATION] introduction/views.py:747
    password = request.POST.get('pass')ControlFlowNode for password
  3. Step 3 [SINK] introduction/views.py:748
    print(password)ControlFlowNode for password

✅ Recommended Fix

Remove the print statements that log sensitive credentials. Instead of printing passwords to the console, implement proper logging without sensitive data. If debugging is needed, use a secure logging framework that redacts sensitive information and ensure logs are stored securely with access controls.

    743:     else:
    744:         return redirect('login')
    745:     
    746:     name = request.POST.get('name')
    747:     password = request.POST.get('pass')
    748:     # Removed insecure print statements
    749:     if name:
    750:         if request.COOKIES.get('admin') == "1":
    751:             return render(
    752:                 request,

📚 References

  • https://cwe.mitre.org/data/definitions/312.html

🏷️ Tags

data-flowsecurity
HIGH CWE-312 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 40e0d63c22b8

Clear-text logging of sensitive information

Logging sensitive information without encryption or hashing can expose it to an attacker.

📍 views.py : Line 854
Full Path: introduction/views.py

⚠️ Vulnerable Code

    849:     if request.user.is_authenticated:
    850: 
    851:         name=request.POST.get('name')
    852:         password=request.POST.get('pass')
    853:         print(name)
>>> 854:         print(password)
    855: 
    856:         if name:
    857:             sql_query = "SELECT * FROM introduction_sql_lab_table WHERE id='"+name+"'AND password='"+password+"'"
    858: 
    859:             sql_instance = sql_lab_table(id="admin", password="65079b006e85a7e798abecb99e47c154")

💡 Explanation

An attacker with access to server logs could steal user credentials, leading to account compromise and potential lateral movement within the system. The clear-text logging also violates privacy regulations and exposes authentication secrets.

🔗 Tainted Variables

ControlFlowNode for Attribute()ControlFlowNode for passwordControlFlowNode for password

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:852
    password=request.POST.get('pass')ControlFlowNode for Attribute()
  2. Step 2 [PROPAGATION] introduction/views.py:852
    password=request.POST.get('pass')ControlFlowNode for password
  3. Step 3 [SINK] introduction/views.py:854
    print(password)ControlFlowNode for password

✅ Recommended Fix

Remove the print statements that log sensitive credentials. Instead of printing passwords to the console, use Django's logging framework with appropriate log levels and ensure passwords are never logged. Additionally, fix the SQL injection vulnerability by using parameterized queries instead of string concatenation.

    if request.user.is_authenticated:

        name=request.POST.get('name')
        password=request.POST.get('pass')
        # REMOVED: print(name)
        # REMOVED: print(password)

        if name:
            sql_query = "SELECT * FROM introduction_sql_lab_table WHERE id=%s AND password=%s"
            # Execute with parameterized query using Django's ORM or cursor.execute(sql_query, [name, password])

            sql_instance = sql_lab_table(id="admin", password="65079b006e85a7e798abecb99e47c154")

📚 References

  • https://cwe.mitre.org/data/definitions/312.html

🏷️ Tags

data-flowsecurity
HIGH CWE-312 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 627fc939a6f1

Clear-text logging of sensitive information

Logging sensitive information without encryption or hashing can expose it to an attacker.

📍 views.py : Line 868
Full Path: introduction/views.py

⚠️ Vulnerable Code

    863:             sql_instance = sql_lab_table(id="slinky", password="b4f945433ea4c369c12741f62a23ccc0")
    864:             sql_instance.save()
    865:             sql_instance = sql_lab_table(id="bloke", password="f8d1ce191319ea8f4d1d26e65e130dd5")
    866:             sql_instance.save()
    867: 
>>> 868:             print(sql_query)
    869: 
    870:             try:
    871:                 user = sql_lab_table.objects.raw(sql_query)
    872:                 user = user[0].id
    873:                 print(user)

💡 Explanation

An attacker with access to application logs could extract password hashes from the logged SQL queries, enabling offline brute-force attacks or credential stuffing. If the query contains plaintext passwords, the attacker could directly compromise user accounts.

🔗 Tainted Variables

ControlFlowNode for Attribute()ControlFlowNode for passwordControlFlowNode for sql_queryControlFlowNode for sql_query

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:852
    password=request.POST.get('pass')ControlFlowNode for Attribute()
  2. Step 2 [PROPAGATION] introduction/views.py:852
    password=request.POST.get('pass')ControlFlowNode for password
  3. Step 3 [PROPAGATION] introduction/views.py:857
    sql_query = "SELECT * FROM introduction_sql_lab_table WHERE id='"+name+"'AND password='"+password+"'"ControlFlowNode for sql_query
  4. Step 4 [SINK] introduction/views.py:868
    print(sql_query)ControlFlowNode for sql_query

✅ Recommended Fix

Remove the print statement that logs the SQL query containing sensitive password data. Instead of logging the raw SQL query, log a sanitized version that excludes sensitive parameters or use Django's logging framework with appropriate log levels. For debugging purposes, consider using Django's debug logging only in development environments with proper filtering.

    863:             sql_instance = sql_lab_table(id="slinky", password="b4f945433ea4c369c12741f62a23ccc0")
    864:             sql_instance.save()
    865:             sql_instance = sql_lab_table(id="bloke", password="f8d1ce191319ea8f4d1d26e65e130dd5")
    866:             sql_instance.save()
    867: 
    868:             # Removed sensitive logging: print(sql_query)
    869: 
    870:             try:
    871:                 user = sql_lab_table.objects.raw(sql_query)
    872:                 user = user[0].id
    873:                 # Removed sensitive logging: print(user)

📚 References

  • https://cwe.mitre.org/data/definitions/312.html

🏷️ Tags

data-flowsecurity
HIGH CWE-022 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 95f43cab690a

Uncontrolled data used in path expression

Accessing paths influenced by users can allow an attacker to access unexpected resources.

📍 views.py : Line 920
Full Path: introduction/views.py

⚠️ Vulnerable Code

    915:         else:
    916:             file=request.POST["blog"]
    917:             try :
    918:                 dirname = os.path.dirname(__file__)
    919:                 filename = os.path.join(dirname, file)
>>> 920:                 file = open(filename,"r")
    921:                 data = file.read()
    922:                 return render(request,"Lab/ssrf/ssrf_lab.html",{"blog":data})
    923:             except:
    924:                 return render(request, "Lab/ssrf/ssrf_lab.html", {"blog": "No blog found"})
    925:     else:

💡 Explanation

An attacker could perform path traversal attacks to read arbitrary files on the server (e.g., /etc/passwd, configuration files, source code) or potentially write files if the vulnerability existed in a write operation context.

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for fileControlFlowNode for filenameControlFlowNode for filename

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:911
    def ssrf_lab(request):ControlFlowNode for request
  2. Step 2 [PROPAGATION] introduction/views.py:916
    file=request.POST["blog"]ControlFlowNode for file
  3. Step 3 [PROPAGATION] introduction/views.py:919
    filename = os.path.join(dirname, file)ControlFlowNode for filename
  4. Step 4 [SINK] introduction/views.py:920
    file = open(filename,"r")ControlFlowNode for filename

✅ Recommended Fix

First, validate and sanitize the user input to ensure it contains only allowed characters and doesn't contain path traversal sequences. Second, restrict file access to a specific safe directory using os.path.abspath() and ensure the resolved path stays within that directory. Third, use a whitelist approach if possible, mapping user input to known safe files. Finally, implement proper error handling that doesn't leak sensitive information.

import os
from django.conf import settings

# ... inside ssrf_lab function ...
else:
    user_file = request.POST["blog"]
    try:
        # Sanitize input and restrict to safe directory
        safe_dir = os.path.join(settings.BASE_DIR, "safe_blog_files")
        
        # Normalize path and prevent directory traversal
        requested_path = os.path.normpath(user_file).lstrip('/')
        if '..' in requested_path or requested_path.startswith('/'):
            raise ValueError("Invalid file path")
        
        # Construct full path and verify it's within safe directory
        full_path = os.path.join(safe_dir, requested_path)
        full_path = os.path.abspath(full_path)
        
        if not full_path.startswith(safe_dir):
            raise ValueError("Path traversal attempt detected")
        
        # Open file with explicit encoding
        with open(full_path, "r", encoding="utf-8") as f:
            data = f.read()
        return render(request, "Lab/ssrf/ssrf_lab.html", {"blog": data})
    except (ValueError, OSError):
        return render(request, "Lab/ssrf/ssrf_lab.html", {"blog": "No blog found"})

📚 References

  • https://cwe.mitre.org/data/definitions/022.html

🏷️ Tags

data-flowcorrectnesssecurity
HIGH CWE-117 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 0dade280d8ad

Log Injection

Building log entries from user-controlled data is vulnerable to insertion of forged log entries by a malicious user.

📍 views.py : Line 647
Full Path: introduction/views.py

⚠️ Vulnerable Code

    642: 
    643:         if x_forwarded_for:
    644:             ip = x_forwarded_for.split(',')[0]
    645:         else:
    646:             ip = request.META.get('REMOTE_ADDR')
>>> 647:         logging.info(f"{now}:{ip}")
    648:         return render (request,"Lab/A10/a10_lab2.html")
    649:     else:
    650:         user=request.POST.get("name")
    651:         password=request.POST.get("pass")
    652:         x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')

💡 Explanation

An attacker could inject fake log entries by including newline characters in the X-Forwarded-For header, allowing them to forge log events, obfuscate attack traces, or corrupt log files for analysis.

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for AttributeControlFlowNode for Attribute()ControlFlowNode for ipControlFlowNode for Fstring

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:638
    def a10_lab2(request):ControlFlowNode for request
  2. Step 2 [PROPAGATION] introduction/views.py:646
    ip = request.META.get('REMOTE_ADDR')ControlFlowNode for Attribute
  3. Step 3 [PROPAGATION] introduction/views.py:646
    ip = request.META.get('REMOTE_ADDR')ControlFlowNode for Attribute()
  4. Step 4 [PROPAGATION] introduction/views.py:646
    ip = request.META.get('REMOTE_ADDR')ControlFlowNode for ip
  5. Step 5 [SINK] introduction/views.py:647
    logging.info(f"{now}:{ip}")ControlFlowNode for Fstring

✅ Recommended Fix

Sanitize the IP address before logging by removing or escaping newline characters. First, create a helper function that strips or replaces newline characters (\n, \r) from the IP string. Then, apply this sanitization to the IP variable before using it in the log message. This prevents log injection attacks while preserving the original IP information.

import re

def sanitize_for_logging(value):
    """Remove newline characters to prevent log injection"""
    if value:
        return re.sub(r'[\r\n]', '', value)
    return value

# In the a10_lab2 function:
if x_forwarded_for:
    ip = x_forwarded_for.split(',')[0]
else:
    ip = request.META.get('REMOTE_ADDR')

sanitized_ip = sanitize_for_logging(ip)
logging.info(f"{now}:{sanitized_ip}")

📚 References

  • https://cwe.mitre.org/data/definitions/117.html

🏷️ Tags

data-flowsecurity
HIGH CWE-117 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 7f2e99762a04

Log Injection

Building log entries from user-controlled data is vulnerable to insertion of forged log entries by a malicious user.

📍 views.py : Line 661
Full Path: introduction/views.py

⚠️ Vulnerable Code

    656:         else:
    657:             ip = request.META.get('REMOTE_ADDR')
    658: 
    659:         if login.objects.filter(user=user,password=password):
    660:             if ip != '127.0.0.1':
>>> 661:                 logging.warning(f"{now}:{ip}:{user}")
    662:             logging.info(f"{now}:{ip}:{user}")
    663:             return render(request,"Lab/A10/a10_lab2.html",{"name":user})
    664:         else:
    665:             logging.error(f"{now}:{ip}:{user}")
    666:             return render(request, "Lab/A10/a10_lab2.html", {"error": " Wrong username or Password"})

💡 Explanation

An attacker could inject newline characters into the username field to forge log entries, manipulate log files to hide malicious activity, or corrupt log formats causing parsing failures in monitoring systems.

🔗 Tainted Variables

ControlFlowNode for requestControlFlowNode for AttributeControlFlowNode for Attribute()ControlFlowNode for userControlFlowNode for Fstring

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/views.py:638
    def a10_lab2(request):ControlFlowNode for request
  2. Step 2 [PROPAGATION] introduction/views.py:650
    user=request.POST.get("name")ControlFlowNode for Attribute
  3. Step 3 [PROPAGATION] introduction/views.py:650
    user=request.POST.get("name")ControlFlowNode for Attribute()
  4. Step 4 [PROPAGATION] introduction/views.py:650
    user=request.POST.get("name")ControlFlowNode for user
  5. Step 5 [SINK] introduction/views.py:661
    logging.warning(f"{now}:{ip}:{user}")ControlFlowNode for Fstring

✅ Recommended Fix

First, sanitize the user input by removing or escaping newline characters before logging. Use a logging formatter that properly escapes special characters, or create a sanitization function that replaces newlines and other control characters. Then modify the logging statements to use the sanitized values instead of raw user input.

import re

def sanitize_log_input(value):
    """Remove newlines and carriage returns to prevent log injection"""
    if value:
        return re.sub(r'[\r\n]', '_', str(value))
    return value

# In the a10_lab2 function:
user_input = request.POST.get("name")
sanitized_user = sanitize_log_input(user_input)

# Then use sanitized_user in logging:
logging.warning(f"{now}:{ip}:{sanitized_user}")
logging.info(f"{now}:{ip}:{sanitized_user}")
logging.error(f"{now}:{ip}:{sanitized_user}")

📚 References

  • https://cwe.mitre.org/data/definitions/117.html

🏷️ Tags

data-flowsecurity
HIGH CWE-22 MEDIUM ✓ VERIFIED TRUE POSITIVE
ID: 9f0c95d4b12f

Path Traversal

📍 views.py : Line 920
Full Path: introduction/views.py

⚠️ Vulnerable Code

    915:         else:
    916:             file=request.POST["blog"]
    917:             try :
    918:                 dirname = os.path.dirname(__file__)
    919:                 filename = os.path.join(dirname, file)
>>> 920:                 file = open(filename,"r")
    921:                 data = file.read()
    922:                 return render(request,"Lab/ssrf/ssrf_lab.html",{"blog":data})
    923:             except:
    924:                 return render(request, "Lab/ssrf/ssrf_lab.html", {"blog": "No blog found"})
    925:     else:

💡 Explanation

Path traversal via user-controlled filename in open() - duplicate of finding 17 from different scanner.

📝 Source Code Context

open(filename
HIGH CWE-327 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 0e5f19ff571b

Use of a broken or weak cryptographic hashing algorithm on sensitive data

Using broken or weak cryptographic hashing algorithms can compromise security.

📍 utility.py : Line 59
Full Path: introduction/utility.py

⚠️ Vulnerable Code

    54: 
    55: def filter_blog(code):
    56:     return code
    57: 
    58: def customHash(password):
>>> 59:     return hashlib.sha256(password.encode()).hexdigest()[::-1]

💡 Explanation

An attacker could perform efficient brute-force or dictionary attacks against the reversed SHA-256 hashes, potentially recovering plaintext passwords that could be used to compromise user accounts.

🔗 Tainted Variables

ControlFlowNode for passwordControlFlowNode for Attribute()

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/utility.py:58
    def customHash(password):ControlFlowNode for password
  2. Step 2 [SINK] introduction/utility.py:59
    return hashlib.sha256(password.encode()).hexdigest()[::-1]ControlFlowNode for Attribute()

✅ Recommended Fix

Replace SHA-256 with a password hashing algorithm designed for password storage, such as bcrypt, scrypt, or Argon2. Use a cryptographically secure salt and appropriate work factors. Remove the string reversal operation as it provides no security benefit and may interfere with proper hash verification.

import bcrypt

def customHash(password):
    # Generate salt and hash with appropriate cost factor
    salt = bcrypt.gensalt(rounds=12)
    hashed_password = bcrypt.hashpw(password.encode(), salt)
    return hashed_password.decode()

📚 References

  • https://cwe.mitre.org/data/definitions/327.html

🏷️ Tags

data-flowsecurity
HIGH CWE-215 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 0c8e2be13933

Flask app is run in debug mode

Running a Flask app in debug mode may allow an attacker to run arbitrary code through the Werkzeug debugger.

📍 app.py : Line 123
Full Path: dockerized_labs/broken_auth_lab/app.py

⚠️ Vulnerable Code

    118:         pass
    119:     
    120:     return redirect(url_for('lab'))
    121: 
    122: if __name__ == '__main__':
>>> 123:     app.run(host='0.0.0.0', port=5000, debug=True)  # Vulnerable: Debug mode enabled in production 

💡 Explanation

An attacker could exploit debug mode to execute arbitrary code via the interactive debugger console, access sensitive debugging information, and view detailed error traces that reveal internal application structure and potential attack vectors.

📝 Source Code Context

app.run(host='0.0.0.0', port=5000, debug=True)  # Vulnerable: Debug mode enabled in production

✅ Recommended Fix

Remove the debug=True parameter from app.run() in production. Instead, control debug mode through an environment variable that defaults to False. This prevents debug mode from being accidentally enabled in production deployments.

if __name__ == '__main__':
    debug_mode = os.environ.get('FLASK_DEBUG', 'False').lower() == 'true'
    app.run(host='0.0.0.0', port=5000, debug=debug_mode)

📚 References

  • https://cwe.mitre.org/data/definitions/215.html

🏷️ Tags

data-flowsecurity
HIGH CWE-287 HIGH ✓ VERIFIED TRUE POSITIVE
ID: daa14f105370

Initializing SECRET_KEY of Flask application with Constant value

Initializing SECRET_KEY of Flask application with Constant value files can lead to Authentication bypass

📍 app.py : Line 8
Full Path: dockerized_labs/broken_auth_lab/app.py

⚠️ Vulnerable Code

    3: import json
    4: from datetime import datetime, timedelta
    5: import base64
    6: 
    7: app = Flask(__name__)
>>> 8: app.secret_key = 'your-secret-key-here'  # Vulnerable: Hardcoded secret key
    9: 
    10: # Vulnerable: Storing user data in memory
    11: users = {
    12:     'admin': {
    13:         'password': 'admin123',  # Vulnerable: Weak password

💡 Explanation

An attacker could forge session cookies, perform session fixation attacks, or decrypt sensitive session data, potentially gaining unauthorized access to user accounts and application functionality.

📝 Source Code Context

app.secret_key = 'your-secret-key-here'  # Vulnerable: Hardcoded secret key

✅ Recommended Fix

Generate a cryptographically secure random secret key at application startup instead of using a hardcoded value. For production deployments, load the secret key from an environment variable or a secure secrets management system. This ensures each deployment has a unique key and prevents key exposure in source code.

import os
import secrets

app = Flask(__name__)
# Generate secure random key if not provided via environment
app.secret_key = os.environ.get('FLASK_SECRET_KEY') or secrets.token_hex(32)

📚 References

  • https://cwe.mitre.org/data/definitions/287.html

🏷️ Tags

data-flowsecurityexperimental
HIGH CWE-798 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 85ab5d44f7e0

Hardcoded Secret Key

Use of Hard-coded Credentials

📍 app.py : Line 9
Full Path: dockerized_labs/broken_auth_lab/app.py
Sink Method: Flask.__init__

⚠️ Vulnerable Code

    7: from flask import Flask, render_template, request, redirect, url_for, make_response, flash
    8: import hashlib
>>> 9: import json
    10: from datetime import datetime, timedelta
    11: import base64

💡 Explanation

Attackers can forge session cookies, bypass authentication, and potentially execute arbitrary code if the secret key is compromised. This allows session hijacking and privilege escalation.

🔗 Tainted Variables

app.secret_key

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] dockerized_labs/broken_auth_lab/app.py:9 (hardcoded value)
    app.secret_key = 'your-secret-key-here'app.secret_key
  2. Step 2 [SINK] dockerized_labs/broken_auth_lab/app.py:9
    app.secret_key = 'your-secret-key-here'app.secret_key

✅ Recommended Fix

Store the secret key in an environment variable or a secure configuration management system. Never hardcode secrets in source code.

import os
app.secret_key = os.environ.get('FLASK_SECRET_KEY')

🏷️ Tags

hardcoded-secretauthenticationsession-management
HIGH CWE-384 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 5a3b1bb0894e

Insecure Session Token Generation and Validation

Session Fixation

📍 app.py : Line 45
Full Path: dockerized_labs/broken_auth_lab/app.py
Sink Method: base64.b64encode

⚠️ Vulnerable Code

    42:     if username in users and users[username]['password'] == password:  # Vulnerable: Plain text password comparison
    43:         response = make_response(redirect(url_for('dashboard')))
    44:         
>>> 45:         # Vulnerable: Insecure session management
    46:         session_token = base64.b64encode(f"{username}:{datetime.now()}".encode()).decode()
    47:         
    48:         if remember_me:
    49:             # Vulnerable: Insecure "Remember Me" implementation
    50:             response.set_cookie('session', session_token, max_age=30*24*60*60)
    51:         else:
    52:             response.set_cookie('session', session_token)

💡 Explanation

Session tokens are predictable (username + timestamp encoded in base64). Attackers can forge session tokens for any user by guessing the username and approximate time of login. This leads to session hijacking and authentication bypass.

🔗 Tainted Variables

session_token

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] dockerized_labs/broken_auth_lab/app.py:45 (user-controlled username and predictable timestamp)
    session_token = base64.b64encode(f"{username}:{datetime.now()}".encode()).decode()session_token
  2. Step 2 [PROPAGATION] dockerized_labs/broken_auth_lab/app.py:50
    response.set_cookie('session', session_token, max_age=30*24*60*60)session_token
  3. Step 3 [SINK] dockerized_labs/broken_auth_lab/app.py:50
    response.set_cookie('session', session_token, max_age=30*24*60*60)session_token

✅ Recommended Fix

Use cryptographically secure random number generators to generate session IDs. Flask-Session or Flask-Login extensions handle secure session management properly.

import secrets
session_token = secrets.token_urlsafe(32)
# Use Flask-Login or Flask-Session for proper session handling

📚 References

🏷️ Tags

session-managementauthenticationcryptography
HIGH CWE-328 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 09e290ee8d3e

Weak Password Reset Token Generation (MD5)

Use of Weak Hash

📍 app.py : Line 78
Full Path: dockerized_labs/broken_auth_lab/app.py
Sink Method: hashlib.md5

⚠️ Vulnerable Code

    75:     for username, user_data in users.items():
    76:         if user_data['email'] == email:
    77:             # Vulnerable: Predictable token generation
>>> 78:             token = hashlib.md5(f"{email}:{datetime.now()}".encode()).hexdigest()
    79:             password_reset_tokens[token] = username
    80:             
    81:             # In a real application, this would send an email
    82:             # Vulnerable: Token exposed in response

💡 Explanation

MD5 is cryptographically broken and unsuitable for security purposes. Predictable input (email + timestamp) makes tokens easily guessable. Attackers can reset passwords for any user by brute-forcing tokens.

🔗 Tainted Variables

token

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] dockerized_labs/broken_auth_lab/app.py:78 (user email and predictable timestamp)
    token = hashlib.md5(f"{email}:{datetime.now()}".encode()).hexdigest()token
  2. Step 2 [SINK] dockerized_labs/broken_auth_lab/app.py:78
    token = hashlib.md5(f"{email}:{datetime.now()}".encode()).hexdigest()token

✅ Recommended Fix

Use cryptographically secure random tokens generated by secrets module or UUID. Ensure tokens have sufficient entropy and are single-use with expiration.

import secrets
token = secrets.token_urlsafe(32)
# Store token with expiration timestamp

🏷️ Tags

password-resetcryptographyauthentication
HIGH CWE-312 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 8f595c306124

Clear-text storage of sensitive information

Sensitive information stored without encryption or hashing can expose it to an attacker.

📍 archive.py : Line 49
Full Path: introduction/playground/A9/archive.py

⚠️ Vulnerable Code

    44:         self.request = request
    45: 
    46:     def info(self,msg):
    47:         now = datetime.datetime.now()
    48:         f = open('test.log', 'a')
>>> 49:         f.write(f"INFO:{now}:{msg}\n")
    50:         f.close()
    51: 
    52:     def warning(self,msg):
    53:         now = datetime.datetime.now()
    54:         f = open('test.log', 'a')

💡 Explanation

An attacker with access to the log file could extract user credentials, leading to account compromise and potential lateral movement within the system. This could also violate data protection regulations if personal information is exposed.

🔗 Tainted Variables

ControlFlowNode for SubscriptControlFlowNode for passwordControlFlowNode for FstringControlFlowNode for msgControlFlowNode for Fstring

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/playground/A9/archive.py:15
    password = request.POST['password']ControlFlowNode for Subscript
  2. Step 2 [PROPAGATION] introduction/playground/A9/archive.py:15
    password = request.POST['password']ControlFlowNode for password
  3. Step 3 [PROPAGATION] introduction/playground/A9/archive.py:16
    L.info(f"POST request with username {username} and password {password}")ControlFlowNode for Fstring
  4. Step 4 [PROPAGATION] introduction/playground/A9/archive.py:46
    def info(self,msg):ControlFlowNode for msg
  5. Step 5 [SINK] introduction/playground/A9/archive.py:49
    f.write(f"INFO:{now}:{msg}\n")ControlFlowNode for Fstring

✅ Recommended Fix

First, remove the password from the log message at line 16 by logging only the username. Second, ensure no sensitive data is ever written to log files by implementing a data sanitization function that strips passwords or other credentials before logging. Third, consider encrypting the log file if it must contain any sensitive information, though avoiding storage is preferable.

Line 16 should be changed from: L.info(f"POST request with username {username} and password {password}")
To: L.info(f"POST request with username {username}")
Additionally, add input validation to ensure passwords are never logged:
def sanitize_for_logging(data):
    sensitive_fields = ['password', 'passwd', 'pwd', 'secret', 'token']
    for field in sensitive_fields:
        if field in data.lower():
            return '[REDACTED]'
    return data

📚 References

  • https://cwe.mitre.org/data/definitions/312.html

🏷️ Tags

data-flowsecurity
HIGH CWE-287 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 35771356b4f5

Initializing SECRET_KEY of Flask application with Constant value

Initializing SECRET_KEY of Flask application with Constant value files can lead to Authentication bypass

📍 settings.py : Line 25
Full Path: pygoat/settings.py

⚠️ Vulnerable Code

    20: 
    21: # Quick-start development settings - unsuitable for production
    22: # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
    23: 
    24: # SECURITY WARNING: keep the secret key used in production secret!
>>> 25: SECRET_KEY = 'lr66%-a!$km5ed@n5ug!tya5bv!0(yqwa1tn!q%0%3m2nh%oml'
    26: 
    27: SENSITIVE_DATA = 'FLAGTHATNEEDSTOBEFOUND'
    28: 
    29: # SECURITY WARNING: don't run with debug turned on in production!
    30: DEBUG = True

💡 Explanation

An attacker could forge session cookies, perform cross-site request forgery (CSRF) attacks, or escalate privileges by signing malicious data. With the hardcoded key, they could also decrypt sensitive application data if obtained from source control.

📝 Source Code Context

SECRET_KEY = 'lr66%-a!$km5ed@n5ug!tya5bv!0(yqwa1tn!q%0%3m2nh%oml'

✅ Recommended Fix

Generate a cryptographically secure random secret key at runtime instead of hardcoding it. For Django, use `django.core.management.utils.get_random_secret_key()` to generate a secure key. Store this key in an environment variable (e.g., `DJANGO_SECRET_KEY`) and load it via `os.environ.get()`. Never commit secret keys to version control.

import os
from django.core.management.utils import get_random_secret_key

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', get_random_secret_key())

# Alternative for production-only: SECRET_KEY = os.environ['DJANGO_SECRET_KEY']

📚 References

  • https://cwe.mitre.org/data/definitions/287.html

🏷️ Tags

data-flowsecurityexperimental
HIGH CWE-287 HIGH ✓ VERIFIED TRUE POSITIVE
ID: ba8fb5a9978e

Initializing SECRET_KEY of Flask application with Constant value

Initializing SECRET_KEY of Flask application with Constant value files can lead to Authentication bypass

📍 settings.py : Line 8
Full Path: dockerized_labs/sensitive_data_exposure/sensitive_data_lab/settings.py

⚠️ Vulnerable Code

    3: 
    4: # Build paths inside the project like this: BASE_DIR / 'subdir'.
    5: BASE_DIR = Path(__file__).resolve().parent.parent
    6: 
    7: # SECURITY WARNING: keep the secret key used in production secret!
>>> 8: SECRET_KEY = 'django-insecure-key-for-demonstration-only'
    9: 
    10: # SECURITY WARNING: don't run with debug turned on in production!
    11: DEBUG = True
    12: 
    13: ALLOWED_HOSTS = ['*']

💡 Explanation

An attacker could forge session cookies, perform cross-site request forgery (CSRF) attacks, or decrypt sensitive data stored by the application. With the secret key, they could impersonate users or escalate privileges.

📝 Source Code Context

SECRET_KEY = 'django-insecure-key-for-demonstration-only'

✅ Recommended Fix

Generate a cryptographically secure random secret key for production use. Store it in an environment variable and load it via os.getenv() with a fallback for development. Never commit hardcoded secrets to version control. Use different keys for different environments.

import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 'django-insecure-dev-key-only-for-local')

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.getenv('DJANGO_DEBUG', 'False') == 'True'

ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS', '*').split(',')

📚 References

  • https://cwe.mitre.org/data/definitions/287.html

🏷️ Tags

data-flowsecurityexperimental
HIGH CWE-312 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 55996729efe8

Clear text storage of sensitive information

Sensitive information stored without encryption or hashing can expose it to an attacker.

📍 profile.html : Line 222
Full Path: dockerized_labs/sensitive_data_exposure/templates/profile.html

⚠️ Vulnerable Code

    217:         var userData = {
    218:             username: "{{ user.username }}",
    219:             apiKey: "{{ user_data.api_key }}" // Yeah it is necessary for this lab.
    220:         };
    221:         
>>> 222:         localStorage.setItem('user_api_key', "{{ user_data.api_key }}");
    223:         
    224:         console.log("Sensitive data exposed in console - check browser dev tools!");
    225:         
    226:         // more bad practices
    227:         // function checkAdminStatus() {

💡 Explanation

An attacker with access to the user's browser (via XSS, malware, or physical access) can extract the API key from localStorage and impersonate the user, potentially gaining unauthorized access to backend services and performing actions on behalf of the legitimate user.

🔗 Tainted Variables

user_data.api_key{{ user_data.api_key }}"{{ use ... key }}"

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] dockerized_labs/sensitive_data_exposure/templates/profile.html:222
    localStorage.setItem('user_api_key', "{{ user_data.api_key }}");user_data.api_key
  2. Step 2 [PROPAGATION] dockerized_labs/sensitive_data_exposure/templates/profile.html:222
    localStorage.setItem('user_api_key', "{{ user_data.api_key }}");{{ user_data.api_key }}
  3. Step 3 [SINK] dockerized_labs/sensitive_data_exposure/templates/profile.html:222
    localStorage.setItem('user_api_key', "{{ user_data.api_key }}");"{{ use ... key }}"

✅ Recommended Fix

Remove the localStorage.setItem call that stores the API key in plain text. Instead, keep the API key server-side and implement a secure authentication mechanism. For client-side API calls, use server-generated session tokens with limited scope and lifetime, or implement a secure proxy endpoint that handles API requests without exposing the raw key to the client.

// Remove the vulnerable line entirely
// localStorage.setItem('user_api_key', "{{ user_data.api_key }}");

// Alternative: If API key must be used client-side, store it in an HTTP-only, secure cookie
// and retrieve it via JavaScript only when needed for authenticated requests
// (though this is still less secure than server-side handling)

📚 References

  • https://cwe.mitre.org/data/definitions/312.html

🏷️ Tags

data-flowsecurity
HIGH CWE-079 HIGH ✓ VERIFIED TRUE POSITIVE
ID: 5c09b9200bcc

DOM text reinterpreted as HTML

Reinterpreting text from the DOM as HTML can lead to a cross-site scripting vulnerability.

📍 csrf_dashboard.html : Line 26
Full Path: introduction/templates/mitre/csrf_dashboard.html

⚠️ Vulnerable Code

    21:         function handleSubmit(){
    22:             var recipent = document.getElementById('input1').value
    23:             var amount = document.getElementById('input2').value
    24:             var url = "/mitre/9/lab/api/"+recipent+"/"+amount 
    25:             console.log(url)
>>> 26:             window.location.href = url
    27:         }
    28:     </script>
    29: </div>
    30: 
    31: {% endblock content %}

💡 Explanation

An attacker could craft malicious input containing JavaScript URLs (javascript:), data URLs (data:), or other schemes to execute arbitrary code in the victim's browser context, potentially leading to session hijacking, phishing, or client-side attacks.

🔗 Tainted Variables

documen ... ).valuerecipentrecipenturlurl

🔄 Data Flow Analysis

  1. Step 1 [SOURCE] introduction/templates/mitre/csrf_dashboard.html:22
    var recipent = document.getElementById('input1').valuedocumen ... ).value
  2. Step 2 [PROPAGATION] introduction/templates/mitre/csrf_dashboard.html:22
    var recipent = document.getElementById('input1').valuerecipent
  3. Step 3 [PROPAGATION] introduction/templates/mitre/csrf_dashboard.html:24
    var url = "/mitre/9/lab/api/"+recipent+"/"+amountrecipent
  4. Step 4 [PROPAGATION] introduction/templates/mitre/csrf_dashboard.html:24
    var url = "/mitre/9/lab/api/"+recipent+"/"+amounturl
  5. Step 5 [SINK] introduction/templates/mitre/csrf_dashboard.html:26
    window.location.href = urlurl

✅ Recommended Fix

Validate and sanitize the user input before constructing the URL. First, ensure the recipient value contains only expected characters (e.g., alphanumeric). Second, encode the recipient parameter using encodeURIComponent() to prevent URL injection. Finally, consider using a safer approach like form submission with CSRF tokens instead of constructing URLs from user input.

function handleSubmit(){
    var recipient = document.getElementById('input1').value;
    var amount = document.getElementById('input2').value;
    
    // Validate recipient contains only alphanumeric characters
    if (!/^[a-zA-Z0-9]+$/.test(recipient)) {
        alert('Invalid recipient');
        return;
    }
    
    // URL encode the recipient parameter
    var encodedRecipient = encodeURIComponent(recipient);
    var url = "/mitre/9/lab/api/" + encodedRecipient + "/" + amount;
    console.log(url);
    window.location.href = url;
}

📚 References

  • https://cwe.mitre.org/data/definitions/079.html

🏷️ Tags

data-flowsecurity
HIGH CWE-601 MEDIUM ✓ VERIFIED TRUE POSITIVE
ID: 7842aadd4381

Open Redirect

📍 csrf_dashboard.html : Line 26
Full Path: introduction/templates/mitre/csrf_dashboard.html

⚠️ Vulnerable Code

    21:         function handleSubmit(){
    22:             var recipent = document.getElementById('input1').value
    23:             var amount = document.getElementById('input2').value
    24:             var url = "/mitre/9/lab/api/"+recipent+"/"+amount 
    25:             console.log(url)
>>> 26:             window.location.href = url
    27:         }
    28:     </script>
    29: </div>
    30: 
    31: {% endblock content %}

💡 Explanation

Layer 2 triggered: Same vulnerability as finding 1 (Open Redirect via unsanitized user input in window.location.href). The finding is a true positive for client-side security risk.

📝 Source Code Context

window.location.href = url
HIGH N/A HIGH ✓ VERIFIED TRUE POSITIVE
ID: 8bcb3184050f

Cross-Site Scripting (XSS)

📍 csrf_dashboard.html : Line 26
Full Path: introduction/templates/mitre/csrf_dashboard.html

⚠️ Vulnerable Code

(Source code not available)

💡 Explanation

Layer 2 triggered: Same vulnerability as findings 1 and 2 (XSS/Open Redirect via unsanitized user input in window.location.href). The finding is a true positive for client-side security risk.

🔄 Data Flow Analysis

    HIGH CWE-79 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: f951cebed800

    Cross-Site Scripting (XSS) via Django safe filter

    Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

    📍 xss_lab.html : Line 28
    Full Path: introduction/templates/Lab/XSS/xss_lab.html
    Sink Method: Django template rendering

    ⚠️ Vulnerable Code

        23: </div><br>
        24: <div class="display">
        25:     {% if company %}
        26:     <h3>Company Name : <i>{{company}}</i></h3>
        27:     <h3>Ceo Name : <i>{{ceo}}</i></h3>
    >>> 28:     <h3>About : <i>{{about}}</i></h3>
        29:     {% elif query %}
        30:     <h3> The company '{{query|safe}}' You searched for is not Part of FAANG</h3>
        31:     {% else %}
        32: 
        33:     {% endif %}

    💡 Explanation

    Attackers can inject malicious JavaScript code that executes in victims' browsers, leading to session hijacking, credential theft, defacement, or redirection to malicious sites. The vulnerability is reflected XSS where user input is directly rendered without proper escaping.

    🔗 Tainted Variables

    qquery

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/templates/Lab/XSS/xss_lab.html:13 (HTTP parameter)
      <input id="search" type="text" name="q" placeholder="Facebook">q
    2. Step 2 [PROPAGATION] introduction/templates/Lab/XSS/xss_lab.html:30
      <h3> The company '{{query|safe}}' You searched for is not Part of FAANG</h3>query
    3. Step 3 [SINK] introduction/templates/Lab/XSS/xss_lab.html:30
      <h3> The company '{{query|safe}}' You searched for is not Part of FAANG</h3>query

    ✅ Recommended Fix

    Remove the 'safe' filter from the template variable to enable Django's automatic HTML escaping. If HTML content is intentionally needed, use specific safe functions for limited HTML like bleach or mark_safe only after thorough validation.

    <h3> The company '{{query}}' You searched for is not Part of FAANG</h3>

    📚 References

    🏷️ Tags

    xssowasp-a03client-sideinjection
    HIGH CWE-79 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 94d65c753364

    Cross-Site Scripting (XSS) via unsafe user input in template

    Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

    📍 xss_lab_2.html : Line 28
    Full Path: introduction/templates/Lab/XSS/xss_lab_2.html
    Sink Method: Django template rendering

    ⚠️ Vulnerable Code

        23:             </button>
        24: </form>
        25: <br>
        26: <p>Hello, {{ username|safe }}</p>
        27: <script>
    >>> 28:   function setCookie(name, value) {
        29:     document.cookie = name + "=" + value + ";path=/;";
        30:   }
        31: 
        32:   function getCookie(name) {
        33:     var name = name + "=";

    💡 Explanation

    Stored XSS vulnerability where attacker-controlled input is rendered without sanitization. Attackers can inject malicious scripts that execute when users view the page, potentially stealing cookies (including the 'flag' cookie), performing actions as the user, or defacing the page.

    🔗 Tainted Variables

    username

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/templates/Lab/XSS/xss_lab_2.html:15 (HTTP parameter)
      <input type="text" class="form-control" id="username" name="username" required>username
    2. Step 2 [PROPAGATION] introduction/templates/Lab/XSS/xss_lab_2.html:26
      <p>Hello, {{ username|safe }}</p>username
    3. Step 3 [SINK] introduction/templates/Lab/XSS/xss_lab_2.html:26
      <p>Hello, {{ username|safe }}</p>username

    ✅ Recommended Fix

    Remove the 'safe' filter and allow Django's automatic escaping. If HTML input is required, implement strict input validation and use a sanitization library like bleach to allow only safe HTML elements and attributes.

    <p>Hello, {{ username }}</p>

    📚 References

    🏷️ Tags

    xssowasp-a03client-sideinjectionstored-xss
    HIGH CWE-79 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: e57b4e3ab6c4

    Cross-Site Scripting (XSS) via JavaScript code injection

    Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

    📍 xss_lab_3.html : Line 28
    Full Path: introduction/templates/Lab/XSS/xss_lab_3.html
    Sink Method: Django template rendering in script tag

    ⚠️ Vulnerable Code

        23:             </button>
        24: </form>
        25: <br>
        26: <p>{{code}}</p>
        27: <script>
    >>> 28:     // LAB 3 JS CODE
        29:     {{code}}
        30: </script>
        31: <br>
        32: <div align="right">
        33:   <button class="btn btn-info" type="button" onclick="window.location.href='/xss'">Back to Lab Details</button>

    💡 Explanation

    Direct JavaScript code injection into a script tag context. Attackers can execute arbitrary JavaScript in victims' browsers, leading to complete client-side compromise including cookie theft, session hijacking, keylogging, and malicious redirects. The vulnerability is particularly dangerous as it injects directly into JavaScript execution context.

    🔗 Tainted Variables

    usernamecode

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/templates/Lab/XSS/xss_lab_3.html:15 (HTTP parameter)
      <input type="text" class="form-control" id="username" name="username" required>username
    2. Step 2 [PROPAGATION] introduction/templates/Lab/XSS/xss_lab_3.html:29
      {{code}}code
    3. Step 3 [SINK] introduction/templates/Lab/XSS/xss_lab_3.html:29
      {{code}}code

    ✅ Recommended Fix

    Never directly inject user input into JavaScript contexts. Use JSON serialization with proper escaping, or pass data via HTML data attributes and retrieve with JavaScript. Implement strict input validation and output encoding for JavaScript contexts.

    <script>
        // Use JSON encoding for data passing
        var userData = JSON.parse('{{ code|escapejs }}');
        // Or use data attributes
        <div data-code="{{ code|escapejs }}"></div>
    </script>

    📚 References

    🏷️ Tags

    xssowasp-a03client-sideinjectionjavascript
    HIGH CWE-918 MEDIUM ✓ VERIFIED TRUE POSITIVE
    ID: 514091775c57

    Server-Side Request Forgery (SSRF) via file path manipulation

    Server-Side Request Forgery (SSRF)

    📍 ssrf_lab.html : Line 13
    Full Path: introduction/templates/Lab/ssrf/ssrf_lab.html
    Sink Method: HTML form input with file path

    ⚠️ Vulnerable Code

        8:     <div style="display:flex;flex-direction:row;align-items:center;margin:15px">
        9:         <form method="post" action="/ssrf_lab">
        10:             {% csrf_token %}
        11:             <input type="hidden" name="blog" value="templates/Lab/ssrf/blogs/blog1.txt">
        12:             <button type="submit" class="btn btn-info"> Blog1 </button>
    >>> 13:         </form>
        14:         <form method="post" action="/ssrf_lab">
        15:             {% csrf_token %}
        16:             <input type="hidden" name="blog" value="templates/Lab/ssrf/blogs/blog2.txt">
        17:             <button type="submit" class="btn btn-info"> Blog2 </button>
        18:         </form>

    💡 Explanation

    Based on the hint in the code discussion page, this appears to be part of an SSRF vulnerability where user-controlled file paths are used to read arbitrary files. Attackers could potentially read sensitive files like .env, configuration files, or system files by manipulating the 'blog' parameter value.

    🔗 Tainted Variables

    blog

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/templates/Lab/ssrf/ssrf_lab.html:11 (HTTP parameter)
      <input type="hidden" name="blog" value="templates/Lab/ssrf/blogs/blog1.txt">blog
    2. Step 2 [PROPAGATION] introduction/templates/Lab/ssrf/ssrf_lab.html:11
      <input type="hidden" name="blog" value="templates/Lab/ssrf/blogs/blog1.txt">blog

    ✅ Recommended Fix

    Implement strict allow-list validation for file paths. Never use user input directly in file operations. Use a mapping of allowed identifiers to predefined file paths, and validate against this mapping.

    # In views.py
    ALLOWED_BLOGS = {
        'blog1': 'templates/Lab/ssrf/blogs/blog1.txt',
        'blog2': 'templates/Lab/ssrf/blogs/blog2.txt',
        'blog3': 'templates/Lab/ssrf/blogs/blog3.txt',
        'blog4': 'templates/Lab/ssrf/blogs/blog4.txt'
    }
    
    def ssrf_lab(request):
        if request.method == "POST":
            blog_id = request.POST.get('blog')
            if blog_id not in ALLOWED_BLOGS:
                return render(request, "Lab/ssrf/ssrf_lab.html", {"blog": "Invalid blog"})
            
            file_path = ALLOWED_BLOGS[blog_id]
            # Safe file reading with path validation
            ...

    📚 References

    🏷️ Tags

    ssrfowasp-a10file-readpath-traversal
    HIGH CWE-918 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: fba21d5a6668

    Server-Side Request Forgery (SSRF) via direct user input in requests.get()

    Server-Side Request Forgery (SSRF)

    📍 ssrf_lab2.html : Line 18
    Full Path: introduction/templates/Lab/ssrf/ssrf_lab2.html
    Sink Method: ssrf_lab2

    ⚠️ Vulnerable Code

        13:     elif request.method == "POST":
        14:         url = request.POST["url"]
        15:         try:
        16:             response = requests.get(url)
        17:             return render(request, "Lab/ssrf/ssrf_lab2.html", {"response": response.content.decode()})
    >>> 18:         except:
        19:             return render(request, "Lab/ssrf/ssrf_lab2.html", {"error": "Invalid URL"})

    💡 Explanation

    Attackers can make the server send HTTP requests to internal services, cloud metadata endpoints (169.254.169.254), localhost services, or arbitrary external systems. This can lead to information disclosure, internal network reconnaissance, or exploitation of internal services that are not exposed to the internet.

    🔗 Tainted Variables

    url

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/templates/Lab/ssrf/ssrf_lab2.html:8 (HTTP parameter)
      <input type="text" class="form-control" id="url" name="url" placeholder="Enter URL">url
    2. Step 2 [PROPAGATION] introduction/templates/Lab/ssrf/ssrf_lab2.html:14
      url = request.POST["url"]url
    3. Step 3 [SINK] introduction/templates/Lab/ssrf/ssrf_lab2.html:18
      response = requests.get(url)url

    ✅ Recommended Fix

    1. Implement an allowlist of permitted domains or URL patterns. 2. Validate and sanitize user input using URL parsing libraries. 3. Use network-level restrictions to prevent access to internal IP ranges. 4. Implement proper error handling that doesn't leak internal information.

    import re
    from urllib.parse import urlparse
    
    def is_allowed_url(url):
        parsed = urlparse(url)
        allowed_domains = ['example.com', 'trusted-site.org']
        
        # Check if domain is in allowlist
        if parsed.netloc not in allowed_domains:
            return False
        
        # Block internal IP addresses
        internal_ips = re.compile(r'^(127\.|192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|169\.254\.)')
        if internal_ips.match(parsed.netloc):
            return False
        
        # Only allow HTTP/HTTPS
        if parsed.scheme not in ['http', 'https']:
            return False
        
        return True
    
    # Usage:
    if is_allowed_url(user_url):
        response = requests.get(user_url, timeout=5)
    else:
        return render(request, "error.html", {"error": "URL not allowed"})

    🏷️ Tags

    ssrfinjectionnetwork
    HIGH CWE-79 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: f0075cc0b9a3

    Cross-Site Scripting (XSS) via unsafe rendering with 'safe' filter

    Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

    📍 ssrf_lab2.html : Line 14
    Full Path: introduction/templates/Lab/ssrf/ssrf_lab2.html
    Sink Method: render

    ⚠️ Vulnerable Code

        12:     {% if response %}
        13:         <div style="width:70%;overflow:scroll;background-color:#000">{{response | safe}}<div>
    >>> 14:     {% endif %}
        15: </div>
        16: 

    💡 Explanation

    Attackers can inject malicious JavaScript code that executes in victims' browsers when they view the SSRF response. This can lead to session hijacking, credential theft, defacement, or malware distribution. Combined with SSRF, attackers can fetch malicious content from controlled servers and have it rendered as HTML.

    🔗 Tainted Variables

    response

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/templates/Lab/ssrf/ssrf_lab2.html:8 (HTTP parameter)
      <input type="text" class="form-control" id="url" name="url" placeholder="Enter URL">url
    2. Step 2 [PROPAGATION] introduction/templates/Lab/ssrf/ssrf_lab2.html:17
      return render(request, "Lab/ssrf/ssrf_lab2.html", {"response": response.content.decode()})response
    3. Step 3 [SINK] introduction/templates/Lab/ssrf/ssrf_lab2.html:14
      {{response | safe}}response

    ✅ Recommended Fix

    1. Remove the 'safe' filter and let Django auto-escape HTML by default. 2. If HTML content is expected, use a sanitization library like bleach. 3. Validate and sanitize the response content before rendering.

    {% if response %}
        <div style="width:70%;overflow:scroll;background-color:#000">
            {{ response }}
        </div>
    {% endif %}
    
    # In Python code:
    import bleach
    
    # Sanitize HTML before rendering
    clean_response = bleach.clean(response.content.decode(), tags=['b', 'i', 'p', 'br'], strip=True)
    return render(request, "template.html", {"response": clean_response})

    📚 References

    🏷️ Tags

    xssinjectionclient-side
    HIGH CWE-22 MEDIUM ✓ VERIFIED TRUE POSITIVE
    ID: 6c0efc78c0f8

    Path Traversal

    📍 apis.py : Line 133
    Full Path: introduction/apis.py

    ⚠️ Vulnerable Code

        128:         return JsonResponse({"message":"method not allowed"},status = 405)
        129:     try:
        130:         code = request.POST.get('code')
        131:         dirname = os.path.dirname(__file__)
        132:         filename = os.path.join(dirname, "playground/A6/utility.py")
    >>> 133:         f = open(filename,"w")
        134:         f.write(code)
        135:         f.close()
        136:     except:
        137:         return JsonResponse({"message":"missing code"},status = 400)
        138:     return JsonResponse({"message":"success"},status = 200)

    💡 Explanation

    The code writes user-controlled data (`request.POST.get('code')`) directly to a fixed file path. While the path is constructed using `os.path.join` with a base directory, the vulnerability is that an attacker can write arbitrary code to a server-side utility file (`playground/A6/utility.py`). This is a **Path Traversal** risk because the attacker could potentially control the file's content to execute malicious code on the server, representing a dangerous architectural flaw. Layer 1 safety is ab

    📝 Source Code Context

    open(filename
    HIGH CWE-22 MEDIUM ✓ VERIFIED TRUE POSITIVE
    ID: 3bf2b39b1aac

    Path Traversal

    📍 test.py : Line 12
    Full Path: introduction/playground/ssrf/test.py

    ⚠️ Vulnerable Code

        7:         else:
        8:             file=request.POST["blog"]
        9:             try :
        10:                 dirname = os.path.dirname(__file__)
        11:                 filename = os.path.join(dirname, file)
    >>> 12:                 file = open(filename,"r")
        13:                 data = file.read()
        14:                 return render(request,"Lab/ssrf/ssrf_lab.html",{"blog":data})
        15:             except:
        16:                 return render(request, "Lab/ssrf/ssrf_lab.html", {"blog": "No blog found"})
        17:     else:
    

    💡 Explanation

    Layer 1: No structural safety (user-controlled `file` variable flows into `open()`). Layer 2: Architectural flaw - Path Traversal vulnerability confirmed. User input from `request.POST["blog"]` is used to construct a file path without validation, leading to arbitrary file read.

    📝 Source Code Context

    open(filename
    HIGH CWE-22 MEDIUM ✓ VERIFIED TRUE POSITIVE
    ID: 826e3a954f29

    Path Traversal

    📍 main.py : Line 8
    Full Path: introduction/playground/ssrf/main.py

    ⚠️ Vulnerable Code

        3: 
        4: def ssrf_lab(file):
        5:     try:
        6:         dirname = os.path.dirname(__file__)
        7:         filename = os.path.join(dirname, file)
    >>> 8:         file = open(filename,"r")
        9:         data = file.read()
        10:         return {"blog":data}
        11:     except:
        12:         return {"blog": "No blog found"}

    💡 Explanation

    Layer 2 TP Trigger: The code shows a Path Traversal vulnerability. The `filename` variable is constructed by joining a base directory with a user-controlled `file` parameter. While `os.path.join` provides some safety on Unix-like systems, the overall pattern is a classic architectural flaw for path traversal if the `file` parameter contains directory traversal sequences (e.g., '../../etc/passwd'). The context does not show any validation or sanitization of the user input before it is passed to `

    📝 Source Code Context

    open(filename
    HIGH CWE-79 MEDIUM ✓ VERIFIED TRUE POSITIVE
    ID: 4ff64b3ea9f6

    Cross-site Scripting (XSS)

    📍 a9.js : Line 40
    Full Path: introduction/static/js/a9.js

    ⚠️ Vulnerable Code

        35:       let data = JSON.parse(result); // parse JSON string into object
        36:       console.log(data.logs);
        37:       document.getElementById("a9_d3").style.display = 'flex';
        38:       for (var i = 0; i < data.logs.length; i++) {
        39:         var li = document.createElement("li");
    >>> 40:         li.innerHTML = data.logs[i];
        41:         document.getElementById("a9_d3").appendChild(li);
        42:       }
        43:     })
        44:     .catch(error => console.log('error', error));
        45: }

    💡 Explanation

    Layer 2 TP Trigger: Client-Side Security & Attack Chains. The finding shows direct assignment of untrusted data (`data.logs[i]`) to `innerHTML`. This is a classic DOM-based XSS vulnerability. The data originates from a parsed JSON response (`result`), which is attacker-controlled if the source is not strictly trusted. No sanitization or safe API (e.g., `textContent`) is used, creating a direct client-side attack chain.

    📝 Source Code Context

    .innerHTML = 
    HIGH CWE-79 MEDIUM ✓ VERIFIED TRUE POSITIVE
    ID: 3eee0d687b61

    Cross-site Scripting (XSS)

    📍 base.html : Line 20
    Full Path: dockerized_labs/insec_des_lab/templates/base.html

    ⚠️ Vulnerable Code

        15:             requestAnimationFrame(() => {
        16:                 html.setAttribute('data-theme', newTheme);
        17:                 localStorage.setItem('theme', newTheme);
        18:                 
        19:                 const themeToggle = document.querySelector('.theme-toggle');
    >>> 20:                 themeToggle.innerHTML = newTheme === 'dark' ? '☀️' : '🌙';
        21:             });
        22:         }
        23: 
        24:         // Set theme on page load
        25:         document.addEventListener('DOMContentLoaded', () => {
    

    💡 Explanation

    Layer 2 TP Trigger: Client-Side Storage Risk. The finding's context shows `localStorage.setItem('theme', newTheme);` on line 17, storing user-controlled data (`newTheme`) in `localStorage`. While the immediate sink (`innerHTML` on line 20) uses a ternary operator with hardcoded emojis, the broader code pattern demonstrates a client-side storage mechanism for dynamic data. The threat model for XSS includes attackers exfiltrating or manipulating data stored in `localStorage`. Therefore, any findin

    📝 Source Code Context

    .innerHTML = 
    HIGH CWE-250 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 546da858ec07

    [IaC] Running as root

    Container runs as root user, which is a security risk

    📍 Dockerfile : Line 1
    Full Path: Dockerfile

    ⚠️ Vulnerable Code

    >>> # Dockerfile missing USER directive:
        2:     1: FROM python:3.11.0b1-buster
        3:     5: WORKDIR /app
        4:     9: RUN apt-get update && apt-get install --no-install-recommends -y dnsutils=1:9.11.5.P4+dfsg-5.1+deb10u11 libpq-dev=11.16-0+deb10u1 python3-dev=3.7.3-1 && apt-get clean && rm -rf /var/lib/apt/lists/*
        5:     13: ENV PYTHONDONTWRITEBYTECODE=1
        6:     14: ENV PYTHONUNBUFFERED=1

    💡 Explanation

    Container runs as root user, which is a security risk

    📝 Source Code Context

    # Dockerfile missing USER directive:
        1: FROM python:3.11.0b1-buster
        5: WORKDIR /app
        9: RUN apt-get update && apt-get install --no-install-recommends -y dnsutils=1:9.11.5.P4+dfsg-5.1+deb10u11 libpq-dev=11.16-0+deb10u1 python3-dev=3.7.3-1 && apt-get clean && rm -rf /var/lib/apt/lists/*
        13: ENV PYTHONDONTWRITEBYTECODE=1
        14: ENV PYTHONUNBUFFERED=1

    ✅ Recommended Fix

    Add 'USER nonroot' or 'USER 1000' to run as non-root user

    Add 'USER nonroot' or 'USER 1000' to run as non-root user

    📚 References

    • https://cwe.mitre.org/data/definitions/250.html

    🏷️ Tags

    iacdocker
    MEDIUM CWE-078 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: d3c2b15a8826

    Unsafe shell command constructed from library input

    Using externally controlled strings in a command line may allow a malicious user to change the meaning of the command.

    📍 mitre.py : Line 241
    Full Path: introduction/mitre.py

    ⚠️ Vulnerable Code

        236: 
        237: @csrf_exempt
        238: def mitre_lab_17_api(request):
        239:     if request.method == "POST":
        240:         ip = request.POST.get('ip')
    >>> 241:         command = "nmap " + ip 
        242:         res, err = command_out(command)
        243:         res = res.decode()
        244:         err = err.decode()
        245:         pattern = "STATE SERVICE.*\\n\\n"
        246:         ports = re.findall(pattern, res,re.DOTALL)[0][14:-2].split('\n')
    

    💡 Explanation

    An attacker could execute arbitrary shell commands on the server by injecting command separators (like ;, &&, |) or nmap options, potentially leading to complete system compromise, data theft, or lateral movement within the network.

    🔗 Tainted Variables

    ControlFlowNode for requestControlFlowNode for AttributeControlFlowNode for Attribute()ControlFlowNode for ipControlFlowNode for ip

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/mitre.py:238
      def mitre_lab_17_api(request):ControlFlowNode for request
    2. Step 2 [PROPAGATION] introduction/mitre.py:240
      ip = request.POST.get('ip')ControlFlowNode for Attribute
    3. Step 3 [PROPAGATION] introduction/mitre.py:240
      ip = request.POST.get('ip')ControlFlowNode for Attribute()
    4. Step 4 [PROPAGATION] introduction/mitre.py:240
      ip = request.POST.get('ip')ControlFlowNode for ip
    5. Step 5 [SINK] introduction/mitre.py:241
      command = "nmap " + ipControlFlowNode for ip

    ✅ Recommended Fix

    First, validate the input to ensure it contains only valid IP addresses or hostnames using a whitelist approach. Second, use subprocess with argument list instead of string concatenation to prevent command injection. Third, implement proper error handling for invalid inputs. Finally, consider using a Python nmap library instead of shell commands.

    import subprocess
    import re
    from django.core.validators import validate_ipv4_address
    from django.core.exceptions import ValidationError
    
    @csrf_exempt
    def mitre_lab_17_api(request):
        if request.method == "POST":
            ip = request.POST.get('ip')
            try:
                # Validate IP address format
                validate_ipv4_address(ip)
                # Use subprocess with argument list
                result = subprocess.run(['nmap', ip], 
                                      capture_output=True, 
                                      text=True, 
                                      timeout=30)
                res = result.stdout
                err = result.stderr
                pattern = "STATE SERVICE.*\\n\\n"
                ports = re.findall(pattern, res,re.DOTALL)[0][14:-2].split('\n')
                # ... rest of your code
            except ValidationError:
                return HttpResponse("Invalid IP address format", status=400)
            except subprocess.TimeoutExpired:
                return HttpResponse("Scan timed out", status=408)

    📚 References

    • https://cwe.mitre.org/data/definitions/078.html

    🏷️ Tags

    data-flowcorrectnesssecurity
    MEDIUM CWE-614 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: d6619a8fd5af

    Failure to use secure cookies

    Insecure cookies may be sent in cleartext, which makes them vulnerable to interception.

    📍 views.py : Line 285
    Full Path: introduction/views.py

    ⚠️ Vulnerable Code

        280:             passwd  = request.POST['pass']
        281:             obj = authLogin.objects.create(name=name,username=user_name,password=passwd)
        282:             try:
        283:                 rendered = render_to_string('Lab/AUTH/auth_success.html', {'username': obj.username,'userid':obj.userid,'name':obj.name,'err_msg':'Cookie Set'})
        284:                 response = HttpResponse(rendered)
    >>> 285:                 response.set_cookie('userid', obj.userid, max_age=31449600, samesite=None, secure=False)
        286:                 print('Setting cookie successful')
        287:                 return response
        288:             except:
        289:                 render(request,'Lab/AUTH/auth_lab_signup.html',{'err_msg':'Cookie cannot be set'})
        290:         except:
    

    💡 Explanation

    Attackers could intercept the userid cookie via man-in-the-middle attacks on unencrypted HTTP connections, leading to session hijacking and account takeover. The current configuration also exposes the application to CSRF attacks due to the insecure SameSite setting.

    📝 Source Code Context

    response.set_cookie('userid', obj.userid, max_age=31449600, samesite=None, secure=False)

    ✅ Recommended Fix

    Set the 'secure' flag to True to ensure cookies are only transmitted over HTTPS connections. Also set 'samesite' to 'Strict' or 'Lax' to prevent CSRF attacks. Remove the 'samesite=None' parameter since it's incompatible with secure=False. Ensure your application is served exclusively over HTTPS.

    response.set_cookie('userid', obj.userid, max_age=31449600, samesite='Strict', secure=True, httponly=True)

    📚 References

    • https://cwe.mitre.org/data/definitions/614.html

    🏷️ Tags

    data-flowsecurity
    MEDIUM CWE-1004 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 4de09e36456e

    Sensitive cookie missing `HttpOnly` attribute

    Cookies without the `HttpOnly` attribute set can be accessed by JS scripts, making them more vulnerable to XSS attacks.

    📍 views.py : Line 285
    Full Path: introduction/views.py

    ⚠️ Vulnerable Code

        280:             passwd  = request.POST['pass']
        281:             obj = authLogin.objects.create(name=name,username=user_name,password=passwd)
        282:             try:
        283:                 rendered = render_to_string('Lab/AUTH/auth_success.html', {'username': obj.username,'userid':obj.userid,'name':obj.name,'err_msg':'Cookie Set'})
        284:                 response = HttpResponse(rendered)
    >>> 285:                 response.set_cookie('userid', obj.userid, max_age=31449600, samesite=None, secure=False)
        286:                 print('Setting cookie successful')
        287:                 return response
        288:             except:
        289:                 render(request,'Lab/AUTH/auth_lab_signup.html',{'err_msg':'Cookie cannot be set'})
        290:         except:
    

    💡 Explanation

    An attacker could steal the user's session cookie via cross-site scripting (XSS) attacks, allowing them to impersonate the user and gain unauthorized access to their account without needing credentials.

    📝 Source Code Context

    response.set_cookie('userid', obj.userid, max_age=31449600, samesite=None, secure=False)

    ✅ Recommended Fix

    Add the `httponly=True` parameter to the `set_cookie()` call to prevent client-side JavaScript from accessing the cookie. Additionally, set `secure=True` if the application uses HTTPS (which it should in production) to prevent transmission over unencrypted connections. Consider also setting `samesite='Lax'` or `samesite='Strict'` for CSRF protection.

    response.set_cookie('userid', obj.userid, max_age=31449600, httponly=True, secure=True, samesite='Lax')

    📚 References

    • https://cwe.mitre.org/data/definitions/1004.html

    🏷️ Tags

    data-flowsecurity
    MEDIUM CWE-078 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 006af1fe9a47

    Unsafe shell command constructed from library input

    Using externally controlled strings in a command line may allow a malicious user to change the meaning of the command.

    📍 views.py : Line 417
    Full Path: introduction/views.py

    ⚠️ Vulnerable Code

        412:             domain=request.POST.get('domain')
        413:             domain=domain.replace("https://www.",'')
        414:             os=request.POST.get('os')
        415:             print(os)
        416:             if(os=='win'):
    >>> 417:                 command="nslookup {}".format(domain)
        418:             else:
        419:                 command = "dig {}".format(domain)
        420:             
        421:             try:
        422:                 # output=subprocess.check_output(command,shell=True,encoding="UTF-8")
    

    💡 Explanation

    An attacker could execute arbitrary shell commands by injecting command separators (like ;, &&, |) or subcommands via the domain parameter, potentially leading to remote code execution, data exfiltration, or system compromise.

    🔗 Tainted Variables

    ControlFlowNode for requestControlFlowNode for AttributeControlFlowNode for Attribute()ControlFlowNode for domainControlFlowNode for domainControlFlowNode for domain

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/views.py:409
      def cmd_lab(request):ControlFlowNode for request
    2. Step 2 [PROPAGATION] introduction/views.py:412
      domain=request.POST.get('domain')ControlFlowNode for Attribute
    3. Step 3 [PROPAGATION] introduction/views.py:412
      domain=request.POST.get('domain')ControlFlowNode for Attribute()
    4. Step 4 [PROPAGATION] introduction/views.py:412
      domain=request.POST.get('domain')ControlFlowNode for domain
    5. Step 5 [PROPAGATION] introduction/views.py:413
      domain=domain.replace("https://www.",'')ControlFlowNode for domain
    6. Step 6 [SINK] introduction/views.py:417
      command="nslookup {}".format(domain)ControlFlowNode for domain

    ✅ Recommended Fix

    First, validate the domain input using a whitelist of allowed characters (alphanumeric, hyphens, dots) or a proper domain regex pattern. Second, avoid using shell=True and instead pass the command as a list of arguments to subprocess.check_output. Third, use shlex.quote() to escape the domain parameter when constructing shell commands if shell=True cannot be avoided.

    import re
    import shlex
    
    # Validate domain input
    domain = request.POST.get('domain', '').strip()
    domain = domain.replace("https://www.", '')
    
    # Validate domain format
    if not re.match(r'^[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]$', domain):
        return HttpResponse("Invalid domain format", status=400)
    
    if os == 'win':
        # Safe approach without shell=True
        output = subprocess.check_output(['nslookup', domain], encoding="UTF-8")
    else:
        # Safe approach without shell=True
        output = subprocess.check_output(['dig', domain], encoding="UTF-8")

    📚 References

    • https://cwe.mitre.org/data/definitions/078.html

    🏷️ Tags

    data-flowcorrectnesssecurity
    MEDIUM CWE-208 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 6514067bc2ba

    Timing attack against Hash

    When checking a Hash over a message, a constant-time algorithm should be used. Otherwise, an attacker may be able to forge a valid Hash for an arbitrary message by running a timing attack if they can

    📍 views.py : Line 1193
    Full Path: introduction/views.py

    ⚠️ Vulnerable Code

        1188:         except:
        1189:             response = render(request, "Lab_2021/A7_auth_failure/lab3.html")
        1190:             response.set_cookie("session_id", None)
        1191:             return response
        1192: 
    >>> 1193:         if USER_A7_LAB3[username]['password'] == password:
        1194:             session_data = AF_session_id.objects.create(session_id=token, user=USER_A7_LAB3[username]['username'])
        1195:             session_data.save()
        1196:             response = render(request, "Lab_2021/A7_auth_failure/lab3.html", {"success":True, "failure":False, "username":username})
        1197:             response.set_cookie("session_id", token)
        1198:             return response
    

    💡 Explanation

    An attacker could perform a timing attack to gradually deduce the stored password hash by measuring response time differences. This could eventually allow them to authenticate as another user without knowing the exact password.

    🔗 Tainted Variables

    ControlFlowNode for Attribute()ControlFlowNode for passwordControlFlowNode for password

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/views.py:1187
      password = hashlib.sha256(password.encode()).hexdigest()ControlFlowNode for Attribute()
    2. Step 2 [PROPAGATION] introduction/views.py:1187
      password = hashlib.sha256(password.encode()).hexdigest()ControlFlowNode for password
    3. Step 3 [SINK] introduction/views.py:1193
      if USER_A7_LAB3[username]['password'] == password:ControlFlowNode for password

    ✅ Recommended Fix

    Replace the direct string comparison with a constant-time comparison function. First, import a secure comparison function like `hmac.compare_digest()` from Python's `hmac` module. Then modify line 1193 to use this function instead of the `==` operator, ensuring the comparison time doesn't depend on the input values. This prevents attackers from inferring password similarities through timing differences.

    import hmac
    
    # ... existing code ...
    
    if hmac.compare_digest(USER_A7_LAB3[username]['password'], password):

    📚 References

    • https://cwe.mitre.org/data/definitions/208.html

    🏷️ Tags

    data-flowsecurityexperimental
    MEDIUM CWE-208 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: a30b9864129c

    Timing attack against secret

    Use of a non-constant-time verification routine to check the value of an secret, possibly allowing a timing attack to retrieve sensitive information.

    📍 views.py : Line 759
    Full Path: introduction/views.py

    ⚠️ Vulnerable Code

        754:                 'Lab_2021/A1_BrokenAccessControl/broken_access_lab_1.html', 
        755:                 {
        756:                     "data":"0NLY_F0R_4DM1N5",
        757:                     "username": "admin"
        758:                 })
    >>> 759:         elif (name=='jack' and password=='jacktheripper'): # Will implement hashing here
        760:             html = render(
        761:             request, 
        762:             'Lab_2021/A1_BrokenAccessControl/broken_access_lab_1.html', 
        763:             {
        764:                 "not_admin":"No Secret key for this User",
    

    💡 Explanation

    An attacker could perform a timing attack to gradually guess the password by measuring response time differences between correct and incorrect character matches, potentially compromising the 'jack' account without brute-forcing the entire password space.

    🔗 Tainted Variables

    ControlFlowNode for passwordControlFlowNode for password

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/views.py:747
      password = request.POST.get('pass')ControlFlowNode for password
    2. Step 2 [SINK] introduction/views.py:759
      elif (name=='jack' and password=='jacktheripper'): # Will implement hashing hereControlFlowNode for password

    ✅ Recommended Fix

    Replace the direct string comparison with a constant-time comparison function that compares all characters regardless of match position. Use Django's built-in `constant_time_compare()` from `django.utils.crypto` or Python's `hmac.compare_digest()`. First, implement password hashing using Django's authentication system with `make_password()` and `check_password()` functions. Then use constant-time comparison for any remaining direct string comparisons.

    from django.contrib.auth.hashers import make_password, check_password
    from django.utils.crypto import constant_time_compare
    
    # Store hashed password during user creation
    hashed_password = make_password('jacktheripper')
    
    # In the view comparison:
    elif name == 'jack' and check_password(password, hashed_password):
        # Or for direct string comparison if needed:
        # elif name == 'jack' and constant_time_compare(password, 'jacktheripper'):

    📚 References

    • https://cwe.mitre.org/data/definitions/208.html

    🏷️ Tags

    data-flowsecurityexperimental
    MEDIUM CWE-208 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: b8a2a1e862aa

    Timing attack against secret

    Use of a non-constant-time verification routine to check the value of an secret, possibly allowing a timing attack to retrieve sensitive information.

    📍 views.py : Line 799
    Full Path: introduction/views.py

    ⚠️ Vulnerable Code

        794:                 {
        795:                     "data":"0NLY_F0R_4DM1N5",
        796:                     "username": "admin",
        797:                     "status": "admin"
        798:                 })
    >>> 799:         elif ( name=='jack' and password=='jacktheripper'): # Will implement hashing here
        800:             html = render(
        801:             request, 
        802:             'Lab_2021/A1_BrokenAccessControl/broken_access_lab_2.html', 
        803:             {
        804:                 "not_admin":"No Secret key for this User",
    

    💡 Explanation

    An attacker could perform a timing attack to gradually guess the secret password by measuring response time differences between correct and incorrect character matches, potentially gaining unauthorized admin access or user impersonation.

    🔗 Tainted Variables

    ControlFlowNode for passwordControlFlowNode for password

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/views.py:783
      password = request.POST.get('pass')ControlFlowNode for password
    2. Step 2 [SINK] introduction/views.py:799
      elif ( name=='jack' and password=='jacktheripper'): # Will implement hashing hereControlFlowNode for password

    ✅ Recommended Fix

    Replace the direct string comparison with a constant-time comparison function that compares all characters regardless of match position. Use Django's built-in `secrets.compare_digest()` or Python's `hmac.compare_digest()` for timing-safe comparison. Also implement proper password hashing instead of storing plaintext credentials in the code.

    import hmac
    
    # Replace lines 799-800 with:
    elif name == 'jack' and hmac.compare_digest(password.encode('utf-8'), b'jacktheripper'):
        # Will implement hashing here (also hash the password and compare hashes)
        html = render(
        request, 
        'Lab_2021/A1_BrokenAccessControl/broken_access_lab_2.html', 
        {
            "not_admin":"No Secret key for this User",
    
    # Also fix the admin comparison on line 794 similarly:
    if name == 'admin' and hmac.compare_digest(password.encode('utf-8'), b'0NLY_F0R_4DM1N5'):

    📚 References

    • https://cwe.mitre.org/data/definitions/208.html

    🏷️ Tags

    data-flowsecurityexperimental
    MEDIUM CWE-208 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: e54aa66219f8

    Timing attack against secret

    Use of a non-constant-time verification routine to check the value of an secret, possibly allowing a timing attack to retrieve sensitive information.

    📍 views.py : Line 826
    Full Path: introduction/views.py

    ⚠️ Vulnerable Code

        821:         username = request.POST["username"]
        822:         password = request.POST["password"]
        823: 
        824:         if username == 'John' and password == 'reaper':
        825:             return render(request,'Lab_2021/A1_BrokenAccessControl/broken_access_lab_3.html', {'loggedin':True, 'admin': False})
    >>> 826:         elif username == 'admin' and password == 'admin_pass':
        827:             return render(request,'Lab_2021/A1_BrokenAccessControl/broken_access_lab_3.html', {'loggedin':True, 'admin': True})
        828:         return render(request, 'Lab_2021/A1_BrokenAccessControl/broken_access_lab_3.html', {'loggedin':False})
        829: 
        830: def a1_broken_access_lab3_secret(request):
        831:     if not request.user.is_authenticated:
    

    💡 Explanation

    An attacker could perform a timing attack to determine the correct password by measuring response time differences between correct and incorrect characters, potentially gaining unauthorized admin access or learning valid credentials through statistical analysis.

    🔗 Tainted Variables

    ControlFlowNode for passwordControlFlowNode for password

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/views.py:822
      password = request.POST["password"]ControlFlowNode for password
    2. Step 2 [SINK] introduction/views.py:826
      elif username == 'admin' and password == 'admin_pass':ControlFlowNode for password

    ✅ Recommended Fix

    Replace the direct string comparison with a constant-time comparison function that doesn't leak timing information. Use Django's built-in `secrets.compare_digest()` or Python's `hmac.compare_digest()` for comparing the password. Also, move the password comparison to a separate function that compares both username and password in constant time, or restructure the logic to avoid early returns that reveal which comparison failed.

    import hmac
    
    # In the view function:
    username = request.POST["username"]
    password = request.POST["password"]
    
    # Constant-time comparison for both username and password
    if hmac.compare_digest(username, 'John') and hmac.compare_digest(password, 'reaper'):
        return render(request, 'Lab_2021/A1_BrokenAccessControl/broken_access_lab_3.html', {'loggedin': True, 'admin': False})
    elif hmac.compare_digest(username, 'admin') and hmac.compare_digest(password, 'admin_pass'):
        return render(request, 'Lab_2021/A1_BrokenAccessControl/broken_access_lab_3.html', {'loggedin': True, 'admin': True})
    return render(request, 'Lab_2021/A1_BrokenAccessControl/broken_access_lab_3.html', {'loggedin': False})

    📚 References

    • https://cwe.mitre.org/data/definitions/208.html

    🏷️ Tags

    data-flowsecurityexperimental
    MEDIUM CWE-208 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 782038aae5ed

    Timing attack against secret

    Use of a non-constant-time verification routine to check the value of an secret, possibly allowing a timing attack to retrieve sensitive information.

    📍 views.py : Line 1065
    Full Path: introduction/views.py

    ⚠️ Vulnerable Code

        1060:             return render(request,"Lab_2021/A2_Crypto_failur/crypto_failure_lab3.html")
        1061:         if request.method == "POST":
        1062:             username = request.POST["username"]
        1063:             password = request.POST["password"]
        1064:             try:
    >>> 1065:                 if username == "User" and password == "P@$$w0rd":
        1066:                     expire = datetime.datetime.now() + datetime.timedelta(minutes=60)
        1067:                     cookie = f"{username}|{expire}"
        1068:                     response = render(request,"Lab_2021/A2_Crypto_failur/crypto_failure_lab3.html",{"success":True, "failure":False , "admin":False})
        1069:                     response.set_cookie("cookie", cookie)
        1070:                     response.status_code = 200
    

    💡 Explanation

    An attacker could perform a timing attack to gradually guess the correct username and password by measuring response time differences, potentially gaining unauthorized access as the 'User' account.

    🔗 Tainted Variables

    ControlFlowNode for passwordControlFlowNode for password

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/views.py:1063
      password = request.POST["password"]ControlFlowNode for password
    2. Step 2 [SINK] introduction/views.py:1065
      if username == "User" and password == "P@$$w0rd":ControlFlowNode for password

    ✅ Recommended Fix

    Replace the direct string comparison with a constant-time comparison function. First, import Django's `constant_time_compare` from `django.utils.crypto`. Then use it to compare both the username and password separately. This ensures the comparison time doesn't reveal information about how many characters matched or which comparison failed.

    from django.utils.crypto import constant_time_compare
    
    # In the view function:
    if constant_time_compare(username, "User") and constant_time_compare(password, "P@$$w0rd"):

    📚 References

    • https://cwe.mitre.org/data/definitions/208.html

    🏷️ Tags

    data-flowsecurityexperimental
    MEDIUM CWE-348 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 8a529283437e

    IP address spoofing

    A remote endpoint identifier is read from an HTTP header. Attackers can modify the value of the identifier to forge the client ip.

    📍 views.py : Line 660
    Full Path: introduction/views.py

    ⚠️ Vulnerable Code

        655:             ip = x_forwarded_for.split(',')[0]
        656:         else:
        657:             ip = request.META.get('REMOTE_ADDR')
        658: 
        659:         if login.objects.filter(user=user,password=password):
    >>> 660:             if ip != '127.0.0.1':
        661:                 logging.warning(f"{now}:{ip}:{user}")
        662:             logging.info(f"{now}:{ip}:{user}")
        663:             return render(request,"Lab/A10/a10_lab2.html",{"name":user})
        664:         else:
        665:             logging.error(f"{now}:{ip}:{user}")
    

    💡 Explanation

    An attacker could spoof their IP address to appear as localhost (127.0.0.1), potentially bypassing security logging or gaining unauthorized access if the IP check is used for authentication decisions elsewhere in the application.

    🔗 Tainted Variables

    ControlFlowNode for Attribute()ControlFlowNode for x_forwarded_forControlFlowNode for ipControlFlowNode for ip

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/views.py:652
      x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')ControlFlowNode for Attribute()
    2. Step 2 [PROPAGATION] introduction/views.py:652
      x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')ControlFlowNode for x_forwarded_for
    3. Step 3 [PROPAGATION] introduction/views.py:655
      ip = x_forwarded_for.split(',')[0]ControlFlowNode for ip
    4. Step 4 [SINK] introduction/views.py:660
      if ip != '127.0.0.1':ControlFlowNode for ip

    ✅ Recommended Fix

    First, remove the IP address check entirely since it's unreliable for security decisions. Instead, implement proper authentication and authorization controls. Second, if you need to log the client IP, use Django's built-in `get_client_ip()` method or a validated approach that checks trusted proxies. Never trust the X-Forwarded-For header without validating against a list of trusted proxies.

    def get_client_ip(request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            # Get the first IP in the list
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip
    
    # In the login check code:
    ip = get_client_ip(request)
    # Remove the IP-based security check:
    # if ip != '127.0.0.1':  # REMOVE THIS LINE
    # Instead, implement proper authentication:
    if authenticate(username=user, password=password):
        logging.info(f"{now}:{ip}:{user}")
        return render(request, "Lab/A10/a10_lab2.html", {"name": user})

    📚 References

    • https://cwe.mitre.org/data/definitions/348.html

    🏷️ Tags

    data-flowsecurityexperimental
    MEDIUM CWE-348 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 4efa375e64f4

    IP address spoofing

    A remote endpoint identifier is read from an HTTP header. Attackers can modify the value of the identifier to forge the client ip.

    📍 views.py : Line 943
    Full Path: introduction/views.py

    ⚠️ Vulnerable Code

        938:     if x_forwarded_for:
        939:         ip = x_forwarded_for.split(',')[0]
        940:     else:
        941:         ip = request.META.get('REMOTE_ADDR')
        942: 
    >>> 943:     if ip == '127.0.0.1':
        944:         return render(request,"Lab/ssrf/ssrf_target.html")
        945:     else:
        946:         return render(request,"Lab/ssrf/ssrf_target.html",{"access_denied":True})
        947: 
        948: @authentication_decorator
    

    💡 Explanation

    An attacker could spoof the X-Forwarded-For header to make their request appear to come from localhost (127.0.0.1), bypassing IP-based access controls and gaining unauthorized access to resources intended only for localhost.

    🔗 Tainted Variables

    ControlFlowNode for Attribute()ControlFlowNode for x_forwarded_forControlFlowNode for ipControlFlowNode for ip

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/views.py:936
      x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')ControlFlowNode for Attribute()
    2. Step 2 [PROPAGATION] introduction/views.py:936
      x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')ControlFlowNode for x_forwarded_for
    3. Step 3 [PROPAGATION] introduction/views.py:939
      ip = x_forwarded_for.split(',')[0]ControlFlowNode for ip
    4. Step 4 [SINK] introduction/views.py:943
      if ip == '127.0.0.1':ControlFlowNode for ip

    ✅ Recommended Fix

    First, never trust the X-Forwarded-For header as it can be easily spoofed by clients. Instead, always use REMOTE_ADDR for the actual client IP address. If you need to check if a request came from localhost, compare REMOTE_ADDR directly to '127.0.0.1' without any header parsing. Remove the X-Forwarded-For logic entirely since it's unreliable for security decisions.

        # Always use REMOTE_ADDR for security decisions - it cannot be spoofed
        ip = request.META.get('REMOTE_ADDR')
        
        if ip == '127.0.0.1':
            return render(request, "Lab/ssrf/ssrf_target.html")
        else:
            return render(request, "Lab/ssrf/ssrf_target.html", {"access_denied": True})

    📚 References

    • https://cwe.mitre.org/data/definitions/348.html

    🏷️ Tags

    data-flowsecurityexperimental
    MEDIUM CWE-020 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: e3399ce46da1

    Construction of a cookie using user-supplied input

    Constructing cookies from user input may allow an attacker to perform a Cookie Poisoning attack.

    📍 app.py : Line 49
    Full Path: dockerized_labs/broken_auth_lab/app.py

    ⚠️ Vulnerable Code

        44:         # Vulnerable: Insecure session management
        45:         session_token = base64.b64encode(f"{username}:{datetime.now()}".encode()).decode()
        46:         
        47:         if remember_me:
        48:             # Vulnerable: Insecure "Remember Me" implementation
    >>> 49:             response.set_cookie('session', session_token, max_age=30*24*60*60)
        50:         else:
        51:             response.set_cookie('session', session_token)
        52:             
        53:         return response
        54:     
    

    💡 Explanation

    An attacker could forge session tokens by predicting the pattern (username + timestamp) or manipulate cookies to impersonate users, leading to account takeover and unauthorized access to sensitive data.

    🔗 Tainted Variables

    ControlFlowNode for ImportMemberControlFlowNode for requestControlFlowNode for requestControlFlowNode for AttributeControlFlowNode for Attribute()ControlFlowNode for usernameControlFlowNode for session_tokenControlFlowNode for session_token

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] dockerized_labs/broken_auth_lab/app.py:1
      from flask import Flask, render_template, request, redirect, url_for, make_response, flashControlFlowNode for ImportMember
    2. Step 2 [PROPAGATION] dockerized_labs/broken_auth_lab/app.py:1
      from flask import Flask, render_template, request, redirect, url_for, make_response, flashControlFlowNode for request
    3. Step 3 [PROPAGATION] dockerized_labs/broken_auth_lab/app.py:37
      username = request.form.get('username')ControlFlowNode for request
    4. Step 4 [PROPAGATION] dockerized_labs/broken_auth_lab/app.py:37
      username = request.form.get('username')ControlFlowNode for Attribute
    5. Step 5 [PROPAGATION] dockerized_labs/broken_auth_lab/app.py:37
      username = request.form.get('username')ControlFlowNode for Attribute()
    6. Step 6 [PROPAGATION] dockerized_labs/broken_auth_lab/app.py:37
      username = request.form.get('username')ControlFlowNode for username
    7. Step 7 [PROPAGATION] dockerized_labs/broken_auth_lab/app.py:45
      session_token = base64.b64encode(f"{username}:{datetime.now()}".encode()).decode()ControlFlowNode for session_token
    8. Step 8 [SINK] dockerized_labs/broken_auth_lab/app.py:49
      response.set_cookie('session', session_token, max_age=30*24*60*60)ControlFlowNode for session_token

    ✅ Recommended Fix

    Replace the insecure session token generation with a cryptographically secure random token. Store the token server-side in a secure session store (like Flask-Session with server-side storage) and associate it with user data. Set secure cookie attributes: HttpOnly, Secure, SameSite=Strict, and use a proper session expiration mechanism instead of hardcoded max_age.

    import secrets
    from flask import session
    
    # Replace lines 44-53 with:
    session_token = secrets.token_urlsafe(32)
    session['user_id'] = get_user_id(username)  # Retrieve from database
    session['authenticated'] = True
    
    response = redirect('/dashboard')
    if remember_me:
        response.set_cookie('session_id', session_token, 
                           max_age=30*24*60*60,
                           httponly=True,
                           secure=True,  # Use True in production
                           samesite='Strict')
    else:
        response.set_cookie('session_id', session_token,
                           httponly=True,
                           secure=True,
                           samesite='Strict')
    # Store session_token in database associated with user_id

    📚 References

    • https://cwe.mitre.org/data/definitions/020.html

    🏷️ Tags

    data-flowsecurity
    MEDIUM CWE-208 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 79a933299608

    Timing attack against secret

    Use of a non-constant-time verification routine to check the value of an secret, possibly allowing a timing attack to retrieve sensitive information.

    📍 app.py : Line 41
    Full Path: dockerized_labs/broken_auth_lab/app.py

    ⚠️ Vulnerable Code

        36: def login():
        37:     username = request.form.get('username')
        38:     password = request.form.get('password')
        39:     remember_me = request.form.get('remember_me')
        40: 
    >>> 41:     if username in users and users[username]['password'] == password:  # Vulnerable: Plain text password comparison
        42:         response = make_response(redirect(url_for('dashboard')))
        43:         
        44:         # Vulnerable: Insecure session management
        45:         session_token = base64.b64encode(f"{username}:{datetime.now()}".encode()).decode()
        46:         
    

    💡 Explanation

    An attacker could perform a timing attack to deduce valid usernames and passwords by measuring response time differences, potentially gaining unauthorized access to user accounts.

    🔗 Tainted Variables

    ControlFlowNode for passwordControlFlowNode for password

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] dockerized_labs/broken_auth_lab/app.py:38
      password = request.form.get('password')ControlFlowNode for password
    2. Step 2 [SINK] dockerized_labs/broken_auth_lab/app.py:41
      if username in users and users[username]['password'] == password: # Vulnerable: Plain text password comparisonControlFlowNode for password

    ✅ Recommended Fix

    Replace the plain text password comparison with a constant-time comparison function. First, import a secure comparison function like `hmac.compare_digest()` from Python's `hmac` module. Then, use this function to compare the provided password with the stored password hash instead of plain text. Additionally, ensure passwords are stored as hashes using a strong algorithm like bcrypt or Argon2.

    import hmac
    from werkzeug.security import check_password_hash
    
    # In the login function:
    if username in users and hmac.compare_digest(
        users[username]['password'].encode('utf-8'), 
        password.encode('utf-8')
    ):
        # Or better yet, use hashed passwords:
        # if username in users and check_password_hash(users[username]['password_hash'], password):

    📚 References

    • https://cwe.mitre.org/data/definitions/208.html

    🏷️ Tags

    data-flowsecurityexperimental
    MEDIUM CWE-208 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 3f85f09bb6f2

    Timing attack against secret

    Use of a non-constant-time verification routine to check the value of an secret, possibly allowing a timing attack to retrieve sensitive information.

    📍 app.py : Line 99
    Full Path: dockerized_labs/broken_auth_lab/app.py

    ⚠️ Vulnerable Code

        94:     flash('Email not found')
        95:     return redirect(url_for('lab'))
        96: 
        97: @app.route('/reset/<token>')
        98: def reset_form(token):
    >>> 99:     if token in password_reset_tokens:
        100:         return render_template('reset.html', token=token)
        101:     return 'Invalid token'
        102: 
        103: @app.route('/dashboard')
        104: def dashboard():
    

    💡 Explanation

    An attacker could perform a timing attack to gradually guess valid password reset tokens by measuring response times, potentially hijacking user accounts by bypassing the password reset mechanism.

    🔗 Tainted Variables

    ControlFlowNode for tokenControlFlowNode for token

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] dockerized_labs/broken_auth_lab/app.py:98
      def reset_form(token):ControlFlowNode for token
    2. Step 2 [SINK] dockerized_labs/broken_auth_lab/app.py:99
      if token in password_reset_tokens:ControlFlowNode for token

    ✅ Recommended Fix

    Replace the direct dictionary membership check with a constant-time comparison. First, retrieve the expected token using a safe method that doesn't leak timing information, then compare using a constant-time comparison function like `hmac.compare_digest()` in Python. This ensures the comparison time doesn't depend on how many characters match between the input and stored tokens.

    from flask import Flask, render_template, redirect, url_for, flash
    import hmac
    
    # ... existing code ...
    
    @app.route('/reset/<token>')
    def reset_form(token):
        # Retrieve the expected token value safely
        expected_token = password_reset_tokens.get('expected_key', '')
        
        # Use constant-time comparison
        if hmac.compare_digest(token, expected_token):
            return render_template('reset.html', token=token)
        return 'Invalid token'

    📚 References

    • https://cwe.mitre.org/data/definitions/208.html

    🏷️ Tags

    data-flowsecurityexperimental
    MEDIUM CWE-208 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 703b2897c759

    Timing attack against secret

    Use of a non-constant-time verification routine to check the value of an secret, possibly allowing a timing attack to retrieve sensitive information.

    📍 app.py : Line 112
    Full Path: dockerized_labs/broken_auth_lab/app.py

    ⚠️ Vulnerable Code

        107:         return redirect(url_for('lab'))
        108:     
        109:     try:
        110:         # Vulnerable: Insecure session validation
        111:         username = base64.b64decode(session_token).decode().split(':')[0]
    >>> 112:         if username in users:
        113:             return render_template('dashboard.html', 
        114:                                 username=username, 
        115:                                 role=users[username]['role'],
        116:                                 email=users[username]['email'])
        117:     except:
    

    💡 Explanation

    An attacker could enumerate valid usernames by measuring response time differences, enabling targeted brute-force attacks. This could lead to account takeover if weak passwords are used.

    🔗 Tainted Variables

    ControlFlowNode for session_tokenControlFlowNode for usernameControlFlowNode for username

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] dockerized_labs/broken_auth_lab/app.py:105
      session_token = request.cookies.get('session')ControlFlowNode for session_token
    2. Step 2 [PROPAGATION] dockerized_labs/broken_auth_lab/app.py:111
      username = base64.b64decode(session_token).decode().split(':')[0]ControlFlowNode for username
    3. Step 3 [SINK] dockerized_labs/broken_auth_lab/app.py:112
      if username in users:ControlFlowNode for username

    ✅ Recommended Fix

    Replace the direct dictionary lookup with a constant-time comparison. First, use a cryptographic hash function (like SHA256) to hash the username from the session token, then compare it against pre-hashed usernames in the users dictionary. This ensures the comparison time doesn't reveal whether a username exists in the system. Additionally, validate the session token structure before processing.

        try:
            # Fixed: Constant-time username validation
            decoded = base64.b64decode(session_token).decode()
            username = decoded.split(':')[0]
            
            # Use constant-time comparison
            username_hash = hashlib.sha256(username.encode()).hexdigest()
            
            # Check if hash exists in pre-computed user hashes
            if username_hash in user_hashes:
                actual_username = user_hashes[username_hash]
                return render_template('dashboard.html', 
                                    username=actual_username, 
                                    role=users[actual_username]['role'],
                                    email=users[actual_username]['email'])
        except:

    📚 References

    • https://cwe.mitre.org/data/definitions/208.html

    🏷️ Tags

    data-flowsecurityexperimental
    MEDIUM CWE-200 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 1eae3a0cc179

    Password Reset Token Information Disclosure

    Exposure of Sensitive Information to an Unauthorized Actor

    📍 app.py : Line 84
    Full Path: dockerized_labs/broken_auth_lab/app.py
    Sink Method: flash

    ⚠️ Vulnerable Code

        81:             # In a real application, this would send an email
        82:             # Vulnerable: Token exposed in response
        83:             flash(f'Password reset link: /reset/{token}')
    >>> 84:             return redirect(url_for('lab'))
        85:     
        86:     flash('Email not found')
        87:     return redirect(url_for('lab'))

    💡 Explanation

    Password reset tokens are exposed in flash messages visible to the user (and potentially logged). An attacker with access to the user's session or logs can capture the token and reset the password, leading to account takeover.

    🔗 Tainted Variables

    token

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] dockerized_labs/broken_auth_lab/app.py:78 (generated token)
      token = hashlib.md5(f"{email}:{datetime.now()}".encode()).hexdigest()token
    2. Step 2 [PROPAGATION] dockerized_labs/broken_auth_lab/app.py:84
      flash(f'Password reset link: /reset/{token}')token
    3. Step 3 [SINK] dockerized_labs/broken_auth_lab/app.py:84
      flash(f'Password reset link: /reset/{token}')token

    ✅ Recommended Fix

    Password reset tokens should be sent via a secure out-of-band channel (e.g., email). Never expose sensitive tokens in HTTP responses.

    # Send token via email using a secure email service
    # Do not include token in flash messages or HTTP responses

    📚 References

    🏷️ Tags

    information-disclosurepassword-resetauthentication
    MEDIUM CWE-489 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 98121ef219a9

    Debug Mode Enabled in Production

    Active Debug Code

    📍 app.py : Line 108
    Full Path: dockerized_labs/broken_auth_lab/app.py
    Sink Method: Flask.run

    ⚠️ Vulnerable Code

        106: 
        107: if __name__ == '__main__':
    >>> 108:     app.run(host='0.0.0.0', port=5000, debug=True)  # Vulnerable: Debug mode enabled in production
        109: 

    💡 Explanation

    Debug mode exposes detailed error messages, stack traces, and interactive debugger (if enabled) to attackers. This can leak sensitive information about the application's internals, including source code paths, configuration details, and potential attack vectors.

    🔗 Tainted Variables

    debug

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] dockerized_labs/broken_auth_lab/app.py:108 (hardcoded value)
      app.run(host='0.0.0.0', port=5000, debug=True)debug parameter
    2. Step 2 [SINK] dockerized_labs/broken_auth_lab/app.py:108
      app.run(host='0.0.0.0', port=5000, debug=True)debug parameter

    ✅ Recommended Fix

    Disable debug mode in production. Use environment variables to control debug settings.

    import os
    debug_mode = os.environ.get('FLASK_DEBUG', 'False').lower() == 'true'
    app.run(host='0.0.0.0', port=5000, debug=debug_mode)

    📚 References

    🏷️ Tags

    debuginformation-disclosuremisconfiguration
    MEDIUM CWE-328 MEDIUM ✓ VERIFIED TRUE POSITIVE
    ID: eafbcf693a92

    Use of Weak Hash

    📍 app.py : Line 86
    Full Path: dockerized_labs/broken_auth_lab/app.py

    ⚠️ Vulnerable Code

        81:     
        82:     # Vulnerable: Password reset token generation
        83:     for username, user_data in users.items():
        84:         if user_data['email'] == email:
        85:             # Vulnerable: Predictable token generation
    >>> 86:             token = hashlib.md5(f"{email}:{datetime.now()}".encode()).hexdigest()
        87:             password_reset_tokens[token] = username
        88:             
        89:             # In a real application, this would send an email
        90:             # Vulnerable: Token exposed in response
        91:             flash(f'Password reset link: /reset/{token}')
    

    💡 Explanation

    Layer 2 TP Trigger: Architectural flaw - Use of cryptographically broken MD5 hash for security-sensitive token generation.

    📝 Source Code Context

    hashlib.md5(
    MEDIUM CWE-208 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: fb383788728e

    Timing attack against secret

    Use of a non-constant-time verification routine to check the value of an secret, possibly allowing a timing attack to retrieve sensitive information.

    📍 api.py : Line 17
    Full Path: introduction/playground/A9/api.py

    ⚠️ Vulnerable Code

        12:         return JsonResponse({"message":"normal get request", "method":"get"},status = 200)
        13:     if request.method == "POST":
        14:         username = request.POST['username']
        15:         password = request.POST['password']
        16:         L.info(f"POST request with username {username} and password {password}")
    >>> 17:         if username == "admin" and password == "admin":
        18:             return JsonResponse({"message":"Loged in successfully", "method":"post"},status = 200)
        19:         return JsonResponse({"message":"Invalid credentials", "method":"post"},status = 401)
        20:     if request.method == "PUT":
        21:         L.info("PUT request")
        22:         return JsonResponse({"message":"success", "method":"put"},status = 200)
    

    💡 Explanation

    An attacker could perform a timing attack to determine the correct username and password character-by-character by measuring response time differences, potentially gaining unauthorized administrative access to the system.

    🔗 Tainted Variables

    ControlFlowNode for passwordControlFlowNode for password

    🔄 Data Flow Analysis

    1. Step 1 [SOURCE] introduction/playground/A9/api.py:15
      password = request.POST['password']ControlFlowNode for password
    2. Step 2 [SINK] introduction/playground/A9/api.py:17
      if username == "admin" and password == "admin":ControlFlowNode for password

    ✅ Recommended Fix

    Replace the direct string comparison with a constant-time comparison function. First, import a secure comparison function like `hmac.compare_digest()` from Python's standard library. Then modify the authentication check to compare both username and password using constant-time operations, ensuring the comparison time doesn't reveal information about which part of the credential is incorrect.

    import hmac
    
    # ... existing code ...
    
        if request.method == "POST":
            username = request.POST['username']
            password = request.POST['password']
            L.info(f"POST request with username {username} and password {password}")
            
            # Constant-time comparison for both username and password
            expected_username = "admin"
            expected_password = "admin"
            
            username_match = hmac.compare_digest(username.encode(), expected_username.encode())
            password_match = hmac.compare_digest(password.encode(), expected_password.encode())
            
            if username_match and password_match:
                return JsonResponse({"message":"Logged in successfully", "method":"post"}, status=200)
            return JsonResponse({"message":"Invalid credentials", "method":"post"}, status=401)

    📚 References

    • https://cwe.mitre.org/data/definitions/208.html

    🏷️ Tags

    data-flowsecurityexperimental
    MEDIUM CWE-830 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 27c224e55677

    Inclusion of functionality from an untrusted source

    Including functionality from an untrusted source may allow an attacker to control the functionality and execute arbitrary code.

    📍 about.html : Line 91
    Full Path: dockerized_labs/sensitive_data_exposure/templates/about.html

    ⚠️ Vulnerable Code

        86:                 </div>
        87:             </div>
        88:         </div>
        89:     </div>
        90: 
    >>> 91:     <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
        92:     <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
        93:     <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
        94: </body>
        95: </html>
    

    💡 Explanation

    An attacker could compromise the CDN or perform a man-in-the-middle attack to inject malicious JavaScript code that executes in users' browsers, leading to session hijacking, data theft, or complete application compromise.

    📝 Source Code Context

    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>

    ✅ Recommended Fix

    Download the jQuery library from the official source and host it locally within your application. Remove the external CDN reference and replace it with a local static file reference. Verify the integrity of the downloaded library using checksums from the official jQuery website. Update your static file serving configuration to include the local jQuery file.

        <script src="{{ url_for('static', filename='js/jquery-3.5.1.slim.min.js') }}"></script>
        <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

    📚 References

    • https://cwe.mitre.org/data/definitions/830.html

    🏷️ Tags

    data-flowsecurity
    MEDIUM N/A HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 9cd0e236d7fb

    js/functionality-from-untrusted-source

    📍 about.html : Line 91
    Full Path: dockerized_labs/sensitive_data_exposure/templates/about.html

    ⚠️ Vulnerable Code

    (Source code not available)

    💡 Explanation

    Layer 2 TP Trigger: Insecure Protocol/Configuration. This finding is a duplicate of the first, identifying the same line of code for loading jQuery from an external CDN without integrity checks. The architectural risk of including functionality from an untrusted source remains valid.

    MEDIUM CWE-830 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 3fd6a893c85b

    Inclusion of functionality from an untrusted source

    Including functionality from an untrusted source may allow an attacker to control the functionality and execute arbitrary code.

    📍 base.html : Line 60
    Full Path: dockerized_labs/sensitive_data_exposure/templates/base.html

    ⚠️ Vulnerable Code

        55:         {% endif %}
        56:         
        57:         {% block content %}{% endblock %}
        58:     </div>
        59: 
    >>> 60:     <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
        61:     <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
        62:     <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
        63:     {% block extra_scripts %}{% endblock %}
        64: </body>
        65: </html>
    

    💡 Explanation

    An attacker could compromise the CDN or perform a man-in-the-middle attack to inject malicious JavaScript code that executes in users' browsers, potentially leading to session hijacking, data theft, or malware distribution.

    📝 Source Code Context

    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>

    ✅ Recommended Fix

    Download the jQuery library from the official source and serve it locally from your application's static files directory. Update the script tag to reference the local file instead of the external CDN. This ensures you control the integrity and availability of the JavaScript library.

        <script src="{{ url_for('static', filename='js/jquery-3.5.1.slim.min.js') }}"></script>
        <script src="{{ url_for('static', filename='js/popper.min.js') }}"></script>
        <script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>

    📚 References

    • https://cwe.mitre.org/data/definitions/830.html

    🏷️ Tags

    data-flowsecurity
    MEDIUM N/A HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 0aa6b88df449

    js/functionality-from-untrusted-source

    📍 base.html : Line 60
    Full Path: dockerized_labs/sensitive_data_exposure/templates/base.html

    ⚠️ Vulnerable Code

    (Source code not available)

    💡 Explanation

    Layer 2 TP Trigger: Insecure Protocol/Configuration - Same finding as #1 but from different scanner. Loading scripts from external CDN without integrity checks creates dependency on untrusted third-party, enabling potential code injection via compromised CDN or MITM attacks.

    MEDIUM CWE-830 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: c140db8dc017

    Inclusion of functionality from an untrusted source

    Including functionality from an untrusted source may allow an attacker to control the functionality and execute arbitrary code.

    📍 index.html : Line 132
    Full Path: dockerized_labs/sensitive_data_exposure/templates/index.html

    ⚠️ Vulnerable Code

        127:                 </div>
        128:             </div>
        129:         </div>
        130:     </div>
        131: 
    >>> 132:     <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
        133:     <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
        134:     <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
        135: </body>
        136: </html>
    

    💡 Explanation

    An attacker could compromise the CDN or perform a man-in-the-middle attack to inject malicious JavaScript code that executes in users' browsers, potentially leading to session hijacking, data theft, or complete application compromise.

    📝 Source Code Context

    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>

    ✅ Recommended Fix

    Download the jQuery library from the official source and serve it locally from your application's static files directory. Update the script tag to reference the local file instead of the CDN URL. This ensures you control the source and integrity of the JavaScript library being loaded.

        <script src="{{ url_for('static', filename='js/jquery-3.5.1.slim.min.js') }}"></script>
        <script src="{{ url_for('static', filename='js/popper.min.js') }}"></script>
        <script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>

    📚 References

    • https://cwe.mitre.org/data/definitions/830.html

    🏷️ Tags

    data-flowsecurity
    MEDIUM N/A HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 9bf53fd5807e

    js/functionality-from-untrusted-source

    📍 index.html : Line 132
    Full Path: dockerized_labs/sensitive_data_exposure/templates/index.html

    ⚠️ Vulnerable Code

    (Source code not available)

    💡 Explanation

    Layer 2 TP Trigger: Insecure Protocol/Configuration. This finding is a duplicate of the first, also identifying the inclusion of a script from an external CDN without integrity checks. The architectural risk remains, making it a True Positive.

    MEDIUM CWE-830 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: d36f722e98dd

    Inclusion of functionality from an untrusted source

    Including functionality from an untrusted source may allow an attacker to control the functionality and execute arbitrary code.

    📍 lesson.html : Line 338
    Full Path: dockerized_labs/sensitive_data_exposure/templates/lesson.html

    ⚠️ Vulnerable Code

        333:         </div>
        334:     </div>
        335: 
        336:     <!-- Font awesome for icons -->
        337:     <script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
    >>> 338:     <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
        339:     <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
        340:     <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
        341: </body>
        342: </html>
    

    💡 Explanation

    An attacker could compromise the CDN or perform a man-in-the-middle attack to inject malicious JavaScript code that executes in users' browsers, potentially leading to session hijacking, data theft, or complete compromise of user accounts.

    📝 Source Code Context

    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>

    ✅ Recommended Fix

    Download the jQuery library from the official source (jquery.com) and host it locally within your application. Replace the external CDN reference with a local file path. Ensure you verify the integrity of the downloaded file using checksums from the official source. Update the script tag to point to your local copy instead of the external CDN.

    <!-- Replace the external jQuery CDN reference with local file -->
    <script src="/static/js/jquery-3.5.1.slim.min.js"></script>
    <!-- Optionally add integrity and crossorigin attributes if using a CDN you trust -->
    <!-- <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> -->

    📚 References

    • https://cwe.mitre.org/data/definitions/830.html

    🏷️ Tags

    data-flowsecurity
    MEDIUM N/A HIGH ✓ VERIFIED TRUE POSITIVE
    ID: e6cb6d279b1d

    js/functionality-from-untrusted-source

    📍 lesson.html : Line 338
    Full Path: dockerized_labs/sensitive_data_exposure/templates/lesson.html

    ⚠️ Vulnerable Code

    (Source code not available)

    💡 Explanation

    Layer 2 triggered: Insecure Protocol/Configuration - Duplicate finding of same vulnerability as #1. Script loaded from CDN without integrity check creates supply chain risk. Both findings point to the same architectural flaw requiring remediation.

    MEDIUM CWE-830 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: b9179a58f0dc

    Inclusion of functionality from an untrusted source

    Including functionality from an untrusted source may allow an attacker to control the functionality and execute arbitrary code.

    📍 login.html : Line 82
    Full Path: dockerized_labs/sensitive_data_exposure/templates/login.html

    ⚠️ Vulnerable Code

        77:                 </div>
        78:             </div>
        79:         </div>
        80:     </div>
        81: 
    >>> 82:     <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
        83:     <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
        84:     <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
        85: </body>
        86: </html>
    

    💡 Explanation

    An attacker could compromise the CDN or perform a man-in-the-middle attack to inject malicious JavaScript code that executes in users' browsers, potentially stealing credentials, session tokens, or performing actions on behalf of authenticated users.

    📝 Source Code Context

    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>

    ✅ Recommended Fix

    Download the required JavaScript libraries (jQuery, Popper.js, Bootstrap) from their official sources and host them locally within your application. Remove the external CDN references and update the script tags to point to your local copies. This ensures you control the integrity and availability of these dependencies.

        <script src="/static/js/jquery-3.5.1.slim.min.js"></script>
        <script src="/static/js/popper.min.js"></script>
        <script src="/static/js/bootstrap.min.js"></script>

    📚 References

    • https://cwe.mitre.org/data/definitions/830.html

    🏷️ Tags

    data-flowsecurity
    MEDIUM CWE-079 HIGH ✓ VERIFIED TRUE POSITIVE
    ID: fbb07cbc2bd6

    Client-side cross-site scripting with additional heuristic sources

    Writing user input directly to the DOM allows for a cross-site scripting vulnerability.

    📍 login.html : Line 64
    Full Path: dockerized_labs/sensitive_data_exposure/templates/login.html

    ⚠️ Vulnerable Code

        59:                                 {{ form.username|safe }}
        60:                             </div>
        61:                             <div class="form-group">
        62:                                 <label for="id_password">Password:</label>
        63:                                 {{ form.password.errors }}
    >>> 64:                                 {{ form.password|safe }}
        65:                             </div>
        66:                             <button type="submit" class="btn btn-primary">Login</button>
        67:                         </form>
        68:                         <p class="mt-3">Don't have an account? <a href="{% url 'register' %}">Register here</a></p>
        69:                         
    

    💡 Explanation

    An attacker could inject malicious JavaScript into the password field that would execute in users' browsers when viewing the login page, potentially stealing session cookies, credentials, or performing actions on behalf of authenticated users.

    📝 Source Code Context

    {{ form.password|safe }}

    ✅ Recommended Fix

    Remove the 'safe' filter from the form field rendering as it disables Django's automatic HTML escaping. Instead, use Django's built-in form rendering methods or explicitly escape the field value. Replace '{{ form.password|safe }}' with '{{ form.password }}' to allow Django's template engine to properly escape potentially dangerous characters.

                                 <div class="form-group">
                                     <label for="id_password">Password:</label>
                                     {{ form.password.errors }}
                                     {{ form.password }}
                                 </div>

    📚 References

    • https://cwe.mitre.org/data/definitions/079.html

    🏷️ Tags

    data-flowexperimentalsecurity
    MEDIUM N/A HIGH ✓ VERIFIED TRUE POSITIVE
    ID: e2aceb2846cc

    js/functionality-from-untrusted-source

    📍 login.html : Line 82
    Full Path: dockerized_labs/sensitive_data_exposure/templates/login.html

    ⚠️ Vulnerable Code

    (Source code not available)

    💡 Explanation

    Layer 2 TP Trigger: Insecure Protocol/Configuration. This finding is a duplicate of finding #1, identifying the same script loaded from a CDN without integrity checks. The risk of loading functionality from an untrusted external source without SRI is a valid TP for configuration flaws.

    MEDIUM N/A HIGH ✓ VERIFIED TRUE POSITIVE
    ID: 019212c1d46b

    js/clear-text-storage-of-sensitive-data

    📍 profile.html : Line 222
    Full Path: dockerized_labs/sensitive_data_exposure/templates/profile.html

    ⚠️ Vulnerable Code

    (Source code not available)

    💡 Explanation

    Layer 2: Architectural Safety - This is another duplicate of finding #2, identifying the clear-text storage of sensitive data (API key) in localStorage, which is a Client-Side Storage Risk.

    🔄 Data Flow Analysis

      MEDIUM CWE-830 HIGH ✓ VERIFIED TRUE POSITIVE
      ID: ca5f77594680

      Inclusion of functionality from an untrusted source

      Including functionality from an untrusted source may allow an attacker to control the functionality and execute arbitrary code.

      📍 register.html : Line 93
      Full Path: dockerized_labs/sensitive_data_exposure/templates/register.html

      ⚠️ Vulnerable Code

          88:                 </div>
          89:             </div>
          90:         </div>
          91:     </div>
          92: 
      >>> 93:     <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
          94:     <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
          95:     <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
          96: </body>
          97: </html>
      

      💡 Explanation

      An attacker could compromise the CDN or perform a man-in-the-middle attack to inject malicious JavaScript code that executes in users' browsers, potentially stealing credentials, session tokens, or performing actions on behalf of authenticated users.

      📝 Source Code Context

      <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>

      ✅ Recommended Fix

      Download the jQuery library from the official source and serve it locally from your application's static directory. Remove the external CDN reference and replace it with a local path. Ensure you verify the integrity of the downloaded file using checksums from the official jQuery website.

          <script src="{{ url_for('static', filename='js/jquery-3.5.1.slim.min.js') }}"></script>
          <script src="{{ url_for('static', filename='js/popper.min.js') }}"></script>
          <script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>

      📚 References

      • https://cwe.mitre.org/data/definitions/830.html

      🏷️ Tags

      data-flowsecurity
      MEDIUM N/A HIGH ✓ VERIFIED TRUE POSITIVE
      ID: 28f94d2f34f0

      js/functionality-from-untrusted-source

      📍 register.html : Line 93
      Full Path: dockerized_labs/sensitive_data_exposure/templates/register.html

      ⚠️ Vulnerable Code

      (Source code not available)

      💡 Explanation

      Layer 2 triggered: Insecure Protocol/Configuration - Same finding as #1 but from different scanner. External CDN dependency without integrity checks represents a real architectural security risk, not a structural false positive.

      MEDIUM CWE-352 HIGH ✓ VERIFIED TRUE POSITIVE
      ID: 04a825185cce

      Missing CSRF protection on authentication forms

      Cross-Site Request Forgery (CSRF)

      📍 broken_access_lab_1.html : Line 10
      Full Path: introduction/templates/Lab_2021/A1_BrokenAccessControl/broken_access_lab_1.html
      Sink Method: N/A

      ⚠️ Vulnerable Code

          8:     <h4 style="text-align:center"> Admins Have the Secretkey</h4>
          9:     <div class="login">
      >>> 10:         <form method="post" action="/broken_access_lab_1">
          11: 
          12:             <input id="input" type="text" name="name" placeholder="User Name"><br>
          13:             <input id="input" type="password" name="pass" placeholder="Password"><br>
          14:             <button style="margin-top:20px" class="btn btn-info" type="submit"> Log in</button>
          15: 
          16: 
          17:         </form>
          18:     </div>

      💡 Explanation

      Attackers can trick authenticated users into submitting malicious requests without their knowledge. This can lead to unauthorized actions being performed on behalf of the user, such as changing passwords, making transactions, or modifying account settings. The vulnerability affects multiple forms including broken_access_lab_1.html (line 10), broken_access_lab_2.html, and cryptographic failure labs.

      🔄 Data Flow Analysis

      1. Step 1 [SOURCE] introduction/templates/Lab_2021/A1_BrokenAccessControl/broken_access_lab_1.html:10 (form submission)
        <form method="post" action="/broken_access_lab_1">N/A

      ✅ Recommended Fix

      1. Add {% csrf_token %} to all POST forms. 2. Ensure Django's CSRF middleware is enabled. 3. Use CSRF protection for state-changing operations. 4. Implement same-site cookies.

      <form method="post" action="/broken_access_lab_1">
          {% csrf_token %}
          <input id="input" type="text" name="name" placeholder="User Name"><br>
          <input id="input" type="password" name="pass" placeholder="Password"><br>
          <button style="margin-top:20px" class="btn btn-info" type="submit">Log in</button>
      </form>
      
      # In Django settings.py
      MIDDLEWARE = [
          # ...
          'django.middleware.csrf.CsrfViewMiddleware',
          # ...
      ]

      📚 References

      🏷️ Tags

      csrfauthenticationaccess-control
      MEDIUM CWE-200 HIGH ✓ VERIFIED TRUE POSITIVE
      ID: 2c1558f2ea8c

      Information Disclosure via exposed secret keys in template files

      Exposure of Sensitive Information to an Unauthorized Actor

      📍 secret.html : Line 6
      Full Path: introduction/templates/Lab_2021/A1_BrokenAccessControl/secret.html
      Sink Method: N/A

      ⚠️ Vulnerable Code

          1: {% extends "introduction/base.html" %}
          2: {% load static %}
          3: {% block content %}
          4: {% block title %}
          5: <title>Cryptographic Failure</title>
      >>> 6: {% endblock %}
          7: SOME_SECRET_KEYS = THIS_FILE_CONTAINS_SECRET_INFORMATION
          8: 
          9: {% endblock %}

      💡 Explanation

      Sensitive information including secret keys and configuration details are exposed in template files that may be accessible through directory traversal, misconfiguration, or source code disclosure. This can lead to complete compromise of the application if secrets like API keys, database credentials, or encryption keys are leaked.

      🔄 Data Flow Analysis

      1. Step 1 [SOURCE] introduction/templates/Lab_2021/A1_BrokenAccessControl/secret.html:7 (hardcoded)
        SOME_SECRET_KEYS = THIS_FILE_CONTAINS_SECRET_INFORMATIONN/A

      ✅ Recommended Fix

      1. Remove all hardcoded secrets from template files. 2. Store secrets in environment variables or secure secret management systems. 3. Use Django's settings system with environment-specific configuration files. 4. Implement proper access controls to prevent unauthorized access to sensitive templates.

      # Store secrets in environment variables
      import os
      
      SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
      DATABASE_PASSWORD = os.environ.get('DB_PASSWORD')
      API_KEY = os.environ.get('API_KEY')
      
      # In Django settings.py
      from django.core.exceptions import ImproperlyConfigured
      
      def get_env_variable(var_name):
          try:
              return os.environ[var_name]
          except KeyError:
              error_msg = f"Set the {var_name} environment variable"
              raise ImproperlyConfigured(error_msg)
      
      SECRET_KEY = get_env_variable('DJANGO_SECRET_KEY')

      📚 References

      🏷️ Tags

      information-disclosuresecretshardcoded
      MEDIUM N/A MEDIUM ✓ VERIFIED TRUE POSITIVE
      ID: 6fcfc8719ccd

      deserialization

      📍 views.py : Line 553
      Full Path: /Users/jyothi/Projects/Test/pygoat/introduction/views.py

      ⚠️ Vulnerable Code

          548:         else:
          549: 
          550:             try :
          551:                 file=request.FILES["file"]
          552:                 try :
      >>> 553:                     data = yaml.load(file,yaml.Loader)
          554:                     
          555:                     return render(request,"Lab/A9/a9_lab.html",{"data":data})
          556:                 except:
          557:                     return render(request, "Lab/A9/a9_lab.html", {"data": "Error"})
          558: 
      

      💡 Explanation

      Potential deserialization: 'request.headers.get' → 'yaml.load'

      🔄 Data Flow Analysis

      1. Step 1 [SOURCE] /Users/jyothi/Projects/Test/pygoat/introduction/views.py:529
        request.headers.get
      2. Step 2 [SINK] /Users/jyothi/Projects/Test/pygoat/introduction/views.py:553
        yaml.load
      MEDIUM N/A MEDIUM ✓ VERIFIED TRUE POSITIVE
      ID: bd3b79be02cb

      path_traversal

      📍 views.py : Line 577
      Full Path: /Users/jyothi/Projects/Test/pygoat/introduction/views.py

      ⚠️ Vulnerable Code

          572:         return render (request,"Lab/A9/a9_lab2.html")
          573:     elif request.method == "POST":
          574:         try :
          575:             file=request.FILES["file"]
          576:             function_str = request.POST.get("function")
      >>> 577:             img  = Image.open(file)
          578:             img = img.convert("RGB")
          579:             r,g,b  = img.split()
          580:             # function_str = "convert(r+g, '1')"
          581:             output = ImageMath.eval(function_str,img = img, b=b, r=r, g=g)
          582: 
      

      💡 Explanation

      Potential path traversal: 'request.headers.get' → 'Image.open'

      🔄 Data Flow Analysis

      1. Step 1 [SOURCE] /Users/jyothi/Projects/Test/pygoat/introduction/views.py:529
        request.headers.get
      2. Step 2 [SINK] /Users/jyothi/Projects/Test/pygoat/introduction/views.py:577
        Image.open
      MEDIUM N/A HIGH ✓ VERIFIED TRUE POSITIVE
      ID: 015cffe15a37

      command_injection

      📍 views.py : Line 920
      Full Path: /Users/jyothi/Projects/Test/pygoat/introduction/views.py

      ⚠️ Vulnerable Code

          915:         else:
          916:             file=request.POST["blog"]
          917:             try :
          918:                 dirname = os.path.dirname(__file__)
          919:                 filename = os.path.join(dirname, file)
      >>> 920:                 file = open(filename,"r")
          921:                 data = file.read()
          922:                 return render(request,"Lab/ssrf/ssrf_lab.html",{"blog":data})
          923:             except:
          924:                 return render(request, "Lab/ssrf/ssrf_lab.html", {"blog": "No blog found"})
          925:     else:
      

      💡 Explanation

      Cross-file flow: apis.py:30 -> views.py:920

      🔄 Data Flow Analysis

      1. Step 1 [SOURCE] /Users/jyothi/Projects/Test/pygoat/introduction/views.py:30
        ssrf_html_input_extractor
      2. Step 2 [SINK] /Users/jyothi/Projects/Test/pygoat/introduction/views.py:920
        open
      MEDIUM N/A MEDIUM ✓ VERIFIED TRUE POSITIVE
      ID: fd7a5f22c34a

      header_injection

      📍 app.py : Line 49
      Full Path: /Users/jyothi/Projects/Test/pygoat/dockerized_labs/broken_auth_lab/app.py

      ⚠️ Vulnerable Code

          44:         # Vulnerable: Insecure session management
          45:         session_token = base64.b64encode(f"{username}:{datetime.now()}".encode()).decode()
          46:         
          47:         if remember_me:
          48:             # Vulnerable: Insecure "Remember Me" implementation
      >>> 49:             response.set_cookie('session', session_token, max_age=30*24*60*60)
          50:         else:
          51:             response.set_cookie('session', session_token)
          52:             
          53:         return response
          54:     
      

      💡 Explanation

      Potential header injection: 'request.form.get' → 'response.set_cookie'

      🔄 Data Flow Analysis

      1. Step 1 [SOURCE] /Users/jyothi/Projects/Test/pygoat/dockerized_labs/broken_auth_lab/app.py:37
        request.form.get
      2. Step 2 [SINK] /Users/jyothi/Projects/Test/pygoat/dockerized_labs/broken_auth_lab/app.py:49
        response.set_cookie
      MEDIUM N/A MEDIUM ✓ VERIFIED TRUE POSITIVE
      ID: a689e28da718

      weak_hash

      📍 app.py : Line 86
      Full Path: /Users/jyothi/Projects/Test/pygoat/dockerized_labs/broken_auth_lab/app.py

      ⚠️ Vulnerable Code

          81:     
          82:     # Vulnerable: Password reset token generation
          83:     for username, user_data in users.items():
          84:         if user_data['email'] == email:
          85:             # Vulnerable: Predictable token generation
      >>> 86:             token = hashlib.md5(f"{email}:{datetime.now()}".encode()).hexdigest()
          87:             password_reset_tokens[token] = username
          88:             
          89:             # In a real application, this would send an email
          90:             # Vulnerable: Token exposed in response
          91:             flash(f'Password reset link: /reset/{token}')
      

      💡 Explanation

      Potential weak hash: 'request.form.get' → 'hashlib.md5(f"{email}:{datetime.now()}".encode()).hexdigest'

      🔄 Data Flow Analysis

      1. Step 1 [SOURCE] /Users/jyothi/Projects/Test/pygoat/dockerized_labs/broken_auth_lab/app.py:37
        request.form.get
      2. Step 2 [SINK] /Users/jyothi/Projects/Test/pygoat/dockerized_labs/broken_auth_lab/app.py:86
        hashlib.md5(f"{email}:{datetime.now()}".encode()).hexdigest
      MEDIUM N/A MEDIUM ✓ VERIFIED TRUE POSITIVE
      ID: bcd90bd723a9

      deserialization

      📍 main.py : Line 36
      Full Path: /Users/jyothi/Projects/Test/pygoat/dockerized_labs/insec_des_lab/main.py

      ⚠️ Vulnerable Code

          31: def deserialize_data():
          32:     try:
          33:         serialized_data = request.form.get('serialized_data', '')
          34:         decoded_data = base64.b64decode(serialized_data)
          35:         # Intentionally vulnerable deserialization, matching PyGoat
      >>> 36:         user = pickle.loads(decoded_data)
          37:         
          38:         if isinstance(user, User):
          39:             if user.is_admin:
          40:                 message = f"Welcome Admin {user.username}! Here's the secret admin content: ADMIN_KEY_123"
          41:             else:
      

      💡 Explanation

      Potential deserialization: 'request.form.get' → 'pickle.loads'

      🔄 Data Flow Analysis

      1. Step 1 [SOURCE] /Users/jyothi/Projects/Test/pygoat/dockerized_labs/insec_des_lab/main.py:23
        request.form.get
      2. Step 2 [SINK] /Users/jyothi/Projects/Test/pygoat/dockerized_labs/insec_des_lab/main.py:36
        pickle.loads
      MEDIUM N/A HIGH ✓ VERIFIED TRUE POSITIVE
      ID: e4cf54b5bda5

      path_traversal

      📍 main.py : Line 7
      Full Path: /Users/jyothi/Projects/Test/pygoat/introduction/playground/ssrf/main.py

      ⚠️ Vulnerable Code

          2: 
          3: 
          4: def ssrf_lab(file):
          5:     try:
          6:         dirname = os.path.dirname(__file__)
      >>> 7:         filename = os.path.join(dirname, file)
          8:         file = open(filename,"r")
          9:         data = file.read()
          10:         return {"blog":data}
          11:     except:
          12:         return {"blog": "No blog found"}

      💡 Explanation

      Cross-file flow: apis.py:30 -> main.py:7

      🔄 Data Flow Analysis

      1. Step 1 [SOURCE] /Users/jyothi/Projects/Test/pygoat/introduction/playground/ssrf/main.py:30
        ssrf_html_input_extractor
      2. Step 2 [SINK] /Users/jyothi/Projects/Test/pygoat/introduction/playground/ssrf/main.py:7
        os.path.join
      MEDIUM CWE-1104 HIGH ✓ VERIFIED TRUE POSITIVE
      ID: 2b1229034be1

      [IaC] Using latest tag

      Image using latest tag

      📍 docker-compose.yml : Line 12
      Full Path: docker-compose.yml

      ⚠️ Vulnerable Code

      >>> 12:     image: pygoat/pygoat:latest

      💡 Explanation

      Image using latest tag

      📝 Source Code Context

      image: pygoat/pygoat:latest

      ✅ Recommended Fix

      Pin to specific image version

      Pin to specific image version

      📚 References

      • https://cwe.mitre.org/data/definitions/1104.html

      🏷️ Tags

      iaccompose
      LOW CWE-922 MEDIUM ✓ VERIFIED TRUE POSITIVE
      ID: c773f010794c

      Insecure Storage of Sensitive Information

      📍 profile.html : Line 222
      Full Path: dockerized_labs/sensitive_data_exposure/templates/profile.html

      ⚠️ Vulnerable Code

          217:         var userData = {
          218:             username: "{{ user.username }}",
          219:             apiKey: "{{ user_data.api_key }}" // Yeah it is necessary for this lab.
          220:         };
          221:         
      >>> 222:         localStorage.setItem('user_api_key', "{{ user_data.api_key }}");
          223:         
          224:         console.log("Sensitive data exposed in console - check browser dev tools!");
          225:         
          226:         // more bad practices
          227:         // function checkAdminStatus() {
      

      💡 Explanation

      Layer 2: Architectural Safety - This finding is a duplicate of finding #2, identifying the same insecure storage of a sensitive API key in localStorage. It is a Client-Side Storage Risk.

      📝 Source Code Context

      localStorage.setItem('user_api_key', "{{ user_data.api_key }}");
              
              console.log("Sensi
      LOW CWE-347 MEDIUM ✓ VERIFIED TRUE POSITIVE
      ID: bedb99158956

      Improper JWT Verification

      📍 sec_mis_lab3.html : Line 30
      Full Path: introduction/templates/Lab/sec_mis/sec_mis_lab3.html

      ⚠️ Vulnerable Code

          25: def sec_misconfig_lab3(request):</br>
          26: &emsp;if not request.user.is_authenticated:</br>
          27: &emsp;&emsp;return redirect('login')</br>
          28: &emsp;try:</br>
          29: &emsp;&emsp;cookie = request.COOKIES["auth_cookie"]</br>
      >>> 30: &emsp;&emsp;payload = jwt.decode(cookie, SECRET_COOKIE_KEY, algorithms=['HS256'])</br>
          31: &emsp;&emsp;if payload['user'] == 'admin':</br>
          32: &emsp;&emsp;&emsp;return render(request,"Lab/sec_mis/sec_mis_lab3.html", {"admin":True} )</br>
          33: &emsp;except:</br>
          34: &emsp;&emsp;payload = {</br>
          35: &emsp;&emsp;&emsp;'user':'not_admin',</br>
      

      💡 Explanation

      Layer 2 triggered: The finding relates to a Client-Side Storage Risk. The JWT token is being read from a cookie (`request.COOKIES["auth_cookie"]`). If this cookie lacks the `HttpOnly` flag (not visible in the snippet but implied by the vulnerability type), it is accessible to client-side JavaScript, making it vulnerable to exfiltration via XSS. This is a modern client-side security risk.

      📝 Source Code Context

      jwt.decode(cookie, SECRET_COOKIE_KEY, algorithms=['HS256'])
      LOW CWE-614 MEDIUM ✓ VERIFIED TRUE POSITIVE
      ID: 2a1d38eb0233

      Sensitive Cookie Without Secure Flag

      📍 dashboard.html : Line 79
      Full Path: dockerized_labs/broken_auth_lab/templates/dashboard.html

      ⚠️ Vulnerable Code

          74: </style>
          75: 
          76: <script>
          77: function logout() {
          78:     // Clear the session cookie and redirect to login
      >>> 79:     document.cookie = "session=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
          80:     window.location.href = "/lab";
          81: }
          82: </script>
          83: {% endblock %} 

      💡 Explanation

      Client-side JavaScript is directly manipulating a session cookie without the Secure flag, exposing it to potential interception over non-HTTPS connections. This is an architectural configuration flaw (Insecure Protocol/Configuration trigger).

      📝 Source Code Context

      document.cookie =