搜索全站

输入至少 2 个字符,查找相关内容。

↑↓ 选择 Enter 打开Esc 关闭

宝塔面板取证

宝塔面板(BT Panel)是国内使用率最高的 Linux 建站与服务器管理面板。

最后更新 2026-10-09版本 v2.0维护 DigiForensics查看历史 1

一、概述

宝塔面板(BT Panel)是国内使用率最高的 Linux 建站与服务器管理面板。

它的取证价值来自一个事实:宝塔不是"一个网站管理工具",而是一个以 root 权限运行的管理入口。面板账户一旦失守,攻击者获得的能力等同于 SSH root 登录——可以写文件、建计划任务、管数据库、改防火墙,本身没有提权过程。

这带来两个方向完全相反的取证需求:

  • 向上追:谁在什么时候用过面板?如果攻击者是从面板进来的,request.log 里的登录记录是最初入口的证据,比任何系统日志都直接。
  • 向下挖:面板数据库里存着这台服务器的全部资产清单——站点、数据库、账号、口令、备份路径。拿到面板数据库等于拿到一张完整的攻击面地图。

与办公环境最大的不同:宝塔面板会自己生成并保留大量日志与状态文件。运维人员日常点过的每一次"保存",都会在 action.log 里留下痕迹。

这些痕迹平时无人注意,在案件里价值极高——它们记录的是"人做了什么操作",而不只是"系统发生了什么"。

本篇的核心问题因此不是"站点被攻击了吗",而是"面板被谁用过"。

如果在 request.log + action.log 中定位到攻击者使用面板的完整时间线,就能解释站点上所有"以 root 权限出现、但没有任何提权痕迹"的可疑文件——因为它们本来就是 root 直接写的。

找不到提权痕迹不等于没有提权需求,只说明入口是面板。

在取证流程中的位置:本篇属于分析环,输入是 Linux 服务器取证工件地图 已固定的站点与面板目录,输出是账号身份、登录时间线、资产清单三条线。它与后面的几篇分工明确:新版数据库的加密处理见 宝塔新版加密数据解密,其他面板见 其他面板取证,面板上传的文件怎么查见 网站目录与 WebShell 检测。面板的定位决定了取证优先级——它应该比系统日志更早被查。

涉及的证据形态(宝塔 7.x 与 8.x 的路径差异必须先判定):

内容 7.x 及较早 8.x / 新版架构
主数据库 /www/server/panel/data/default.db /www/server/panel/data/db/default.db
站点与数据库明细 在 default.db 内 拆出 data/db/database.db、data/db/panel.db
IV 文件 无 /www/server/panel/data/div.pl
请求日志 /www/server/panel/logs/request.log 同左(可能有归档)
操作日志 /www/server/panel/data/log/ 同左
站点配置 / 面板入口 /www/server/panel/vhost/ 同左;入口为 data/admin_path.pl、data/port.pl

版本判定的实用方法:看 data/db/ 目录是否存在且有多个 .db 文件,以及 div.pl 是否存在。有 data/db/ + div.pl = 新版架构(7.7 之后引入)。判定结果直接决定后续用哪套解密方案。** users 表是取证价值最高的一张表**,它同时回答三个问题:面板上有哪些账号(是否存在攻击者创建的陌生账户)、谁登录过、从哪登录(login_ip 与 login_time 直接指向来源)。

request.log 是整个面板取证中最高价值的单个文件(格式类似 Nginx access.log):

时间戳告诉你有没有人在非常规时间登录;来源 IP 告诉你是不是陌生地址;HTTP 状态码告诉你登录成功还是失败(POST /login 200 通常是成功跳转);User-Agent 告诉你是不是脚本而非人(python-requests、curl 是明显信号);后续请求路径告诉你登录后做了什么(/file、/database、/crontab 是敏感操作)。** action.log 记录"执行了什么操作"**,前者定位入口,后者定位行为。

两者缺一:只有 request.log 不知道做了什么,只有 action.log 不知道是谁。

