17.1 MySQL Forensics: Internal Architecture, Data Directory, Information Schema & Utility Programs

Key Takeaways

  • The MySQL data directory defaults to /var/lib/mysql on Linux and C:\\ProgramData\\MySQL\\MySQL Server 8.0\\Data on Windows, and its exact path is resolved from the datadir system variable rather than assumed.
  • InnoDB stores each table in its own tablespace file named table_name.ibd, while ibdata1 holds the shared system tablespace; undo tablespaces were moved out of ibdata1 into separate undo_001 and undo_002 files in MySQL 8.0.
  • The binary log records every data-modifying statement or row change and is the single richest MySQL forensic artifact; mysqlbinlog renders it readable, and the file header magic number is 0xFE62696E for unencrypted and 0xFD62696E for encrypted binary logs.
  • MySQL 8.0 eliminated the per-table .frm definition files by moving the data dictionary into the InnoDB mysql.ibd tablespace, so schema history is no longer recoverable from loose files on disk.
  • The INFORMATION_SCHEMA views expose table, column, privilege, and routine metadata, including CREATE_TIME and UPDATE_TIME values that date schema tampering and rogue account creation.
Last updated: September 2026

17.1 MySQL Forensics: Internal Architecture, Data Directory, Information Schema & Utility Programs

Quick Answer: Blueprint Domain 3 requires the internal architecture of MySQL, the structure of the data directory, data storage in SQL Server and MySQL, database evidence repositories, MySQL forensics, viewing the Information Schema, and MySQL utility programs for forensic analysis; Domain 5 adds MySQL forensics for a WordPress website database. The data directory (datadir, typically /var/lib/mysql) holds one .ibd tablespace per table, the shared ibdata1 system tablespace, and the binary log — which is the highest-value artifact because it records every data-modifying operation with a timestamp and server ID.


MySQL Server Architecture in Forensic Terms

MySQL separates a server layer (connection handling, parser, optimizer, query cache in older versions) from a pluggable storage engine layer. For forensics, the storage engine determines where the bytes are:

EngineOn-disk layoutForensic implication
InnoDB (default since 5.5)table.ibd per table, plus shared ibdata1; ACID with redo/undo logsDeleted rows often survive in pages until reorganization; undo and redo logs hold pre-images
MyISAM (legacy)table.MYD (data), table.MYI (index), table.frmSimple carving target; no transaction log
MEMORYRAM onlyContent exists only in a memory image — lost on service restart
CSV / ARCHIVEPlain or compressed flat filesDirectly readable without the server

[!IMPORTANT] Never trust a default path. Resolve the real one from the running server or the configuration file: SELECT @@datadir, @@log_bin_basename, @@general_log_file, @@slow_query_log_file, @@version; On Linux the configuration is /etc/my.cnf or /etc/mysql/my.cnf; on Windows it is my.ini beside the install. Document the values in the case notes — a relocated datadir on a separate volume is common and easy to miss when imaging.


Structure of the Data Directory

ObjectPurpose
<dbname>/One subdirectory per database (a "schema")
<table>.ibdPer-table InnoDB tablespace: data pages plus indexes (default since 5.6)
ibdata1Shared system tablespace: change buffer, doublewrite buffer (pre-8.0.20), internal dictionary data
mysql.ibdMySQL 8.0 data dictionary — the schema catalog that replaced per-table .frm files
undo_001, undo_002Undo tablespaces, separated out of ibdata1 in MySQL 8.0
ib_logfile0/1, or #innodb_redo/ (8.0.30+)Redo log — physical page changes for crash recovery
binlog.000001, binlog.indexBinary log and its index
<host>.errError log: startup, shutdown, crashes, aborted connections
<host>-slow.logSlow query log
<host>.pid, mysql.sockRuntime artifacts (presence indicates the server was running at acquisition)
*.pemServer TLS key material

MySQL 8.0 changed schema forensics. In 5.x, every table had a readable .frm definition file, so an examiner could reconstruct dropped tables' structure from loose files. In 8.0 the dictionary moved into mysql.ibd, so a dropped table's definition disappears from the filesystem and must instead be recovered from the binary log's DDL statements or from a backup.


The Logs, Ranked by Forensic Value

1. Binary Log — the Transaction Ledger

The binary log records every statement or row change that modified data, with timestamps, server ID, thread ID, and (in ROW format) the complete before-and-after image of each changed row.

# Render a binary log as readable SQL with timestamps
mysqlbinlog --base64-output=DECODE-ROWS --verbose binlog.000017 > /evidence/binlog17.sql

# Constrain to the incident window
mysqlbinlog --start-datetime="2026-09-14 02:00:00"             --stop-datetime="2026-09-14 06:00:00"             --base64-output=DECODE-ROWS --verbose binlog.0000*
binlog_formatWhat is recordedForensic value
STATEMENTThe literal SQL textShows attacker syntax; non-deterministic statements may not replay identically
ROW (default in 8.0)Per-row before/after imagesRecovers deleted row contents even when the table was truncated
MIXEDStatement, switching to row where unsafeHybrid

File header magic number: unencrypted binary logs begin with FE 62 69 6E (0xFE + bin); encrypted binary logs begin with FD 62 69 6E. That one byte tells the examiner immediately whether the log can be parsed directly or requires the server's keyring.

2. General Query Log — Everything, When Enabled

Records every statement received, including SELECT, plus connect and disconnect events with the client host. It is usually off in production for performance reasons, but when an attacker enables it, or when a security-conscious deployment leaves it on, it is the closest thing to a database keylogger.

3. Slow Query Log

