Access Jet Fixed-Key / Date-Key Obfuscation¶
🟢 Cleartext / Decode (Jet 3/4) | 🔴 Real Encryption (ACE 2010+) — database-open obfuscation for .mdb (Jet 3/4) and .accdb (ACE) containers
Overview¶
- Non-password gate: the database-open password for every Access variant is out-of-corpus — no
[MS-ACCESS],[MS-JET], or[MS-ACE]Open Specification exists; all database-open password content below is UNCONFIRMED community RE - Jet 3/4 (
.mdb): password stored as a reversible XOR mask in the header page — body pages are cleartext, no real encryption; instant decode given only the file header - ACE 2010+ (
.accdb): reportedly uses ECMA-376 agile-style SHA-512 + AES-256-CBC per 4096-byte page with per-page block keys — real encryption if confirmed - The one spec-derived component is the VBA project lock inside Access databases, which conforms to
[MS-OVBA]; see VBA Project Password - Used by: Access
- Structural analogue (ACE 2010+): ECMA-376 Agile Encryption ([MS-OFFCRYPTO] SS2.3.4.10–2.3.4.15)
KDF Pipeline¶
Jet 3: Fixed-Key XOR (UNCONFIRMED)¶
flowchart LR
A["Header Page 0\noffset 0x42"] --> B["Read password\nfield bytes"]
B --> C["XOR with\nFIXED_KEY"]
C --> D["Strip null/space\npadding"]
D --> E["Cleartext\nPassword"]
Jet 4: Date-Key XOR (UNCONFIRMED)¶
flowchart LR
A["Header Page 0\noffset 0x72"] --> B["Read IEEE 754\ndouble (LE)"]
B --> C["Truncate to\nint32"]
C --> D["AND 0xFFFFFFFF\n4-byte mask"]
E["Header Page 0\noffset 0x42"] --> F["Read password\nfield bytes"]
D --> G["XOR field bytes\nwith mask\n(mask recycled mod 4)"]
F --> G
G --> H["Strip null/space\npadding"]
H --> I["Cleartext\nPassword"]
Verification Flow¶
Jet 3/4 passwords are instant decode — no verification loop is needed. Read the header, apply the XOR mask, and the cleartext password falls out.
ACE 2010+: Agile-Style KDF (UNCONFIRMED)¶
flowchart TD
A["Password\n(UTF-16LE)"] --> B["H0 = SHA-512\n(salt || password)"]
B --> C["Spin Loop\n100,000 iterations\nHn = SHA-512(LE32(i) || Hn-1)"]
C --> D["Derive verifier key\nHfinal = SHA-512(Hn || blockKey)"]
D --> E["AES-256-CBC Decrypt\nencryptedVerifierHashInput"]
E --> F["SHA-512(verifier)"]
D --> G["AES-256-CBC Decrypt\nencryptedVerifierHashValue"]
G --> H["storedHash"]
F --> I{"SHA-512(verifier)\n==\nstoredHash?"}
H --> I
I -->|"Yes"| J["Password correct\nDerive page keys"]
I -->|"No"| K["Wrong password"]
On-Disk Structure¶
Jet 3/4 Header Page 0 (UNCONFIRMED)¶
flowchart LR
subgraph PAGE0["Database Header Page 0"]
direction TB
HDR["Engine Version\n(early header bytes)"]
PWD["Password Field\noffset 0x42\n~0x14-0x28 bytes\nXOR-masked"]
DATE["Creation Date\noffset 0x72\n8 bytes IEEE 754 double LE\n(Jet 4 only)"]
HDR --> PWD --> DATE
end
subgraph BODY["Body Pages 1..N"]
direction TB
CLEAR["Table/Index Data\nStored in CLEARTEXT\nNo encryption"]
end
PAGE0 --> BODY
ACE 2010+ Structure (UNCONFIRMED)¶
flowchart LR
subgraph PAGE0ACE["Page 0 (Plaintext Region)"]
direction TB
EINF["Encryption Info\nXML descriptor\nsalt, spinCount\nhashAlgorithm, cipherAlgorithm\nkeyBits"]
end
subgraph ENCPAGES["Pages 1..N (Encrypted)"]
direction TB
EP["4096-byte pages\nAES-256-CBC\nblockKey =\nencodingKey XOR pageNumber(LE32)"]
end
PAGE0ACE --> ENCPAGES
Quick Reference¶
| Parameter | Jet 3 (UNCONFIRMED) | Jet 4 (UNCONFIRMED) | ACE 2007 (UNCONFIRMED) | ACE 2010+ (UNCONFIRMED) |
|---|---|---|---|---|
| Container | .mdb |
.mdb |
.accdb |
.accdb |
| Scheme | Fixed-key XOR | Date-key XOR | RC4 | SHA-512 + AES-256-CBC |
| Body encryption | None (cleartext) | None (cleartext) | RC4 (key width unconfirmed) | AES-256-CBC per page |
| Salt | None | None (date-derived mask) | UNCONFIRMED | 16 bytes (UNCONFIRMED) |
| Iteration | None | None | UNCONFIRMED | 100,000 (UNCONFIRMED) |
| Password field | Header 0x42, ~0x14 bytes | Header 0x42, ~0x28 bytes | UNCONFIRMED | XML descriptor in page 0 |
| Spec status | OUT-OF-CORPUS | OUT-OF-CORPUS | OUT-OF-CORPUS | OUT-OF-CORPUS |
Extraction Target¶
Jet 3/4: Fields to Extract (UNCONFIRMED)¶
| Field | Source Location | Type | Example |
|---|---|---|---|
| Engine version | Header page early bytes | integer | Jet 3 vs Jet 4 discriminator |
| Password field | Header offset 0x42 | byte array (~0x14-0x28 bytes) | XOR-masked password bytes |
| Creation date (Jet 4) | Header offset 0x72 | IEEE 754 double, little-endian | Date value for mask derivation |
ACE 2010+: Fields to Extract (UNCONFIRMED)¶
| Field | Source Location | Type | Example |
|---|---|---|---|
| Salt | XML descriptor in page 0, saltValue |
base64 -> bytes | 16-byte salt |
| Spin count | XML descriptor, spinCount |
decimal integer | 100000 |
| Hash algorithm | XML descriptor, hashAlgorithm |
string | SHA512 |
| Cipher algorithm | XML descriptor, cipherAlgorithm |
string | AES |
| Key bits | XML descriptor, keyBits |
decimal integer | 256 |
| Encrypted verifier | XML descriptor, encryptedVerifierHashInput |
base64 -> bytes | (verifier blob) |
| Encrypted verifier hash | XML descriptor, encryptedVerifierHashValue |
base64 -> bytes | (hash blob) |
| Encrypted key value | XML descriptor, encryptedKeyValue |
base64 -> bytes | (key blob) |
Python function signatures these map to:
def decode_jet3_password(
header: bytes, # full header page (page 0)
fixed_key: bytes, # UNCONFIRMED: hardcoded XOR key from mdbtools/Jackcess
) -> str:
"""Decode Jet 3 database-open password by XOR with fixed key (UNCONFIRMED)."""
...
def decode_jet4_password(
header: bytes, # full header page (page 0)
) -> str:
"""Decode Jet 4 database-open password by date-derived XOR mask (UNCONFIRMED)."""
...
def verify_ace2010_password(
password: str,
salt: bytes, # from XML descriptor saltValue
spin_count: int, # from XML descriptor spinCount
hash_algo: str, # from XML descriptor hashAlgorithm
key_bits: int, # from XML descriptor keyBits
encrypted_verifier: bytes, # from XML descriptor encryptedVerifierHashInput
encrypted_verifier_hash: bytes, # from XML descriptor encryptedVerifierHashValue
) -> bool:
"""Verify ACE 2010+ database-open password via agile-style KDF (UNCONFIRMED)."""
...
1. What it is and where it is used¶
- Microsoft Access databases use a proprietary paged file format managed by two engines: the Jet engine (
.mdb, Access 97 through Access 2003) and the Access Connectivity Engine (ACE,.accdb, Access 2007 and later) - Neither engine's file format is covered by a published
[MS-*]Open Specification - Grep of the complete converted
[MS-*]corpus confirms this:[MS-OFFCRYPTO]contains zero references to Jet, ACE,.mdb,.accdb, or "database password"; only incidental glossary mentions of "access database" appear in[MS-XLS]and[MS-DOC] - Access databases are not OLE/CFB Compound Files and are not OPC/ZIP packages — they are flat paged files with a header page (page 0) that carries the database metadata, including the obfuscated password field
- The database-open password is an access-control gate enforced by the engine at open time; in the Jet 3 and Jet 4 variants, the database body pages are stored in cleartext regardless of whether a password is set
- The one exception is the VBA project embedded within an Access database — because Access is a VBA host application, the embedded VBA project storage conforms to
[MS-OVBA]and its protection gates (project password, protection state, visibility state) are fully specified by that document; see VBA Project Password for the complete algorithm - The VBA project lock is orthogonal to the database-open password; it gates only the VBA editor UI's access to macro source code
- Related container documentation: Access format
2. Corpus status: out-of-corpus declaration¶
OUT-OF-CORPUS. There is no
[MS-ACCESS],[MS-JET], or[MS-ACE]specification in the Microsoft Open Specifications corpus. Every byte offset, field width, constant, and algorithm step attributed to the database-open password mechanism in this document is sourced from community reverse-engineering (mdbtools, Jackcess) and is marked UNCONFIRMED. Do not implement from these notes alone; read the mdbtools and Jackcess source code and validate against real files before relying on any value.
[MS-OFFCRYPTO](v20240820) defines encryption for Word, Excel, and PowerPoint documents — it makes no reference to Access, Jet, ACE,.mdb, or.accdbfiles; the database-open password mechanism is therefore entirely outside the scope of[MS-OFFCRYPTO][MS-OVBA](v20240820) defines the VBA project file format structure — it applies to VBA projects embedded in Access databases but does not define or reference the database-open password
3. Profiles / parameters table¶
| Profile | Engine / era | Container | Obfuscation scheme | Body encryption | Spec status |
|---|---|---|---|---|---|
| Jet 3 | Access 97 | .mdb |
UNCONFIRMED: fixed-key XOR of stored password field in header page 0 | None — body pages are cleartext | OUT-OF-CORPUS |
| Jet 4 | Access 2000 / 2002 / 2003 | .mdb |
UNCONFIRMED: date-key XOR (mask derived from creation-date double at header offset 0x72) of stored password field | None — body pages are cleartext | OUT-OF-CORPUS |
| ACE 2007 | Access 2007 | .accdb |
UNCONFIRMED: RC4 keyed by a hash of the password; body is encrypted | UNCONFIRMED: RC4 (key width unconfirmed — 40 or 128 bit) | OUT-OF-CORPUS |
| ACE 2010+ | Access 2010 / 2013 / 2016 / 2019 / 2021 / LTSC 2024 | .accdb |
UNCONFIRMED: structurally resembles ECMA-376 agile encryption with SHA-512 + AES-256-CBC, spinCount 100000, per-4096-byte-page CBC | UNCONFIRMED: AES-256-CBC per page, blockKey = encodingKey XOR pageNumber (LE32) | OUT-OF-CORPUS; structural analogue: [MS-OFFCRYPTO] sections 2.3.4.10–2.3.4.15 |
| VBA project lock (all versions) | Access 97 through LTSC 2024 | VBA project storage within .mdb or .accdb |
[MS-OVBA] Data Encryption (reversible XOR, section 2.4.3) wrapping SHA-1 password hash (section 2.4.4.4) | None — VBA module streams are compressed but not encrypted | Spec-derived: [MS-OVBA] sections 2.3.1.15, 2.3.1.16, 2.3.1.17, 2.4.3, 2.4.4 |
4. Byte layout / on-disk structure (UNCONFIRMED — community RE)¶
Access databases are paged files, not compound files. The database-open password lives in the header page (page 0). All offsets and field sizes below are from community reverse-engineering (mdbtools, Jackcess DatabaseImpl) and are UNCONFIRMED.
4.1. Jet 3 / Jet 4 header page layout (UNCONFIRMED)¶
| Field | Offset (hex) | Size | Type | Meaning | Source |
|---|---|---|---|---|---|
| Engine version marker | varies (early header) | varies | integer | Distinguishes Jet 3 vs Jet 4 engine format | UNCONFIRMED (Jackcess) |
| Stored password field | 0x42 | ~0x14 to 0x28 bytes (engine-dependent) | byte array | Password bytes masked by the XOR key; null/space padded | UNCONFIRMED (Jackcess, mdbtools) |
| Creation-date value | 0x72 | 8 bytes | IEEE 754 double, little-endian | Jet 4 only: used to derive the 4-byte XOR mask; Jet 3 does not use this field for the password | UNCONFIRMED (Jackcess) |
4.2. ACE header / encryption info (UNCONFIRMED)¶
| Field | Location | Description | Source |
|---|---|---|---|
| Encryption info descriptor | UNCONFIRMED: page 0, plaintext region | ACE 2010+: an XML descriptor structurally resembling the ECMA-376 agile EncryptionInfo blob, containing salt, spinCount, hash algorithm, cipher algorithm, and key bits |
UNCONFIRMED (Jackcess) |
| Encrypted pages | Pages 1..N | ACE 2010+: each 4096-byte page encrypted with AES-256-CBC; blockKey = encodingKey XOR pageNumber (LE32) | UNCONFIRMED (Jackcess) |
4.3. VBA project storage (spec-derived)¶
When a VBA project is present, it is embedded as an OLE/Compound File Binary storage within the Access file. The PROJECT stream ([MS-OVBA] section 2.3.1) is a small INI-style text blob containing three protection records:
| Line (property) | Spec section | Content |
|---|---|---|
CMG="..." (ProjectProtectionState) |
[MS-OVBA] section 2.3.1.15 | Obfuscated 4-byte bitfield: bit 0 = fUserProtected, bit 1 = fHostProtected, bit 2 = fVBEProtected |
DPB="..." (ProjectPassword) |
[MS-OVBA] section 2.3.1.16 | Obfuscated payload: no-password sentinel (1-byte 0x00), 29-byte Password Hash Data Structure (SHA-1 hash + 4-byte Key), or (VBA 5.0 only) plaintext password |
GC="..." (ProjectVisibilityState) |
[MS-OVBA] section 2.3.1.17 | Obfuscated 1-byte flag: 0x00 = not visible, 0xFF = visible |
All three records are obfuscated by the same reversible Data Encryption algorithm ([MS-OVBA] section 2.4.3). See section 5 below and VBA Project Password for the full algorithm.
5. VBA project password — spec-derived algorithm ([MS-OVBA])¶
This section reproduces the VBA project password mechanism from [MS-OVBA] as it applies to Access databases. Access is a VBA host application; the embedded VBA project uses the identical protection scheme as Excel, Word, and PowerPoint. The complete algorithm with all pseudocode is documented in VBA Project Password; a summary follows.
5.1. Data Encryption ([MS-OVBA] section 2.4.3)¶
VBA uses a reversible byte-at-a-time XOR cipher for the CMG, DPB, and GC records. The algorithm's conventions ([MS-OVBA] section 2.4.3):
- XOR denotes a bitwise exclusive OR operation.
- BAND denotes a bitwise AND operation.
- All operations resulting in integer overflow store only low-order bits (high-order bit truncation).
5.1.1. Encrypted Data Structure ([MS-OVBA] section 2.4.3.1)¶
| Field | Size | Description |
|---|---|---|
| Seed | 1 byte | Random encryption seed |
| VersionEnc | 1 byte | Encrypted version byte; decrypts to Version = 2 |
| ProjKeyEnc | 1 byte | Encrypted project key; decrypts to ProjKey (checksum of ProjectCLSID) |
| IgnoredEnc | variable, (Seed BAND 6) / 2 bytes | Arbitrary obfuscation bytes |
| DataLengthEnc | 4 bytes | Encrypted length of DataEnc, in little-endian |
| DataEnc | variable | The encrypted payload |
5.1.2. Encryption algorithm ([MS-OVBA] section 2.4.3.2)¶
Input parameters: Data (byte array to obfuscate), Length (unsigned 32-bit integer, length of Data).
SET ProjKey TO 0.
FOR each CharacterByte IN the string ProjectId.ProjectCLSID:
ADD CharacterByte TO ProjKey.
END FOR
VersionEnc = Seed XOR Version -- Version MUST be 2
ProjKeyEnc = Seed XOR ProjKey
SET UnencryptedByte1 TO ProjKey.
SET EncryptedByte1 TO ProjKeyEnc.
SET EncryptedByte2 TO VersionEnc.
-- IgnoredEnc (obfuscation padding)
SET IgnoredLength TO (Seed BAND 6) / 2.
FOR Counter FROM 1 TO IgnoredLength INCLUSIVE:
SET TempValue TO any value.
SET ByteEnc TO (TempValue XOR (EncryptedByte2 + UnencryptedByte1)).
APPEND IgnoredEnc WITH ByteEnc.
SET EncryptedByte2 TO EncryptedByte1.
SET EncryptedByte1 TO ByteEnc.
SET UnencryptedByte1 TO TempValue.
END FOR
-- DataLengthEnc (4 bytes, little-endian)
FOR each Byte IN Length in little-endian order:
SET ByteEnc TO (Byte XOR (EncryptedByte2 + UnencryptedByte1)).
APPEND DataLengthEnc WITH ByteEnc.
SET EncryptedByte2 TO EncryptedByte1.
SET EncryptedByte1 TO ByteEnc.
SET UnencryptedByte1 TO Byte.
END FOR
-- DataEnc
FOR each DataByte IN Data:
SET ByteEnc TO (DataByte XOR (EncryptedByte2 + UnencryptedByte1)).
APPEND DataEnc WITH ByteEnc.
SET EncryptedByte2 TO EncryptedByte1.
SET EncryptedByte1 TO ByteEnc.
SET UnencryptedByte1 TO DataByte.
END FOR
5.1.3. Decryption algorithm ([MS-OVBA] section 2.4.3.3)¶
Input: an Encrypted Data Structure (section 2.4.3.1). Output: Length (unsigned 32-bit integer), Data (byte array).
Version = Seed XOR VersionEnc -- Version MUST be 2
ProjKey = Seed XOR ProjKeyEnc
SET UnencryptedByte1 TO ProjKey.
SET EncryptedByte1 TO ProjKeyEnc.
SET EncryptedByte2 TO VersionEnc.
-- Skip IgnoredEnc
IgnoredLength = (Seed BAND 6) / 2
FOR each ByteEnc IN IgnoredEnc:
SET Byte TO (ByteEnc XOR (EncryptedByte2 + UnencryptedByte1)).
SET EncryptedByte2 TO EncryptedByte1.
SET EncryptedByte1 TO ByteEnc.
SET UnencryptedByte1 TO Byte.
END FOR
-- Decrypt Length (4 bytes, reassembled little-endian)
SET ByteIndex TO zero.
FOR each ByteEnc IN DataLengthEnc:
SET Byte TO (ByteEnc XOR (EncryptedByte2 + UnencryptedByte1)).
SET TempValue TO 256 raised to the power of ByteIndex.
MULTIPLY TempValue by Byte.
ADD TempValue TO Length.
SET EncryptedByte2 TO EncryptedByte1.
SET EncryptedByte1 TO ByteEnc.
SET UnencryptedByte1 TO Byte.
INCREMENT ByteIndex.
END FOR
-- Length equals the length of DataEnc.
-- Decrypt Data
FOR each ByteEnc IN DataEnc:
SET Byte TO (ByteEnc XOR (EncryptedByte2 + UnencryptedByte1)).
APPEND Data WITH Byte.
SET EncryptedByte2 TO EncryptedByte1.
SET EncryptedByte1 TO ByteEnc.
SET UnencryptedByte1 TO Byte.
END FOR
5.2. ProjectPassword payload ([MS-OVBA] section 2.3.1.16)¶
The DPB= line's EncryptedPassword field (minimum 16 hex digits, i.e. 8 bytes) decrypts via section 2.4.3.3 to one of three cases:
-
No password:
Data=0x00,Length= 1. The VBA project has no password set. -
Password hash (SHOULD case):
Data= a 29-byte Password Hash Data Structure ([MS-OVBA] section 2.4.4.1),Length= 29. When this case applies,ProjectId.ProjectCLSIDMUST be{00000000-0000-0000-0000-000000000000}([MS-OVBA] section 2.3.1.16). -
Plaintext password (MAY case, VBA 5.0 only):
Data= null-terminated MBCS password string,Length= byte count including null terminator. Product behavior note<6>([MS-OVBA] section 2.3.1.16): "VBA 5.0 will save the encrypted plain text password."
5.3. Password Hash Data Structure ([MS-OVBA] section 2.4.4.1)¶
| Field | Offset (within 29-byte structure) | Size | Description |
|---|---|---|---|
| Reserved | 0 | 1 byte | MUST be 0xFF; MUST be ignored |
| GrbitKey | 1 (bits 7..4 of byte 1) | 4 bits | Each bit flags a corresponding null byte of Key |
| GrbitHashNull | 1 (bits 3..0 of byte 1) through byte 3 (bits 7..4) | 20 bits | Each bit flags a corresponding null byte of PasswordHash |
| KeyNoNulls | 4 | 4 bytes | Key with null bytes replaced per Encode Nulls (section 2.4.4.2) |
| PasswordHashNoNulls | 8 | 20 bytes | SHA-1 digest with null bytes replaced per Encode Nulls (section 2.4.4.2) |
| Terminator | 28 | 1 byte | MUST be 0x00 |
5.4. Encode Nulls ([MS-OVBA] section 2.4.4.2)¶
Replaces 0x00 bytes in the input with 0x01 and records the positions in a bitfield.
FOR each Byte IN InputBytes:
IF Byte EQUALS 0x00 THEN:
APPEND EncodedBytes WITH 0x01.
APPEND GrbitNull WITH one bit set to FALSE.
ELSE:
APPEND EncodedBytes WITH Byte.
APPEND GrbitNull WITH one bit set to TRUE.
END IF
END FOR
5.5. Decode Nulls ([MS-OVBA] section 2.4.4.3)¶
Reverses Encode Nulls: reads the bitfield and restores 0x00 bytes.
SET Index TO 0.
FOR each Bit IN GrbitNull:
IF Bit EQUALS FALSE THEN:
APPEND DecodedBytes WITH 0x00.
ELSE:
APPEND DecodedBytes WITH EncodedBytes[Index].
END IF
INCREMENT Index.
END FOR
5.6. Password Hash Algorithm ([MS-OVBA] section 2.4.4.4)¶
Parameters: Password (byte array, MBCS-encoded per PROJECTCODEPAGE), Key (4 bytes, any value; when validating, use the stored Key from the Password Hash Data Structure).
Output: PasswordHash (20 bytes).
LET BytesToHash be a variable array of bytes.
APPEND BytesToHash WITH Password.
APPEND BytesToHash WITH Key.
SET PasswordHash TO the SHA-1 cryptographic digest of BytesToHash, as specified by [RFC 3174].
This is a single-pass SHA-1 with no iteration. The password is MBCS-encoded (not UTF-16LE), and the 4-byte Key is appended after the password bytes (i.e. the hash input is Password || Key).
5.7. Password Hash Validation ([MS-OVBA] section 2.4.4.5)¶
Parameters: NewPassword (MBCS byte array), Key (4 bytes, from stored hash), OldPasswordHash (20 bytes, from stored hash).
Output: Valid (Boolean).
LET NewHash be an array of 20 bytes.
CALL Password Hash Algorithm (section 2.4.4.4) with NewPassword and Key RETURNING NewHash.
IF NewHash EQUALS OldPasswordHash THEN:
SET Valid TO TRUE.
ELSE:
SET Valid TO FALSE.
END IF
6. Database-open password — UNCONFIRMED community RE¶
All content in this section is UNCONFIRMED and sourced from community reverse-engineering (mdbtools, Jackcess). There is no [MS-*] specification for any of these mechanisms. Do not implement from these notes; read the mdbtools and Jackcess source code first and validate against real files.
6.1. Jet 3 — fixed-key XOR (Access 97, UNCONFIRMED)¶
Source: mdbtools, Jackcess DatabaseImpl.
- The stored password field at header offset 0x42 is XOR-masked with a hardcoded fixed key
- The fixed key bytes are not in this corpus; they must be read from the mdbtools or Jackcess source code
- The database body pages are stored in cleartext; the password gates only the engine's open check
-- UNCONFIRMED pseudocode (community RE)
field = header_bytes[0x42 : 0x42 + PWD_LEN] -- PWD_LEN ~0x14, engine-dependent
cleartext_password = XOR(field, FIXED_KEY) -- FIXED_KEY from mdbtools/Jackcess
strip null/space padding from cleartext_password
There is no key derivation function, no iteration, no salt. The mask is static and invertible.
6.2. Jet 4 — date-key XOR (Access 2000-2003, UNCONFIRMED)¶
Source: Jackcess DatabaseImpl.
- The stored password field at header offset 0x42 is XOR-masked with a 4-byte key derived from the creation-date value stored as an 8-byte IEEE 754 little-endian double at header offset 0x72
- There is no key derivation function, no iteration, no salt
- The mask is derived from file metadata stored in the same header and is invertible given the header bytes
-- UNCONFIRMED pseudocode (community RE, Jackcess model)
date_double = read_ieee754_double_LE(header_bytes[0x72 : 0x72 + 8])
mask_int = truncate_to_int32(date_double) BAND 0xFFFFFFFF
pwd_mask = serialize_little_endian_32(mask_int) -- 4 bytes
field = header_bytes[0x42 : 0x42 + PWD_LEN]
FOR i FROM 0 TO len(field) - 1:
cleartext_password[i] = field[i] XOR pwd_mask[i MOD 4]
END FOR
strip null/space padding from cleartext_password
Some Jet 4 files may additionally RC4-encode parts of the header page with a separate encoding key (UNCONFIRMED, distinct from the database password). This does not affect the password recovery procedure.
6.3. ACE 2007 — RC4 keyed by password hash (UNCONFIRMED)¶
Source: community RE tools.
- The database body is encrypted with RC4, keyed by a hash of the user-supplied password
- The exact hash algorithm, key width (40-bit or 128-bit), and verifier structure are UNCONFIRMED in this corpus
- This is a real encryption scheme, not a reversible mask
6.4. ACE 2010+ — agile-style SHA-512 + AES-256-CBC (UNCONFIRMED)¶
Source: Jackcess.
Community reverse-engineering describes a scheme structurally resembling the ECMA-376 agile encryption specified in [MS-OFFCRYPTO] sections 2.3.4.10 through 2.3.4.15, but applied per-page with Access-specific deviations:
- Page 0 (plaintext region) holds an XML descriptor structurally resembling the agile
EncryptionInfoblob, containing salt, spinCount, hash algorithm identifier, cipher algorithm identifier, and key bit count - UNCONFIRMED parameters: SHA-512 hash, AES-256-CBC cipher, spinCount = 100000
- Subsequent 4096-byte pages are each encrypted with AES-256-CBC; the block key for each page is reportedly
encodingKey XOR pageNumberwherepageNumberis a little-endian 32-bit integer - The key derivation is reportedly the same spin loop as ECMA-376 agile:
H_0 = Hash(salt || password), thenH_{n+1} = Hash(iterator_LE || H_n)forspinCountiterations
For the closest documented analogue, see ECMA-376 Agile Encryption. However, the per-page blockKey deviation means this is not a direct reuse of the OOXML agile scheme. Do not assume compatibility without RE validation.
7. User-level security (Jet ULS, .mdw file) — UNCONFIRMED, out-of-corpus¶
- Jet user-level security (ULS) is a separate mechanism from the database-open password
- It uses an external workgroup information file (
.mdw) to store per-user and per-group credentials and permissions - ULS was supported in Jet 3 and Jet 4 (
.mdbonly) and was retired in Access 2007; it is not available for.accdbfiles - There is no
[MS-*]specification for Jet ULS - The
.mdwfile format, its password storage mechanism, and the permission model are entirely out-of-corpus - Community reverse-engineering (mdbtools) describes some aspects of the
.mdwformat, but details are not reproduced here
8. Security considerations¶
8.1. VBA project lock — from the spec¶
The [MS-OVBA] specification explicitly states the security posture of VBA project protection:
"When data in a VBA project, such as a password, is encrypted, the information necessary to decrypt the data is stored with the encrypted data. The design of this encryption is to obfuscate sensitive information, not to secure it." — [MS-OVBA] section 4.2
The spec identifies the following encrypted items ([MS-OVBA] section 4.2): - Project password (ProjectPassword, section 2.3.1.16) - Project protection state (ProjectProtectionState, section 2.3.1.15) - Project visibility state (ProjectVisibilityState, section 2.3.1.17)
Additionally, the VBA source code in the module streams is compressed ([MS-OVBA] section 2.4.1) but not encrypted. The project lock gates only the VBA editor UI; the actual macro code is readable from the module streams without the password.
The Content Hash ([MS-OVBA] section 2.4.2) provides an integrity digest for the VBA project, but ProjectId, ProjectProtectionState, ProjectPassword, and ProjectVisibilityState are explicitly excluded from the normalized data used to compute this hash ([MS-OVBA] section 2.4.2.6). This means the protection state and password can be altered without invalidating the content hash.
- Product behavior note
<5>([MS-OVBA] section 2.3.1.15): VBA 5.0 saves0x00000000for the protection state regardless of whether protection is enabled - Product behavior note
<6>([MS-OVBA] section 2.3.1.16): VBA 5.0 saves the encrypted plaintext password rather than a hash
8.2. Database-open password — UNCONFIRMED observations¶
- Jet 3/4
.mdb(UNCONFIRMED): The XOR mask is invertible given only the file header bytes. The database body is not encrypted. The database-open password provides no confidentiality for the data; it is an access-control gate enforced by the Jet engine at open time. Any tool that reads the file directly (bypassing the engine's open check) can access all table data without the password. - ACE
.accdb(UNCONFIRMED): The 2010+ scheme reportedly encrypts the database body with AES-256-CBC per-page, which — if confirmed — provides actual confidentiality. The ACE 2007 RC4 scheme reportedly encrypts the body as well, though the effective key width is unconfirmed.
9. Spec references¶
Spec-derived (VBA project lock)¶
| Section | Title | Relevance |
|---|---|---|
| [MS-OVBA] section 2.3.1.15 | ProjectProtectionState | Bitfield specifying who locked the VBA project (user, host, VBE) |
| [MS-OVBA] section 2.3.1.16 | ProjectPassword | Obfuscated password hash or plaintext; three payload cases |
| [MS-OVBA] section 2.3.1.17 | ProjectVisibilityState | Single-byte visibility flag (0x00 = hidden, 0xFF = visible) |
| [MS-OVBA] section 2.4.3 | Data Encryption | Reversible XOR obfuscation algorithm used for CMG, DPB, GC |
| [MS-OVBA] section 2.4.3.1 | Encrypted Data Structure | Wire format: Seed, VersionEnc, ProjKeyEnc, IgnoredEnc, DataLengthEnc, DataEnc |
| [MS-OVBA] section 2.4.3.2 | Encryption | Encryption procedure with chained XOR |
| [MS-OVBA] section 2.4.3.3 | Decryption | Decryption procedure; inverse of section 2.4.3.2 |
| [MS-OVBA] section 2.4.4 | Password Hash | Custom format for storing a SHA-1 password hash with null-encoding |
| [MS-OVBA] section 2.4.4.1 | Password Hash Data Structure | 29-byte structure: Reserved, GrbitKey, GrbitHashNull, KeyNoNulls, PasswordHashNoNulls, Terminator |
| [MS-OVBA] section 2.4.4.2 | Encode Nulls | Replaces 0x00 with 0x01 and records positions in bitfield |
| [MS-OVBA] section 2.4.4.3 | Decode Nulls | Restores 0x00 bytes from bitfield |
| [MS-OVBA] section 2.4.4.4 | Password Hash Algorithm | SHA-1(Password MBCS |
| [MS-OVBA] section 2.4.4.5 | Password Hash Validation | Compare new hash against stored hash using same Key |
| [MS-OVBA] section 4.1 | Project Integrity Verification | Content Hash used to verify VBA project integrity |
| [MS-OVBA] section 4.2 | Encryption Method | Explicit statement: "designed to obfuscate, not to secure" |
[MS-OVBA] product note <5> |
section 2.3.1.15 | VBA 5.0 saves 0x00000000 regardless of protection state |
[MS-OVBA] product note <6> |
section 2.3.1.16 | VBA 5.0 saves encrypted plaintext password |
Out-of-corpus (database-open password)¶
No [MS-*] spec references exist. Community sources:
| Source | Coverage |
|---|---|
| mdbtools | Jet 3/4 .mdb file format, fixed-key XOR, header offsets |
Jackcess (DatabaseImpl) |
Jet 3/4 .mdb header layout, date-key derivation, ACE .accdb encryption |
| [MS-OFFCRYPTO] sections 2.3.4.10–2.3.4.15 | Structural analogue only: ECMA-376 agile encryption (does not reference Access) |
Cross-references¶
- VBA Project Password — full algorithm for the VBA project lock
- XOR Obfuscation — Office document XOR obfuscation, same family of reversible masks
- ECMA-376 Agile Encryption — closest documented analogue for ACE 2010+
- Access format — container documentation for
.mdband.accdb - IRM/RMS Encryption — another non-password protection mechanism
The boundary¶
The database-open password for every Access variant (Jet 3, Jet 4, ACE 2007, ACE 2010+) is entirely outside the [MS-*] Open Specifications corpus. The UNCONFIRMED community RE notes in this document are a starting point for investigation, not a build specification. Before implementing any database-open password handling, read the Jackcess and mdbtools source code directly and validate against real .mdb and .accdb files.