面板上有哪些账号、谁在什么时候从哪登录过、面板账号的口令是否被改过、这台服务器上部署了哪些站点与数据库、面板是否暴露在公网、运维人员声称没做过的操作在日志里有没有。

站点目录里的 WebShell 具体怎么工作(那要进 网站目录与 WebShell 检测);新版架构里加密字段的解密(见 宝塔新版加密数据解密);action.log 可能已被攻击者删除或清空——不存在或 size 为 0 时记为"操作日志缺失",不要臆测内容;面板日志有轮转,覆盖的时段只能写"未覆盖"。

判读上的一条硬规矩:区分"运维的正常访问"与"攻击者访问",判据不是"IP 陌生",而是时间模式——运维通常在工作时间、有 Referer、UA 是真实浏览器;攻击者常在深夜、UA 是脚本、Referer 为空。

内容边界:本文用于已获得合法授权的取证场景。面板数据库中包含真实口令等敏感信息,提取与分析必须在受控环境内完成,不得二次扩散——报告中一律脱敏。本篇检测面板暴露面与异常账户时,写的是如何识别、如何排除误报(如运维用脚本做批量操作同样表现为脚本 UA),不涉及面板的利用方式。

读者前提:需要 SQLite 基础与结构 的表读取能力、Linux 服务器取证工件地图 的路径全景,以及基本的 Web 服务与面板认知。建议先做版本架构判定(3.1 第 1 步)——7.x 与 8.x 的路径和解密方案完全不同,判错版本后面全错。

二、核心原理

2.1 目录结构与 7.x / 8.x 路径差异

宝塔几乎把所有东西都装在 /www 下(早期版本可在安装时自定义路径,部分环境装在 /www 以外的目录,以实际样本为准)。关键差异集中在数据库文件的位置:

内容 7.x 及较早 8.x / 新版架构 取证价值
面板程序 /www/server/panel/ 同左 ★★★
主数据库 /www/server/panel/data/default.db /www/server/panel/data/db/default.db ★★★★★
站点与数据库明细 在 default.db 内 拆出 data/db/database.db、data/db/panel.db ★★★★★
IV 文件 无 /www/server/panel/data/div.pl ★★★★
请求日志 /www/server/panel/logs/request.log 同左(可能有归档) ★★★★★
操作日志 /www/server/panel/data/log/ 同左 ★★★★★
站点配置 /www/server/panel/vhost/ 同左 ★★★★★
站点根目录 /www/wwwroot/ 同左 ★★★★★
网站日志 /www/wwwlogs/ 同左 ★★★★★
备份 /www/backup/ 同左 ★★★★
面板入口配置 /www/server/panel/data/admin_path.pl 同左 ★★★★
面板端口配置 /www/server/panel/data/port.pl 同左 ★★★★
SSL 配置 /www/server/panel/data/ssl.pl、panel_ssl.pl 同左 ★★★★

版本判定的实用方法:看 data/db/ 目录是否存在且有多个 .db 文件,以及 div.pl 是否存在。有 data/db/ + div.pl = 新版架构(7.7 之后引入),旧版只有单一的 data/default.db。判定结果决定了后续用哪套解密方案,见第 03 篇。

2.2 面板数据库 default.db 的结构

旧版 default.db 是标准 SQLite3,可以直接用 sqlite3 打开读表。核心表:

表名 内容 取证价值
config 面板配置(端口、安全入口、口令哈希、MySQL root 口令等) ★★★★★
users 面板账户:username、password、salt、login_ip、login_time 等 ★★★★★
sites 站点列表:name、path、status ★★★★★
databases 数据库列表:name、username、password、accept ★★★★★
logs 部分版本的记录表(结构随版本变化) ★★★

users 表的取证价值最高,它同时回答三个问题:

  1. 面板上有哪些账号——是否存在攻击者创建的陌生账户。
  2. 谁登录过、从哪登录——login_ip 与 login_time 直接指向来源。
  3. 口令是否被改过——配合 salt 与 password 字段可判断。

⚠️ 安全提示:default.db 里的 databases.password、config 中的 MySQL root 口令,在旧版里部分场景下是明文或可逆编码。这既是取证机会,也是敏感数据——取出的数据库文件必须按涉密证据管理,不要随意复制到个人设备。

2.3 新版架构的数据库拆分

新版(7.7+ 之后)把原本一个大库拆成多个,原因是不同数据需要不同的加密策略:

/www/server/panel/data/db/
├── default.db      ← 主库(较旧版本的部分表)
├── database.db     ← 数据库账户信息(databases 表,含加密口令)
└── panel.db        ← 面板自身配置(config 表,含 mysql_root 等)

新版的加密特征:敏感字段值形如 BT-0x: + 一段 Base64 密文。识别到 BT-0x: 前缀,就说明遇到的是新版加密机制。解密流程与可运行的 Python 脚本见第 03 篇。

本篇只需记住一件事:看到 BT-0x: 就不要试图手工猜,去第 03 篇。

2.4 面板日志:request.log 的价值

这是整个面板取证中最高价值的单个文件。它记录面板 Web 界面的所有 HTTP 请求,格式与 Nginx access.log 类似:

2024-03-17 22:41:03 198.51.100.24 GET /login 200 1024 "-" "Mozilla/5.0 ..."
2024-03-17 22:41:07 198.51.100.24 POST /login 200 512 "-" "Mozilla/5.0 ..."
能回答的问题 用到的字段
有没有人在非常规时间登录面板 时间戳
是不是陌生 IP 来源 IP
登录成功还是失败 HTTP 状态码(POST /login 200 通常是成功跳转)
是不是脚本而非人 User-Agent(python-requests、curl 等是明显信号)
登录后做了什么 后续请求路径(/file、/database、/crontab 等是敏感操作)

取证关键点:先确定日志的时间跨度(request.log 会轮转,更早的可能已压缩或删除)。

区分"运维人员的正常访问"和"攻击者访问"——判据不是"IP 陌生",而是时间模式:运维通常在工作时间、有 Referer、UA 是真实浏览器,攻击者常在深夜、UA 是脚本、Referer 为空。

最后把面板日志与网站 access.log 对齐:若 POST /file(文件管理上传)之后网站日志出现对应文件的首次访问,两条时间线吻合即可锁定"面板上传"路径。

2.5 操作日志 action.log

request.log 记录"谁访问了哪个 URL",action.log 记录"执行了什么操作"。前者定位入口,后者定位行为。action.log 通常记录面板内的敏感动作:登录/登出、修改密码、添加网站、修改数据库、编辑文件、创建计划任务、备份、启停服务。

取证价值:找到非常规时间的操作;找到运维人员声称没做过的操作;定位攻击者利用面板做了哪几件事(上传文件 → 建计划任务 → 改配置)。

⚠️ 陷阱:action.log 格式在不同版本中差异较大,且可能被攻击者删除或清空。先看文件是否存在、size 与 mtime 是否异常(被修改过),再看内容。若不存在或 size 为 0,记为"操作日志缺失",不要臆测内容。

2.6 面板 SSL 与安全入口

机制 配置文件 含义 取证意义
安全入口 data/admin_path.pl 面板 URL 带一段随机路径 入口泄露本身即风险信号
面板 SSL data/ssl.pl、panel_ssl.pl 面板走 HTTPS 加密传输,但日志仍明文记录
面板端口 data/port.pl 默认 8888,旧版 888 端口是识别面板类型的第一线索

取证判断:若 admin_path.pl 内容为空或为默认值(如 /),说明安全入口被关闭,是可写入报告的安全风险项。

若面板仍走 HTTP,登录口令在网络中明文传输,可作为攻击路径的辅助推断。

面板端口对外暴露本身就是高危——若 8888 监听 0.0.0.0 而非仅内网,说明面板直接暴露公网,是被入侵的常见前置条件。

2.7 面板本身是攻击目标

把这一条单列,因为它决定了取证的重点。通过面板,攻击者无需任何提权漏洞就能完成:

