☰
网络安全技能大赛加固取证与渗透测试实战复盘
2026/10/30 8:35:44 网站建设 项目流程

简介:《2021年全国职业院校技能大赛网络安全竞赛试题》文档面向职业院校网络安全竞赛选手、指导教师及备赛者,以赛题任务梳理服务器、数据库、流量、监控、服务与防火墙等考点,适合备赛训练、课堂教学和赛前查漏补缺。压缩包仅含1个docx文件,约119KB,篇幅紧凑,覆盖登录安全加固、数据库加固、流量完整性保护、事件监控、SSH/VSFTPD/IIS服务加固和防火墙策略,并延伸至网络安全事件响应、数字取证调查与应用安全模块。文中给出Windows与Linux双平台的操作要求与关键命令,如密码长度不少于16位、登录提示语、MySQL普通账户运行与权限收敛、HTTP跳转HTTPS、审核策略开启、SSH端口修改与禁止root登录、禁用445/23端口及限制ICMP等,便于对照复现实验步骤。已有959人学习,可作为赛题库与训练清单使用。

1. 一套赛题拆成两条主线:加固能背,取证得看

赛题拿到手那天,我先做的不是开靶机,而是把两份模块的任务清单抄在白纸上。A 模块十八个动作,B 模块十三个 Flag,按「改配置」和「找证据」分成两堆。网络安全技能大赛的题面有个很明显的规律:A 模块全是确定性动作,路径对、参数对,分数就是你的;B 模块全是证据链,命令谁都会敲,难的是判断哪一条输出才是答案。这套题我在两台机器上来回跑了三遍。第一遍照着题面抄,第二遍关掉题面凭记忆复现,第三遍专门用来记那些「写了但没生效」的坑。下面按这个顺序走:加固怎么改、取证怎么找、渗透怎么串,以及我翻过的车。新手可以照着命令走一遍,熟手可以直接看判断依据那几段。

2. 加固类任务:从密码策略到防火墙的逐条复现

2.1 密码与登录策略:两条线四个动作

密码长度 16 位这件事,Windows 侧最容易,net accounts /minpwlen:16一条就完事,图形界面里对应的是本地安全策略的账户策略。真正值得说的是 Linux 侧。题面让你改/etc/login.defs的PASS_MIN_LEN,照做没错,但你要清楚它管的是什么:这个字段只影响新建用户时的默认行为,真正在改密码时卡长度的是 PAM 模块pam_pwquality。所以改完login.defs,最好顺手看一眼/etc/security/pwquality.conf里的minlen,不然你会在验证环节发现「明明改了还能设短密码」,然后怀疑人生。

# 备份再改,别直接 sed -i 覆盖原文件 cp /etc/login.defs /etc/login.defs.bak sed -i 's/^PASS_MIN_LEN.*/PASS_MIN_LEN\t16/' /etc/login.defs grep -n "PASS_MIN_LEN" /etc/login.defs # 校验 PAM 侧的密码质量配置,确认没有被更小的 minlen 覆盖 grep -E "minlen|minclass" /etc/security/pwquality.conf

上面这段的关键不是 sed,是第二段的grep。sed里用了\t是为了对齐,你写成空格也不影响功能。跑完拿一个新用户试一下passwd,输 8 位密码看它报不报错——这是最笨也最可靠的验证,比看配置文件放心得多。

登录提示语要动两个地方,这点很多人只做一个。Linux 本地登录的/etc/issue和 SSH 远程的Banner是分开的,题面写得很清楚:/etc/issue加本地欢迎语,sshd_config里加Banner /etc/ssh/banner并在 ssh 目录下建文件写内容。判断依据是:/etc/issue由 getty 打印,你 SSH 进来看到的是 sshd 打印的 Banner,两者不走同一条路径。Windows 侧对应的是安全选项里的「交互式登录:试图登录的用户的消息标题/文本」,注册表位置在LegalNoticeCaption和LegalNoticeText。

