無字母數字Webshell之提高篇

前幾天有同學提出了一個問題,大概代碼如下:

if(isset($_GET['code'])){
$code = $_GET['code'];
if(strlen($code)>35){
die("Long.");
}
if(preg_match("/[A-Za-z0-9_$]+/",$code)){
die("NO.");
}
eval($code);
}else{
highlight_file(__FILE__);
}

這個代碼如果要getshell,怎樣利用?

這題可能來自是我曾寫過的一篇文章:《一些不包含數字和字母的Webshell》,裡面介紹瞭如何構造無字母數字的webshell。其中有兩個主要的思路:

1.利用位運算

2.利用自增運算符

當然,這道題多了兩個限制:

1.webshell長度不超過35位

2.除了不包含字母數字,還不能包含$和_

難點呼之欲出了,我前面文章中給出的所有方法,都用到了PHP中的變量,需要對變量進行變形、異或、取反等操作,最後動態執行函數。但現在,因為$不能使用了,所以我們無法構造PHP中的變量。

所以,如何解決這個問題?

PHP7 下簡單解決問題

我們將上述代碼放在index.php中,然後執行docker run –rm -p 9090:80 -v pwd:/var/www/html php:7.2-apache,啟動一個php 7.2的服務器。

php7中修改了表達式執行的順序:http://php.net/manual/zh/migration70.incompatible.php :

無字母數字Webshell之提高篇

PHP7前是不允許用($a)();這樣的方法來執行動態函數的,但PHP7中增加了對此的支持。所以,我們可以通過(‘phpinfo’)();來執行函數,第一個括號中可以是任意PHP表達式。

所以很簡單了,構造一個可以生成phpinfo這個字符串的PHP表達式即可。payload如下(不可見字符用url編碼表示):

(~%8F%97%8F%96%91%99%90)();
無字母數字Webshell之提高篇

PHP5的思考

我們使用docker run –rm -p 9090:80 -v pwd:/var/www/html php:5.6-apach來運行一個php5.6的web環境。

此時,我們嘗試用PHP7的payload,將會得到一個錯誤:

無字母數字Webshell之提高篇

原因就是php5並不支持這種表達方式。

在我在知識星球裡發出帖子的時候,其實還沒想到如何用PHP5解決問題,但我有自信解決它,所以先發了這個小挑戰。後來關上電腦仔細想想,發現當思路禁錮在一個點的時候,你將會鑽進牛角尖;當你用大局觀來看待問題,問題就迎刃而解。

當然,我覺得我的方法應該不是唯一的,不過一直沒人出來公佈答案,我就先拋鑽引玉了。

大部分語言都不會是單純的邏輯語言,一門全功能的語言必然需要和操作系統進行交互。操作系統裡包含的最重要的兩個功能就是“shell(系統命令)”和“文件系統”,很多木馬與遠控其實也只實現了這兩個功能。

PHP自然也能夠和操作系統進行交互,“反引號”就是PHP中最簡單的執行shell的方法。那麼,在使用PHP無法解決問題的情況下,為何不考慮用“反引號”+“shell”的方式來getshell呢?

PHP5+shell打破禁錮

因為反引號不屬於“字母”、“數字”,所以我們可以執行系統命令,但問題來了:如何利用無字母、數字、$的系統命令來getshell?

好像問題又回到了原點:無字母、數字、$,在shell中仍然是一個難題。

此時我想到了兩個有趣的Linux shell知識點:

1.shell下可以利用.來執行任意腳本

2.Linux文件名支持用glob通配符代替

第一點曾在《 小密圈裡的那些奇技淫巧 》露出過一角,但我沒細講。.或者叫period,它的作用和source一樣,就是用當前的shell執行一個文件中的命令。比如,當前運行的shell是bash,則. file的意思就是用bash執行file文件中的命令。

用. file執行文件,是不需要file有x權限的。那麼,如果目標服務器上有一個我們可控的文件,那不就可以利用.來執行它了嗎?

這個文件也很好得到,我們可以發送一個上傳文件的POST包,此時PHP會將我們上傳的文件保存在臨時文件夾下,默認的文件名是/tmp/phpXXXXXX,文件名最後6個字符是隨機的大小寫字母。

第二個難題接踵而至,執行. /tmp/phpXXXXXX,也是有字母的。此時就可以用到Linux下的glob通配符:

*可以代替0個及以上任意字符

?可以代表1個任意字符

那麼,/tmp/phpXXXXXX就可以表示為/*/?????????或/???/?????????。

但我們嘗試執行. /???/?????????,卻得到如下錯誤:

無字母數字Webshell之提高篇

這是因為,能夠匹配上/???/?????????這個通配符的文件有很多,我們可以列出來:

無字母數字Webshell之提高篇

可見,我們要執行的/tmp/phpcjggLC排在倒數第二位。然而,在執行第一個匹配上的文件(即/bin/run-parts)的時候就已經出現了錯誤,導致整個流程停止,根本不會執行到我們上傳的文件。

思路又陷入了僵局,雖然方向沒錯。

深入理解glob通配符

大部分同學對於通配符,可能知道的都只有*和?。但實際上,閱讀Linux的文檔( http://man7.org/linux/man-pages/man7/glob.7.html ),可以學到更多有趣的知識點。

其中,glob支持用[^x]的方法來構造“這個位置不是字符x”。那麼,我們用這個姿勢幹掉/bin/run-parts:

無字母數字Webshell之提高篇

排除了第4個字符是-的文件,同樣我們可以排除包含.的文件:

無字母數字Webshell之提高篇

現在就剩最後三個文件了。但我們要執行的文件仍然排在最後,但我發現這三個文件名中都不包含特殊字符,那麼這個方法似乎行不通了。

繼續閱讀glob的幫助,我發現另一個有趣的用法:

無字母數字Webshell之提高篇

就跟正則表達式類似,glob支持利用[0-9]來表示一個範圍。

我們再來看看之前列出可能干擾我們的文件:

無字母數字Webshell之提高篇

所有文件名都是小寫,只有PHP生成的臨時文件包含大寫字母。那麼答案就呼之欲出了,我們只要找到一個可以表示“大寫字母”的glob通配符,就能精準找到我們要執行的文件。

翻開ascii碼錶,可見大寫字母位於@與[之間:

無字母數字Webshell之提高篇

那麼,我們可以利用[@-[]來表示大寫字母:

無字母數字Webshell之提高篇

顯然這一招是管用的。

構造POC,執行任意命令

當然,php生成臨時文件名是隨機的,最後一個字符不一定是大寫字母,不過多嘗試幾次也就行了。

最後,我傳入的code為?>=. /???/????????[@-[];?>,發送數據包如下:

無字母數字Webshell之提高篇

成功執行任意命令。


分享到:


相關文章: