繁体   English   中英

如果服务器在 pickle.loads 之前调用 pickle.dumps 有没有办法进行 RCE?

[英]If a server calls pickle.dumps before pickle.loads is there any way for RCE?

免责声明:这个问题不是出于恶意目的!! 我正在我自己的虚拟机上工作!

这里的文章演示了加载不受信任的 pickle 数据如何导致远程代码执行,我正在研究在没有安全问题的情况下使用此工作流的方法。

我的问题如下 - 如果我已经让 webapp 在 Flask 中获得一个请求,在request.form上使用pickle.dumps() ,然后在之前转储的内容上使用pickle.loads() ,是否还有执行恶意代码的方法?

示例服务器代码:

@blueprint.route('/test', methods=['GET', 'POST'])
def test():
    test=pickle.dumps(request.form) 
    test2=pickle.loads(test) # THE CODE SHOULD BE EXECUTED AT THIS POINT
    return ...

这个工作流程仍然容易受到攻击吗? 根据我的理解,最常见的 pickle 漏洞利用类型是通过pickle.loads()传递和解释 b64 字符串。 然而,是否有可能达到同样的结果,如果pickle.dumps()之前调用形式pickle.loads()

我尝试了几件事,但没有任何结果。 如果您知道密码,请告诉我:)

这是同一篇文章中的恶意用户代码示例

    import pickle
    import base64
    import os
    
    
    class RCE:
        def __reduce__(self):
            cmd = ('echo EXECUTED THIS STATEMENT')
            return os.system, (cmd,)
    
    
    if __name__ == '__main__':
        pickled = pickle.dumps(RCE())
        print(base64.urlsafe_b64encode(pickled))
        # Running pickle.loads(pickle.dumps(RCE())) would execute 'echo EXECUTED THIS STATEMENT'
        # I need to pass through RCE() because pickle.dumps() and pickle.loads() are server-side

这将返回一个 base64 字符串,当被pickle.loads()解释时,将执行cmd 中的代码。

但是如何在请求中传递RCE()的结果,以便它可以在服务器端被pickle.dumps()转储,然后在pickle.loads()之前仍然执行恶意代码?

示例(此代码不起作用):

客户代码

class RCE:
    def __reduce__(self):
        cmd = ('echo EXECUTED THIS STATEMENT')
        return os.system, (cmd, )

data = {
    'test': RCE()
}
s = requests.Session()
r = s.post(URL + "/test", data=data)

服务器端代码

@blueprint.route('/test', methods=['GET', 'POST'])
def test():
    test=pickle.dumps(request.form) 
    test2=pickle.loads(test) # THE CODE SHOULD BE EXECUTED AT THIS POINT
    return ...

示例(此代码有效):

客户代码

class RCE:
    def __reduce__(self):
        cmd = ('echo EXECUTED THIS STATEMENT')
        return os.system, (cmd, )

data = {
    'test': pickle.dumps(RCE())
}
s = requests.Session()
r = s.post(URL + "/test", data=data)

服务器端代码

@blueprint.route('/test', methods=['GET', 'POST'])
def test():
    test2=pickle.loads(request.form['test']) # THE CODE SHOULD BE EXECUTED AT THIS POINT
    return ...

我的想法如下,是否有可能有一个字符串,当在服务器端被pickle.dumps()序列化时,返回与在客户端执行pickle.dumps(RCE())相同的值. 当然,由于request.form方面的原因,服务器端的pickle.dumps()的结果会有些不同。 根据我的理解,只要字符串中有可执行代码, pickle.loads()就会执行它。

不,服务器无法通过转储然后加载来执行远程代码,但您也无法加载腌制数据结构。

我将使用pickletools.dis来演示实际发生的情况:

import pickle
import pickletools
class RCE:
    def __reduce__(self):
        return eval, ("print('MALICIOUS PYTHON CODE HERE')",)

pickled_malicious = pickle.dumps(RCE())
print("what is executed when loading malicious pickle:")
pickletools.dis(pickled_malicious)
print("pickle is type:", type(pickled_malicious))

pickled_string = pickle.dumps(pickled_malicious)
print("what is executed when loading the dump of malicious")
pickletools.dis(pickled_string)

当加载恶意代码时,我们加载函数evalos.system以及参数,然后REDUCE操作代码运行该函数:

what is executed when loading malicious pickle:
    0: \x80 PROTO      3
    2: c    GLOBAL     'builtins eval'
   17: q    BINPUT     0
   19: X    BINUNICODE "print('MALICIOUS PYTHON CODE HERE')"
   59: q    BINPUT     1
   61: \x85 TUPLE1
   62: q    BINPUT     2
   64: R    REDUCE
   65: q    BINPUT     3
   67: .    STOP

腌制的恶意代码本身虽然只是一个字节对象,

pickle is type: <class 'bytes'>

因此,如果您转储加载只会加载文字字节对象(或者如果您正在执行 base64 编码,则可能是字符串,但无论哪种方式,此时它都只是文字)

what is executed when loading the dump of malicious
    0: \x80 PROTO      3
    2: C    SHORT_BINBYTES b"\x80\x03cbuiltins\neval\nq\x00X#\x00\x00\x00print('MALICIOUS PYTHON CODE HERE')q\x01\x85q\x02Rq\x03."
   72: q    BINPUT     0
   74: .    STOP
highest protocol among opcodes = 3

这意味着,如果服务器只是在输入中调用pickle.dumps (它是包含 pickle 数据的 base64 数据或字节数据的字符串,无论哪种方式,它在转储时都只是一个文字值)然后当它调用pickle.loads在该结果上,它只会取回原始输入。

任何将用户输入解释为 pickle 数据的场景都是脆弱的——但你在这里没有这样做——你是从已知的安全输入(输入字符串)创建 pickle 数据,然后加载它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM