1. 程式人生 > 程式設計 >thinkphp諸多限制條件下如何getshell詳解

thinkphp諸多限制條件下如何getshell詳解

前言

先說說2020_n1CTF的web題Easy_tp5復現問題。

這個題在保留thinkphp的RCE點的同時,並且RCE中ban掉許多危險函式,只能允許單引數的函式執行。對於現在在網路中流傳的檔案包含的點也增加了限制。

smile yyds!

先說一下這個題限制條件:

  • thinkphp版本:5.0.0
  • php版本:7
  • 對於包含檔案增加了限制

thinkphp諸多限制條件下如何getshell詳解

ban掉所有的單引數危險函式

thinkphp諸多限制條件下如何getshell詳解

設定open_basedir為web目錄

thinkphp諸多限制條件下如何getshell詳解

設定僅在public目錄下可寫

thinkphp諸多限制條件下如何getshell詳解

在TP5.0.0的中,目前公佈的只是存在利用Request類其中變數被覆蓋導致RCE。如果ban掉單引數可利用函式那麼只能用檔案包含,但是檔案包含做了限制不能包含log檔案,所以只能從別的方面入手。

這些限制都太大了,所以需要想辦法去上傳一個shell來完成後續繞disable_function。

首先TP5.0.0目前只存在通過覆蓋Request中的某些變數導致RCE,其餘細節不再贅述,我們看看大概程式碼執行點在哪裡。

thinkphp諸多限制條件下如何getshell詳解

call_user_func是程式碼執行點,我們基本上所有PHP自帶的可利用函式基本被ban掉,所以我們需要從自寫的函式呼叫來入手,首先我們需要看下這個點。可回撥函式不僅僅指的是簡單函式,還可以是一些物件的方法,包括靜態方法。

thinkphp諸多限制條件下如何getshell詳解

方法一thinkphp\library\think\Build::module

我們可以這樣通過呼叫這個類的靜態方法module,來實現寫檔案的操作。

thinkphp諸多限制條件下如何getshell詳解

我們先看看這個該怎麼走,我們看到這個mkdir是在application建立目錄,但是由於許可權問題肯定無法建立。根據TP報錯即退出的機制從而中斷執行。那麼我們可以通過../public/test來建立目錄。

我們會進入到buildhello函式中。

thinkphp諸多限制條件下如何getshell詳解

走完流程發現我們可以在public建立了一個test模組,同樣看到test/controller/Index.php中我們所寫的../public/test儲存了下來那麼我們就繞過,但是執行完之後會發現一些語法錯誤導致程式碼不能執行。

thinkphp諸多限制條件下如何getshell詳解

由於這部分內容可控那我們就把他變得符合語法執行,我們可以這麼做test;eval($_POST[a]);#/../../public/test;

,這樣就符合語法。

thinkphp諸多限制條件下如何getshell詳解

但是還有一個問題需要解決,就是我們這樣的payload會設定一個不存在目錄從而可以符合語法並且加入eval函式。但是現在還存在一個跨越不存在目錄的問題。

thinkphp諸多限制條件下如何getshell詳解

linux環境

thinkphp諸多限制條件下如何getshell詳解

win環境

thinkphp諸多限制條件下如何getshell詳解

在Linux中不能建立不存在的目錄,但是在win下就可以。但是報錯是warning,並不會中斷執行,並且在bindhello函式中我們會看到:

thinkphp諸多限制條件下如何getshell詳解

其中mkdir函式存在recursive引數為true,允許遞迴建立多級巢狀的目錄。這樣就可以使mkdir中使用不存在的目錄就可以進行繞過。但是現在有個問題:前面的mkdir中的warning報錯被TP捕獲到直接會退出無法執行後面的內容,那麼我們就需要使用一些辦法進行抑制報錯。我們經常做題會用到一個函式error_reporting,我們可以使用error_reporting(0)抑制報錯。

我們再回到程式碼執行點,我們發現call_user_func函式執行完的值會執行迴圈再次回到call_user_func()中當回撥函式的引數進行使用。因此需要考慮一下怎麼調整才能讓我們執行並且抑制報錯。

1.如果我們將error_reporting放在前面執行,無論引數是什麼都會返回0從而導致後面執行程式碼不可控。

2.如果我們將think\Build::module放前面,那麼thinkphp報錯也不能執行成功。

