关键词:宝塔面板、BT Panel、aaPanel、default.db、request.log、面板接管、action.log
难度:进阶
前置知识:SQLite 基础、HTTP 与 Web 服务器基本概念、Linux 文件系统与时间戳
一、概述
宝塔面板(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 表的取证价值最高,它同时回答三个问题:
- 面板上有哪些账号——是否存在攻击者创建的陌生账户。
- 谁登录过、从哪登录——
login_ip 与 login_time 直接指向来源。
- 口令是否被改过——配合
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 步:分析面板请求日志(核心步骤)
这是定位"谁在什么时候用过面板"的主战场。