2025数证杯决赛 团体赛

2025数证杯决赛团体赛服务器hermes

围绕 2025数证杯决赛 的公开复盘与解题记录。

上传者:怪叔叔 发布日期:2026-05-22 54 次阅读

第25届数证杯决赛 - 团体服务器取证 完整解题报告

比赛时间: 2026年5月 检材数量: 2 台服务器镜像 题目范围: Q36-Q52(共 17 题) 最终得分: 全部解答完毕 ✅


目录

  1. 环境信息
  2. 解题过程详解
  3. 关键中间过程
  4. 数据库表结构参考
  5. 常用命令模板
  6. 经验总结

一、环境信息

项目 检材一 (GitLab) 检材二 (OIM后端)
IP 地址 192.168.100.234 192.168.100.233
主机名 localhost.localdomain iZj6chw5inj1ussq3xplaoZ
操作系统 CentOS 7 CentOS 7
内核版本 3.10.0-1160.119.1.el7.x86_64 3.10.0-1127.19.1.el7.x86_64
SSH 端口 22 22
初始账号 root / 123456 root / 123456
主要服务 GitLab (端口 9999) OIM 后台管理 (Spring Boot)
Docker 容器 - redis, mysql_master, nginx-web, elasticsearch

SSH 连接方式:

ssh -i ~/.ssh/id_ed25519 root@192.168.100.234   # 检材一 (GitLab)
ssh -i ~/.ssh/id_ed25519 root@192.168.100.233   # 检材二 (OIM后端)

二、解题过程详解

Q36 - systemd 开机自启服务数量

题目: 通过对服务器中的检材一镜像进行分析,该服务器上systemd管理的开机自启的服务数量是多少?(答案格式:仅数字)

答案:34

解题思路

  1. 连接检材一服务器
  2. 使用 systemctl list-unit-files 命令列出所有已启用(enabled)的服务
  3. 统计输出结果中的服务数量

关键命令

# 列出所有已启用的 systemd 服务
systemctl list-unit-files --type=service --state=enabled

# 输出末尾显示:
# "34 unit files listed."

知识点

  • --type=service:只列出服务类型(排除 target、mount 等)
  • --state=enabled:只列出开机自启的服务
  • 输出最后一行会显示总数

Q37 - 系统内核编译时间(中国时区)

题目: 通过对服务器中的检材一镜像进行分析,该服务器当前运行的系统内核的编译时间是多少?(答案格式:2024-12-24 15:29:38)

答案:2024-06-04 22:43:51

解题思路

  1. 使用 uname -v 查看内核编译时间戳
  2. 原始输出为 UTC 时间,需转换为 中国时区(UTC+8)

关键命令

# 查看内核版本和编译时间
uname -v
# 输出:#1 SMP Tue Jun 4 14:43:51 UTC 2024

# 查看完整内核信息
uname -a

时间转换计算

  • 原始时间:Tue Jun 4 14:43:51 UTC 2024
  • 中国时区:UTC + 8h = 22:43:51
  • 最终答案:2024-06-04 22:43:51

知识点

  • uname -v 显示内核版本号和编译时间(UTC)
  • CTF 中涉及时间的题目通常需要时区转换
  • UTC + 8h = 北京时间(中国标准时间)

Q38 - GitLab 服务端口

题目: 通过对服务器中的检材一镜像进行分析,该服务器上GitLab服务的端口是多少?(答案格式:仅数字)

答案:9999

解题思路

  1. GitLab Omnibus 配置文件位于 /etc/gitlab/gitlab.rb
  2. Nginx 监听端口在配置文件中定义为 nginx['listen_port']

关键命令

# 查看 GitLab 配置文件中关于端口的设置
cat /etc/gitlab/gitlab.rb | grep listen_port
# 输出:nginx['listen_port'] = 9999

# 或使用 grep 精确查找
grep "listen_port" /etc/gitlab/gitlab.rb

知识点

  • GitLab Omnibus 使用 Ruby DSL 配置文件
  • Nginx 端口配置键为 nginx['listen_port']
  • 默认端口为 80,此处被修改为 9999

Q39 - GitLab 登录失败次数

题目: 通过对服务器中的检材一镜像进行分析,该服务器上GitLab服务被登录失败的次数是多少?(答案格式:仅数字)

答案:6

解题思路

  1. GitLab Rails 应用日志位于 /var/log/gitlab/gitlab-rails/
  2. 登录操作对应 SessionsController#create
  3. 登录失败的状态码为 status=0(在 GitLab JSON 日志中表示失败)
  4. 筛选所有符合条件的日志条目并计数

关键命令

