盘古石杯 2026 团队赛

2026盘古石初赛(服务器部分)

围绕 盘古石杯 的公开复盘与解题记录。

上传者:DaSuA1 发布日期:2026-05-29 107 次阅读

容器密码:VeZeTANHVkklvVljLnpOYeBwCJFYD5tFpf67f5kNuqP1G5jj

服务器

PVE集群安装配置介绍:https://zhuanlan.zhihu.com/p/617024637

PVE = VMware ESXi + Docker + Linux 运维工具 的融合体。

物理服务器
│
├── CPU
├── 内存
├── 硬盘
└── 网卡
      │
      ▼
PVE(使用pveqm管理整个节点)
│
├── KVM 虚拟机(使用qm管理KVM虚拟机)
│     ├── Windows
│     ├── Ubuntu
│     └── CentOS
│
└── LXC 容器(使用pct管理LXC容器)
      ├── nginx
      ├── mysql
      └── redis

总体先说一下PVE的网络规则,首先得有两张真实网卡,一张网卡桥接在vmbr0做普通网络的通信。还要再多设置一张网卡桥接在vmbr1上做Ceph网络专门用于存储网络。因为PVE集群的性能节点就在存储上,占CPU不高但非常吃存储,所以要专门为Ceph配置一个专门的存储网络。拓扑图如下 image-20260511154945613.png

集群服务器通信问题:

查看pvecm status发现其他几台机器都不在集群中,也ping不通,可能是网络问题。网络通信问题不会的把网络配置文件丢给AI读image-20260511112743588.png

查看ip a,这里本来ens160网卡是down的问题出在这。ip link set ens160 up先把ens160打开。

物理网卡是ens,vmbr应该是kvm虚拟机的桥接网卡。像lxc的话桥接网卡就取名为lxcbr image-20260511113121390.png image-20260511113140824.png image-20260511113155124.png

再修改/etc/network/interfaces(网卡配置文件)里的bridge-ports为ens160,一开始不是这张网卡,所以没挂上自然连接不了。这里网络配置就是这样的不使用原本的ens网卡,而是将网络桥接至vmbr0和vmbr1。 image-20260511113422776.png

最后systemctl restart networking

验证一下,互相ping image-20260511113509707.png

然后就是解决Ceph网络问题,先看下配置ceph.conf,要把vmbr1都配置成170网段,再添加张170段的VM2网卡为vmbr1做桥接。配置vmbr1的步骤同vmbr1。也可以直接键入如下命令桥接上网卡。

# pve-node1 (170.50 → 0.50)ip addr add 192.168.170.50/24 dev vmbr1
# pve-node2 (170.51 → 0.51)ip addr add 192.168.170.51/24 dev vmbr1
# pve-node3 (170.52 → 0.52)ip addr add 192.168.170.52/24 dev vmbr1

image-20260511195614553.png image-20260511205924343.png

两个网络都可以互通了,但是Web虚拟机还无法启动,让codex帮我排查一下问题

后来虽然网络通了,但服务没有自动恢复。
systemctl list-units 'ceph-mon@*' 'ceph-mgr@*' 'ceph-osd@*'查到后面*后的内容
对三台节点做了这些处理:
systemctl reset-failed ceph-mon@* ceph-mgr@* ceph-osd@* 
systemctl restart ceph-mon@... 
systemctl restart ceph-mgr@... 
systemctl restart ceph-osd@... 
​
然后 VM 100 还是启动失败,新的真实原因是:
kvm: cannot set up guest memory 'pc.ram': Cannot allocate memory 
你的 pve-node1 只有约 3.8GiB 内存,但 VM 100 原来配置了:
所以它分配不了8G。我已经临时把 VM 100 内存降到 1024 MB,现在 VM 100 可以启动,QMP 也正常:

直接成功了,但是web机器登录进去需要账号密码 image-20260512163331990.png

