2024数证杯预选赛 2024 团队赛

2024数证杯预选赛服务器题hermes

围绕 2024数证杯预选赛 的公开复盘与解题记录。

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

2024数证杯预选赛 — server1-server2 服务器取证解题全过程

环境信息

服务器 角色 IP:端口 用户/密码
server1 站点服务器 192.168.100.118:22 root/123456
server2 数据库服务器 192.168.100.119:22 root/123456

server1 环境概览

  • OS: Alibaba Cloud Linux 3 (Soaring Falcon)
  • Java Spring Boot 应用: /data/cal-0.0.1-SNAPSHOT.jar
  • Active profile: sxj, 配置文件 application-sxj.yaml
  • 域名: sxj.elitzoe.cn, sx.tiantianqb.com
  • APP名称: 顺心借 (网贷/借款平台)
  • 组件: Redis Sentinel + RabbitMQ + Consul 服务发现 + Nginx
  • Nginx: 端口82(前端静态), 8082(Java后端), 443(HTTPS→8082)

server2 环境概览

  • Docker MySQL 8.0.39, 容器名 mysql8.0.39
  • docker compose v2.27.1
  • 数据库: sxj_prod, 用户 sxy/Sxy000**

Q1 — 站点服务器从哪个云服务平台上调证过来的?

答案

阿里云

解题思路

通过多个维度确认为阿里云ECS实例:

  1. 主机名格式: iZbp1gma2uf9hvsnbu9mdkZ — 这是阿里云ECS实例的标准命名格式(以 iZ 开头)
  2. 内核版本: Linux 5.10.134-12.2.al8.x86_64 — al8 表示 Alibaba Linux 8 系列内核
  3. 阿里云安骑士: /usr/local/aegis/ 目录存在 (aegis_client, AliSecCheck, globalcfg)
  4. cloud-init配置: datasource_list: [ AliYun ], distro: aliyun
  5. 操作系统: NAME="Alibaba Cloud Linux" VERSION="3 (Soaring Falcon)"
  6. 阿里云服务: /usr/sbin/aliyun-service, /usr/sbin/aliyun_installer 存在

重点命令

# server1
hostname && cat /etc/hostname && uname -a
ls -la /usr/local/aegis/
cat /etc/os-release | head -5
cat /etc/cloud/cloud.cfg | grep -i "platform\|vendor\|aliyun"

Q2 — 站点服务器中数据库的密码是?

答案

Sxy000****

解题思路

应用为 Java Spring Boot,数据库配置在 application-sxj.yaml 文件中。通过解压 JAR 包读取配置文件获取数据库连接信息。

重点命令

# server1 - 解压JAR读取配置文件
unzip -p /data/cal-0.0.1-SNAPSHOT.jar BOOT-INF/classes/application-sxj.yaml

配置内容包含:

  • url: jdbc:mysql://rm-bp18td28bsh13f5jy.mysql.rds.aliyuncs.com:***@2024#
  • username: sxy
  • password: Sxy000**

Q3 — 站点服务器用于提供服务发现的工具名是?

答案

consul

解题思路

在 /data/ 目录下发现 consul 数据目录,包含标准 Consul 组件:

  • raft/ — Raft共识算法数据
  • serf/ — Serf服务编排
  • services/ — 服务注册信息
  • node-id — 节点标识
  • checkpoint-signature — 检查点签名

重点命令

# server1
ls -la /data/consul/

Q4 — 站点服务器数据库配置文件名是?

答案

application-sxj.yaml

解题思路

Spring Boot 多 profile 架构:

  1. application.yaml 设置 spring.profiles.active: 'sxj'
  2. 实际数据库连接配置在 application-sxj.yaml 中
  3. JAR 内共15个profile配置文件(axj, dev, jdd, liu, mxh, ryj, ssh, sxj, test, xfm, xpt等),实际生效的是 sxj
  4. jar.sh 启动命令为 java -jar cal-0.0.1-SNAPSHOT.jar,无 -Dspring.profiles.active 覆盖参数
  5. JAR 内无 database.php 文件(题目格式示例仅为PHP参考)

重点命令