# 本地登录提示 echo "For authorized users only" >> /etc/issue # SSH 远程登录提示 echo "For authorized users only" > /etc/ssh/banner chmod 644 /etc/ssh/banner sed -i 's|^#Banner.*|Banner /etc/ssh/banner|' /etc/ssh/sshd_config grep -n "^Banner" /etc/ssh/sshd_config

chmod 644容易被忽略:sshd 是以 root 启动后降权读取文件的,权限太开或者属主不对都可能读不到,表现就是登录进来什么提示都没有,而sshd_config看着完全正确。改完systemctl restart sshd,然后从另一台机器ssh过去亲眼确认一遍。

会话超时要算乘法,这是最典型的失分点。ClientAliveInterval是探测间隔,ClientAliveCountMax是允许无响应的次数,实际断开时间是两者相乘。要小于等于 5 分钟,可以写300配0,也可以写60配5。我一般用300 + 0,因为0表示客户端一旦无响应就直接断,语义干净。RDP 侧在「远程桌面会话主机设置」里改会话空闲限制,或者直接进RDP-Tcp属性看会话那一页。

用户user1只允许上班时间登录,命令是net user "user1" /time:M-F,9:00-18:00。M-F表示周一到周五,时间用 24 小时制,整条命令里不能有多余空格,尤其是逗号后面。验证方式是net user user1看「允许的登录时间」那一行是否变成了你要的区间。

「只允许管理员账号从网络访问本机」这条,坑在优先级。组策略里有「从网络访问此计算机」和「拒绝从网络访问此计算机」两条,后者优先级更高。你在允许列表里只留 Administrators,看着很对,但只要拒绝列表里挂着普通用户组,效果是一样的——不过判分机器往往只看允许列表的成员。稳妥做法是:允许列表只留 Administrators,拒绝列表清空。

2.2 数据库加固:五步里有两步最容易写错

以普通用户跑 mysqld,改的是my.cnf的[mysqld]段,不是[client]也不是文件末尾随便加一行。段写错了,配置压根不生效,你还是用 root 在跑数据库。

删除默认库这一步,drop database test;只是删了库,mysql.db表里可能还留着test%的授权记录,严格一点的判分会查。顺手清掉更稳。

改管理员用户名这条,题面给的是update user set user='SuperRoot' where user='root';。注意版本差异:MySQL 5.7 之后用户表里密码字段从password改成了authentication_string,8.0 里直接没有PASSWORD()函数。所以按题面写会报语法错,得看版本走。

-- 5.6 及更早:字段名是 password,函数是 PASSWORD() UPDATE mysql.user SET password = MD5('P@ssw0rd1!') WHERE user = 'user1'; -- 5.7:字段名换成 authentication_string,但仍兼容 PASSWORD 函数 UPDATE mysql.user SET authentication_string = PASSWORD('P@ssw0rd1!') WHERE user = 'user1'; -- 8.0:没有 PASSWORD(),要显式指定插件才能得到 32 位 MD5 串 ALTER USER 'user1'@'localhost' IDENTIFIED WITH mysql_native_password BY 'P@ssw0rd1!'; -- 三条路都别忘了这句,否则改了不生效 FLUSH PRIVILEGES;

判断依据很直接:select version();先看一眼。8.0 默认是caching_sha2_password,你不显式指定插件,存进去的就不是题面要的 MD5 串,select出来一看长度不对立刻就知道。

改名那条同样有版本坑:mysql.user表里的root通常不止一条,localhost、127.0.0.1、::1各一条,where user='root'会把它们全改成SuperRoot,这通常是对的,但改完必须用新名字登录验证一次,别改完就往下走。

授权那条grant select,insert,delete,update on testdb.* to user1@'localhost';,两个细节:一,引号。赛题文档里的引号经常是复制粘贴时变成全角,粘进客户端直接报 1064,肉眼还看不出来;二,MySQL 8 不允许对不存在的用户直接 grant,得先create user。