面板登录成功(一个 Web 口令)
   ↓
文件管理 → 以 root 权限上传 WebShell 到任意站点
   ↓
计划任务 → 新建 crontab(面板以 root 执行命令)
   ↓
数据库 → 导出/修改业务库,或读取数据库口令
   ↓
终端 → 面板自带 root SSH 终端,执行任意命令

因此本篇的核心问题不是"站点被攻击了吗",而是"面板被谁用过"。

如果能在 request.log + action.log 中定位到攻击者使用面板的完整时间线,就能解释站点上所有"以 root 权限出现、但没有任何提权痕迹"的可疑文件——因为它们本来就是 root 直接写的。


三、操作步骤

3.1 第 1 步:确认面板存在与版本架构

# 面板主目录
ls -d /mnt/df/www/server/panel 2>/dev/null && echo "→ 宝塔面板环境"

# 判定架构版本(旧版单库 vs 新版 data/db/ 拆分)
ls -l /mnt/df/www/server/panel/data/default.db 2>/dev/null
ls -l /mnt/df/www/server/panel/data/db/ 2>/dev/null
ls -l /mnt/df/www/server/panel/data/div.pl 2>/dev/null     # 新版才有

# 从面板自身记录里读版本号(以实际文件为准,字段名可能随版本变化)
cat /mnt/df/www/server/panel/data/version.pl 2>/dev/null
strings -n 6 /mnt/df/www/server/panel/class/common.py 2>/dev/null | grep -iE 'version' | head -5

上述版本读取路径在不同小版本中并不完全一致,务必以实际样本验证;读不到就用 data/db/ 目录与 div.pl 的有无来判定架构分支。

3.2 第 2 步:固定面板数据与日志

面板 SQLite 数据库在面板运行时可能正在被写,所以现场在线时应优先 cp(不要在停服后才想起)。所有操作只读。

# 面板数据目录(default.db / div.pl / admin_path.pl / port.pl / log/)
cp -rp /mnt/df/www/server/panel/data /evidence/bt-data/
# 面板日志(request.log 是最高价值文件,优先)
cp -rp /mnt/df/www/server/panel/logs /evidence/bt-logs/
# 站点配置
cp -rp /mnt/df/www/server/panel/vhost /evidence/bt-vhost/
# 记录哈希,保证证据链完整
find /evidence/bt-data /evidence/bt-logs -type f -exec sha256sum {} + | tee /evidence/bt-hashes.txt

3.3 第 3 步:读取面板账户(users 表)

sqlite3 直接以只读方式打开(file:...?mode=ro 确保不写入):

# 列出所有表(先看结构,避免猜表名)
sqlite3 "file:/evidence/bt-data/default.db?mode=ro" ".tables"

# 读面板账户(新版若 default.db 里没有,看 data/db/panel.db)
sqlite3 -header -column "file:/evidence/bt-data/default.db?mode=ro" \
  "SELECT username, login_ip, login_time FROM users;"

# 全部字段(确认列名后再筛)
sqlite3 "file:/evidence/bt-data/default.db?mode=ro" "PRAGMA table_info(users);"
sqlite3 -header -column "file:/evidence/bt-data/default.db?mode=ro" "SELECT * FROM users;"
检查项 期望 异常信号
面板账户数量 1~2 个(管理员 + 可选) 多个账户,或名字陌生的账户
login_ip 管理员常用 IP / 内网段 陌生公网 IP,尤其是与站点被攻击时间吻合的
login_time 常规工作时间 深夜时段,或与入侵时间吻合
是否有 safe_path/入口被改 保持原样 被清空(安全入口关闭)

务必交叉验证:面板 users 表的 login_time/login_ip 是数据库内的最后一次登录记录,会被后续登录覆盖。要还原完整登录历史,必须以 request.log 为主。见 3.4。

3.4 第 4 步:分析面板请求日志(核心步骤)

这是定位"谁在什么时候用过面板"的主战场。

安全验证 当前请求需要先完成一次滑块验证。