# server1 - 列出所有配置文件
unzip -l /data/cal-0.0.1-SNAPSHOT.jar | grep -E "\.yaml|\.yml|\.properties"

# 确认 active profile
unzip -p /data/cal-0.0.1-SNAPSHOT.jar BOOT-INF/classes/application.yaml

# 确认启动参数无覆盖
cat /data/jar.sh

# 验证datasource配置在sxj文件中
unzip -p /data/cal-0.0.1-SNAPSHOT.jar BOOT-INF/classes/application-sxj.yaml | grep -A5 datasource

Q5 — 该网站涉及的APP名称是?

答案

顺心借

解题思路

  1. 配置文件确认: application-sxj.yaml 中 app.name: 顺心借
  2. 浏览器验证: 访问 http://192.168.100.118:82/#/login,页面 h1 标题明确显示"顺心借"
  3. 密码登录验证: qc用户(19883213882/123456) POST /admin/index/loginByPassword → token有效
  4. 页面导航栏:后台管理 | 财务审核 | 数据报表

重点命令

# 浏览器访问登录页 http://192.168.100.118:82/#/login 验证标题

# API密码登录验证
curl -X POST http://localhost:8082/admin/index/loginByPassword \
  -H 'Content-Type: application/json' \
  -d '{"mobile":"19883213882","password":"123456"}'

Q6 — 存储大量身份证照的OSS中的AccessKeyID后八位是?

答案

EuZJybzD

解题思路

从 application-sxj.yaml 中获取 OSS 配置:

  • accessKeyId: LTAI5tBt7rHreKjDEuZJybzD (共24字符)
  • 取后8位: EuZJybzD

重点命令

# 从配置文件提取
unzip -p /data/cal-0.0.1-SNAPSHOT.jar BOOT-INF/classes/application-sxj.yaml | grep -i accesskey

Q7 — 站点服务器用于消息转发代理工具所使用的端口号是?

答案

5672

解题思路

application-sxj.yaml 中配置了 RabbitMQ,端口为 5672。通过 netstat 确认端口正在监听。

重点命令

# server1 - 确认RabbitMQ端口
netstat -tlnp | grep 5672

Q8 — 站点服务器用于启动定时任务的代码片段存在于?

答案

MobileStatusTask.class

解题思路

  1. JAR包中搜索 Task 相关类,发现 com/rh/cal/task/MobileStatusTask.class
  2. javap 反编译确认:
    • @Scheduled(cron = "0 * * * * ?") — Spring 定时任务注解
    • 常量池确认 Lorg/springframework/scheduling/annotation/Scheduled;Lorg/springframework/stereotype/Component;
    • scheduled() 方法日志输出"执行定时任务"+时间戳,调用 userService.uptInMobileStatus()
    • 有 try-catch 异常处理

重点命令

# server1
cd /tmp && unzip -l /data/cal-0.0.1-SNAPSHOT.jar | grep -i "task\|Task"
unzip -o /data/cal-0.0.1-SNAPSHOT.jar BOOT-INF/classes/com/rh/cal/task/MobileStatusTask.class
javap -p MobileStatusTask.class
javap -c -p MobileStatusTask.class
javap -v MobileStatusTask.class | grep -i "scheduled\|cron"

Q9 — 站点服务器用于验证用户输入的验证码是否匹配的代码片段存在于?

答案

AdminIndexConller.class

解题思路

  1. JAR包中搜索 Index 相关类,发现 AdminIndexConller.class(注意拼写是 Conller 不是 Controller)
  2. javap 反编译 login() 方法完整确认验证码匹配逻辑:
    • stringRedisTemplate.opsForValue().get(ADMIN_PHONE_CODE + dto.getMobile()) — 从Redis获取验证码
    • dto.getCode().equals(redisCode) — 用户输入与Redis验证码对比
    • AssertUtil.isTrue(..., "验证码错误") — 不匹配则抛"验证码错误"
  3. 额外发现:main方法包含 DigestUtil.md5Hex("admin+123") — 密码加密测试代码

重点命令

