ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

从评分点到验证命令:信息安全赛项任务书拆解与落地

从评分点到验证命令:信息安全赛项任务书拆解与落地

简介:面向职业院校信息安全管理与评估赛项选手与指导教师的2015年高职组“神州数码”杯正式任务书,可作为备赛训练、赛题复盘与教学案例使用。资源为1个PDF文件,约287KB,完整保留赛项时间安排、三阶段任务划分、分值分布及提交规范,并给出网络拓扑、IP地址规划表与设备初始化信息。内容覆盖防火墙、WAF、日志系统、堡垒机与三层交换机的平台搭建和策略配置,也包含IIS安全加固与证书签发、数据库攻防、CSRF与XSS、密码嗅探、文件包含、SQL注入及Linux系统防护等攻防要点,第三阶段还设置系统加固与分组对抗。已有323人学习,适合按任务书还原实验环境、拆解评分点并开展专项训练。

1. 任务书先翻最后一页:信息安全管理与评估赛项到底在考什么

拿到一份高职组信息安全管理与评估赛项的任务书,多数人第一件事是翻拓扑图,我习惯先翻最后一页的提交要求——那里写着交什么文件、放在哪个目录、叫什么名字,判分脚本只认这几行。2015 年那一版的任务书尤其典型:拓扑本身不复杂,可评分点密密麻麻,任何一个接口没起来、任何一条策略顺序写反,后面靠它串起来的几十分会一起塌掉。这份任务书的本质不是考卷,是一张把“搭平台、配安全设备、做渗透验证、出应急报告”压在几个小时里跑完的流水线图纸,它不考你背了多少名词,考的是能不能连续交付可被脚本验证的结果。带队教练可以拿它拆训练计划,已经做了几年运维、想补一套完整攻防闭环的人也能照着练。

2. 三阶段任务书的拆解:把评分点翻译成设备、接口与验证命令

任务书正文看起来是散的要求,其实结构固定:先给环境和约束,再按阶段列任务,最后定交付物。拆不动它的人,通常是把它当成“知识考试大纲”在读,一条条对着找自己会不会;拆得动的人,会把它当成“验收清单”在读,每一条都追问三个问题——谁执行、执行完长什么样、判分方怎么确认。

2.1 三阶段的分值分布与时间预算

这类赛项一般分成三段:平台搭建、安全设备配置与应用加固、应急响应与评估取证。三段的判分方式完全不同,前两段高度依赖自动化探测,第三段高度依赖人工比对文档,所以时间分配不能平均。

阶段典型交付物判分方式我一般给的时间占比
平台搭建交换机、路由器、防火墙配置,全网连通脚本远程登录回读配置 + 主动探测端口与路由35%
安全设备与加固访问控制策略、日志上报、主机基线、Web 加固配置回读 + 功能验证(探测被拦截、日志确有记录)40%
应急响应与评估取证材料、处置记录、评估报告人工比对关键词、时间线、结论是否自洽25%

第一段是基础,分值密度最高但最不该丢,因为它的判分几乎全是“通/不通”的二元结果,没有部分分。第二段是拿分的地方,同样一条“禁止外部访问数据库”,只写策略不写日志可能只拿一半。第三段看起来是写作题,实际上是前面所有操作的结果汇总,前面做干净了,这一段就是抄自己写过的命令。

有个反直觉的经验:很多人把时间全砸在第一段追求“完美拓扑”,结果第二段只写了一半策略,第三段报告空着交。正确做法是第一段做到“全部连通 + 边界可控”就立刻停手,把 15% 左右的时间预留给最后写报告,因为报告是唯一不需要再动设备就能拿分的地方。

2.2 把每一条要求还原成设备与接口

任务书的要求都是自然语言,比如“外部网络只能访问对外发布的 Web 服务,不允许直接访问数据库”。这句话要翻译成三样东西:设备、配置对象、验证手段。