Captures statements exceeding long_query_time. Mass-extraction queries — an unbounded SELECT * FROM wp_users or a blind-SQLi timing payload using SLEEP() or BENCHMARK() — are slow by nature, so this log frequently captures injection activity that no other log retained.

4. Error Log

Startup and shutdown times (which bracket periods where the server could not have been altered through SQL), crashes, Access denied failures, and aborted connections. A sudden restart immediately after a compromise often marks the attacker loading a plugin or a UDF.


Viewing the Information Schema

INFORMATION_SCHEMA is a set of read-only views over the data dictionary. These queries are the standard forensic sweep of a live or restored instance:

-- Schema inventory with creation and last-update times
SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE, TABLE_ROWS, CREATE_TIME, UPDATE_TIME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql','information_schema','performance_schema','sys')
ORDER BY CREATE_TIME DESC;

-- Accounts and their hosts, including anonymous or wildcard-host accounts
SELECT user, host, plugin, account_locked, password_last_changed FROM mysql.user;

-- Who holds dangerous global privileges
SELECT GRANTEE, PRIVILEGE_TYPE FROM INFORMATION_SCHEMA.USER_PRIVILEGES
WHERE PRIVILEGE_TYPE IN ('SUPER','FILE','PROCESS','GRANT OPTION','CREATE USER');

-- Stored routines and triggers, a classic persistence hiding place
SELECT ROUTINE_SCHEMA, ROUTINE_NAME, CREATED, LAST_ALTERED, ROUTINE_DEFINITION
FROM INFORMATION_SCHEMA.ROUTINES ORDER BY LAST_ALTERED DESC;
SELECT TRIGGER_SCHEMA, TRIGGER_NAME, EVENT_OBJECT_TABLE, ACTION_STATEMENT, CREATED
FROM INFORMATION_SCHEMA.TRIGGERS;

CREATE_TIME on a table created during the intrusion window, a mysql.user row with host = '%', a FILE-privileged account, or a trigger that re-creates a backdoor row whenever it is deleted are all findings these queries surface directly.

[!WARNING] FILE privilege is the pivot from database compromise to host compromise. It enables SELECT ... INTO OUTFILE to write a web shell into the document root and LOAD_FILE() to read /etc/passwd. Any account holding FILE that should not, and any INTO OUTFILE statement in the binary or general query log, is a server-compromise indicator, not merely a database one.


MySQL Utility Programs for Forensic Analysis

UtilityForensic use
mysqlbinlogRender binary logs as timestamped SQL; the primary reconstruction tool
mysqldumpProduce a logical, text-diffable snapshot of schema and data from a restored copy
mysqlcheckCheck and report table corruption without repairing (use --check only; never --repair on evidence)
myisamchkLow-level MyISAM table inspection
mysqladminstatus, processlist, variables — live state capture including active connections
ibd2sdiExtract serialized dictionary information from an .ibd file (8.0) to recover table structure
innochecksumVerify InnoDB page checksums offline — detects direct tampering with tablespace files
strings / hex editorCarve readable row fragments and deleted records directly from .ibd pages

Worked Scenario: WordPress Database Compromise

WordPress is the blueprint's named case because it is the most common real-world MySQL victim.

Tables that matter: wp_users (login, user_pass as a phpass/bcrypt hash, user_email, user_registered), wp_usermeta (holds wp_capabilities, which is what actually grants the administrator role), wp_options (siteurl, home, active_plugins, plus _transient_ rows attackers abuse for payload storage), wp_posts (injected spam or SEO-poisoning content), and wp_comments.

Investigation sequence:

  1. Acquire the data directory from a forensic image, not by querying the live compromised host, and hash every file.
  2. Run INFORMATION_SCHEMA.TABLES ordered by CREATE_TIME and UPDATE_TIME — an administrator account inserted during the intrusion window shows as a recent wp_users update.
  3. Parse the binary log across the window with mysqlbinlog --base64-output=DECODE-ROWS --verbose and search for INSERT INTO wp_users, UPDATE wp_usermeta ... wp_capabilities, and UPDATE wp_options targeting siteurl or active_plugins.
  4. Check wp_usermeta for a wp_capabilities value of a:1:{s:13:"administrator";b:1;} on an account whose user_registered timestamp falls inside the window — the standard privilege-escalation artifact.
  5. Search wp_options and wp_posts for Base64 or hex-encoded blobs, eval(, and injected <script> or hidden-link markup — the SEO-poisoning payload described in the GOOTLOADER chain.
  6. Correlate each database timestamp to the Apache/IIS access log to identify the request that carried the injection, and to the filesystem for a web shell dropped via INTO OUTFILE.

MySQL timestamp caution: TIMESTAMP columns are stored in UTC and converted to the session time_zone on read, while DATETIME columns are stored literally with no zone conversion. Record SELECT @@global.time_zone, @@session.time_zone at acquisition, or the reconstructed timeline will be wrong by the offset.

Loading diagram...
MySQL Data Directory Layout and Forensic Artifact Value
Test Your Knowledge

An attacker executed TRUNCATE TABLE wp_comments on a MySQL 8.0 server to destroy evidence of injected spam. The server runs with the default binlog_format. Which artifact and command most completely recovers the destroyed row contents?

A
B
C
D
Test Your Knowledge

Reviewing INFORMATION_SCHEMA and the mysql.user table on a restored image of a breached web database, an examiner finds an account created during the intrusion window with host set to % and holding the FILE privilege. Why does this finding escalate the scope of the incident beyond the database?

A
B
C
D
Test Your Knowledge

An examiner needs to determine whether a MySQL 8.0 binary log recovered from a forensic image can be parsed directly with mysqlbinlog or requires the server keyring. What is the fastest reliable check?

A
B
C
D