# server1
cd /tmp && unzip -l /data/cal-0.0.1-SNAPSHOT.jar | grep "Index"
unzip -o /data/cal-0.0.1-SNAPSHOT.jar BOOT-INF/classes/com/rh/cal/controller/admin/AdminIndexConller.class
javap -p AdminIndexConller.class
javap -c -p AdminIndexConller.class

Q10 — 数据库服务器中Docker容器镜像中mysql的镜像ID号前6位是?

答案

23b013

解题思路

在 server2 上查看 Docker 镜像列表,mysql:8.0.39 镜像 ID 为 23b013c7c67d,取前6位。

重点命令

# server2
docker images

Q11 — 数据库服务器中DockerCompose的版本号是?

答案

2.27.1

解题思路

直接查询 docker compose 版本。

重点命令

# server2
docker compose version  # v2.27.1

Q12 — 数据库服务器中用于存储后台登录账号的数据表名是?

答案

sys_user

解题思路

首先通过 hosts 文件发现 RDS 域名指向 server2 (192.168.100.119),然后通过 server2 的 MySQL 容器连接数据库,查看 sxj_prod 库中所有表,确认 sys_user 为后台管理员登录账号表。

重点命令

# 发现RDS域名映射
grep "rds.aliyuncs" /etc/hosts
# 输出: 192.168.100.119 rm-bp18td28bsh13f5jy.mysql.rds.aliyuncs.com

# server2 - 连接数据库查看表
docker exec mysql8.0.39 mysql -h 192.168.100.119 -P 3306 -u sxy -p'Sxy000**' sxj_prod -e "SHOW TABLES;"

# 查看表结构
docker exec mysql8.0.39 mysql -h 192.168.100.119 -P 3306 -u sxy -p'Sxy000**' sxj_prod -e "DESC sys_user;"

Q13 — 后台管理员"xpt-0"所绑定的手机号码是?

答案

19521510863

解题思路

从 sys_user 表中查询 name='xpt-0' 的记录,获取 phone 字段。

重点命令

# server2
docker exec mysql8.0.39 mysql -h 192.168.100.119 -P 3306 -u sxy -p'Sxy000**' sxj_prod \
  -e "SELECT id, phone, name FROM sys_user WHERE name='xpt-0';"

Q14 — 用户首次借款初始额度是?

答案

4000

解题思路

global_config 表中 config_key=11 对应"用户初始额度",content=4000。

重点命令

# server2
docker exec mysql8.0.39 mysql -h 192.168.100.119 -P 3306 -u sxy -p'Sxy000**' sxj_prod \
  -e "SELECT * FROM global_config WHERE config_key=11;"

Q15 — 受害者在平台中一共结款了几次?

答案

1857

解题思路

  1. 初始查询: SELECT COUNT(*) FROM order_from WHERE status=5 AND repayment_time IS NOT NULL; → 1856 (误加条件)
  2. 修正查询: SELECT COUNT(*) FROM order_from WHERE status=5; → 1857
  3. 浏览器后台验证: 使用 qc 用户登录后台,首页显示"总还款订单量 1857"确认

差异分析:有1条记录 status=5 但 repayment_time IS NULL,初始查询漏计。

重点命令

# 正确查询
docker exec mysql8.0.39 mysql -h 192.168.100.119 -P 3306 -u sxy -p'Sxy000**' sxj_prod \
  -e "SELECT COUNT(*) FROM order_from WHERE status=5;"

# 浏览器后台验证 (需先设置Redis验证码)
redis-cli -a 'Sxy000**' SET ADMIN-PHONE19883213882 1234 EX 600
curl -X POST http://localhost:8082/admin/index/login \
  -H 'Content-Type: application/json' \
  -d '{"mobile":"19883213882","code":"1234"}'
# 访问 http://192.168.100.118:82/#/home 查看"总还款订单量"

Q16 — 该平台中所有下单用户成功完成订单总金额是?

答案

11408100

解题思路 (经历两次修正)

第一次查询 (错误): SELECT SUM(arrival_amount) FROM order_from WHERE status=5; → 6626555

  • 问题:使用的是到账金额而非合同金额,且只统计了已完成(status=5)

