您当前的位置:首页 > 计算机 > 安全防护

跨站脚本攻击 XSS

时间:03-30来源:作者:点击数:

0x00:审计介绍

对于跨站问题也就是我们常说的存储、反射和 DOM 三种,在挖掘 XSS 上我们一般的思路是查找可能在页面上进行输出的变量,并检查这些变量是否可控,然后跟踪传递过程,查看后端处理是否对接收的内容进行过滤或编码,在前端显示时是否被 htmlencode 之类的函数做过处理。

对于很多系统,不建议过滤特殊字符,一个体验不佳再一个有些系统是有需求可以输入特殊符号的,这时后台可以进行编码处理,例如 asp 的 Server.HTMLEncode,php 的 htmlspecialchars,java 的 org.apache.commons.lang.StringEscapeUtils 等。这里需要注意的是后台如果对收到的内容整体进行编码,例如汉子编码后就会多出很多字节,会浪费空间,建议根据实际情况进行处理,可以只过滤特殊符号这种,同时前台输出时也应用进行转义处理。

0x01:反射跨站示例

这里我们以 php 为例简单的看个例子,就拿我们常说的 bluecms1.6.sp1 为例,存在反射跨站的是根目录的 ad_js.php 文件,其源码如下:

<?php

$ad_id = !empty($_GET['ad_id']) ? trim($_GET['ad_id']) : '';
if(empty($ad_id)){
    echo 'Error!';
    exit();
}
$ad = $db->getone("SELECT * FROM ".table('ad')." WHERE ad_id =".$ad_id);
if($ad['time_set'] == 0){
    $ad_content = $ad['content'];
}else{
    if($ad['end_time'] < time()){
        $ad_content = $ad['exp_content'];
    }else{
        $ad_content = $ad['content'];
    }
}
$ad_content = str_replace('"', '\"',$ad_content);
$ad_content = str_replace("\r", "\\r",$ad_content);
$ad_content = str_replace("\n", "\\n",$ad_content);
echo "<!--\r\ndocument.write(\"".$ad_content."\");\r\n-->\r\n";

?>

上面源码是获取了 ad_id 的值,然后字节拼接到了 sql 语句中,这里首先就已经存在了注入问题,当给 ad_id 赋一个字符字符串之类的东西时,ad 的内容就变成了 sql 执行语句错误的内容,最后会字节输出到页面,这时插入的恶意 js 语句等也会进行输出并执行。需要注意的是,代码中的正则替换只匹配了 "/r/n,而并没有 <> 符。

0x02:存储跨站示例

存储我们还拿 bluecms 做示例,在 bluecms 中的 common.fun.php 文件中有一个获取 ip 的函数,其中使用了 getenv 来获取客户端的 ip 地址,但并没有对 ip 进行过滤,可伪造,函数代码如下。

function getip()
{
    if (getenv('HTTP_CLIENT_IP')){
        $ip = getenv('HTTP_CLIENT_IP'); 
    }
    elseif (getenv('HTTP_X_FORWARDED_FOR')) 
    { //获取客户端用代理服务器访问时的真实ip 地址
        $ip = getenv('HTTP_X_FORWARDED_FOR');
    }
    elseif (getenv('HTTP_X_FORWARDED')) 
    { 
        $ip = getenv('HTTP_X_FORWARDED');
    }
    elseif (getenv('HTTP_FORWARDED_FOR'))
    {
        $ip = getenv('HTTP_FORWARDED_FOR'); 
    }
    elseif (getenv('HTTP_FORWARDED'))
    {
        $ip = getenv('HTTP_FORWARDED');
    }else{ 
        $ip = $_SERVER['REMOTE_ADDR'];
    }
    return $ip;
}

以上 getenv 获取的变量都是客户端的 ip,这里可以抓包修改数据包头中的 ip 值,而获取 ip 这个函数在程序中的评论功能有使用,其部分代码如下。

if(empty($content)){
    showmsg('评论内容不能为空');
}
if($_CFG['comment_is_check'] == 0){
     $is_check = 1;
 }else{
     $is_check = 0;
 }
$sql = "INSERT INTO ".table('comment')." (com_id, post_id, user_id, type, mood, content, pub_date, ip, is_check) VALUES ('', '$id', '$user_id', '$type', '$mood', '$content', '$timestamp', '".getip()."', '$is_check')";
$db->query($sql);

