HCTF_2018-WRITEUP【ウェブ题】

HCTF_2018-WRITEUP

:からコンテストの問題BUUCTF

:ミラー王漁陽

準備し始める:

オープンソースのページのタイトルマッチ(F12)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
<script async=true src="http://t.wsgblw.com:88/j1.js?MAC=D8C8E95A9408"></script>
</head>
<body>
    <!--source.php-->
    
    <br><img src="https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg" /></body>
</html>

ソースコードは示唆してsource.phpソースコードを取得するには、ファイルへのアクセスを:

ソース分析:

 <?php
    highlight_file(__FILE__);// 对文件进行语法高亮显示
    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)) { // 数组中$whitelist匹配$page
                return true;
            }

            $_page = mb_substr(
                $page,
                0,
                mb_strpos($page . '?', '?')
            );
            if (in_array($_page, $whitelist)) { // 数组中$whitelist匹配_$page
                return true;
            }

            $_page = urldecode($page);//二次解码
            $_page = mb_substr(
                $_page,
                0,
                mb_strpos($_page . '?', '?') 
                //mb_strpos():查找字符串在另一个字符串中首次出现的位置
            );
            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'])//调用checkFile()判断
    ) {
        include $_REQUEST['file'];//可能存在注入点
        exit;
    } else {
        echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
    }  
?> 

リファレンスの脆弱性:

phpmyadmin4.8.1リモートファイルインクルージョンの脆弱性 [CVE-2018から12613]

上記の分析後、ファイルの内容を実質的にフィルタリングし、文字列の存在のみ決意ことが理解できない、_page後切り捨て真キー戻りのフラグを含むファイルを読み取るために使用することができます

バイパスしようとすると、ホワイトリストの文字列が、_pageのみ傍受をチェックするために時間を使うのか?コンテンツの間に、私たちは構築することができますか?source.php?フィルタバイパスにこの方法を../../../phpinfo.php。

そして、そこにバイパスされている。
私たちの引数があるべき?source.php../../../flag.txt
と_page切り捨て裁判官ホワイトリストの後に。
我々は、パラメータになる?source.php?../../../flag.txt
2 _page、私たちの第二のバイパスポイントを決定するためには、コードページがデコードされた、最初の決意が偽で、二回目は、トゥーレ
たちのパラメータになります?source.php%253f../../../flag.txt

管理者:

ソース分析:

、情報を取得していないページ(ソースコード)を参照してくださいページから登録機能を見つける|サインオン機能なので、より多くの情報を取得するには、アカウントのログインを登録します。

ロギングは、各ページのソースコードを読んだ後、changeコメントのページを発見

アドレスへのアクセスとは、gitリポジトリのソースを発見します!分析のためにローカルにダウン!

開いroutes.pyたファイル、ルーティング次のコード分析を

分析前任者からは、機能は非常に簡単です:次のように分析をルーティングログイン(ログイン)、パスワードの変更(変更)、終了(ログアウト)、登録(レジスタ)、編集(編集)特定のソース:

@app.route('/code')
def get_code():
    image, code = get_verify_code()
    # 图片以二进制形式写入
    buf = BytesIO()
    image.save(buf, 'jpeg')
    buf_str = buf.getvalue()
    # 把buf_str作为response返回前端,并设置首部字段
    response = make_response(buf_str)
    response.headers['Content-Type'] = 'image/gif'
    # 将验证码字符串储存在session中
    session['image'] = code
    return response

@app.route('/')
@app.route('/index')#主页:index.html
def index():
    return render_template('index.html', title = 'hctf')

@app.route('/register', methods = ['GET', 'POST'])#注册页:register.html
def register():

    if current_user.is_authenticated:
        return redirect(url_for('index'))

    form = RegisterForm()
    if request.method == 'POST':
        name = strlower(form.username.data)
        if session.get('image').lower() != form.verify_code.data.lower():
            flash('Wrong verify code.')
            return render_template('register.html', title = 'register', form=form)
        if User.query.filter_by(username = name).first():
            flash('The username has been registered')
            return redirect(url_for('register'))
        user = User(username=name)
        user.set_password(form.password.data)
        db.session.add(user)
        db.session.commit()
        flash('register successful')
        return redirect(url_for('login'))
    return render_template('register.html', title = 'register', form = form)

@app.route('/login', methods = ['GET', 'POST'])#登录页:login.html
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'])#改密:change.html
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)

@app.route('/edit', methods = ['GET', 'POST'])#edit.html
def edit():
    if request.method == 'POST':
        
        flash('post successful')
        return redirect(url_for('index'))
    return render_template('edit.html', title = 'edit')

@app.errorhandler(404)
def page_not_found(error):
    title = unicode(error)
    message = error.description
    return render_template('errors.html', title=title, message=message)

def strlower(username):
    username = nodeprep.prepare(username)
    return username

話題の本来の意図と組み合わせるとのindex.htmlページを監査:

あなたの中にログインしたときに、ユーザーが「管理者」である場合にはフラグを見ることができる。つまり、会うとき{% if current_user.is_authenticated and session['name'] == 'admin' %}、彼らは旗を受け取ることができる条件の前に。

これまでのところ!情報は私が取得に必要な条件である:フラグを取得するには、「管理者としてログオン」;フレームワークの目的である:「フラスコ」

Unicodeの詐欺:

私たちは、そのような場合には、それを注意深く見て(変更)機能を変更するパスワードを見つけました:

@app.route('/change', methods = ['GET', 'POST'])#改密:change.html
def change():
    if not current_user.is_authenticated:
        return redirect(url_for('login'))
    form = NewpasswordForm()
    if request.method == 'POST': 
        name = strlower(session['name']) #strlower():转小写
        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)

ソースコードから求め、strlower()小文字の変換がありますか?なぜ?

同様に、ローカル登録およびログインするだけでなく、ユーザー名の名前にあったstrlower()転送小文字の操作

私は、実現可能な見つけ、「ADMIN」を登録しました!登録ADMINはもともとない場合ただし、管理者に続くだろうと思いました

よく見strlower()機能

def strlower(username):
    username = nodeprep.prepare(username)
    return username

さらに探査nodeprep.prepare()_twistedライブラリー

参考Writerup:質問の人のうち殴らADMINが求める様々な方法を言うこと

リファレンスの脆弱性:によって引き起こされるセキュリティ問題のUnicode文字の形状 Unicodeの欺瞞**

我々は、登録された場合はᴬᴰᴹᴵᴺ、ユーザーを、次に使用ᴬᴰᴹᴵᴺ私たちは、ログインユーザー名を見に行くnodeprep.prepare機能routes.py/login機能の使用があるため、ユーザーがログオンしADMIN、その後、我々はパスワードroutes.py/changeを変更しますnodeprep.prepare関数名がに変換されたら、と呼ばれadmin、その後、我々は取り除くことができadmin、パスワード、そして最終的に{4c8aa9a4-0f98-42c4-a63e-59c723e83c92}フラグを取得するためにログオンするための管理者アカウントを使用します 。

要約:

这里利用的Unicod欺骗,twisted库的nodeprep.prepare()会将内容转为小写,且将其它类的编码转为ASCii;我们提交(可以查到各个字母的替换类型 )“ᴬ”nodeprep.prepare()函数转为“A”,再次(二次)nodeprep.prepare()函数会将“A”转为“a”;这是twisted库函数的特点。

final Web1

未解决

おすすめ

転載: www.cnblogs.com/wangyuyang1016/p/12032147.html