第二次修正:

  1. 浏览器后台验证: 首页"所有下单用户"卡片显示"通过订单总金额" = 11408100
  2. API验证: GET /admin/statistics/getGlobalConfigList → loanVO.loanMoney = 11408100.0
  3. 正确查询: SELECT SUM(quota) FROM order_from WHERE status>=4; → 11408100

字段关系:

  • status=4 (REPAYMENT/待还款): quota = 1213400
  • status=5 (SUCCESS/已完成): quota = 10194700
  • 总计: 1213400 + 10194700 = 11408100

教训: 题目"所有下单用户成功完成订单总金额"对应后台"通过订单总金额",统计范围是 status>=4 (待还款+已完成) 的合同金额总和。

重点命令

# 正确查询
docker exec mysql8.0.39 mysql -h 192.168.100.119 -P 3306 -u sxy -p'Sxy000**' sxj_prod \
  -e "SELECT SUM(quota) FROM order_from WHERE status>=4;"

# API验证 (需token)
curl -H "admin-token: <token>" http://localhost:8082/admin/statistics/getGlobalConfigList

Q17 — 该平台中逾期费率是?

答案

0.1

解题思路

global_config 表中 config_key=14 对应"逾期费率",content=0.1。

重点命令

# server2
docker exec mysql8.0.39 mysql -h 192.168.100.119 -P 3306 -u sxy -p'Sxy000**' sxj_prod \
  -e "SELECT * FROM global_config WHERE config_key=14;"

Q18 — 该平台中累计还款总金额是?

答案

10194700

解题思路 (经历修正)

初始查询 (错误): SELECT SUM(actual_repayment_amount) FROM order_detail WHERE status=5; → 9585392

  • 问题:使用了明细表 order_detail 而非业务表 order_from

修正过程:

  1. 浏览器后台验证: 首页显示"总还款金额" = 10194700
  2. API验证: GET /admin/statistics/getGlobalConfigList → paidUpLoanVO.sumMoney = 10194700
  3. 源码确认: 追溯 getStaHkOrderAcountVO SQL = SELECT SUM(quota) FROM order_from WHERE status=5 → 10194700
  4. 正确查询: SELECT SUM(quota) FROM order_from WHERE status=5; → 10194700

教训: 后台统计使用的是业务表 order_from 的 quota 字段,而非明细表 order_detail 的 actual_repayment_amount。

重点命令

# 正确查询
docker exec mysql8.0.39 mysql -h 192.168.100.119 -P 3306 -u sxy -p'Sxy000**' sxj_prod \
  -e "SELECT SUM(quota) FROM order_from WHERE status=5;"

# 源码追溯
unzip -p /data/cal-0.0.1-SNAPSHOT.jar "BOOT-INF/classes/mapper/*.xml" | grep -A5 "getStaHkOrderAcountVO"

Q19 — 该平台总共设置了多少种借款额度?

答案

18

解题思路

quota 表存储借款额度配置,is_delete=0 表示未删除的有效记录。

重点命令

# server2
docker exec mysql8.0.39 mysql -h 192.168.100.119 -P 3306 -u sxy -p'Sxy000**' sxj_prod \
  -e "SELECT COUNT(*) FROM quota WHERE is_delete=0;"

Q20 — 该平台一共有多少个借款渠道?

答案

131

解题思路 (经历修正)

初始查询 (错误): SELECT COUNT(DISTINCT channel) FROM channel_data WHERE status=1; → 129

  • 问题:使用了 DISTINCT 去重,但题目问的是渠道记录总数

修正过程:

  1. 用户确认: 渠道总共140个,开启状态(status=1)131个
  2. 正确查询: SELECT COUNT(*) FROM channel_data WHERE status=1; → 131
  3. 差异分析: 有2个channel重复(DDD2和某个中文名称各出现2次),DISTINCT后少2条

重点命令

# 统计总记录和开启数
docker exec mysql8.0.39 mysql -h 192.168.100.119 -P 3306 -u sxy -p'Sxy000**' sxj_prod \
  -e "SELECT COUNT(*) total, SUM(CASE WHEN status=1 THEN 1 ELSE 0 END) active_count FROM channel_data;"