但是如果我們放入一箇中間值,在第一次執行能夠成功建立目錄,並且error_reporting還能成功執行,這時候就需要用到PHP弱型別比較,PHP中 0 == null,0 == 非數字開頭的字串。

payload如下可示:

thinkphp諸多限制條件下如何getshell詳解

thinkphp諸多限制條件下如何getshell詳解

方法二使用註釋符繞過語法產生的錯誤

payload如下:

thinkphp諸多限制條件下如何getshell詳解

這樣就會使用註釋符註釋掉後面的語法錯誤,然後使用?>包裹住,後面跟上自己用的payload即可。但是這樣會產生一個問題,無法在win環境下使用,win下資料夾中不能帶這些字元/ \ : * ? " < > |

方法三檔案包含&php偽協議

這種操作就是,我們通過之前的think\Build::module寫檔案進去,寫入的內容是我們rot13編碼過的。然後通過think\__include_file呼叫我們寫入檔案的內容,因為這個過濾不夠完全,可以讓我們包含我們所寫的內容。

thinkphp諸多限制條件下如何getshell詳解

thinkphp諸多限制條件下如何getshell詳解

方法四覆蓋日誌路徑寫入

因為題目將error_log函式ban掉了,所以這個非預期解是在不ban掉error_log函式的情況下所實現的。

payload具體如下:

thinkphp諸多限制條件下如何getshell詳解

1.通過json_decode使得我們傳入的{"type":"File","path":"/var/www/html/null/public/logs"}轉換成內建類stdClass的一個物件。

2.再通過get_object_vars將其轉換成陣列傳入到think\Log::init中。

3.在其中會new了一個\think\log\driver\File,並且傳入的引數是我們的'path'=>/var/www/html/null/public/logs,那麼會觸發類中的__construct,將其預設的path給覆蓋掉。

thinkphp諸多限制條件下如何getshell詳解

thinkphp諸多限制條件下如何getshell詳解

4.最後因為我們觸發漏洞點的特殊性,肯定會報錯使得報錯資訊可以被計入到log檔案裡。

thinkphp諸多限制條件下如何getshell詳解

thinkphp諸多限制條件下如何getshell詳解

5.之後再通過think\Lang::load包含。

thinkphp諸多限制條件下如何getshell詳解

thinkphp諸多限制條件下如何getshell詳解

thinkphp諸多限制條件下如何getshell詳解

方法五 ::竟然可以呼叫非靜態方法

下面是個簡單的例子。

<?php
class A{
 public function test1($a){
  echo "test1".$a;
 }
 static function test2($a){
  echo "test2".$a;
 }
 public function test3($a){
  $this->b = $a;
  echo "test3".$this->b;
 }
}

call_user_func("A::test1","x");
echo "</br>";
call_user_func("A::test2","x");
echo "</br>";
call_user_func("A::test3","x");
echo "</br>";
//$xxx=new A();
//call_user_func(array($xxx,'test3'),"x");

我們看看會怎麼執行。

會發現使用::呼叫了public類的方法並且能夠成功執行,但是會報錯。並且::僅僅適合在方法中沒有寫$this的情況,因為$this指代的是這個物件,找不到物件自然會報錯。那麼我們看一下下面的payload就會一眼明白,payload其實用了跟上面預期解抑制錯誤的另一種方法,然後抑制報錯讓TP不會遇錯停止執行。

這個題解的payload如下:

thinkphp諸多限制條件下如何getshell詳解

1.因為PHP本身的錯誤處理被thinkphp所替代進行處理,所以上面就是將thinkphp所替代錯誤進行處理的方法給覆蓋掉導致沒有辦法正常執行。

2.呼叫self::path方法,可以拋棄掉我們上一個執行的返回值,並且返回我們所輸入的path。為什麼會返回path,path為什麼是我們輸入的值,這個就是之前提到的程式碼執行點他是覆蓋了Request類的引數,所以方法返回的是$this->path,這個我們可以控制。

thinkphp諸多限制條件下如何getshell詳解

3.之後呼叫base64_decode,返回值就是我們base64解碼的內容。

4.解碼後的返回值就會進入\think\view\driver\Php::Display中,然後進入eval執行程式碼。

thinkphp諸多限制條件下如何getshell詳解

thinkphp諸多限制條件下如何getshell詳解

總結

到此這篇關於thinkphp諸多限制條件下如何getshell詳解的文章就介紹到這了,更多相關tp諸多限制條件下getshell內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!