2.3 服务加固与防火墙:端口、超时、白名单

SSH 改端口到 2222,改完sshd_config只是第一步。开着 SELinux 的机器,sshd 绑不上非标准端口,报的是权限错误。得先semanage port -a -t ssh_port_t -p tcp 2222,或者临时setenforce 0验证是不是它在拦。防火墙也要放行,两个动作缺一不可。PermitRootLogin no改的时候注意把行首的#去掉,注释没去等于没改。

VSFTPD 这块要重点说,因为题面给的参考配置和你真正要的答案不一致。任务要求「无任何操作的超时时间为 5 分钟」,而参考注释里idle_session_timeout=600是 10 分钟。抄下去就丢分。四个超时参数的含义得拎清:

参数作用5 分钟对应的值
connect_timeout主动模式下建立数据连接的等待时长按题意保留默认即可
accept_timeout被动模式下建立数据连接的等待时长按题意保留默认即可
data_connection_timeout数据连接空闲多久后断开300
idle_session_timeout控制连接无命令操作的超时300

「无任何操作」对应的是最后一个,idle_session_timeout。改完重启 vsftpd,用 ftp 客户端连上去发呆五分钟,看它断不断,这比读配置快。

IIS 日志那条,格式选 W3C,字段只勾日期、时间、客户端 IP、用户名、方法五项,多勾一个都可能被判错。最大并发连接数在站点「高级设置」里,填 1000。

防火墙部分是题量最大的一块,也是最容易乱序的。

# Windows:禁 445 和 23,方向必须写 in netsh advfirewall firewall add rule name="block-smb-445" dir=in action=block protocol=TCP localport=445 netsh advfirewall firewall add rule name="block-telnet-23" dir=in action=block protocol=TCP localport=23 # Linux:禁 23 iptables -A INPUT -p tcp --dport 23 -j DROP # 禁 ping 的三种写法,任选一种 echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all # 临时 echo "net.ipv4.icmp_echo_ignore_all=1" >> /etc/sysctl.conf && sysctl -p # 永久 iptables -A INPUT -p icmp --icmp-type 8 -s 0/0 -j DROP # 走防火墙 # 只允许 172.16.1.1 连 SSH:先放行再拒绝,顺序不能反 iptables -A INPUT -s 172.16.1.1 -p tcp --dport 2222 -j ACCEPT iptables -A INPUT -p tcp --dport 2222 -j DROP

最后那段顺序是命门。iptables 规则是自上而下匹配,先 DROP 后 ACCEPT 的话,白名单永远匹配不到。另外端口别照抄 22——上一节刚把 SSH 改成了 2222,防火墙还守着 22,结果就是谁都连不上,包括你自己。改完iptables-save > /etc/sysconfig/iptables落盘,否则重启一次全白干。

HTTP 强制跳 HTTPS 是在 IIS 里装 URL 重写模块,加一条匹配.*的入站规则,条件写{HTTPS}等于OFF,动作重定向到https://{HTTP_HOST}/{R:1},状态码 301。证书的 CN 要和访问用的域名对得上,题面里给的那个域名,绑站点时别写错一个字符。

3. 内存取证与流量分析:证据链断在细节上

3.1 内存镜像的第一步:先定 profile 再谈其他

拿到内存镜像的第一件事永远是imageinfo,不是直接上netscan。profile 猜错,后面所有插件输出的都是垃圾。

# Volatility 2 的起手式 vol.py -f server1.raw imageinfo # 确认 profile 后,提取网络连接和主机名 vol.py -f server1.raw --profile=Win7SP1x64 netscan vol.py -f server1.raw --profile=Win7SP1x64 printkey \ -K "ControlSet001\Control\ComputerName\ComputerName"

imageinfo会给出建议 profile 列表,取第一个一般就对。netscan输出里重点看ESTABLISHED状态的连接,ForeignAddr加上ForeignPort就是题目要的ip:端口格式。主机名那条走注册表键,printkey出来的ComputerName值就是。