# 查找重复channel
docker exec mysql8.0.39 mysql -h 192.168.100.119 -P 3306 -u sxy -p'Sxy000**' sxj_prod \
  -e "SELECT channel, COUNT(*) cnt FROM channel_data WHERE status=1 GROUP BY channel HAVING cnt > 1;"

# 正确查询
docker exec mysql8.0.39 mysql -h 192.168.100.119 -P 3306 -u sxy -p'Sxy000**' sxj_prod \
  -e "SELECT COUNT(*) FROM channel_data WHERE status=1;"

Q21 — 该平台对已完成用户收取了总计多少元服务费?

答案

4051915

解题思路 (经历修正)

初始查询 (错误): SELECT ROUND(SUM(rate_money)) FROM order_from WHERE status=5; → 3568145

  • 问题:使用了业务表 order_from 的 rate_money 字段,且仅统计 status=5

修正过程 — 三层交叉验证:

  1. Enum字节码分析:

    javap -c -v AmountBillTypeEnum.class | grep -E 'ldc|Utf8'
    

    确认: type=1(借款金额), type=2(打款金额), type=3(服务费), type=4(展期费用), type=5(逾期金额), type=6(减免金额), type=7(逾期减免金额), type=8(还款金额)

  2. API验证:

    curl -X POST http://localhost:8082/admin/amount/bill/getTjList \
      -H "admin-token: <token>" -H "Content-Type: application/json" -d '{}'
    

    返回 type=3: sumMoney=4051915, num=2044

  3. 源码确认: AmountBillMapper.xml 中 getTjList SQL: SELECT type, count(amount_id) num, sum(money) sumMoney ... FROM amount_bill (仅可选时间范围过滤,无状态过滤)

  4. 数据库验证: SELECT SUM(money) FROM amount_bill WHERE type=3; → 4051915.00

差异分析:

  • order_from.rate_money(status=5) = 3568145 (仅已完成订单的初始服务费)
  • amount_bill.type=3 join status=5 = 3566745 (关联已完成订单的服务费账单)
  • amount_bill.type=3 (全部) = 4051915 (平台全部服务费收入)

后台"交易统计"页面展示的是 amount_bill 表全部记录,而非仅关联已完成订单。

重点命令

# Enum字节码分析
javap -c -v AmountBillTypeEnum.class | grep -E 'ldc|Utf8'

# 正确查询
docker exec mysql8.0.39 mysql -h 192.168.100.119 -P 3306 -u sxy -p'Sxy000**' sxj_prod \
  -e "SELECT SUM(money) FROM amount_bill WHERE type=3;"

# API验证
curl -X POST http://localhost:8082/admin/amount/bill/getTjList \
  -H "admin-token: <token>" -H "Content-Type: application/json" -d '{}'

# 源码追溯
unzip -p /data/cal-0.0.1-SNAPSHOT.jar "BOOT-INF/classes/mapper/*.xml" | grep -A5 "getTjList"

后台网站恢复过程

RabbitMQ 启动失败

错误: systemctl start rabbitmq-server 启动失败 分析: 下载 erl_crash.dump,发现 {epmd_error,"iZbp1gma2uf9hvsnbu9mdkZ",address} 根因: /etc/hosts 中主机名映射到旧IP 172.20.148.2,当前实际IP为192.168.100.118 修复:

sed -i 's|^172\.20\.148\.2\tiZbp1gma2uf9hvsnbu9mdkZ.*|192.168.100.118\tiZbp1gma2uf9hvsnbu9mdkZ|' /etc/hosts
systemctl start rabbitmq-server  # active (running) PID 28999

前台JS地址替换

37个JS文件包含外网IP 47.96.140.186,全部替换为 192.168.100.118:

cd /www/admin/ && find ./ -type f -name '*.js' -exec sed -i 's/47.96.140.186/192.168.100.118/g' {} +