任务书原句落到哪台设备配置对象验证方式
外部只能访问 Web 服务边界防火墙安全域策略:untrust 到 dmz,目的地址为 Web 主机,服务为 http/https,动作放行从外网侧主机访问数据库端口,应当超时
不允许直接访问数据库边界防火墙同区域默认拒绝,且不写放行从外网侧主机访问数据库端口,应当超时
内网用户可访问外网边界防火墙 + 出口路由源地址转换 + trust 到 untrust 放行内网主机访问公网地址,抓包确认源地址被转换
所有访问必须留痕防火墙 + 日志服务器会话日志开关、日志服务器地址、策略命中计数触发一次访问后在日志服务器上按源地址检索

翻译的时候有个细节容易被忽略:任务书里“不允许”这三个字,很多时候不需要你写一条拒绝策略,需要的是“默认拒绝 + 不写放行”。多写一条拒绝策略本身没错,但如果它挡住了后面本该放行的流量,你会在验证阶段浪费大量时间去查一条自己刚写的东西。

2.3 用一张表锁定评分点与验证命令

把任务书每一条编号,做成一张自己用的验收表,赛前就准备好验证命令。这张表的价值在于:比赛现场脑子会乱,但表不会乱,做完一条划一条,剩下的时间就能精确估算。

编号评分点验证命令期望结果常见失败原因
1VLAN 间互通ping目标网段网关通Trunk 没放行对应 VLAN
2外网访问 Webcurl -I http://<公网地址>返回 200源地址转换没写或策略顺序错
3数据库被阻断nc -zv <数据库地址> 3306超时,非 refused默认动作设成了放行
4日志上报成功在日志服务器按时间检索能查到本次会话日志服务器地址写错、时区不一致
5主机基线回读口令策略与服务列表与要求一致改了没保存,重启回滚

这张表还有个副作用好处:写完第 5 行你会发现,很多任务是同一条命令的不同参数,可以批量做。真正拉开差距的不是会不会配,是能不能在四十分钟内把三条策略拆成二十条可验证的检查项,然后按检查项干活。

3. 平台搭建落地:VLAN 规划、防火墙安全域与策略顺序的三个必调参数

平台搭建这一段,任务书给的信息通常只有一张拓扑和一张地址表,剩下的规划要自己做。规划做得好不好,直接决定后面加固和取证阶段顺不顺——比如管理网段单独划出来,后面远程回读配置才不会因为一条策略把自己挡在外面。

3.1 拓扑落地:VLAN 划分与地址规划表

先把网段定死,再动设备。我一般固定四个段:用户段、服务器段、管理段、对外发布段。管理段必须独立,这是铁律,因为加固阶段你会关掉一堆服务,管理面一旦和业务面混在一起,很容易把自己锁死。

VLAN网段网关用途接入设备
10192.168.10.0/24192.168.10.254办公用户接入交换机
20192.168.20.0/24192.168.20.254对外发布的 Web接入交换机
30192.168.30.0/24192.168.30.254数据库接入交换机
99192.168.99.0/24192.168.99.254设备管理核心交换机

规划完之后落配置,接入层这一段最容易出问题的是 Trunk 放行范围,很多人图省事直接放行全部 VLAN,结果管理段广播到不该到的地方,后面做流量分析时全是噪声。

# 接入交换机:先建 VLAN,再收窄 Trunk 放行范围 system-view vlan 10 description office vlan 20 description dmz-web vlan 30 description db vlan 99 description mgmt interface GigabitEthernet0/0/1 port link-type access port default vlan 10 interface GigabitEthernet0/0/24 port link-type trunk port trunk allow-pass vlan 10 20 30 99 undo port trunk allow-pass vlan 1 save

这段配置的逻辑很直白:access 口把终端固定到某个 VLAN,trunk 口只放行业务真正需要的 VLAN,最后显式撤掉 VLAN 1 的放行。参数上唯一需要按现场改的是端口号和 VLAN 号,注意port default vlan之后接口会退出 access 口默认的 VLAN 1,如果不撤 VLAN 1,很多设备会同时带着两个 VLAN 标签跑,抓包时会看到意外的带标签帧。

save这一步单独说。命令行配置完如果不保存,设备重启或者被别人切了电,前面半小时就白干。这是现场最高频的失误之一。

3.2 防火墙安全域与策略顺序的三个必调参数

防火墙这一段,任务书通常只写“实现边界访问控制”,具体策略要自己推。推的时候有三个参数必须调对,调错任何一个都会让后面的验证陷入混乱。