https://www.cnblogs.com/lyydbk/p/19757080 龙信杯服务器部分。卡在web这台虚拟机进不去,这里问了群里的师傅说是可以像龙信杯的esxi这样导出虚拟机镜像在火眼里重置密码

qm config 100先看看配置

boot: order=scsi0;ide2;net0
cores: 2
cpu: x86-64-v2-AES
ide2: local:iso/CentOS-7-x86_64-Minimal-1810.iso,media=cdrom,size=918M
memory: 1024
meta: creation-qemu=10.1.2,ctime=1776321610
name: web
net0: virtio=BC:24:11:05:5B:A0,bridge=vmbr0,firewall=1
numa: 0
ostype: l26
parent: first
scsi0: Ceph_pgscup_pool:vm-100-disk-0,iothread=1,size=60G   #这里有一个60G的磁盘
scsihw: virtio-scsi-single
smbios1: uuid=b9dd3c3a-6ac5-49e4-b4d3-2017ab408f40
sockets: 1
vmgenid: 96b24edb-9b6e-4992-a84e-8d1f5500b37d

ceph osd lspools:列出存储池

1 .mgr
2 Ceph_pgscup_pool

rbd ls Ceph_pgscup_pool:查看 Ceph_pgscup_pool 这个 pool 里有哪些虚拟磁盘

vm-100-disk-0

image-20260512193311974.png

这里拓展一下PVE集群的管理存储命令

ceph命令学习

ceph -s  集群状态
ceph health  健康信息
ceph osd pool ls  列出所有池名称
rbd export  导出虚拟磁盘
rbd ls <pool>  列出池中所有 RBD 镜像
rbd export <pool>/<image> <local-file>  导出镜像为本地文件(raw 格式)

这里由于pve1节点磁盘空间不足没法导出web的镜像到本机火眼直接绕密分析。

现在采用第二种方法

单行绕过虚拟机密码

Console控制台启动机器的时候长按Esc进入在 GRUB 菜单中,用箭头键选择第一行

按e键进入编辑模式。

找到以linux16开头的行。将光标移动到此行的末尾,先按空格键,然后添加 rd.break。这会让系统在初始化早期进入一个紧急 shell。

image-20260513173640624.png

修改后,按Ctrl+ X 或F10启动系统。

系统启动后会进入一个紧急模式的 shell,提示符为 switch_root:/#image-20260513173801223.png

执行以下命令重新挂载根文件系统为可写:mount -o remount,rw /sysroot

切换根目录:chroot /sysroot

现在可以修改 root 密码:passwd root

输入新密码两次(输入时不会显示,注意大小写)。

如果系统启用了 SELinux(默认启用),需要更新文件系统标签:touch /.autorelabel

退出 chroot 并重启:exit 、reboot

之后用ssh连接虚拟机,发现死活连不上。这样开启监听,再连2222端口看一下具体是什么原因导致连接不上

/usr/sbin/sshd -ddd -p 2222

问了AI,是centos常见ssh不上的原因,要让下面这个文件保证这些配置。

vi /etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication yes
UsePAM yes
UseDNS no
GSSAPIAuthentication no

image-20260513202604481.png

访问网站显示502,重启systemctl start php-fpm nginx试试。变成了sWhoops, looks like something went wrong. 这里是Laveral站缺少 .env 里的 APP_KEY,所以首页报错这个。

codex帮我清空了原来的配置,添加了新的APP_KEY之后启动成功了。
cd /var/www/html
创建 .env
php artisan key:generate --force
php artisan config:clear
php artisan cache:clear
php artisan route:clear
php artisan view:clear
chown -R nginx:nginx storage bootstrap/cache
systemctl restart php-fpm nginx

网站启动成功 image-20260513204953590.png

后面好像数据库也不能在navicat连接,只有本机能连接。

查一下用户可以从哪里连,果然都是只有本地能连的用户 image-20260515204540568.png

那么直接创建一个能哪都能连接的账号,赋予所有权限就可以了