服务启动顺序

  1. RabbitMQ: systemctl start rabbitmq-server → active (running) PID 28999, 端口 5672/15672/25672
  2. Consul: cd /root/ && ./consul.sh → OK
  3. Java Spring Boot: cd /data && sh jar.sh → PID 11759, 端口 8082
  4. Nginx: 端口 82 (前端静态)
  5. Redis: 端口 6379

后台登录方法

方法一:密码登录 (推荐)

# qc用户(19883213882)密码=123456, MD5=e10adc3949ba59abbe56e057f20f883e
curl -X POST http://localhost:8082/admin/index/loginByPassword \
  -H 'Content-Type: application/json' \
  -d '{"mobile":"19883213882","password":"123456"}'
# 返回 token, 用于后续API调用

方法二:Redis验证码注入

redis-cli -a 'Sxy000**' SET ADMIN-PHONE19883213882 1234 EX 600
curl -X POST http://localhost:8082/admin/index/login \
  -H 'Content-Type: application/json' \
  -d '{"mobile":"19883213882","code":"1234"}'

Shiro Realm 用户状态注意:

  • sys_user.state=1 (Boolean true) → 已禁用 (LockedAccountException "该用户已被禁用 !")
  • sys_user.state=0 (Boolean false) → 正常可登录
  • qc 用户 state=0 可登录,xpt-0/xpt-1/tw 用户 state=1 被禁用

答案汇总

题号 题目要点 答案 关键验证方法
Q1 云服务平台 阿里云 cloud-init AliYun, aegis, Alibaba Cloud Linux
Q2 数据库密码 Sxy000** application-sxj.yaml
Q3 服务发现工具 consul /data/consul/ 目录结构
Q4 数据库配置文件名 application-sxj.yaml Spring Boot active profile=sxj
Q5 APP名称 顺心借 app.name + 浏览器登录页标题
Q6 OSS AccessKeyID后八位 EuZJybzD LTAI5tBt7rHreKjDEuZJybzD 后8位
Q7 消息代理端口 5672 RabbitMQ
Q8 定时任务代码文件名 MobileStatusTask.class @Scheduled 字节码验证
Q9 验证码匹配代码文件名 AdminIndexConller.class login()方法字节码
Q10 MySQL镜像ID前6位 23b013 docker images
Q11 DockerCompose版本 2.27.1 docker compose version
Q12 后台登录账号表名 sys_user SHOW TABLES + DESC
Q13 xpt-0绑定手机 19521510863 sys_user查询
Q14 首次借款初始额度 4000 global_config config_key=11
Q15 受害者结款次数 1857 COUNT(status=5) + 后台验证
Q16 成功完成订单总金额 11408100 SUM(quota) status>=4, 三层验证
Q17 逾期费率 0.1 global_config config_key=14
Q18 累计还款总金额 10194700 SUM(quota) status=5, 三层验证
Q19 借款额度种类数 18 COUNT(is_delete=0)
Q20 借款渠道数 131 COUNT(status=1)非DISTINCT
Q21 服务费总计 4051915 amount_bill type=3, 三层验证

关键经验总结

1. 统计类题目必须三层交叉验证

  • 第一层:浏览器后台UI查看统计数据
  • 第二层:curl直接调用后端API获取JSON
  • 第三层:源码追溯 Controller → Service → Mapper XML 找到实际SQL

2. 金额字段语义容易混淆

  • quota = 合同金额/借款额度
  • arrival_amount = 到账金额
  • rate_money = 服务费 (初始合同)
  • 关系: quota = arrival_amount + rate_money
  • 账单表 amount_bill 记录实际发生的每一笔费用 (含展期、补扣等)

3. COUNT vs COUNT DISTINCT 陷阱

  • Q20: 题目问"多少个借款渠道"=记录数131,而非去重值129
  • 存在重复channel时需通过后台UI确认计数方式

4. Java字节码分析技巧

  • javap -v classfile | grep -i "Lorg/springframework" 查看Spring注解引用
  • Enum常量池直接暴露业务含义映射: javap -c -v Enum.class | grep -E 'ldc|Utf8'

5. Shiro用户状态字段反直觉

  • state=1 = 已禁用, state=0 = 正常 (Boolean类型)
  • 验证前必须确认目标用户 state=0