第一个是默认动作与策略匹配顺序。绝大多数设备是自上而下首条匹配,匹配到就停。这意味着一条写在最上面的“任意到任意放行”会让下面所有策略失效,而且验证时会发现“阻断生效了”——因为流量确实通了,你以为策略写了一堆其实一条没生效。我的做法是:新策略一律插到最上面,写完再回头把顺序按“具体优先、宽泛在后”重排,默认动作用显式拒绝。

第二个是会话老化时间。数据库连接池、日志上报这类长连接业务,如果老化时间设得太短,表现是“ping 得通、端口也通,但业务时好时坏”。比赛现场这种问题极难定位,因为每一层单独看都是通的。常见做法是把老化时间从默认的短值调到分钟级,或者针对特定服务单独放长。

第三个是策略命中计数与日志开关。这是排查“流量到底走到了哪一条策略”的唯一手段,也是向判分方证明策略确实生效的证据。不开计数,你只能靠猜。

# 边界防火墙:先划安全域,再写策略,最后开日志与计数 system-view firewall zone trust add interface GigabitEthernet1/0/1 firewall zone untrust add interface GigabitEthernet1/0/2 firewall zone dmz add interface GigabitEthernet1/0/3 security-policy rule name permit-web-from-untrust source-zone untrust destination-zone dmz destination-address 192.168.20.10 32 service http https action permit session logging rule name deny-db-from-untrust source-zone untrust destination-zone dmz destination-address 192.168.30.10 32 service mysql action deny default action deny

逐条看:安全域必须先绑定接口,没绑定的接口流量会走默认域,策略写对了也不生效;rule name只是标识,方便回读时肉眼核对,命名建议把方向和对象都写进去;destination-address一定要精确到主机,写网段会把同段的其他服务一起放行;session logging决定这条策略的会话能不能被日志服务器收到;default action deny是显式写出来的默认拒绝,比依赖设备隐含默认更可靠,回读配置时也一目了然。

参数怎么改:如果任务书要求“外部可访问多个 Web 主机”,把目的地址改成一个地址组,把主机逐个加进去,不要图快写成网段。如果要求“只允许特定源地址访问”,加source-address,方向和对象别搞反。

3.3 配置即文档:用脚本批量回读与比对

比赛现场最怕的不是不会配,是配完了不知道自己配了什么。我的习惯是每完成一个阶段就远程回读一次配置,存成带时间戳的文件,最后提交时直接拿来回填报告。

#!/usr/bin/env bash set -euo pipefail HOST="192.168.99.1" USER="netops" STAMP=$(date +%Y%m%d-%H%M) OUT="capture/fw-core.${STAMP}.conf" mkdir -p "$(dirname "$OUT")" # BatchMode 避免交互式输密码卡住整个脚本 ssh -o BatchMode=yes -o ConnectTimeout=8 "$USER@$HOST" \ "display current-configuration" > "$OUT" # 记录哈希,证明这份回读在提交前没被改过 sha256sum "$OUT" >> capture/chain.log wc -l "$OUT"

脚本里三个参数值得说:ConnectTimeout给 8 秒,是因为现场网络偶尔抖,给太短会误判设备不可达,给太长会把整个脚本卡住;BatchMode=yes让脚本在需要交互时直接失败而不是挂起;sha256sum看起来多余,但在第三阶段写报告时,你能证明“报告里引用的配置就是当时回读的那一份”,这在人工比对环节是加分项。

回读完还要能快速看出“这次和上次差在哪”。配置动辄几百行,肉眼比对不现实,用脚本只挑策略相关的行做差分。

import difflib import pathlib BASE = pathlib.Path("capture/fw-core.baseline.conf") CURR = pathlib.Path("capture/fw-core.current.conf") # 只保留策略与安全域相关的行,降低噪声 KEYS = ("security-policy", "rule name", "source-zone", "destination-zone", "source-address", "destination-address", "service", "action", "default action", "firewall zone") def extract(path): lines = path.read_text(encoding="utf-8", errors="ignore").splitlines() return [l.strip() for l in lines if any(k in l for k in KEYS)] diff = difflib.unified_diff( extract(BASE), extract(CURR), fromfile="baseline", tofile="current", lineterm="", n=2 ) print("\n".join(diff) or "无差异")