#增加新的可以远程访问的用户
CREATE USER 'remote'@'%' IDENTIFIED BY '123456';        #创建一个%表示任何ip可连的账号
GRANT ALL PRIVILEGES ON *.* TO 'remote'@'%';        #赋予所有权限
FLUSH PRIVILEGES;       #让权限立即生效
#像删除的话就这样
drop user remote@'%';
flush privileges;
或者直接修改root的Host也可以,Host代表可访问主机
UPDATE user SET Host = '%' WHERE Host = '127.0.0.1' AND User = 'root';
flush  privileges;
好吧这种方法尝试失败,可能是限制root用户访问了

1、分析pve集群,请给出pve主机版本号?[答案格式:1.2.3] image-20260512201335218.png

2、分析pve集群,请给出pve主机内核版本?[答案格式:1.2.3-123-abc] image-20260512201612810.png

3、分析pve集群,请给出pve集群名?[答案格式:abc132] image-20260512201849482.png

4、分析pve集群,请给出加入集群所用指纹的前6位?[答案格式:AA:BB:CC]

加入集群所用指纹指的是主节点集群根证书(pve-root-ca.pem)的 SHA256 哈希值。主要用于子节点怎么确认主节点是真正的 PVE 主节点,采用自己的 TLS 根证书认证。根节点位置在这,计算其SHA256即可得到答案

/etc/pve/pve-root-ca.pem

image-20260513102513536.png

5、分析pve集群,请给出pve集群中主机所用的时间服务器地址?[答案格式:www.baidu.com] ntp.aliyun.com

PVE/Debian 默认常用 chrony 做时间同步,时间服务器一般写在 /etc/chrony/chrony.conf 里的 server 或 pool 行 image-20260514091715526.png

6、分析pve集群,请给Ceph存储的资源池名?[答案格式:Abc_def] Ceph_pgscup_pool image-20260514092055164.png

7、分析pve集群,请给出Ceph存储资源池的类别?[答案格式:ABC] RBD

直接查找/etc/pve/storage.cfg可以找到存储池配置信息 image-20260514101554296.png

8、分析pve集群,请给出Ceph集群的ID的前8位??[答案格式:a1b2c3d4] ceph fsid

**查看当前 Ceph 存储集群的唯一标识符 **ceph fsid

9、分析pve集群,请给出Ceph存储设置的最小副本数?[答案格式:123]

ceph osd pool get Ceph_pgscup_pool min_size image-20260514102207951.png

10、分析pve集群,请给出pve集群中虚拟机的快照创建时间?[答案格式:2025-01-11-11:01:01]

web控制面板就可以看到快照状态了2026-04-16-15:05:19 image-20260514102419307.png

11、对集群内的服务器进行分析,提取该服务器使用的Linux内核完整版本号?[答案格式:1.2.3-123abc_123] 3.10.0-957.el7.x86_64

这应该问的是web这台机器 image-20260514102651839.png

12、对集群内的服务器进行分析,提取该服务器SSH服务监听的TCP端口号?[答案格式:123] 22 image-20260514103218195.png

13、对集群内的服务器进行分析,提取该服务器网卡的IP地址?[答案格式:127.0.0.1] image-20260514103821029.png

14、对集群内的服务器进行分析,提取金麟资本理财网站对应的域名?[答案格式:baidu.com] jlzb.vip image-20260514105010686.png

15、对集群内的服务器进行分析,服务器中有个加密工具,请给出该工具的名字?[答案格式:abc_def.sh] encrypt_tool.py

刚进来root用户目录下就有这个解密工具 image-20260514105132020.png

16、对集群内的服务器进行分析,服务器中加密工具在加密数据库备份文件时使用的密码是什么?[答案格式:ABC@123] JDSJ2026@Backup image-20260514110004567.png

tail -n +44 encrypt_tool.py | gzip -cd那就直接解压后面44行得到python源码 image-20260514110649705.png

17、对集群内的服务器进行分析,服务器中加密工具第1层加密的XOR密钥是什么?[答案格式:按实际填写] 0x5A image-20260514110924089.png