浏览器搜索关键词这条比较绕,因为内存里没有现成的「搜索历史」表。常见思路是两条:一是strings全量导出后按搜索引擎域名附近的关键词去搜,二是用对应的浏览器历史插件从内存里的 SQLite 缓存中恢复。

# 思路一:导出全部字符串,围绕搜索引擎域名找上下文 strings -el server1.raw > strings_utf16.txt grep -i -A2 -B2 "search?q=" strings_utf16.txt | head -50

-el是导出 UTF-16 小端编码的字符串,Windows 内部大量使用宽字符,不加这个参数你会漏掉几乎全部浏览器相关内容。-A2 -B2是打印匹配行的上下文,URL 经常被拆成碎片存储,光看一行不够。输出会很长,head先看前几十条,找到规律再缩小范围。

3.2 hashdump 与六位密码的破解

从内存里取用户 hash 是标准动作,hashdump插件直接吐 SAM 里的 NTLM 串。拿到之后是纯离线的活,跟靶机没关系了。

# 导出内存中的用户 hash vol.py -f server1.raw --profile=Win7SP1x64 hashdump # 六位纯数字,用掩码攻击最快 hashcat -m 1000 -a 3 hash.txt ?d?d?d?d?d?d # 六位不限字符,用全字符集,注意 ?a 是大写敏感的全集 hashcat -m 1000 -a 3 hash.txt ?a?a?a?a?a?a

-m 1000指的是 NTLM 哈希类型,这个编号记不住可以hashcat --example-hashes | grep -i ntlm查。-a 3是掩码模式。题面说密码是 6 位,但没说字符集,先用?d跑纯数字,几十秒出不来再换?a——全字符集六位的空间是 95 的六次方,量级差得非常远,别一上来就怼。

这里有个容易忽略的点:hashdump输出的格式是用户名:rid:lmhash:nthash:::,中间是六个冒号,不是三个。喂给 hashcat 之前把整行原样保留,别手动切 nthash,切错了工具不报错,只是永远跑不出来。

3.3 挖矿进程与注册服务:从连接到进程再到服务

找矿池地址的路径是固定的:连接 → 进程 → 命令行或内存。netscan里挑出不认识的外网连接,记住对应的 PID,然后pslist或者cmdline追下去。

# 看进程列表,找可疑的高 CPU 占用项或陌生名字 vol.py -f server1.raw --profile=Win7SP1x64 pslist # 看进程的完整命令行,矿池地址常常直接写在参数里 vol.py -f server1.raw --profile=Win7SP1x64 cmdline -p <PID>

cmdline是效率最高的一步。很多挖矿程序启动参数里直接带着stratum+tcp://ip:port,一行就出答案,根本不用去 dump 进程内存。命令行里没有的,才需要memdump -p <PID> -D ./然后strings去找。

注册服务这条走svcscan:

# 列出内存中所有服务,重点看 BinaryPath 指向临时目录或用户目录的 vol.py -f server1.raw --profile=Win7SP1x64 svcscan | grep -i -B2 -A6 "Temp"

判断依据是BinaryPath。正常服务的可执行文件在System32或Program Files下,指向\Temp\、\Users\Public\、\AppData\的基本可以确定是后门注册的服务。svcscan输出字段多,-A6是为了把ServiceName、DisplayName、BinaryPath一起带出来,只看一行容易把显示名当服务名填错。

3.4 流量分析:五个问题对应五组过滤器

Wireshark 做 FTP 取证,其实就五个问题,每个问题对应一到两个过滤器,理清映射关系比死记命令有用得多。

找账号密码,过滤器打ftp,看USER和PASS两条请求。注意PASS是明文传的,这就是 FTP 协议本身的问题,也是这道题的出题点。找登录成功的时间,看服务端回的230 Login successful,那条报文的时间戳就是答案,别拿USER那一帧的时间。找登录后第一条命令,过滤器换成ftp.request.command,找到230之后第一条request.command即可。找下载的文件名,看RETR命令的参数。找文件内容,要么ftp-data过滤后 Follow TCP Stream,要么File → Export Objects → FTP-DATA直接导出。