KEYS这个元组是核心参数,里面的关键词按你所用设备的实际输出调整,判断标准是“要覆盖所有影响流量走向的行”。n=2表示差异上下文各保留两行,太小看不出层级关系,太大输出会刷屏。这个脚本跑出来的结果直接贴进报告,比手写“我配置了若干策略”有说服力得多。

4. 渗透验证与加固回写:Web 题在二十分钟内拿确定分的做法

第二阶段的 Web 部分最容易失控:给了靶场地址,很多人一头扎进去挖,挖到一半时间没了,加固也没写。稳妥的做法是把它拆成三段固定动作——收集、验证、回写,每段限时,超时就跳到下一段。

4.1 信息收集的固定动作清单

收集阶段只做四件事,按顺序做,每件都有明确产出物。

TARGET="192.168.20.10" mkdir -p scan # 1. 全端口扫描,先知道有什么在跑 nmap -Pn -sT -p- --min-rate 2000 -oN scan/full.txt "$TARGET" # 2. 对开放端口做服务指纹 nmap -Pn -sV -p 80,443,3306,8080 -oN scan/service.txt "$TARGET" # 3. 目录枚举,字典别贪大,两千条跑不完就换五百条 gobuster dir -u "http://$TARGET" -w dict/common-2k.txt -t 20 -o scan/dir.txt # 4. 抓页面里所有参数名,这是后面验证的入口 curl -s "http://$TARGET/" | grep -oE 'name="[^"]+"' | sort -u > scan/params.txt

参数上--min-rate控制发包速率,现场网络带宽有限,设太高会丢包导致漏端口,一般两千左右够用。-sT用完整 TCP 连接,比半开扫描慢但结果更可靠,比赛环境不追求隐蔽性,可靠优先。目录枚举的字典大小是关键权衡:大字典命中率高但耗时长,我的经验是先用小字典跑一轮,把命中的路径记下来,时间还有富余再上大字典。

收集完,产出应该是四份文件:端口列表、服务版本、目录清单、参数清单。接下来所有验证动作都从这四份文件里挑,不再凭感觉。

4.2 漏洞点的验证与取证留痕

验证的目的不是拿到数据,是拿到“可复现、可提交”的证据。最常见的几类问题,验证方式其实很统一:给一个正常输入,再给一个变形输入,比较响应差异。

# 布尔型注入验证:对比两次响应的长度与状态码 URL="http://192.168.20.10/login.php" curl -s -o /tmp/a.html -w "%{http_code} %{size_download}\n" -d "user=admin&pass=test" "$URL" curl -s -o /tmp/b.html -w "%{http_code} %{size_download}\n" -d "user=admin' and '1'='1&pass=test" "$URL" curl -s -o /tmp/c.html -w "%{http_code} %{size_download}\n" -d "user=admin' and '1'='2&pass=test" "$URL"

判断逻辑是:如果第二次和第一次的响应长度接近,而第三次明显不同,说明输入被带进了查询条件且影响结果。-w把状态码和响应体长度打出来,这两个数字就是证据;-o把响应体落盘,报告里要附。参数上不要用--data-urlencode之外的拼接方式,否则特殊字符会被 shell 吃掉,你会误判成“没有漏洞”。

把这类比对写成脚本,一次跑完所有参数,能省下大量时间。

import hashlib import pathlib import requests BASE = "http://192.168.20.10" TIMEOUT = 5 OUT = pathlib.Path("evidence") OUT.mkdir(exist_ok=True) # (路径, 参数名, 正常值, 变形值) CASES = [ ("/login.php", "user", "admin", "admin' and '1'='2"), ("/search.php", "kw", "test", "test' and '1'='2"), ] session = requests.Session() for path, param, normal, payload in CASES: results = [] for value in (normal, payload): r = session.post(BASE + path, data={param: value}, timeout=TIMEOUT) digest = hashlib.sha256(r.content).hexdigest()[:16] results.append((len(r.content), r.status_code, digest)) name = f"{path.strip('/').replace('/', '_')}_{param}.txt" (OUT / name).write_text( f"normal={results[0]}\npayload={results[1]}\n", encoding="utf-8" ) print(name, results)