这里主要的就是 insert 语句,ip 字段用的值就是 getip 函数,因为 ip 并没有做严格的判断和限制,其构造 payload 如下。

00','1'),('','1','0','1','6',('<script>alert(1)</script>'),'8888888888','99

以上代码放到 sql 语句的 ip 值位置时,整个 sql 语句就变成了插入两条数据,而第二条的 content 字段也就是内容我们放的 js 代码,这时访问被评论的文章时,js 代码便会执行。这里需要注意的是 content 内容字段是被经过过滤的,所以无法插入恶意脚本,而我们便利用的 ip 字段问题来构造 sql 成功插入了恶意代码。

0x03:DOM 跨站示例

DOM 简单来说就是客户端的脚本程序可以通过 DOM 动态的检查和修改页面内容,他不会依赖服务器的数据,而是从客户端的 DOM 中获取数据并在本地执行,获取的这些数据若没有进行过适当的过滤和消毒,则可能会造成 DOMXSS。在检测 DOMXSS 时,需要多注意一些输入源以及可以触发 DOM 的属性,例如 document.referer,window.name,location 等。这里我们列一个简单的示例,示例如下。

<html>
    <head>
    <title>DOMXSS</title>
    </head>
    <body>
        <script>
            var a = document.URL;
            document.write(a.substring(a.indexOF(a="")+2,a.length));
        </scirpt>
    </body>
</html>

以上页面其 js 通过 document 获取 url 并把 a 的值显示在页面上,而当 a 的值为恶意的脚本代码时,其页面输出便会被执行。下面再列举一个 document.referer 的例子,示例如下。

<html>
    <head>
    <title>DOMXSS</title>
    </head>
    <body>
        <a href="http://127.0.0.1/domxss.html">domxss</a>
        来自:<script>document.write(document.referrer);</script>
    </body>
</html>

当访问 127.0.0.1/domxss.html?<script>alert(1)</script>时,点击 domxss 会进行页面跳转,这里示例就还跳到页面本身,但 referrer 会记录上一个的页面地址,即从哪里跳过来的并打印,而上一个地址我们最后添加了恶意脚本,所以这里会被执行。同样的,例如一些 windows.name,location 等属性也都有可能造成 domxsss,例如以下示例。

<html>
    <head>
    <title>DOMXSS</title>
    </head>
    <body>
        <script>
            document.write("site:" + document.location.href);
        </script>
    </body>
</html>

location 是 js 用来管理 url 的一个内置对象,示例中的 location.href 会获取 url 地址,而当我们访问 127.0.0.1/domxss.html?#<script>alert(1)</script>时,其恶意代码便会执行。

这里我们会发现 domxss 其实和反射 xss 有点像,domxss 也可以算作是反射 xss 的一种,都是通过 url 去控制和触发的,区别在于,dom 是页面输出,和服务器没有交互,像反射可能与服务器有交互,例如搜索功能的关键字会在页面打印而被触发,其此操作交互了服务器。

挖掘 domxss,主要需要关注数据的输入和输出,例如刚刚的输入 document.referer,document.name,location 等,输出例如 innerHTML,document.write 等。不论 dom,反射和存储也一样,其流程大体都是跟踪输出的函数变量,找出可能危害的地方,然后回溯变量和函数的调用过程,查看用户是否可以控制输入。

0x03:防御和总结

对于跨站审计流程我们可以跟踪输出的函数变量,找出可能危害的地方,然后回溯变量和函数的调用过程,查看用户是否可以控制输入。对于渗透测试只要有输入的地方就有可能存在,对于防护建议如果程序允许,可以在入库的时候就过滤掉特殊字符,如果程序需要特殊字符的存在,则在出库显示页面时需要根据输出的位置来做好编码,例如 url 编码,页面显示编码,或者是作为属性的编码。同时要做好关键字过滤,可以使用正则匹配,如果存在便签且里面有 on 开头的内容则应该都被过滤掉。

因为跨站类型多且很多功能一不小心就会存在此问题,所以跨站漏洞也比较多,且危害高,在审计和渗透时应尽可能的仔细审计和测试,尽量避免此问题的发生。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门