# 命令行等价操作,适合批量处理或者写进脚本 tshark -r capture.pcapng -Y "ftp.request.command" -T fields -e frame.time -e ftp.request.command tshark -r capture.pcapng -Y "ftp.response.code == 230" -T fields -e frame.time

-Y是显示过滤器,语法和 Wireshark 界面里完全一致,会写界面就会写命令。-T fields -e指定只输出感兴趣的字段,比-V全量详细输出好读太多,尤其是要在一堆帧里找特定值的时候。

导出文件内容那一步有个坑:ftp-data流里下载的不一定是纯文本,可能是压缩包。先file一下看类型,是压缩包就解开再看,别拿乱码去提交 Flag。

4. 渗透测试:Nmap 到 MSF 的完整作业链

4.1 扫描输出:-oX不只是存个文件

题面问「以 xml 格式向指定文件输出信息必须要使用的参数」,答案是-oX。这个参数的意义不只是留档,它是后面 MSF 能db_import的前提——MSF 只认 XML 格式的扫描结果,你存成普通文本,导入那一步就断了。

# -sV 探版本,-oX 输出 XML,-oN 同时存一份人能读的 nmap -sV -oX /root/scan.xml -oN /root/scan.txt 192.168.1.10 # 确认 XML 结构完整,根节点必须是 nmaprun head -3 /root/scan.xml

-sV是版本探测,不做这一步扫描结果里就没有服务版本,MSF 匹配模块时会少一大截线索。-oN是给眼睛看的,-oX是给机器吃的,两个一起出最省事。head那一步是自检,XML 里根节点是<nmaprun>才算合格。

4.2 MSF 数据库:初始化和导入是两个动作

很多人卡在db_import报错,原因往往是数据库根本没起来。msfdb init是初始化,之后进msfconsole还要db_status确认已连接。

# 第一步:初始化数据库 msfdb init # 第二步:进控制台,确认连接状态 msfconsole -q msf6 > db_status msf6 > db_import /root/scan.xml msf6 > hosts

hosts就是题面问的「查看导入数据要用的命令」。db_import成功时会打印导入了多少主机、多少服务,没打印就是没成功。hosts输出里应该能看到靶机 IP 和开放端口,看不到就回去查db_status。

判断依据很简单:hosts是查主机表,services是查服务表,vulns是查漏洞表,三个命令对应三种视角。题面问「查看导入的数据」,主机级别用hosts就够。

4.3 CVE 模块检索与回显判读

search cve-2019-0708这条,回显里会带一行Disclosure Date,把日期按YYYY-MM-DD格式填进去就行。搜索时大小写和连字符都不影响,cve-2019-0708、CVE-2019-0708结果一样,但写成cve20190708就搜不到了。

调用检测模块这一步,题面要的是「回显结果中最后一个单词」。这属于典型的「以你机器的实际输出为准」的题,网上流传的答案不一定对得上你的环境。

# 进模块 msf6 > use auxiliary/scanner/rdp/cve_2019_0708_bluekeep # 看需要配哪些参数,RHOSTS 是必填 msf6 > show options # 配目标、跑检测 msf6 > set RHOSTS 192.168.1.10 msf6 > run

跑完的输出通常是一句结论性的话,末尾单词落在vulnerable或者patched一类词上。别背答案,跑完看清楚再填。熟手的做法是把整行回显复制下来,从后往前数字符,避免中间有标点被漏掉。

5. 避坑清单:五条我真实翻过的车

5.1 加固侧:写了但没生效的三处