脚本里hashlib那一步不是为了安全,是为了给响应体一个短标识,方便在报告里写“两次响应哈希不同”而不用贴整页 HTML。TIMEOUT设 5 秒,靶场响应一般很快,超过这个值多半是目标挂了或者被拦了。CASES列表是唯一需要手填的部分,从 4.1 生成的参数清单里挑,优先挑带查询语义的(搜索、登录、详情页 id)。

留痕这件事要养成习惯:每个结论都落一个文件,文件名带目标和参数,内容带正常值、变形值、两次响应的关键指标。现场时间紧,但这一步花不了两分钟,回报是报告阶段不用重跑。

4.3 加固回写:把 exploit 变成配置项

验证完之后必须回写加固,否则这一段的分数是半份。加固不是把服务关掉,是在保留功能的前提下收紧边界,这一点任务书通常写得很明确。

# Web 服务加固:上传目录只允许静态访问,禁止解析脚本 server { listen 80; server_name _; root /var/www/html; location ^~ /uploads/ { location ~ \.(php|jsp|asp|aspx)$ { deny all; } add_header Content-Disposition "attachment"; } # 关掉版本号回显,减少指纹信息 server_tokens off; # 限制请求体大小,防止超大文件上传打满磁盘 client_max_body_size 8m; }

location ^~的优先级高于正则匹配,这样/uploads/下的请求会先命中这个块,再判断后缀,顺序反过来就失效了。add_header Content-Disposition "attachment"强制下载而不是在浏览器里渲染,能挡住一部分基于解析的执行路径。client_max_body_size设 8m 是按业务实际需要调的,设太小会误伤正常上传,设太大等于没设。

应用侧还要配套两件事:上传文件的存储目录不要放在 Web 根目录下,或者放了也要用上面的规则封死解析;数据库账号按最小权限授予,只给业务表增删改查,不给drop、不给跨库。这两条在任务书里常常是独立评分点,写完记得在验收表上划掉。

5. 避坑与现场排查:让分数凭空蒸发的六个细节

这一段是最贵的经验。下面六条都是我在模拟环境里真实翻过的车,每条按现象、原因、解决写,照着对一遍能省下不少时间。

5.1 设备与策略类故障

第一条:配置全对,重启后全没了。现象是验证阶段一切正常,交卷前设备被统一重启,再连上去发现配置回到出厂状态。原因是命令行配置只写进了运行配置,没写进启动配置。解决办法是把保存动作做成习惯,每个设备配完立刻保存,并且在回读脚本里加一步校验启动配置的行数是否与运行配置一致。更稳妥的做法是把保存命令写进每段配置脚本的最后一行,不给自己忘的机会。

第二条:策略写了一大堆,实际一条没生效。现象是阻断类要求全部“验证通过”——因为流量根本就没被拦,你以为策略生效了,其实是一条靠前的宽泛放行先匹配走了。原因是设备按首条匹配,宽泛规则写在上面会吃掉下面所有具体规则。解决办法是配置完成后回读策略列表,从上往下读一遍,确认顺序是“具体在前、宽泛在后”,并且默认动作是显式拒绝。验证时不要只测“该拦的拦住了”,还要测“该放的放行了”,两个方向都测才能发现顺序问题。

第三条:管理面被自己的策略切断。现象是刚配完一条限制策略,SSH 立刻断开,再也连不上,只能想办法从别的路径进去或者重启。原因是策略没有给管理网段留放行,或者管理流量走的安全域不在放行范围内。解决办法是任何限制性策略生效前,先在策略列表最上面单独加一条“管理网段到设备本身全部放行”,并且这条规则永远不要删。这条规则不影响业务判分,但能保住你后面所有的操作。

5.2 时间与日志类故障

第四条:日志里有记录,但时间对不上。现象是日志服务器上确实收到了会话日志,可时间戳和你在靶场上看到的操作时间差了八小时,写报告时无法把攻击行为和日志条目对应起来。原因是设备和日志服务器的时区配置不一致,一个用本地时间一个用协调世界时。解决办法是开局第一件事就把所有设备和日志服务器的时间源统一,明确记录用的是哪个时区,并在报告里写清楚这个基准。时间线对不上,第三阶段的分数基本就没了。

