🛡️ZZUCTF 平台渗透事件复盘报告

字数 13967阅读时长≈ 35 分钟
2026-10-20
2026-10-20
材料基础:① 平台服务端全量原始日志(HTTP 访问日志 / 登录审计 / 数据库字段审计);② 攻击方 AI 自动化工具实例的完整运行记录(规划引擎决策日志、各迭代执行脚本、结果文件、运行日志——源码与二进制均仅离线存档,未执行)。
取证结论:两份材料在时间、指纹、行为三方面逐条吻合,可确认二者记录的是同一次攻击,材料真实性成立。

一、事件概况

本次事件系一位25级同学错误使用 AI 自动化渗透工具所致。该工具即前段时间攻击韩国多家银行的知名 Agent 引擎 ARTEX。
该工具在收到针对本平台的渗透测试指令后,由 LLM 驱动持续自主地对平台实施攻击,行为包括但不限于:
  • 对平台主机的 SSH 服务做大规模口令爆破;
  • 批量注册测试账户(成功建立 85 个账户,累计注册请求 250 次);
  • 通过密码喷洒(password spraying)方式尝试批量获取账号控制权限(成功 1 个);
  • 对公开接口进行批量遍历查询(队伍信息接口被遍历 968 次,收割全站真实用户名);
  • 尝试调用管理员接口获取信息;
  • 尝试通过"旁站"方式渗透同一主机上的其他业务系统(该行为已超出操作者给予的提示词范围)。
一句话概括:这是一次由本地部署的「AI 自主渗透系统」对平台发起的、未成功的系统性渗透测试。
💡
本次攻击没有攻破任何技术防线,它攻破的是人的习惯。
大家的数据都是完全安全的,未发生未授权访问,也没有相关证据指向数据泄露等恶劣行为。

二、责任说明与影响评估

我本人对此负有不可推卸的责任。 由于蓝队经验不足,未对平台相关接口做速率控制,也未对关键服务采取充分保护,客观上为 AI 在服务器上的大量攻击与尝试创造了条件。
经完整复盘与日志分析,我们向所有同学保证:
  • 所有选手的敏感数据、隐私数据均未泄露。 攻击者唯一取得的是一枚最低权限的普通用户账户(不可访问任何管理功能),攻击者对该账户未做任何修改;
  • 本次事件未造成任何严重或恶性影响。 无 flag 泄露、无权限提升、无服务器失守、无数据外泄;
  • 平台一直以最高网络安全标准要求自身,高度重视大家的隐私信息。从某种意义上说,我们也需要"感谢"攻击者与 ARTEX——是它发现并暴露了我们的问题。
我代表个人与实验室团队,向所有选手致以诚挚的歉意。
关于攻击的实际执行者:考虑到该同学不了解工具的危害,且未实际造成严重后果,我们暂不主动采取进一步行动,希望同学们引以为戒,树立正确的网络安全价值观。
本次应急响应有数十名实验室同学参与分析与取证,大家投入了大量时间排查、讨论消息超过一千条,最终通过日志与工具材料锁定攻击者、完整复原整条攻击链。在此感谢每一位参与的同学。

三、事件概览

项目
内容
事件性质
平台的一名参赛者,使用 AI 自动化渗透工具对平台进行未授权安全测试
攻击时间
2026-10-19 凌晨(北京时间),攻击流量窗口约 1 小时 49 分钟
攻击来源
校内某网络出口(经核查为操作者本人的出口,ip位于宿舍网段),本报告以「攻击源」代称
攻击工具
ARTEX —— AI 自主渗透引擎(LLM 驱动;「规划者 + 执行者」双层循环;自带扫描、爆破、脚本生成能力)
攻击规模
约 5,700 次 HTTP 请求 + 3,000 余次登录尝试 + 2,000 余次 SSH 鉴权尝试;并发使用 5 类工具指纹
攻击路径
侦察 → 版本指纹 → 攻击面广度枚举 → 用户名收割 → 弱口令轮转喷洒 → 账户接管 → 权限提升尝试(未果)→ 主机与旁站扩散(未果)
实际战果
取得 1 个低权限普通账户(对全部管理端点访问 100% 被拒),注册大量测试账户
平台损失
0(无 flag 泄露、无权限提升、无服务器失守、无数据外泄)
处置状态
攻击源与操作者身份均已定位;受害账户已强制重置;全站弱口令已完成一轮离线复核;相关防护已进入整改
补充说明两个数字口径:
  • 测试账户数:本次攻击成功建立 85 个账户,注册请求合计 250 次(其中 83 次因用户名重复被拒、50 次触发限流、32 次被客户端主动断开)。事件初期"两百多个账户"的口径,是把重复尝试与限流请求一并计入所致;
  • 工具指纹:该工具并非"一个脚本",而是一套调度系统,同一时间在源 IP 上并发出现 curl/8.21.0、裸 Mozilla/5.0、nmap NSE、无 UA 客户端(含其独有组件 norma/0.4)五类指纹。