18、对集群内的服务器进行分析,给出MySQL数据库root用户的密码?[答案格式:abc@123] pgs@cupo26

由16题截图可知

19、对集群内的服务器进行分析,请给出网站后台数据库中存放聊天记录的数据表名字?[答案格式:adb_def]

连接数据库,发现里面啥也没有,于是看看目录下是否存在.sql文件

可以看到网站目录下存在一个1.sql文件,但是导入时发现报错

image-20260515210620731.png

解决办法打开MySQL的配置文件(通常是my.cnfmy.ini),这里直接在/etc/my.cnf下,在[mysqld]部分添加或修改以下行

[mysqld]
max_allowed_packet=16M

systemctl restart mysqld时候发现没有mysql,原来服务是mariadb(早年 MySQL 被收购后,社区分叉出 MariaDB,日常使用和mysql毫无区别),怪不得配置文件在/etc/my.cnf

导入数据库的时候发现报错,第一个是报错,这个说明了超出了允许导入的最大大小。SELECT @@max_allowed_packet;查看一下原本只能导入1mb太小了。给他设置大一点。

Got a packet bigger than 'max_allowed_packet' bytes
mysql> SET GLOBAL net_buffer_length=1000000;
mysql> SET GLOBAL max_allowed_packet=1000000000;

接下来再次导入又发现no database selected,原来是数据库文件里就少写了东西,加上就可以正常导入了。 image-20260517201132529.png

打开一看应该就是这个表了 image-20260517202335070.png

20、对集群内的服务器进行分析,分析网站后台用户密码加密算法中type=0时的初始盐值是什么[答案格式:ABC] ABCDEFG

grep -R "salt" /var/www/html查找一下salt就可以找到答案了 image-20260517203856784.png

image-20260517203807392.png

21、对集群内的服务器进行应用取证,提取该Laravel应用的APP_KEY值的后8位?[答案格式:英文数字混合字符串] otS+rWI=

好吧,这里之前这里缺少APP_KEY直接让AI给重新设置了一个。看来是要找到APP_KEY

一般是在/var/www/html/.env文件里配置,这里看到了一个.env.obf。是加密过的,记得之前/root目录下有个加密脚本,可能跟这个有关试着依照加密脚本写一个解密脚本。 image-20260517204115675.png

# -*- coding: utf-8 -*-
import base64
​
XOR_KEY = 0x5A
BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
CUSTOM_TABLE = "ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9876543210+/"
​
def xor_decrypt(data):
    result = []
    for i, c in enumerate(data):
        result.append(chr(ord(c) ^ ((XOR_KEY + i) % 256)))
    return ''.join(result)
​
def char_swap_decode(data):
    trans = str.maketrans(CUSTOM_TABLE, BASE64_TABLE)
    return data.translate(trans)
​
def deobfuscate(hex_data):
    # 第4层逆向: hex -> 字符串
    step1 = bytes.fromhex(hex_data).decode('utf-8')
    # 第3层逆向: 自定义字符表 -> 标准Base64
    step2 = char_swap_decode(step1)
    # 第2层逆向: Base64解码
    step3 = base64.b64decode(step2).decode('utf-8')
    # 第1层逆向: XOR解密
    step4 = xor_decrypt(step3)
    return step4
