材料基础:① 平台服务端全量原始日志(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 赛事名的定向构造,覆盖多个年份 |
- 喷洒脚本的关键逻辑(原文节选,变量名保留):外层循环是口令、内层循环是用户名。
- 这个打法有两个目的:
- 规避锁定与限流——同一账号在很久以后才会被再次尝试,不会触发"连续失败锁定";整体速率控制在限流阈值之下;
- 最大化"一条弱口令命中多个人"的概率——先用覆盖面最广的口令扫一遍所有人。
- 目标筛选: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"而改变:收缩攻击面、消除弱口令、加固认证、保留可观测性。本次事件中,平台之所以能全程掌握攻击动向、在攻击停止后完整复现整条链路,靠的正是这三件事我们平时在做——
- 全量日志留存(每一次请求、每一次登录都有记录);
- 数据库级审计(账户关键字段的变更可追溯、可跨时间比对);
- 流量形态分析能力(从"每分钟固定次数"这种机器节拍里识别出攻击)。