四、我方响应时间线

 
本节以团队群内的讨论与处置节奏为主轴。每个节点分三部分:我方动作、当时的研判、事后被证据坐实或推翻的结论。这样既能还原"当时是怎么想错的、又是怎么纠正的",也便于与攻击方记录对照。
 
20:38 · 事件触发与初次通报
 
  • 我方动作 —
 
在出题群内通报平台遭攻击。
信号有两个:
一是管理员的用户管理中出现大量使用占位邮箱域名(@example.com)的账户,且均处于已激活状态;
二是访问日志中出现成批的未授权访问请求。
 
  • 当时研判 —
怀疑存在 0day,当时我们想的是,平台应该是有邮箱注册验证机制的,攻击者理论上做不到使用这样的邮箱注册大量账户
怀疑攻击者绕过了注册审核,甚至是调用了管理层的 API
 
  • 事后核验 — 用注册接口的状态码分布可精确还原当时的现场:
 
20:41 · 来源判定:校内
  • 我方动作 —
1.确认访问来源位于校内,排除经反向代理或校外跳板进入的可能
2.确认注册账户规模
3.定位到被使用账户的未授权访问行为。
  • 证据 — 攻击者拿到账户前后都对管理端点做过尝试,401(未携带凭据)与 403(已登录但权限不足)相继出现:
 
20:43 · 注册激活配置确认
  • 我方动作 —
一名管理员确认平台注册配置存在问题:账户在未完成邮箱验证的情况下即被激活,直接获得普通用户权限。
经排查,无法确认是哪位管理员启用了平台的“新用户自动激活”功能,但没有相关证据指向该接口被恶意调用,故怀疑是某位管理员的误操作。
  • 证据 — 74 个占位邮箱账户全部处于可用状态,可正常登录、报名、提交答案。
 
20:48 · 攻击源定位到校内寝室网络
  • 我方动作 —
确认攻击源为校内寝室网络出口;
具备校园态势感知权限的同学上线,协助查询,该地址在态势感知系统中未被标记,无相关攻击信息。
  • 说明 — 这解释了"为什么没有告警":攻击请求速率低(每分钟固定次数)、UA 伪装成普通浏览器,形态上不触发流量型告警。
💡
从另一方面来说,态势感知系统的误报率挺高的,但是对真实的攻击行为却没有进行有效的发现、标记和阻止
 
20:55 · 主机层痕迹浮现
  • 我方动作 — 服务器日志确认第一批攻击痕迹是针对 SSH 服务的大规模口令爆破,累计 2,067 次,全部失败。
  • 干扰信息 — 同期收到:被命中账户的主人反映其 QQ 邮箱疑似被盗。事后确认此事与本事件无关,但当时把排查方向引向了"本人被盗号后发动攻击"。
  • 口径差异说明 — 工具侧自述尝试了 22 类用户名 × 177 条口令(约 3,894 组合),服务端只记录到约 2,000 次失败鉴权。原因是爆破后期服务端开始丢弃连接(工具侧日志中出现连接横幅读取错误),大量组合在握手阶段即被拒绝、未进入鉴权环节。这本身说明连接层限速对爆破起到了抑制作用。
 
21:05 · 排查重心偏向受害账户主人
  • 我方动作 — 请该同学自查其出口地址、检查终端是否存在恶意程序。
 
21:12 · 真正的操作者进入视野
  • 我方动作 — 尝试通过校园网地址分配规则定位到具体人员,准备联系相关老师;在核验日志时发现,攻击出口地址与一名在校学生的地址一致。
  • 意义 — 本事件的关键转折:排查方向从"账户主人是否自导自演"转向"另一名真实人员"。
 
21:13 · 引入「Agent 自动化渗透」假设
  • 我方动作 — 讨论是否可能存在自动化 Agent 对平台实施渗透,以及该学生与攻击事件的关系。
 
21:14 · 初步取证完成(第一版结论)
  • 我方动作 — 自动化取证给出第一版结论:85 个测试账户全部由同一出口注册、UA 全部为脚本特征、该地址进行了大量撞库与 SSH 爆破。受此结论与账户名本身的暗示影响,当时把攻击者锁定在被命中账户的主人(亲爱的 Alan Walker 同学)身上。
  • 复盘注 — 这一版结论在攻击行为特征上是准确的,但在谁在攻击上判错了。错在把"被使用的账户"等同于"账户的主人"。
 