现象:SSH 端口改成 2222,sshd_config里明明白白,重启服务却起不来。原因:SELinux 不允许 sshd 绑定非标准端口,日志里报的是权限拒绝,但systemctl status只给你一行红字。解决:semanage port -a -t ssh_port_t -p tcp 2222放开端口标签,或者临时setenforce 0确认是不是它在拦。确认完记得开回去,赛后环境还要用。

现象:VSFTPD 超时配了,测的时候五分钟没断。原因:抄的是题面参考注释里的idle_session_timeout=600,那是 10 分钟。解决:idle_session_timeout=300。这条我建议每次改完都用grep -n "timeout" /etc/vsftpd/vsftpd.conf把四个参数一起打出来对一遍,光看一个容易漏。

现象:iptables 白名单配完,自己也连不上了。原因:要么规则顺序反了(DROP 在前),要么端口写的是 22 而 SSH 已经改成了 2222,要么-A追加导致新规则排在旧的 DROP 后面。解决:用-I INPUT 1插到链首而不是-A追加,配置前先iptables -L -n --line-numbers看清楚现有规则,配完iptables-save落盘。

5.2 取证侧:证据链断在细节上的两处

现象:hashdump出来的 hash 喂给 hashcat,跑了两小时没结果。原因:手动从整行里切了 nthash,切的时候少截或多截了字符,hashcat 不校验长度,只是默默跑一个错的串。解决:一行原样保存,让 hashcat 自己解析。真跑不出来,先hashcat --identify确认格式被正确识别。

现象:流量分析里 FTP 登录时间填了USER那条的时间,判分不对。原因:USER是客户端提交用户名的时间,登录成功是服务端回230的时间,两者可能差几秒,题目问的是「登录」成功。解决:过滤器写ftp.response.code == 230,取那帧时间戳,精确到秒。

6. 收在最后:改完必验的六项,比多改十项更值

这套题我最大的体会是,分数不在你改了多少项,而在你能证明多少项真的生效了。A 模块十八个动作,哪怕全改完,只要有三处是「写了没生效」,判分照样扣。所以最后一章不讲新知识,讲一套我赛后固定下来的验证动作,六项,每项不超过半分钟。

检查项验证命令看什么
密码策略net accounts/grep PASS_MIN_LEN /etc/login.defs最小值是否为 16
SSH 加固ssh -p 2222 -o PreferredAuthentications=password user@host端口通、root 被拒、有 Banner
服务超时grep -n "timeout" /etc/vsftpd/vsftpd.conf四个值,重点看 300
防火墙iptables -L INPUT -n --line-numbers顺序,ACCEPT 在 DROP 之前
审计策略auditpol /get /category:*登录、特权、策略、进程四项状态
数据库select user,host,plugin from mysql.user;用户名新版、插件类型
# 一条命令把 Linux 侧的关键项打出来,赛后复盘或者赛前热身都能用 for f in /etc/login.defs /etc/ssh/sshd_config /etc/vsftpd/vsftpd.conf; do echo "=== $f ===" grep -nE "PASS_MIN_LEN|^Port|^PermitRootLogin|^Banner|^ClientAlive|^anonymous_enable|timeout" "$f" done iptables -L INPUT -n --line-numbers

这段脚本没什么技巧,就是把散落在三个文件里的关键行一次性拉出来。真正有用的是它逼你每次都看一遍——人眼扫一行配置,比翻三个文件快得多,也比事后拍脑袋可靠得多。

回过头看,我翻得最狠的一次车不在技术点上。那次我把PermitRootLogin no改了、重启了、验证了 root 登不上,自我感觉非常好,结果题目问的是「禁止 root 用户远程登录」,而我在sshd_config里同时留着PasswordAuthentication yes,判分脚本判的是整条策略的严格程度。从那以后我每次改配置都强制走一遍「改—重启—换机验证—回头看题面原文」这四步,尤其是最后一步。赛题的字眼都很抠,差一个词,你的答案就不在采分点上。

把这套流程固化下来之后,A 模块我再没在「写了没生效」上丢过分。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询