​
def decrypt_file():
    encrypted = "5464684e5a735a764f48497533427a6233795033334f4873346b3750794870344c714a6b52617348535935495673732f4d62786c5171562b51465a7853684c6164334758674e5036646932576f4e4c6964332f576c314c326434365773684c5a64344757694e4c4564334b57744e4c34643469576e684c47643347586c684c48643236586a314c2b64344f57674e4c7a6434695767684c556434755767314c5464354b5767784c6b6435475773684c766432715873314c7564326d576b68504d6469655768314c71643347576a684c6264324f5766684c686432365873684c5264352f5772314c7a64356958744e5048646b4b586e31504a646b475872314c38646a53586568502f6469615767314c73643357576c784c6c6433575765784c6f64334b5769684c346433715767314c67643365576f684c43646b4f58704e506d646932586c785069646a43586a31506c646a4f586b4e4c46646b6d586d31504d646c325873784c6f6432695867785064646a6158673150336469715866785030646969586968506264694b5868314c57646b5134786d4d655770555644565954444531634b5764354b72563152616c7053554d7a49706b46477070714f594e4955586f7441366f717a4338697a44382b7a63517a445563584345683178366b6e793773347936592b574a492f4e48686a4e70394e586346795749357754593157556e35704e7246394f48707a4d543469455546724e6239725258426d4d5764374f71706f4c62683047493977555335374d614a644b58462f4f714a6c4c684c48646961576b4e4c6a643371576c4e504e6434475771784c7a64346d5774684c5764354b5770784c4f64354f576e4e4c56643479577368506c64324f5767684c6864327157664e4c3364332f57664e50466435325767784c70643275576b684c32643379576a784c6b64337957694e4c6b64322f576b784c726433575870684c57643571576d4e4c4a6469353d"
    decrypted = deobfuscate(encrypted)
    print("[OK] 解密完成")
    print("输入文件:", decrypted)
​
if __name__ == "__main__":
    decrypt_file()
APP_NAME=金鳞资本
APP_ENV=local
APP_KEY=base64:QmhkrWMLYbZsQkINFr5Jd1eNiDEVduTbfSNlotS+rWI=
APP_DEBUG=true
APP_URL=http://192.168.0.70
​
LOG_CHANNEL=stack
​
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=jinqin
DB_USERNAME=root
DB_PASSWORD=pgscup@o26
​
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=database
QUEUE_CONNECTION=sync

这里又试着恢复网站:

先关墙连数据库systemctl stop firewalld

下面是配置了一下远程连接,但是把本地root误删了,不用管我这里

异地连mysql -h 192.168.0.70 -uroot -p
pgscup@o26
​
#增加新的可以远程访问的用户
CREATE USER 'root'@'localhost' IDENTIFIED BY '123456';      #创建一个%表示任何ip可连的账号
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost';      #赋予所有权限
FLUSH PRIVILEGES;       #让权限立即生效
​
#像删除的话就这样
drop user remote@'%';
flush privileges;
​
systemctl restart php-fpm nginx

然后之前导入的数据库名字取成了www_9nw_cc这里我又改了一下配置文件DB_DATABASE以及DB_PASSWORD的数据。但是启动变成了这样,这是从www_9nw_cc去查session表了,但是并没有这个session表,可能是sql文件缺失了。这个session表的作用就是先会在每个请求前先查一下session数据。这里再将database改成file就不会再查数据库了。 image-20260520212330131.png

这样就启动起来了。

image-20260520212729357.png

b2bba0c0dbd57dcf9fd081bd153b7ee7原本加密的密码

根据前面的加密逻辑改密6e20b1394f05e1f9188ffff90147b4eb 123456 image-20260521125013910.png

22、对集群内的服务器进行取证分析,金麟资本理财网站后台有多少个机器人?[答案格式:123] 3 image-20260517210959194.png

23、对集群内的服务器进行资金流水取证,提取该平台数据库中聊天记录总数?[答案格式:123] 11494

数据表中聊天表是空的,可能是删掉了,应该还会存在备份。在home目录下发现了数据库的备份,还是enc加密的,这里的解密逻辑也在/root目录下那个加密逻辑里面写了 image-20260521114908186.png

这样就可以得到解密后的sql文件了 image-20260521120336987.png

看了一下sql文件还没有写用哪个库的语句,加上去就可以正常导入了 image-20260521130555821.png

24、对集群内的服务器进行数据库取证,提取该平台数据库中注册用户总记录数?[答案格式:123] 21722 image-20260522114857942.png

25、对集群内的服务器进行取证分析,提取平台内用户季丽华的身份证号?[答案格式:18位身份证] 370100196901274436 image-20260522115027628.png