# 方法1:使用 Python 解析 JSON 日志
zcat /var/log/gitlab/gitlab-rails/production_json.log* | python3 -c "
import sys, json
count = 0
for line in sys.stdin:
    obj = json.loads(line.strip())
    if 'SessionsController' in obj.get('controller', '') and obj.get('status') == 0:
        count += 1
        print(f\"{obj.get('time')} - {obj.get('controller')}#{obj.get('action')} status={obj.get('status')}\")
print(f'Total: {count}')
"

# 方法2:使用 grep 快速筛选(粗略统计)
zcat /var/log/gitlab/gitlab-rails/production_json.log* | grep -c 'SessionsController.*status.*0'

6次失败时间线(UTC)

序号 时间 (UTC) 说明
1 2025-11-06T02:51:38Z 首次登录尝试
2 2025-11-06T02:52:10Z 第二次尝试
3 2025-11-06T02:52:26Z 第三次尝试
4 2025-11-06T02:52:43Z 第四次尝试
5 2025-11-06T02:52:48Z 第五次尝试
6 2025-11-06T05:08:40Z 第六次尝试(root 尝试以 Mia 登录)

知识点

  • GitLab Rails 日志为 JSON 格式(production_json.log)
  • SessionsController#create = 用户登录动作
  • status=0 = 登录失败(密码错误或账号不存在)
  • status=302 = 登录成功(重定向到首页)
  • 日志可能已被 gzip 压缩,需用 zcat 读取

Q40 - GitLab root 用户密码哈希与明文

题目: 通过对服务器中的检材一镜像进行分析,该服务器上GitLab服务的root用户的密码是多少?

答案:abc123654

哈希值: $2a$10$joK1mu.0whkjO8IvGSG/X.wVlUofNpX6hplpT85SQjdp.Dg0l7AKO

解题思路

  1. GitLab 使用 PostgreSQL 作为数据库,但需要以系统用户 git 身份通过 peer 认证连接
  2. users 表中查询 root 用户的 encrypted_password 字段(bcrypt 哈希)
  3. 使用 hashcat 进行掩码攻击破解

关键命令

# 1. 以 git 用户身份连接 PostgreSQL(peer 认证)
su git -s /bin/bash -c "/opt/gitlab/embedded/bin/psql -h /var/opt/gitlab/postgresql -U gitlab -d gitlabhq_production"

# 2. 查询 root 用户的密码哈希
su git -s /bin/bash -c "/opt/gitlab/embedded/bin/psql -h /var/opt/gitlab/postgresql -U gitlab -d gitlabhq_production -t -A -c \"SELECT encrypted_password FROM users WHERE username='root';\""

# 3. 将哈希保存到文件
echo '$2a$10$joK1mu.0whkjO8IvGSG/X.wVlUofNpX6hplpT85SQjdp.Dg0l7AKO' > hash.txt

# 4. hashcat 掩码攻击破解(-m 3200 = bcrypt)
hashcat -m 3200 -a 3 hash.txt 'abc?d?d?d?d?d?d'
# -m 3200: bcrypt 哈希模式
# -a 3: mask attack(掩码攻击)
# abc?d?d?d?d?d?d: 以 abc 开头 + 7位数字

# 5. 验证结果
echo 'abc123654' | openssl passwd -1 -salt $(echo '$2a$10$joK1mu.0whkjO8IvGSG/X.wVlUofNpX6hplpT85SQjdp.Dg0l7AKO' | cut -c5-25)

破解过程详解

  • 哈希类型: bcrypt($2a$ 标识)
  • hashcat 模式: 3200(bcrypt)
  • 掩码规则: abc?d?d?d?d?d?d = abc + 7位数字
  • 破解结果: abc123654

知识点

  • GitLab 使用 PostgreSQL peer 认证,必须以系统用户 git 身份连接
  • GitLab 内置的 PostgreSQL 在 /var/opt/gitlab/postgresql/ 目录
  • bcrypt 哈希格式:$2a$<cost>$<salt><hash>
  • hashcat 掩码字符:?d = 数字 0-9, ?l = 小写字母, ?u = 大写字母, ?s = 特殊字符

Q41 - 仓库第一次提交时间(中国时区)

题目: 通过对服务器中的检材一镜像进行分析,该服务器上GitLab服务的仓库中第一次提交的时间是多少?(答案格式:2024-12-24 15:29:38)

答案:2025-11-06 11:07:02

解题思路

  1. GitLab 仓库可能存储在自定义路径(非默认 /var/opt/gitlab)
  2. 通过 git_data_dirs 配置找到实际仓库路径
  3. 使用 git log 查看最早的 commit 时间
  4. 转换为 中国时区

关键命令

# 1. 查找 GitLab 自定义仓库路径
cat /etc/gitlab/gitlab.rb | grep git_data_dirs
# 输出显示仓库在 /opt/gitlab-ce/git-data/

# 2. 找到具体仓库
find /opt/gitlab-ce/git-data/repositories -name '*.git' -type d
# 输出:/opt/gitlab-ce/git-data/repositories/@hashed/4e/07/4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce.git

# 3. 查看最早的 commit
cd /opt/gitlab-ce/git-data/repositories/@hashed/4e/07/4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce.git
git log --reverse --format="%ai %s" | head -1

# 4. 查看完整 commit 信息
git log --reverse -1 --pretty=fuller

知识点

  • GitLab Omnibus 可通过 git_data_dirs 配置自定义仓库路径
  • 默认路径为 /var/opt/gitlab/git-data/repositories/
  • git log --reverse 按时间正序排列(最早的在前)
  • %ai 显示作者日期(ISO 8601 格式,本地时区)

Q42 - root 用户电子邮件

题目: 通过对服务器中的检材一镜像进行分析,该服务器上GitLab服务的root用户的电子邮件是什么?(答案格式:a@b.com

答案:wangdefa888@gmail.com

解题思路

  1. 以 git 用户身份连接 PostgreSQL
  2. 从 users 表查询 root 用户的 email 字段

关键命令

# 查询 root 用户邮箱
su git -s /bin/bash -c "/opt/gitlab/embedded/bin/psql -h /var/opt/gitlab/postgresql -U gitlab -d gitlabhq_production -t -A -c \"SELECT email FROM users WHERE username='root';\""
# 输出:wangdefa888@gmail.com

# 查看所有用户信息(用于其他题目分析)
su git -s /bin/bash -c "/opt/gitlab/embedded/bin/psql -h /var/opt/gitlab/postgresql -U gitlab -d gitlabhq_production -c \"SELECT id, username, name, email, last_sign_in_at FROM users ORDER BY id;\""

知识点

  • -t 参数:只显示数据(无表头)
  • -A 参数:非对齐输出(纯文本格式)
  • GitLab users 表包含完整的用户信息

Q43 - Git仓库被谁下载传播(用户名)

题目: 通过对服务器中的检材一镜像进行分析,该服务器上GitLab服务的仓库是被哪个用户下载传播的?(答案格式:用户名)

答案:Mia

解题思路

这是一个典型的日志关联分析题,需要通过多条线索还原完整的事件时间线:

  1. 第一步:查看所有用户最后登录时间

    • 发现 Mia 是唯一一个在 2025-11-06 新登录的非管理员用户
    • 其他普通用户最后登录时间都是 2025-10-06
  2. 第二步:分析 GitLab Rails 日志

    • 搜索 Projects::RepositoriesController#archive(下载 ZIP 操作)
    • 找到 05:09:47 UTC 的下载记录
  3. 第三步:还原完整时间线

    • root 管理员尝试以 Mia 账号登录 → 密码失败
    • root 在管理员后台重置了 Mia 的密码
    • root 以 Mia 身份登录成功
    • Mia 修改了自己的密码并重新登录
    • Mia 下载了仓库 ZIP 包
  4. 第四步:Nginx 访问日志确认

    • URL: /ccy/oimmanage/-/archive/main/oimmanage-main.zip
    • 文件大小: 56MB,状态码 200

完整事件时间线(UTC)

时间 操作 控制器 状态码
05:08:36-05:08:40 root 尝试以 Mia 账号登录,密码失败 SessionsController#create 0
05:08:44-05:08:48 root 查看 Mia 的用户页面 UsersController#show/activity/calendar 200
05:08:48-05:09:22 root 在管理员后台重置 Mia 密码 Admin::UsersController 302
05:09:28 root 以 Mia 身份登录成功 SessionsController#create 302
05:09:28-05:09:38 Mia 修改自己的密码 UserSettings::PasswordsController 302
05:09:40 Mia 用新密码重新登录 SessionsController#create 302
05:09:47 下载仓库 ZIP 包(关键动作) Projects::RepositoriesController#archive 200

Nginx 日志确认

172.23.194.1 - - [06/Nov/2025:13:09:48 +0800] "GET /ccy/oimmanage/-/archive/main/oimmanage-main.zip HTTP/1.1" 200 56207514

关键命令

# 1. 查看所有用户最后登录时间,定位异常登录
su git -s /bin/bash -c "/opt/gitlab/embedded/bin/psql -h /var/opt/gitlab/postgresql -U gitlab -d gitlabhq_production -t -A -c \"SELECT id, username, name, email, last_sign_in_at FROM users ORDER BY id;\""

# 2. 查看 GitLab Rails 日志,筛选 archive(下载仓库ZIP)操作
zcat /var/log/gitlab/gitlab-rails/production_json.log* | python3 -c "
import sys, json
for line in sys.stdin:
    obj = json.loads(line.strip())
    controller_action = obj.get('controller', '') + '#' + obj.get('action', '')
    if 'archive' in controller_action.lower():
        print(json.dumps(obj, indent=2))
"

# 3. Nginx 访问日志确认下载记录
zcat /var/log/gitlab/nginx/gitlab_access.log* | grep "archive"

# 4. 审计日志确认操作者身份
zcat /var/log/gitlab/gitlab-rails/audit_json.log* | python3 -c "
import sys, json
for line in sys.stdin:
    obj = json.loads(line.strip())
    author = obj.get('author_name', '')
    user = obj.get('meta', {}).get('user', '') if isinstance(obj.get('meta'), dict) else ''
    time = obj.get('time', '')
    print(f'{time} | {author} | {user}')
"

# 5. 查看 SessionsController 相关日志(登录事件)
zcat /var/log/gitlab/gitlab-rails/production_json.log* | python3 -c "
import sys, json
for line in sys.stdin:
    obj = json.loads(line.strip())
    if 'SessionsController' in obj.get('controller', ''):
        print(f\"{obj.get('time')} | {obj.get('controller')}#{obj.get('action')} | status={obj.get('status')}\")
"

# 6. 查看 Admin::UsersController 相关日志(管理员操作)
zcat /var/log/gitlab/gitlab-rails/production_json.log* | python3 -c "
import sys, json
for line in sys.stdin:
    obj = json.loads(line.strip())
    if 'Admin::UsersController' in obj.get('controller', ''):
        print(f\"{obj.get('time')} | {obj.get('controller')}#{obj.get('action')} | status={obj.get('status')}\")
"

知识点

  • Projects::RepositoriesController#archive = GitLab 下载仓库 ZIP
  • Nginx URL 模式:/group/project/-/archive/branch/project-branch.zip
  • 管理员可通过 Admin::UsersController 重置其他用户密码
  • 审计日志(audit_json.log)记录操作者身份
  • 通过 last_sign_in_at 可快速定位异常登录的用户

Q44 - 检材二 git remote 远程服务器地址

题目: 通过对服务器中的检材二镜像进行分析,嫌疑人项目中git配置的远程服务器地址是多少?(答案格式:192.168.1.1)

答案:172.23.194.159

解题思路

  1. 连接检材二服务器(192.168.100.233)
  2. 找到项目仓库 /home/CCY/oim-server-manage/.git/config
  3. 提取 [remote "origin"] 下的 url 字段
  4. 从 URL 中提取 IP 地址部分

关键命令

# 1. 查找所有 Git 仓库
find /home /root -name '.git' -type d 2>/dev/null
# 输出:/home/CCY/oim-server-manage/.git

# 2. 查看 Git 远程配置
cat /home/CCY/oim-server-manage/.git/config

# 输出内容:
# [remote "origin"]
#     url = http://172.23.194.159:9999/ccy/oimmanage
#     fetch = +refs/heads/:refs/remotes/origin/

# 3. 或使用 git 命令直接查看
cd /home/CCY/oim-server-manage && git remote -v
# 输出:
# origin  http://172.23.194.159:9999/ccy/oimmanage (fetch)
# origin  http://172.23.194.159:9999/ccy/oimmanage (push)

# 4. 查看全局 Git 配置(辅助确认)
cat /root/.gitconfig

Git 配置文件内容

[remote "origin"]
    url = http://172.23.194.159:9999/ccy/oimmanage
    fetch = +refs/heads/:refs/remotes/origin/

分析

  • 远程服务器 IP: 172.23.194.159
  • 端口: 9999(与检材一上的 GitLab 实例一致)
  • 仓库路径: /ccy/oimmanage
  • 题目要求答案格式为 IP 地址,只需提取 IP 部分

知识点

  • Git 远程仓库配置在 .git/config[remote "origin"] 段落中
  • url 格式:http(s)://IP:PORT/pathgit@IP:path
  • git remote -v 可查看远程仓库地址(fetch 和 push)

Q45 - OIM 后台管理服务端口

题目: 通过对服务器中的检材二镜像进行分析,嫌疑人后台管理服务的端口是多少?(答案格式:仅数字)

答案:13000

解题思路

  1. 在检材二服务器上找到项目路径 /home/CCY/oim-server-manage/
  2. 该项目是一个 Java Spring Boot 应用(oim-server-manage-server.jar)
  3. Spring Boot 的服务端口配置在 application.yml 文件中
  4. 读取配置文件,找到 server.port 的值

关键命令

# 1. 查看 Spring Boot 主配置文件
cat /home/CCY/oim-server-manage/config/application.yml

# 2. 提取端口配置
cat /home/CCY/oim-server-manage/config/application.yml | grep -A 1 'server:'
# 输出:
# server:
#   port: 13000

# 3. 查看启动脚本确认运行方式
cat /home/CCY/oim-server-manage/startup.sh

# 4. 查看当前监听端口(辅助验证)
ss -tlnp
# 输出中应包含 13000 端口的监听信息

关键配置内容

server:
  port: 13000

spring:
  datasource:
    url: jdbc:mysql://172.23.194.157:3307/CCY?useUnicode=true&characterEncoding=utf8
    username: root
    password: 123789
  redis:
    host: 172.23.194.157
    port: 6379

系统架构分析

  • 应用类型: OIM(Online IM)后台管理服务
  • 技术栈: Spring Boot + MyBatis-Plus + Redis + MySQL
  • 数据库: MySQL(172.23.194.157:3307)
  • 缓存: Redis(172.23.194.157:6379)

知识点

  • Spring Boot 项目的服务端口配置在 application.ymlapplication.properties 中的 server.port 字段
  • 配置文件可能位于项目目录的 config/ 子目录下
  • 也可通过 unzip -p app.jar BOOT-INF/classes/application.yml 从 JAR 包中提取

Q46 - Spring Boot 框架版本

题目: 通过对服务器中的检材二镜像进行分析,嫌疑人后台管理服务使用的spring boot框架版本是多少?(答案格式:1.0.0)

答案:1.5.8

解题思路

  1. 检材二上的项目是一个 Spring Boot 应用
  2. 项目中没有 pom.xml 或 build.gradle(只有打包后的 JAR 文件)
  3. 通过 unzip -l 查看 JAR 包内部结构
  4. BOOT-INF/lib/ 目录下找到 Spring Boot 相关依赖
  5. 从依赖文件名中提取版本号

关键命令

# 1. 查找项目构建文件(确认无 pom.xml / build.gradle)
find /home/CCY/oim-server-manage -name 'pom.xml' -o -name 'build.gradle'
# 输出为空,说明只有打包后的 JAR

# 2. 解压查看 JAR 包内部结构,获取 Spring Boot 版本
unzip -l /home/CCY/oim-server-manage/oim-server-manage-server.jar | grep 'spring-boot'

# 输出:
# BOOT-INF/lib/spring-boot-1.5.8.RELEASE.jar
# BOOT-INF/lib/spring-boot-autoconfigure-1.5.8.RELEASE.jar
# BOOT-INF/lib/spring-boot-starter-web-1.5.8.RELEASE.jar
# BOOT-INF/lib/spring-boot-starter-1.5.8.RELEASE.jar
# ...

# 3. 如果系统有 jar 命令也可以用
jar tf /home/CCY/oim-server-manage/oim-server-manage-server.jar | grep 'spring-boot'

# 4. 从 JAR 包中提取嵌入的 application.yml(辅助确认)
unzip -p /home/CCY/oim-server-manage/oim-server-manage-server.jar BOOT-INF/classes/application.yml

关键输出

BOOT-INF/lib/spring-boot-1.5.8.RELEASE.jar
BOOT-INF/lib/spring-boot-autoconfigure-1.5.8.RELEASE.jar
BOOT-INF/lib/spring-boot-starter-web-1.5.8.RELEASE.jar

所有 Spring Boot 相关依赖版本一致:1.5.8.RELEASE

知识点

  • Spring Boot 打包后的 JAR 是可执行 JAR(fat JAR),内部结构包含 BOOT-INF/lib/ 目录存放所有依赖
  • 通过 unzip -ljar tf 可以列出 JAR 包内容,无需实际解压
  • 依赖 JAR 文件名中直接包含版本号,如 spring-boot-1.5.8.RELEASE.jar
  • 1.5.8.RELEASE 是 Spring Boot 1.x 系列的稳定版本,发布于 2017 年
  • 答案格式要求为 1.0.0,所以提取为 1.5.8

Q47 - OIM 后台管理系统登录时查询的数据库表

题目: 通过对服务器中的检材二镜像进行分析,嫌疑人后台管理系统在登录时,查询的数据库表名是什么?(答案格式:表名)

答案:im_user

解题思路

  1. 修改 Docker MySQL root 密码为 123789,导入 CCY.sql 数据
  2. 开启 MySQL general_log 记录所有 SQL 查询
  3. 从前端 JS 代码中提取登录 API 路径和参数格式
  4. 启动 jar 包(端口 13000)和 nginx-admin(端口 8000)
  5. 使用 curl 模拟登录请求
  6. 查看 general_log,捕获登录时执行的 SQL

前端 JS 分析

// 从 main.a49ed8c4e67805ee7bba.js 提取的登录函数
i.login = function(e, t, n) {
  var r = {account: e, password: t}, i = {body: r};
  o.default.post("/manage/index/login", i, n)
}

// 密码加密(MD5)
// pages-login-Login.ac749ee0.js
MD5(password)  // 发送前进行 MD5 加密

关键命令

# 1. 开启 MySQL general_log
docker exec mysql_master mysql -u root -p123789 -e "
SET GLOBAL general_log = 'ON';
SET GLOBAL general_log_file = '/var/lib/mysql/general.log';
SET GLOBAL log_output = 'FILE';
"

# 2. 清空日志(可选)
docker exec mysql_master truncate /var/lib/mysql/general.log

# 3. 模拟登录请求
curl -s -X POST http://localhost:13000/manage/index/login \
  -H "Content-Type: application/json" \
  -d '{"body":{"account":"admin","password":"123456"}}'

# 4. 查看 general_log 捕获的 SQL
docker exec mysql_master cat /var/lib/mysql/general.log | grep "Query"

# 输出:
# Query   select * from im_user where account='admin'

捕获的 SQL

select * from im_user where account='admin'

补充发现

  • im_user 表中无 admin 账号
  • 实际存在的后台管理账号包括:CG07, sw23914, BY24, DYY34 等
  • 登录逻辑要求 type 字段必须为 "1""2",数据库中所有用户 type 均为 "0"
  • 前端 MD5 加密密码后发送,后端直接比较 MD5 哈希值

知识点

  • MySQL general_log 可记录所有执行的 SQL 查询
  • 通过模拟 API 请求 + general_log 是发现后端数据库操作的有效方法
  • 前端 JS 逆向可提取 API 路径和参数格式
  • Spring Boot + MyBatis-Plus 项目的登录通常查询用户表

Q48 - 后台管理系统中删除的用户数量

题目: 通过对服务器中的检材二镜像进行分析,嫌疑人后台管理系统中删除的用户数量是多少?(答案格式:仅数字)

答案:510

解题思路

  1. 查询 im_user 表,发现 flag 字段有两种值:01
  2. flag='0' 的用户有 510
  3. flag='1' 的用户有 6879 个
  4. 所有 flag='0' 用户的 cancelTime 字段均为 NULL,说明是通过逻辑删除标记的
  5. 结论:flag='0' = 已删除用户

关键命令

# 1. 查询 flag 字段分布
docker exec mysql_master mysql -u root -p123789 CCY --default-character-set=utf8mb4 -e "
SELECT flag, COUNT(*) as count FROM im_user GROUP BY flag;
"
# 输出:
# +------+-------+
# | flag | count |
# +------+-------+
# | 0    |   510 |
# | 1    |  6879 |
# +------+-------+

# 2. 查询已删除用户数量(flag='0')
docker exec mysql_master mysql -u root -p123789 CCY --default-character-set=utf8mb4 -e "
SELECT COUNT(*) FROM im_user WHERE flag='0';
"
# 输出:510

# 3. 验证 cancelTime 字段
docker exec mysql_master mysql -u root -p123789 CCY --default-character-set=utf8mb4 -e "
SELECT flag, cancelTime, COUNT(*) FROM im_user WHERE flag='0' GROUP BY cancelTime;
"
# 所有 flag='0' 用户的 cancelTime 均为 NULL

知识点

  • flag 字段为逻辑删除标记:'0'=已删除,'1'=正常
  • 逻辑删除不物理删除记录,便于数据恢复和审计
  • MySQL 查询含中文字段需加 --default-character-set=utf8mb4

Q49 - xinchao 参与管理的最早的群名

题目: 通过对服务器中的检材二镜像进行分析,用户xinchao参与管理的最早的群名称是什么?(答案格式:群名称)

答案:测试

解题思路

  1. im_user 表中找到用户 xinchao,获取其 userId
  2. im_group_member 表中查询该用户的管理记录(position IN ('1','2'))
  3. 关联 im_group 表获取群名
  4. 按 createTime 升序排序,取最早的群

关键命令

# 1. 查找 xinchao 用户
docker exec mysql_master mysql -u root -p123789 CCY --default-character-set=utf8mb4 -e "
SELECT id, account, name FROM im_user WHERE name='xinchao';
"
# 输出:
# +--------------------------------------+---------+---------+
# | id                                   | account | name    |
# +--------------------------------------+---------+---------+
# | cb7e10f9-6e29-464a-aa96-748ef4fb9e7b | wwwww5  | xinchao |
# +--------------------------------------+---------+---------+

# 2. 查找参与管理的最早的群
docker exec mysql_master mysql -u root -p123789 CCY --default-character-set=utf8mb4 -e "
SELECT g.name as group_name, g.createTime, gm.position 
FROM im_group_member gm 
JOIN im_group g ON gm.groupId = g.id 
WHERE gm.userId = 'cb7e10f9-6e29-464a-aa96-748ef4fb9e7b' 
AND gm.position IN ('1','2') 
ORDER BY g.createTime ASC;
"
# 输出:
# +------------+---------------------+----------+
# | group_name | createTime          | position |
# +------------+---------------------+----------+
# | 测试       | 2021-10-18 10:06:34 | 2        |
# | ...        | ...                 | ...      |
# +------------+---------------------+----------+

# 最早的群为"测试",xinchao 在其中为管理员(position=2)

知识点

  • im_group_member.position 字段含义:1=群主,2=管理员,3=普通成员
  • "参与管理" = position IN ('1','2')
  • JOIN 查询关联两个表获取完整信息
  • ORDER BY createTime ASC 按时间升序排列

Q50 - Elasticsearch 索引中群消息数量

题目: 通过对服务器中的检材二镜像进行分析,该服务器上elasticsearch存储的群消息数量是多少?(答案格式:仅数字)

答案:3

解题思路

  1. 找到 ES 容器 adoring_fermat(elasticsearch:7.9.3),已退出状态
  2. xpack.security.enabled=true,环境变量凭据为 ccyQo:ccy#@132
  3. 但 basic license 下 native realm security index 不可用,认证失败
  4. 数据目录位于 /root/elasticsearch-data
  5. 启动无认证 ES 容器挂载同一数据目录到端口 9201
  6. 查询 _cat/indices 发现 3 个索引,其中 ccy_group_message_index 有 3 条群消息

关键命令

# 1. 查看 ES 容器信息
docker ps -a | grep elastic
docker inspect adoring_fermat

# 2. 查看环境变量(凭据)
docker inspect adoring_fermat | grep -A 50 Env
# ELASTICSEARCH_USERNAME=ccyQo
# ELASTICSEARCH_PASSWORD=ccy#@132

# 3. 启动无认证 ES 挂载原数据目录
docker run -d --name es_noauth -p 9201:9200 \
  -e "discovery.type=single-node" \
  -e "xpack.security.enabled=false" \
  -v /root/elasticsearch-data:/usr/share/elasticsearch/data \
  docker.elastic.co/elasticsearch/elasticsearch:7.9.3

# 4. 等待 ES 启动完成
sleep 10

# 5. 查看所有索引
curl -s http://localhost:9201/_cat/indices?v
# 输出:
# health status index                          docs.count
# yellow open   ccy_user_message_index         10
# yellow open   ccy_group_message_index        3
# yellow open   ccy_offline_message_index      12

# 6. 查询群消息数量
curl -s http://localhost:9201/ccy_group_message_index/_count
# 输出:{"count":3}

# 7. 查看群消息详情(验证)
curl -s http://localhost:9201/ccy_group_message_index/_search?pretty

ES 索引结构

索引名称 文档数量 说明
ccy_user_message_index 10 用户私聊消息
ccy_group_message_index 3 群消息(本题答案)
ccy_offline_message_index 12 离线消息

知识点

  • ES basic license 下 native realm security 不可用,需 bypass
  • 通过挂载数据目录到无认证容器可绕过 xpack security
  • _cat/indices 查看所有索引及其文档数量
  • /_count API 快速获取索引中文档总数

Q51 - 用户"小雨末"的手机号

题目: 通过对服务器中的检材二镜像进行分析,用户"小雨末"的手机号码是多少?(答案格式:手机号)

答案:14499466022

解题思路

  1. 直接在 im_user 表的 name/nickname/account 字段搜索"小雨末" → 结果为空
  2. 该用户的名字字段存储为 "YM",而非"小雨末"
  3. im_user_add_request(好友申请表)中搜索 requestUserName LIKE '%小雨末%'
  4. 找到 requestUserId = 76d866ae418946cba6eec6bc423613ed
  5. 用该 user id 回到 im_user 表查询手机号
  6. 得到 mobile = +861****6022,去掉国家码前缀 +86

关键命令

# 1. 在 im_user 表中直接搜索(结果为空)
docker exec mysql_master mysql -u root -p123789 CCY --default-character-set=utf8mb4 -e "
SELECT id, account, name, nickname FROM im_user 
WHERE name LIKE '%小雨末%' OR nickname LIKE '%小雨末%' OR account LIKE '%小雨末%';
"

# 2. 在好友申请表中搜索"小雨末"
docker exec mysql_master mysql -u root -p123789 CCY --default-character-set=utf8mb4 -e "
SELECT requestUserId, requestUserName FROM im_user_add_request 
WHERE requestUserName LIKE '%小雨末%';
"
# 输出:
# +--------------------------------------+---------------+
# | requestUserId                        | requestUserName |
# +--------------------------------------+---------------+
# | 76d866ae418946cba6eec6bc423613ed    | 小雨末         |
# +--------------------------------------+---------------+

# 3. 根据 user id 回到 im_user 查手机号
docker exec mysql_master mysql -u root -p123789 CCY --default-character-set=utf8mb4 -e "
SELECT id, account, name, nickname, mobile FROM im_user 
WHERE id = '76d866ae418946cba6eec6bc423613ed';
"
# 输出:
# +--------------------------------------+---------+------+------+--------------+
# | id                                   | account | name | nickname | mobile     |
# +--------------------------------------+---------+------+------+--------------+
# | 76d866ae418946cba6eec6bc423613ed    | ...     | YM   | ...    | +861****6022|
# +--------------------------------------+---------+------+------+--------------+

# 4. 去掉 +86 前缀,得到最终答案:14499466022

知识点

  • im_user_add_request 表中存储了好友申请时的用户昵称
  • 昵称可能与 im_user.name 字段不一致
  • mobile 字段包含国家码前缀 +86,答案需去掉前缀
  • 跨表关联查询是常见的数据取证技巧

Q52 - 贷款利息

题目: 通过对服务器中的检材二镜像进行分析,该系统存储的贷款利息是多少?(答案格式:仅数字)

答案:0.8

解题思路

  1. im_system_group_notice(系统群公告)表中搜索与"贷款"、"利息"相关的公告
  2. 找到公告内容:"!!!【!!!重要通知!!!】所有群交单统一贷款利息为0.8%,所有人切记!!!"
  3. 提取利息数值:0.8

关键命令

# 在系统群公告表中搜索贷款利息相关信息
docker exec mysql_master mysql -u root -p123789 CCY --default-character-set=utf8mb4 -e "
SELECT * FROM im_system_group_notice 
WHERE notice LIKE '%贷款%' OR notice LIKE '%利息%';
"

# 输出:
# +----+--------------------------------------------------------------------------+
# | id | notice                                                                   |
# +----+--------------------------------------------------------------------------+
# | 1  | !!!【!!!重要通知!!!】所有群交单统一贷款利息为0.8%,所有人切记!!! |
# +----+--------------------------------------------------------------------------+

# 答案:0.8

知识点

  • im_system_group_notice 表存储系统级公告
  • LIKE 模糊查询可用于搜索关键词
  • 中文内容查询需加 --default-character-set=utf8mb4

三、关键中间过程

Git Stash 中恢复 JAR 包

背景: 检材二上的 JAR 包是旧版本,需要从 git stash 中恢复正确的 JAR 包。

发现过程

  1. 检材二 /home/CCY/oim-server-manage.git 仓库
  2. git stash list 为空(stash ref 已被删除)
  3. 遍历未打包的 loose objects,发现两个额外 commit:
    • 8f5fcabc — stash index commit: "index on dev: c21d6ee 'dev'"
    • 617655b9 — stash merge commit: "On dev: oim-server-manage-server.jar"(双 parent)
  4. stash merge commit 的 tree 中包含正确的 JAR 包,blob hash 为 17b2de3380d238709558af466d701cdc724b8a0c

关键命令

cd /home/CCY/oim-server-manage

# 1. 查找所有 loose objects(不在 pack 文件中的对象)
find .git/objects -type f ! -path '*/pack/*' ! -path '*/info/*'

# 2. 检查每个 object 的类型
for obj in $(find .git/objects -type f ! -path '*/pack/*' ! -path '*/info/*' | sed 's|.git/objects/||;s|/||'); do
    echo "$obj: $(git cat-file -t $obj)"
done

# 3. 查看 stash index commit
git cat-file -p 8f5fcabc
# 输出:
# tree ...
# parent c21d6ee...
# author ...
# committer ...
# 
# index on dev: c21d6ee 'dev'

# 4. 查看 stash merge commit
git cat-file -p 617655b9
# 输出:
# tree 415c698a...
# parent c21d6ee... (HEAD)
# parent 8f5fcabc... (index)
# author ...
# committer ...
# 
# On dev: oim-server-manage-server.jar

# 5. 查看 tree 内容
git cat-file -p 415c698a
# 输出包含:
# 100644 blob 17b2de3380d238709558af466d701cdc724b8a0c    oim-server-manage-server.jar

# 6. 提取 JAR 包
git show 17b2de3380d238709558af466d701cdc724b8a0c > oim-server-manage-server.jar

# 7. 验证文件大小和格式
ls -lh oim-server-manage-server.jar  # 62MB
file oim-server-manage-server.jar    # Zip archive data

Git Stash 结构说明

stash merge commit (双 parent)
├── parent 1: HEAD commit (c21d6ee 'dev')
└── parent 2: index commit (8f5fcabc)
    └── parent: HEAD commit (c21d6ee)
  • Index commit: 保存当前的 index 状态
  • Merge commit: 包含实际的文件变更(JAR 包)
  • 即使 git stash drop 或删除 ref,commit 仍作为 loose objects 存在

Nginx Web 容器配置与登录分析

容器信息

  • 名称: nginx-web
  • 镜像: nginx:latest
  • 端口映射: 80 -> 80, 443 -> 443
  • 状态: 可通过 docker start nginx-web 启动

Nginx 配置(/etc/nginx/conf.d/default.conf)

server {
    listen 80;
    
    # 静态文件目录
    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
    
    # 代理到后端 Spring Boot 服务
    location /manage-server/ {
        proxy_pass http://127.0.0.1:13000/;
    }
    
    # 应用协议接口
    location /app-protocol/ {
        root /tmp/;
    }
}

登录 API 分析

项目
API 路径 /manage/index/login(nginx 代理路径 /manage-server/manage/index/login
请求方法 POST
Content-Type application/json
请求体格式 {"body":{"account":"用户名","password":"MD5(密码)"}}

关键发现

  1. 前端 MD5 加密: 前端 JS 代码显示密码在发送前会进行 MD5() 加密
  2. 后端直接比较 MD5: 反编译 IndexService.class 发现后端直接将传入的 password 与数据库中存储的 MD5 哈希值进行比较
  3. 用户类型限制: 登录逻辑要求 type 字段必须为 "1""2",否则返回"账号不存在!"。数据库中所有用户 type 均为 "0"
  4. 数据库连接: jar 包内 application.yml 配置的数据库为 CCY

干扰项排查(Docker MySQL ry 库)

现象

  • Docker MySQL 容器中只包含 ry 数据库
  • 但 application.yml 指向 CCY 数据库

排查过程

# 1. 查看 Docker MySQL 中的数据库
docker exec mysql_master mysql -u root -p123789 -e "SHOW DATABASES;"
# 输出:information_schema, mysql, performance_schema, ry

# 2. 查看 application.yml 中的数据库配置
cat /home/CCY/oim-server-manage/config/application.yml | grep -A 5 'datasource:'
# 输出:url: jdbc:mysql://172.23.194.157:3307/CCY

# 3. 结论:ry 是干扰项,实际使用 CCY 数据库
# 4. 需要导入 CCY.sql 创建 CCY 数据库
docker exec mysql_master mysql -u root -p123789 -e "CREATE DATABASE IF NOT EXISTS CCY;"
docker exec -i mysql_master mysql -u root -p123789 CCY --default-character-set=utf8mb4 < CCY.sql

知识点

  • Docker 中的 ry 数据库是干扰项
  • 始终以 application.yml 配置为准
  • 需要手动导入 SQL 文件创建正确的数据库

四、数据库关键表与字段规则

im_user 表(用户信息)

字段 类型 说明
id VARCHAR(UUID) 用户唯一标识
account VARCHAR 登录账号
name VARCHAR 姓名/名称(可能与 nickname 不同)
nickname VARCHAR 昵称
mobile VARCHAR 手机号(含 +86 前缀)
flag CHAR(1) '0'=已删除, '1'=正常
type CHAR(1) '1'或'2'可登录后台, '0'为普通用户
password VARCHAR MD5哈希值
cancelTime DATETIME 取消时间(逻辑删除时可能为 NULL)

im_group_member 表(群成员)

字段 类型 说明
groupId VARCHAR 群 ID
userId VARCHAR 用户 ID
position INT 1=群主, 2=管理员, 3=普通成员

"参与管理" = position IN ('1','2')

im_user_add_request 表(好友申请)

字段 类型 说明
requestUserId VARCHAR 发送者用户 ID
requestUserName VARCHAR 发送者昵称(可能与 im_user.name 不一致)

im_system_group_notice 表(系统群公告)

字段 类型 说明
id INT 公告 ID
notice TEXT 公告内容

Elasticsearch 索引

索引名称 说明 文档数量
ccy_user_message_index 用户私聊消息 10
ccy_group_message_index 群消息 3
ccy_offline_message_index 离线消息 12

ES xpack security bypass: 启动无认证容器挂载原数据目录 /root/elasticsearch-data


五、常用命令模板

MySQL 查询(中文需加编码参数)

# 标准查询格式
docker exec mysql_master mysql -u root -p123789 CCY --default-character-set=utf8mb4 -e "SQL语句"

# 示例:查询用户信息
docker exec mysql_master mysql -u root -p123789 CCY --default-character-set=utf8mb4 -e "
SELECT id, account, name, nickname, mobile FROM im_user WHERE name='xinchao';
"

JAR 包内嵌配置提取

# 从 JAR 包中提取 application.yml
unzip -p oim-server-manage-server.jar BOOT-INF/classes/application.yml

# 查看 JAR 包依赖(获取版本信息)
unzip -l oim-server-manage-server.jar | grep 'spring-boot'

Git loose objects 遍历(恢复被删除的 stash)

# 查找所有 loose objects
find .git/objects -type f ! -path '*/pack/*' ! -path '*/info/*'

# 检查 object 类型
git cat-file -t <hash>  # 返回: commit, tree, blob, tag

# 查看 object 内容
git cat-file -p <hash>

# 从 blob 提取文件
git show <blob_hash> > output.jar

GitLab 日志分析

# Rails JSON 日志解析(Python)
zcat /var/log/gitlab/gitlab-rails/production_json.log* | python3 -c "
import sys, json
for line in sys.stdin:
    obj = json.loads(line.strip())
    # 筛选条件
    if 'keyword' in str(obj).lower():
        print(json.dumps(obj, indent=2))
"

# Nginx 访问日志
zcat /var/log/gitlab/nginx/gitlab_access.log* | grep "keyword"

PostgreSQL 连接(GitLab peer 认证)

su git -s /bin/bash -c "/opt/gitlab/embedded/bin/psql -h /var/opt/gitlab/postgresql -U gitlab -d gitlabhq_production -t -A -c \"SQL语句\""

六、经验总结

关键知识点回顾

  1. GitLab 数据库连接: PostgreSQL peer 认证,必须以系统用户 git 身份连接
  2. bcrypt 密码破解: hashcat -m 3200,支持掩码攻击(-a 3)
  3. OIM 系统架构: Spring Boot + MyBatis-Plus + Redis + MySQL
  4. 干扰项识别: Docker 中的 ry 数据库是干扰项,jar 包实际连接 CCY 数据库
  5. 时区转换: UTC + 8h = 中国时区(北京时间)
  6. Git Stash 取证: git stash drop 后 commit 仍作为 loose objects 存在于 .git/objects/ 中,可通过遍历未打包 objects 恢复
  7. GitLab 自定义仓库路径: 通过 git_data_dirs 配置在 /opt/gitlab-ce/git-data,非默认 /var/opt/gitlab
  8. OIM 登录表为 im_user 通过 MySQL general_log 捕获实际 SQL
  9. Docker MySQL root 密码修改: ALTER USER 'root'@'localhost' IDENTIFIED BY 'newpass'; FLUSH PRIVILEGES;
  10. CCY.sql 导入: 先创建数据库,然后导入 SQL 文件
  11. GitLab 仓库下载取证: Projects::RepositoriesController#archive = 下载仓库ZIP
  12. GitLab 管理员密码重置: root 可通过 Admin::UsersController 重置其他用户密码

常见陷阱

陷阱 说明
时区问题 日志中的时间多为 UTC,需 +8h 转换为 中国时区
干扰数据库 Docker 中可能有多个数据库,需以 application.yml 为准
中文编码 MySQL 查询含中文字段必须加 --default-character-set=utf8mb4
手机号格式 im_user.mobile 含 +86 前缀,答案需去掉前缀
自定义路径 GitLab 仓库可能在非默认路径,需检查 git_data_dirs 配置
ES 认证 bypass basic license 下 native realm 不可用,需挂载数据到无认证容器

取证技巧总结

  1. 日志关联分析: 结合多条日志(Rails + Nginx + Audit)还原事件时间线
  2. general_log 捕获 SQL: 通过模拟 API 请求 + MySQL general_log 发现后端数据库操作
  3. 跨表关联查询: 当直接搜索为空时,尝试在其他相关表中查找线索
  4. Git 对象遍历: loose objects 是恢复被删除数据的有效途径
  5. JAR 包分析: fat JAR 可通过 unzip/jar 命令查看内部结构和依赖版本

附录:答案汇总

题目 问题简述 答案
Q36 systemd 开机自启服务数量 34
Q37 系统内核编译时间(中国时区) 2024-06-04 22:43:51
Q38 GitLab 服务端口 9999
Q39 GitLab 登录失败次数 6
Q40 GitLab root 用户密码 abc123654
Q41 仓库第一次提交时间(中国时区) 2025-11-06 11:07:02
Q42 root 用户电子邮件 wangdefa888@gmail.com
Q43 Git仓库被谁下载传播(用户名) Mia
Q44 git remote 远程服务器地址 172.23.194.159
Q45 OIM 后台管理服务端口 13000
Q46 Spring Boot 框架版本 1.5.8
Q47 登录时查询的数据库表 im_user
Q48 删除的用户数量 510
Q49 xinchao 最早管理的群名 测试
Q50 ES 群消息数量 3
Q51 "小雨末" 手机号 14499466022
Q52 贷款利息 0.8

报告生成时间:2026年5月16日 工具支持:Hermes Agent