21:15 · 关联时间锚点(11:19)
  • 我方动作 — 取得该学生的联系方式,核对攻击当日其位置信息。
  • 意义 — 这段流量当时被记为"与凌晨脚本无关的浏览",事后成为把操作者与攻击行为绑定的时间锚点:它不是工具流量,而是真人操作。
 
21:19 · 第一个被证伪的密码
  • 我方动作 — 账户主人(Alan Walker 同学)提供了一份其可能使用的弱口令。经哈希比对,该候选值与平台账户所有历史版本均不匹配(含空格、全角字符、大小写等大量变体)。同时确认该账户有正常、连续、可解释的注册与参赛记录。
  • 证据 — 平台密码哈希为 ASP.NET Core Identity V3 格式,我们按格式逐字节比对:
  • 结论 — 该候选确系本人记错;同时"密码由用户名衍生而来"这一假设被排除。
 
21:24 · 一次误报
  • 我方动作 — 排查中发现一条疑似匿名网络出口(Tor,洋葱路由)的登录记录,一度提高对攻击者技术水平的评估。经核验,该地址为普通商用代理,并非匿名网络出口,且登录者系管理团队成员。
  • 证据 — 该出口地址经官方出口清单双向核验无匹配;登录后 4 分钟即使用"创建测试容器"功能,行为连续性与出题工作完全吻合。(创建测试容器是管理员的功能哦,普通用户是没法创建的)
 
21:30 · 账户历史与凭据变更全部澄清
  • 我方动作 — 完成账户画像:历史登录全部为浏览器行为;密码曾在更早时间点重置过一次;10-19 期间由脚本接管登录;用户名变更发生在更早日期,与本次攻击无关。
 
21:35 · 排除社工库来源
  • 我方动作 — 尝试通过公开泄露数据查询该账户邮箱对应的密码,查询无结果。据此判断攻击者并非通过社工库获得密码,但当时仍未排除账户主人自导自演的可能性。
 
21:51 · 操作者自述使用自动化工具
  • 我方动作 — 负责联络的同学确认:该学生承认使用 AI 自动化渗透工具对平台实施渗透。我方第一时间要求其提交工具运行日志以便核实;对方对行为的严重性缺乏认识。
  • 意义 — 身份与工具来源同时确认,但"口述"尚不足以与日志严格绑定。
 
22:15 · 无工具日志则链条不闭合
  • 我方动作 — 讨论认为:在没有工具侧运行记录的情况下,无法把"某人的工具"与"服务端记录的这次攻击"绑定,仍保留"外部攻击者操纵账户"的可能。
 
22:25 · 服务端侧独立复原
  • 我方动作 — 上传服务器全量访问日志,数十名成员并行筛选。从日志中确认:存在对队伍清单接口的持续遍历,并成功批量获取了数据。
  • 意义 — 这是"密码喷洒"假设的铺垫:先有用户名清单,才有针对性喷洒。
 
22:30 · 处置基调
  • 我方动作 — 讨论认为本次攻击未造成实质破坏、未见主观恶意,先与操作者沟通并安抚,同步进入证据核实阶段。
 
22:31 · 识别出「按问题清单逐条测试」的行为
  • 我方动作 — 确认攻击者部分测试账户的命名与工具的执行迭代编号一一对应,属"把已知问题列表逐条验证"的典型 Agent 行为。
 
22:52 · 工具侧材料送达
  • 我方动作 — 收到操作者提交的自动化工具完整实例压缩包(源码 + 配置 + 全部运行数据),立即投入离线取证。
 
23:20 · 命中的口令浮出水面
  • 我方动作 — 在工具侧记录中找到了命中的确切口令与命中时刻。工具侧记录与服务端记录在秒级吻合。当时的第一反应是"这不可能"——命中的竟是字典的第 1 条。
  • 证据(双侧对照):
"为什么偏偏是第 1 条"见第八节——这不是运气,而是轮转喷洒的必然结果。
 
23:23 · 要求停止实例
  • 我方动作 — 确认该工具实例在攻击结束后仍以"心跳巡检"方式长时间存活(最后一次心跳在当日 22:37),立即通知操作者彻底停止全部实例,防止二次攻击。
  • 风险说明 — 实例存活意味着"剩余 30 条口令未跑完、更高权限账户尚未命中"这一计划仍可能被继续执行。
 
23:25 · 横向扩散范围确认
  • 我方动作 — 确认该工具在同一时段也对本机其他业务系统做了扫描与渗透尝试:对知识库系统做了登录喷洒(0 命中),对一条未在运维文档中记录的组网通道做了端口记录,并尝试通过容器代理接口访问内网地址。
 