26、对集群内的服务器进行资金流水取证,提取该平台数据库中钱包流水金额第二大的用户名字?[答案格式:张三] 燕春梅

SELECT ur.name, t.total_amount
FROM (
    SELECT 
        w.user_id,
        SUM(w.change) AS total_amount
    FROM wallet_log w
    GROUP BY w.user_id
) AS t
INNER JOIN user_real ur
    ON t.user_id = ur.user_id
ORDER BY ABS(t.total_amount) DESC
LIMIT 2;

这个语句敲出来还是蛮复杂的,参考Serendipity师傅的语句。 image-20260522191214847.png

由于我是脚本小子,直接使用网矩进行联表然后排序。这里说明一下左右内外联表,做联表就是左边数据全部写上去,右边若有为null的值则不统计上去。其余的同理 image-20260522190939558.png

27、对集群内的服务器进行数据库取证,提取该平台法币交易中交易笔数最多的卖家的交易笔数?[答案格式:123] 1377

只筛选出已确认的交易记录(is_sure=1的记录)

按卖家ID进行分组统计

计算每个卖家的总交易笔数

找出其中交易笔数最多的卖家

SELECT seller_id, COUNT(*) AS deal_count
FROM legal_deal
where is_sure=1
GROUP BY seller_id
ORDER BY deal_count DESC
LIMIT 1;

image-20260522123626804.png

下面温故一下SQL语句

聚合函数:COUNT() SUM() AVG() MAX() MIN()
常见的count用法,count用来统计不为空的数值
COUNT(*)
COUNT(字段名)
COUNT(DISTINCT 字段名)  DISTINCT是去重语句
COUNT经常配合GROUP BY做分类统计
​
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id;

筛选前

order_id user_id
1 1001
2 1001
3 1002

筛选后

user_id order_count
1001 2
1002 1
常见的group by用法,先分组后统计,经常与几个聚合函数搭配使用
SELECT user_id, SUM(money)
FROM recharge
GROUP BY user_id;

筛选前

user_id money
1001 50
1001 20
1002 30

筛选后

user_id SUM(money)
1001 70
1002 30
Having对分组后的结果进行筛选,与where的区别是:having是对分组结果的筛选放group by后,where对原始数据进行筛选放group by前
SELECT user_id, COUNT(*) AS cnt
FROM orders
GROUP BY user_id
HAVING cnt > 2;     #这里就是对筛选后的cnt进行筛选

筛选前

user_id
1001
1001
1001
1002

group by后

user_id cnt
1001 3
1002 1

having后

user_id cnt
1001 3
ORDER BY 排序
ORDER BY age ASC升序 / DESC降序
LIMIT 10;   只取前十条结果
JOIN ON 联表分析(默认为内连接模式 INNER JOIN)
LEFT JOIN
RIGHT JOIN
​
SELECT ur.name, t.total_amount
FROM t
INNER JOIN user_real ur ON t.user_id = ur.user_id   #这里是先进行联表再进行筛选

t表

user_id total_amount
1 100
2 200
3 300

user_real表

user_id name
1 张三
2 李四

联表后,结果只会1、2。user_id = 3 因为在 user_real 表找不到,所以不会显示。

name total_amount
张三 100
李四 200

28、对集群内的服务器进行资金流水取证,提取该平台已完成结算的杠杆交易中保证金总额最多的用户的保证金总额?[答案格式:100.00] 37240.76070238

select user_id, sum(caution_money) as money
from lever_transaction
where `status` = 3
group by user_id
ORDER BY money DESC
LIMIT 5;

image-20260522201025844.png

29、对集群内的服务器进行资金流水取证,提取该平台商家中余额最小的商家的手机号?[答案格式:18036310808] 15860623709 image-20260522201631880.png

30、对集群内的服务器进行资金流水取证,提取该平台商家中余额最小的商家的余额?[答案格式:100.0] 8461.4

上一题可知