第五条:日志开了,但查不到想看的会话。现象是策略上开了会话日志,日志服务器上按源地址检索却什么都没有。原因通常是三个:日志服务器地址写错、日志级别不够、或者这条流量压根没匹配到你开日志的那条策略。排查顺序建议从策略命中计数入手,先确认这条策略有没有被命中,再查日志通道。命中计数为零说明流量走了别的路径,问题不在日志,在策略。

5.3 交付与管理面类故障

第六条:所有活都干完了,交付目录被判分脚本判为空。现象是自查一切正常,成绩出来某一整段零分。原因是文件名大小写不符、目录层级多了一层、或者打包时把隐藏文件漏了。解决办法是赛前就把提交清单做成一张表,逐项对名字、对路径、对文件大小,交卷前十分钟只做这一件事,不再动设备。这条听起来最low,但它是唯一一个“做完了却拿零分”的入口。

排查顺序上,我的习惯是自下而上:先确认链路和地址,再确认策略命中,最后才看应用。反过来查,你会在应用层浪费大量时间去怀疑一个其实没问题的服务。

6. 应急响应取证:把日志里的时间戳拼成一条能交上去的证据链

第三阶段给的材料通常是一份流量包、一批设备日志、一台被做过手脚的主机,要求说清楚“什么时候、从哪来、做了什么、现在处置到什么程度”。这一段拼的不是技术深度,是证据链的完整性——每一条结论后面都得跟一个可核对的时间戳和来源。

6.1 时间线重建:三处取时间,一处对齐

我固定从三个地方取时间:边界设备的会话日志、主机的系统审计日志、应用的访问日志。三处的精度和时区都不一样,先全部换算到同一个基准,再排序。

# 取证前先固化,避免"我改一下再看"把证据链弄断 EVID="case/web01" mkdir -p "$EVID" # 镜像只读挂载,绝不写回原盘 mount -o ro,loop,offset=$((2048*512)) disk.img /mnt/evidence # 只提取案发时间窗内的日志,并逐个记哈希 find /mnt/evidence/var/log -type f -newermt "2025-06-01 00:00" -print0 \ | xargs -0 sha256sum > "$EVID/hash.txt" # 把三处日志的时间字段抽出来,统一成同一格式后合并排序 grep -hE "2025-06-01" /mnt/evidence/var/log/auth.log >> "$EVID/timeline.raw"

offset=$((2048*512))是按常见分区起始扇区算的,实际值要用分区工具确认,写错挂载会失败或者挂到错误的分区上。-o ro是硬性要求,取证阶段任何写操作都会让证据的可信度打折。sha256sum落盘这一步,是为了在报告里能写“以下日志文件的哈希为……”,人工比对时这一行很值钱。

6.2 报告里必须出现的四块内容

报告不用写长,四块内容写实就够:事件概览(时间、涉及主机、影响范围)、时间线(按分钟排列,每条带来源)、处置动作(做了什么、做完验证结果)、证据清单(文件名 + 哈希 + 一句话说明)。时间线那部分我一般做成表格,一行一个动作,最后一列写“来源:某设备日志第几行”,这样任何一条结论都能被追溯。

有一点值得单独提醒:处置动作要写“验证结果”,不能只写“已修复”。比如“关闭了异常账号并确认该账号无法再登录”,比“已删除异常账号”有力得多,因为前者包含了验证。

6.3 一条我一直在用的习惯

不管比赛还是真实环境,我处理任何一台疑似被入侵的主机,第一动作永远是“先取证再动手”——镜像、哈希、日志三件套落盘之后才开始杀进程、删文件。这个顺序反了,后面所有的分析都变成“我记得当时看到过”,写不进报告,也说服不了任何人。

模拟环境里我吃过一次亏:为了赶时间,直接在原机上删了可疑文件,结果报告里写“发现可疑文件并删除”,检查的人问了一句“文件是什么、什么时候创建的”,我一个字都答不上来。从那以后,取证先行就成了肌肉记忆。这个赛项真正教给人的,可能也就是这个顺序感——先固定事实,再谈处置。

希望帮到你。

本文还有配套的精品资源,点击获取

返回列表