23:34 · 全站弱口令复核
  • 我方动作 — 用攻击者本人的 31 条字典离线比对全站用户密码哈希,核查"若该工具跑完全部口令还会命中谁"。结果发现另一名同学的密码同样落在该字典中,立即从后台重置其密码并通知本人。
  • 结论 — 本次"止损"不是靠防御生效,而是靠攻击方预算耗尽(工具侧自述 ran out of budget)。这直接决定了后续必须做全站弱口令治理。
 
23:36 · 事件收束
  • 我方动作 — 团队陆续输出结论与总结,应急响应阶段结束,进入归档与复盘。

五、攻击方行为时间线(工具侧实录)

下列时间取自攻击工具自身的运行记录(规划引擎决策日志、执行脚本、结果文件落盘时间),可视为攻击方行为的直接实录;每条均可与服务端日志对上。
北京时间
攻击者动作
工具侧证据
02:23
任务下发:目标为平台域名,目标描述为「深度测试」
规划引擎首条记录(任务配置)
02:23
自动侦察启动:抓取首页与 robots.txt,读取平台公开配置接口
工具调用日志(命令记录)
02:24
一次性派出 6 条并行攻击方向
规划引擎意图派发记录(意图 #3~#11)
02:24 - 03:00
按方向并行执行:批量注册测试账户、探测各功能接口、测试限流与竞态、上传点测试
执行脚本 + 服务端请求记录
03:00
全端口扫描(全端口 + 高速率),识别主机对外暴露的服务清单
扫描工具输出文件(自带启动时间戳)
03:02 - 03:08
遍历平台公开的队伍信息接口(参数从 1 递增枚举),批量收集全站真实用户名
枚举脚本 + 结果文件(508 个用户名)
03:09 - 03:10
探测平台密码策略,据此定制一份 31 词的弱口令字典
字典文件落盘时间
03:11 - 03:18
对 498 个真实用户名做弱口令轮转喷洒(约每秒 1.3 次,低速规避限流)
喷洒脚本执行日志
03:15
命中一枚普通用户账户(字典第 1 条即命中),保存会话凭据,立即探测管理接口被拒
命中结果文件 + 凭据文件
03:00 - 03:49
并行对主机 SSH 服务做弱口令爆破(22 类用户名 × 177 条口令)
爆破脚本;工具侧自述「全部失败」
03:21 - 04:12
继续派出新方向:令牌伪造、Markdown/XSS、CSRF、管理员定向爆破、图像解压炸弹、限速绕过、基础设施端口探测
规划引擎新增意图记录(意图总数达 82 条)
04:04 - 04:12
用命中账户发起最后一轮:批量探测管理端点、枚举越权可能,全部被拒;随后攻击流量停止
执行记录 + 服务端请求记录
04:13 - 11:14
工具实例保持「心跳巡检」状态(约每 10 分钟唤醒一次),未再产生攻击流量
规划引擎心跳记录
11:17 - 11:21
(同一人)用真机浏览器登录平台,浏览排行榜、动态与本人队伍——这是操作者本人在攻击之后的活动,也成为关联其身份的时间锚点
服务端同源请求(非工具流量)
22:37
规划引擎最后一次心跳唤醒(仍要求产出新目标),但攻击未恢复
规划引擎最后一条记录

两条线并排看

北京时间
攻击方(工具侧)
我方(服务端可观测)
02:23
任务下发、侦察启动
首次出现非浏览器的脚本化探测
02:24 - 03:00
六条方向并行铺开、批量建号
短时间大量注册(秒级爆发)、多类接口试探
03:01
全端口扫描
3 秒内数十个特征路径请求,扫描器 UA 特征
03:02 - 03:08
用户名收割(1~600 遍历)
队伍接口被高频匀速遍历(机器节拍)
03:11 - 03:20
弱口令轮转喷洒
登录接口精确匀速失败(约 77 次/分)
03:15
命中、抓凭据、探管理权
同秒两次登录成功 + 立即管理接口 403
03:00 - 03:49
SSH 爆破(并行)
大规模失败鉴权,同源
03:54 - 04:03
内网跳板尝试、编码绕过试探
请求密度陡增,出现内网地址与数据库端口的代理访问
04:04 - 04:12
用命中账户做提权最后尝试
命中账户被集中用于管理端点探测,全部被拒
04:12
攻击流量停止
流量戛然而止,此后全天无同源脚本流量
11:17 - 11:21
工具无活动(仅心跳)
同源地址出现真人浏览器访问 → 身份锚点

六、ARTEX 是什么:工具的架构与运作方式

要理解这次攻击为什么"看起来像一个人、实际是一台机器",必须先理解这套工具的结构。

6.1 双层循环:规划者 + 执行者

ARTEX 采用「规划者(planner)+ 执行者(worker)」双层循环:
  • 规划者维护一张「探索图谱」,里面存着四类对象:
    • goal(目标,例如"深度测试")、
    • asset(资产,接口/主机/服务)、
    • intent(意图,即"下一步要打什么")、
    • fact(事实,即"打完得到的结论")。
  • 执行者每次领取一条 intent,自己决定怎么写脚本、跑什么命令,然后把结论写成 fact 回写图谱。
  • 规划者被心跳定时唤醒(本次为分钟级/十分钟级),读图 → 判断目标是否达成 → 派发新 intent。全程没有人参与。
本次任务的目标配置就这么一行:

6.2 一次典型的规划者唤醒(原文节选)

下面是规划者在某次心跳唤醒时收到的提示(工具内部模板,节选)。可以看到它的"工作方式"是读图、判目标、补方向、去重:
注意这段文字本身就是证据:它把"打了什么、结论是什么、下一步写什么"记得清清楚楚。清单一共 82 条意图——这就是它 1 小时 49 分钟里的完整"作战地图"。

6.3 它能干哪些事:82 条意图的分类

从规划记录里可以还原它的能力面。挑最有代表性的:
方向
代表意图(节选)
结论
版本与攻击面
#3 精确版本指纹 + 对照开源仓库历史提交与已知 CVE
官方最新版,无可用 CVE
注入类
#10 SQL/命令/模板注入,覆盖搜索、过滤、排序参数
全否定(参数化查询)
上传类
#5 头像/附件/图标上传的任意写入、路径穿越、存储型 XSS
全否定(内容哈希重命名 + 转码)
认证授权
#6 注册/登录、角色模型、IDOR/越权、管理员接口暴露
发现用户名泄露面
令牌伪造
#18/#25/#82 会话令牌是否为 JWT、能否 alg=none / 弱密钥伪造
非 JWT,架构上不可伪造
竞态条件
#11 flag 重复提交、容器生命周期、队伍邀请的并发竞态
计分有防重,但三个写接口可稳定触发 500
图像炸弹
#29/#36/#40 GIF 解压炸弹 → 升级为未认证服务中断
单请求可放大至 GB 级解码,但未致 OOM
限速绕过
#71 伪造 X-Forwarded-For、直连后端端口绕过代理限速
限速判定依据可伪造
横向扩散
#33/#39/#42 SSH、#43/#60/#78 知识库、#46 机器人、#49 K8s、#58 RDP、#63 邮件
绝大多数未取得有效认证
用户名收割
#45 遍历公开队伍接口,收割全员用户名 + userId
成功:465 队 / 508 人
弱口令喷洒
#35/#47/#48 对 498 个账号做轮转喷洒
命中 1 个(普通用户)
这张表说明了一个关键判断:它没有创造任何新技术。它走的每一步——枚举用户名、喷洒弱口令、爆破 SSH、翻源码找已知问题——都是教科书上的老套路。它唯一的优势是不知疲倦、不会遗漏、且会把每条结论完整记账。

6.4 操作者的环境特征(取证可比对点)

线索
内容
工具工作目录
某本机盘符路径下的 ARTEX 项目目录(全部脚本的工作目录变量均指向此处)
外部工具
nmap 二进制位于本机另一个独立目录(扫描输出里含其完整路径)
运行形态
Windows 本机部署;页面/API 监听 127.0.0.1:8787,流量代理 127.0.0.1:8788,内置 PostgreSQL 127.0.0.1:5433
HTTP 客户端
对平台用 curl,对知识库用 Python http.client,UA 均伪装为 Mozilla/5.0
LLM
由操作者在页面「LLM 配置」中填入模型地址、API Key 与模型名(本次为国产大模型)

七、渗透路径详解

本节按攻击的时间顺序,把整条链路拆成七个阶段。每个阶段给出:工具怎么想(意图)、实际怎么打(动作)、工具侧证据、服务端日志、结论。便于大家对照学习。

阶段一:指纹识别(02:23 - 02:24)

  • 意图(#3):对目标做精确版本指纹识别(前端构建哈希、公开配置接口、响应头、错误页),并对照开源仓库历史提交与已知 CVE。
  • 动作与结论:它第一件事不是打,而是先搞清目标是什么。提取出版本号与构建时间后,确认这是一个官方最新正式版,没有可直接利用的公开漏洞。
  • 附带产出:从前端代码中自动提取出 93 个 API 端点,作为后续攻击面清单。

阶段二:攻击面广度枚举(02:24 - 03:00)

  • 意图(#5/#6/#8/#10/#11):同时铺开六条线——版本指纹、SSRF/内网访问、文件上传与存储型 XSS、认证与授权、注入、业务逻辑竞态。
  • 覆盖面:账户注册、登录、头像上传、队伍操作、比赛报名、答案提交、容器启停、代理通道、密码找回、管理端点、实时通知连接——几乎是平台的全部功能面。
  • 多数结论是否定的(这恰恰说明平台核心实现是扎实的):
  • 批量建号:它按"漏洞类型 + 序号"自动生成账户名,快速铺开测试矩阵:
  • 服务端对应记录:注册请求秒级爆发(峰值 13~14 次/秒),合计 250 次注册请求。

阶段三:端口扫描与资产测绘(03:00 - 03:01)

  • 意图(#33):解析域名 → 扫描常见与管理端口 → 探测是否暴露 Docker API、数据库、调试面板等后端服务。
  • 动作:先对常见端口做定向扫描,再对全端口做高速率扫描。
  • 服务端对应记录:扫描器特征在 3 秒内集中爆发(nmap NSE 特征路径与随机标记):

阶段四:用户名收割(03:02 - 03:08)——本次攻防的关键转折

这是整个事件最值得复盘的一步。
  • 意图(#45):遍历公开队伍接口,收割全站用户名与 userId,作为喷洒目标名单。
  • 关键接口:队伍详情接口无需登录即可访问,且响应里直接包含队伍成员的用户名。实测(不带任何 Cookie)如下:
  • 攻击者的脚本(工具侧的枚举脚本原文,变量名保留,域名已替换):
  • 服务端对应记录:该接口在攻击窗口内被请求 968 次,编号从 1 连续遍历,呈完美机器节拍(每分钟固定次数):
  • 产出:465 支队伍 / 508 个去重用户名。
  • 结论:用户名不是猜出来的,是读出来的。 这一步把原本"用户名 + 密码"双未知的问题,压缩成只剩"密码"一个未知量。
这一步打破了一个常见误解:"攻击者要先知道用户名才能撞库"。事实上,只要有一个能枚举出用户名的公开接口,用户名就不再是秘密。
 

阶段五:弱口令轮转喷洒(03:11 - 03:18)

  • 意图(#35):利用收割来的全员用户名 + 已确认的"登录无锁定、无独立限速",用常见弱口令低速喷洒,重点尝试命中高权限账号。
  • 字典定制:先用一个探测账户测出平台的密码策略,据此构造 31 条字典。它不使用纯通用字典,而是混合三类构造:
类别
构造思路
说明
通用弱口令
大小写混合词 + 连续数字
最常见的「首字母大写 + 小写词 + 6 位数字」形态,以及经典英文词 + 数字
本校构造词
校名缩写 + 年份
针对平台所属学校的定向构造,覆盖多个年份
赛事构造词
赛事名 + 年份
针对 CTF 赛事名的定向构造,覆盖多个年份
  • 喷洒脚本的关键逻辑(原文节选,变量名保留):外层循环是口令、内层循环是用户名。
  • 这个打法有两个目的:
  1. 规避锁定与限流——同一账号在很久以后才会被再次尝试,不会触发"连续失败锁定";整体速率控制在限流阈值之下;
  1. 最大化"一条弱口令命中多个人"的概率——先用覆盖面最广的口令扫一遍所有人。
  • 目标筛选:498 个(剔除了自建的测试账户与管理员账户;脚本里有一份明确的排除名单)。
  • 服务端对应记录:登录接口在 03:11 ~ 03:20 精确匀速失败,呈机器节拍:
  • 结果:第 1 条口令跑完 498 个目标即命中 1 个,且全程唯一命中。
全站 508 人里,只有2个人的密码落在这 31 条里。这不是平台的技术漏洞,这是一次概率收割。
 

阶段六:账户接管与权限提升尝试(03:15 - 04:12)——全线失败

  • 命中后动作序列,在服务端一秒钟内全部可见(这就是"脚本接管"的铁证):
  • 工具侧命中记录(原文,敏感字段已隐去):
  • 它的自我复盘(规划者原文节选):
  • "用完即弃"的判定依据:它没有修改该账户的任何凭据——未改密码、未改邮箱、未改用户名。这正是"账户被盗用"而非"本人操作"的典型特征。
  • 提权尝试(全部失败):
    • 尝试伪造成会话令牌 —— 架构上不可行(非 JWT,角色存于数据库);用该会话访问管理配置、用户列表、比赛编辑、题目 flag 管理、令牌管理等端点 —— 全部 403;
    • 对管理员账号做定向弱口令爆破 —— 未命中;
    • 尝试账户自助接管、批量赋值提权 —— 不可行(它自己确认 #44 无自助接管路径)。

阶段七:主机与旁站扩散(03:00 - 04:12)——全线失败

应用层打不动,它转向主机与同机的其他业务系统:
  • SSH 爆破(#42/#57):22 类用户名 × 177 条口令,约 3,894 组合,全部失败。
  • 同机知识库系统(#43/#59/#60/#76/#78):这是一个独立的 Django 应用,与本平台同机部署。
  • 容器代理接口(#20/#72/#81):尝试把平台的容器代理通道当作内网跳板。
  • 其他服务:邮件服务(非开放中继、头注入被拒)、K8s API 与 kubelet(匿名认证已关闭,全部 401)、机器人管理界面(鉴权已加固)、远程桌面 —— 完成扫描与指纹识别,均未取得有效认证。
  • 未识别高端口的意外收获:扫描记录到了一组未在运维文档中记录的组网通道端口——这条通道也因被攻击者扫到而进入了我们的整改视野。

八、双侧证据比对与关键问答

8.1 逐项比对

比对项
工具侧记录
服务端日志
结论
活动起止
首条决策 02:23:23;末次流量 04:12
首个请求 02:23:29;末个请求 04:12:09
吻合(秒级)
独特工具指纹
工具独有组件标识串(版本号形式)
该标识串在全平台 15 天日志中仅出现 2 次,全部落在本次攻击窗口
铁证级吻合
端口扫描
扫描输出文件自带启动时间戳与随机标记
同期出现扫描器特征路径与同一随机标记
吻合(同一会话)
用户名收割
枚举脚本 + 结果文件(508 个用户名)
队伍接口被遍历 968 次,含对受害账户所在队伍的访问
吻合(时序、数量级一致)
命中时刻
落盘命中记录、保存凭据
03:15:01 同秒两次登录成功 + 立即管理接口被拒
吻合(秒级)
测试账户群
账户名按「漏洞类型 + 序号」自动生成
注册了同批账户名
吻合(命名规则完全对应)
内网穿透尝试
脚本中出现指向内网地址与数据库端口的代理请求
同期日志出现同样的内网地址与端口组合
吻合
SSH 爆破
自述 22 用户 × 177 口令,失败
累计 2,000 余次失败鉴权
吻合(口径差异见 8.3)
11:17 浏览行为
工具此刻无活动(仅心跳)
同期出现真人浏览器访问
同一出口,经核查即操作者本人

8.2 「为什么第一条口令就命中」

这是复盘中最反直觉的一点,需要单独说清。结论:这不是运气,而是轮转喷洒的必然结果。
第一,字典不是给某个人定制的。 它由三类构造词混合而成(见阶段五)。如果用"定向字典"的思路看,它应该只包含某个人的特征词——但事实上,我们在全站范围用这份字典做离线比对,发现有两个人的密码都落在里面:
一份"为某人定制"的字典,不可能同时装着另一个人的口令。所以它是通用弱口令 + 平台构造词的组合字典,命中对象是"平台上恰好使用弱口令的人",而这样的人不止一个。
第二,"命中在第几条"取决于受害者用哪条口令,与运气无关。 轮转喷洒的执行顺序是:拿第 1 条口令把 498 个人全试一遍,再换第 2 条……因此:
  • 若受害者的口令是字典的第 1 条 → 攻击者在第 1 轮就命中,外人看着"第一条就中,太巧了";
  • 若受害者的口令是字典的第 11 条 → 攻击者要跑到第 11 轮才命中,完全不会引起怀疑。
本次被命中的账户,其口令恰好是这份字典里最通用、覆盖面最广的那一条,所以"第 1 轮"就中了。而另一位同学的口令排在第 11 条,攻击者还没跑到就停了——工具侧自述原因是 ran out of budget(预算耗尽),计划里"剩余 30 条口令未跑完"。
第三,换算成概率,数字并不惊人。 498 个目标 × 31 条口令 = 15,438 次尝试,命中 1 次。这属于偏低的命中率(业界常见弱口令字典对一般用户库的命中率在 0.5% ~ 5% 量级)。也就是说,这份字典的效率并不高,只是恰好有一个人把门锁在了它第一把钥匙的形状上。
真正的风险点在这里:本次的"止损"不是靠防御生效,而是靠攻击方预算耗尽。如果它预算充足、继续跑完 31 条,受害者就会从一个变成两个。

8.3 三处需要修正或说明的认知差异

1)命中的密码与现场反馈不一致——现已澄清
事件初期,我们在向受害用户核实"原密码"时,得到一个候选值,但该候选经密码哈希比对与平台账户全部历史版本均不匹配,当时判断为"用户记错"。工具侧记录提供了确定答案:实际命中的口令,是攻击者字典的第 1 条,一个符合平台密码策略的常见 8 位弱口令——它与受害用户提供的候选值并不相同。
这一条也顺带解释了另一个疑问:攻击者的字典并非"用户名衍生型"(我们曾验证该用户名的衍生变体一千余个,全部不匹配),因此其口令来源是通用泄露字典,与用户名无关。
2)SSH 爆破数量口径不同,属合理差异
工具侧自述尝试了近 4,000 个"用户名 × 口令"组合,而服务端记录到的失败鉴权约 2,000 次。差异原因已由工具侧自证:爆破后期服务端开始丢弃连接(工具侧日志中出现连接横幅读取错误),大量组合在握手阶段即被拒绝,未进入鉴权环节。这实际上说明平台的连接层限速对爆破起到了抑制作用。
3)队伍枚举数量略有出入,不影响结论
枚举脚本在上界范围内多次重跑,两次落盘的队伍总数略有不同(说明期间队伍数据有变动),但受害账户所在队伍被准确命中这一点在两侧都得到确认。

九、安全提醒

9.1 请不要使用弱密码注册任何平台

在平台未采取有效防护措施时,弱密码极易遭受密码喷洒式攻击。事后,我们使用攻击者字典做了离线复盘,发现有两个账户使用了弱密码:
  • 一名同学的密码为 Aa123456,恰好位于攻击者字典的第 1 位;
  • 另一名同学的密码为 P@ssw0rd,位于攻击者字典的第 11 位。
我们已对相关账户采取重置密码措施。在此提醒各位同学,尤其是有平台管理员权限的管理员,务必保护好自己的账户安全,拒绝使用弱密码。

9.2 请自查你的密码是否属于「弱密码族」

如果你在任何平台使用的密码属于以下任一形态,请立即更换,并同步更换你在邮箱、社交、学校系统中复用的同一密码:
  • 「常见词 / 姓名 / 学号 / 年份 + 123 / 000」;
  • 「首字母大写 + 小写词 + 连续数字」(本次被命中的两个密码都属于这一族);
  • 任何在公开泄露字典中出现过的口令。
密码管理器生成的高强度随机密码,是抵御这类"喷洒式"攻击最有效、成本最低的手段。

9.3 关于 Agent 与 AI 攻击

本次真实的 ARTEX 攻击事件让我们深刻认识到:AI 攻击对常规防护手段构成了颠覆性挑战。
一方面,我们需要立即提升自身防护水平;另一方面,也再次警告所有同学:
  • Agent 的攻击行为可能极其恶性且超出你的掌控——本次工具在得到"深度测试"指令后,不仅打了平台,还去尝试渗透同机的其他业务系统(已超出提示词范围),甚至把"剩余口令未跑完、高权限账号未命中"写进了自己的待办计划;
  • 务必看管好自己的 Agent,防止它做出威胁安全的行为;
  • 未经许可,严禁使用 Agent 和相关工具对未授权平台进行渗透测试——这是严重违法行为。
对于比赛期间采用 Agent 直接攻击平台的选手,我们将采取直接封禁、并上报网络中心的策略。

十、复盘结语:两条线合在一起看

把两侧时间线叠起来,会看到一个清晰的事实:从攻击发生到确认攻击停止,我们对攻击行为的形态判断始终正确;而对"操作者是谁"的结论,在拿到更硬的证据后被及时纠正。
  • 攻击者 03:02 开始遍历队伍接口,我们 03:08 就识别出该接口携带了不应公开的信息;
  • 攻击者 03:11 开始喷洒,我们把它的"匀速失败"定性为撞库;
  • 攻击者 03:15 命中,我们在同一分钟确认"撞库命中一枚真实账户",并抓住了"命中即探测管理权"的行为特征;
  • 攻击者 04:12 停手,我们当天就完成了全量溯源与证据固定;
  • 攻击者 11:17 又用同一出口、以真人浏览器登录平台,我们当时把它记作"与凌晨脚本无关的浏览";经核查,这次访问同样出自操作者本人——它反而成了锁定其身份的时间锚点;
  • 而我们对"账户主人是否自导自演"的怀疑,在 21:14 达到顶点,又在 21:19(密码证伪)、21:30(登录史全部为浏览器)、23:20(喷洒原文)三个节点上被逐层推翻。
真正让这次复盘的结论从"未遂"变成"闭环"的,是拿到攻击方工具实例运行记录之后的那次比对:两侧的时间、指纹、行为严丝合缝。这既确认了攻击主体,也替我们补齐了最后一块拼图——那枚命中的口令到底是什么,以及它为什么偏偏"第一条就中"。
最后,防守方的应对逻辑并没有因为"对手是 AI"而改变:收缩攻击面、消除弱口令、加固认证、保留可观测性。本次事件中,平台之所以能全程掌握攻击动向、在攻击停止后完整复现整条链路,靠的正是这三件事我们平时在做——
  • 全量日志留存(每一次请求、每一次登录都有记录);
  • 数据库级审计(账户关键字段的变更可追溯、可跨时间比对);
  • 流量形态分析能力(从"每分钟固定次数"这种机器节拍里识别出攻击)。