欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

PHP-CGI遠(yuǎn)程代碼執(zhí)行漏洞分析與防范

 更新時(shí)間:2017年05月07日 08:57:55   作者:離別歌  
本文給大家介紹的是PHP-CGI遠(yuǎn)程代碼執(zhí)行漏洞(CVE-2012-1823)分析和防范,這是最近爆出的一個(gè)php的比較嚴(yán)重的漏洞,這里分享給大家。

CVE-2012-1823出來(lái)時(shí)據(jù)說(shuō)是“PHP遠(yuǎn)程代碼執(zhí)行漏洞”,曾經(jīng)也“轟動(dòng)一時(shí)”,當(dāng)時(shí)的我只是剛踏入安全門(mén)的一個(gè)小菜,直到前段時(shí)間tomato師傅讓我看一個(gè)案例,我才想起來(lái)這個(gè)漏洞。通過(guò)在 Vulhub 中對(duì)這個(gè)漏洞環(huán)境的搭建與漏洞原理的分析,我覺(jué)得還挺有意思的,故寫(xiě)出一篇文章來(lái),和大家分享。

首先,介紹一下PHP的運(yùn)行模式。

下載PHP源碼,可以看到其中有個(gè)目錄叫sapi。sapi在PHP中的作用,類(lèi)似于一個(gè)消息的“傳遞者”,比如我在《 Fastcgi協(xié)議分析 && PHP-FPM未授權(quán)訪問(wèn)漏洞 && Exp編寫(xiě) 》一文中介紹的fpm,他的作用就是接受Web容器通過(guò)fastcgi協(xié)議封裝好的數(shù)據(jù),并交給PHP解釋器執(zhí)行。

除了fpm,最常見(jiàn)的sapi應(yīng)該是用于Apache的mod_php,這個(gè)sapi用于php和apache之間的數(shù)據(jù)交換。

php-cgi也是一個(gè)sapi。在遠(yuǎn)古的時(shí)候,web應(yīng)用的運(yùn)行方式很簡(jiǎn)單,web容器接收到http數(shù)據(jù)包后,拿到用戶請(qǐng)求的文件(cgi腳本),并fork出一個(gè)子進(jìn)程(解釋器)去執(zhí)行這個(gè)文件,然后拿到執(zhí)行結(jié)果,直接返回給用戶,同時(shí)這個(gè)解釋器子進(jìn)程也就結(jié)束了?;赽ash、perl等語(yǔ)言的web應(yīng)用多半都是以這種方式來(lái)執(zhí)行,這種執(zhí)行方式一般就被稱(chēng)為cgi,在安裝Apache的時(shí)候默認(rèn)有一個(gè)cgi-bin目錄,最早就是放置這些cgi腳本用的。

但cgi模式有個(gè)致命的缺點(diǎn),眾所周知,進(jìn)程的創(chuàng)建和調(diào)度都是有一定消耗的,而且進(jìn)程的數(shù)量也不是無(wú)限的。所以,基于cgi模式運(yùn)行的網(wǎng)站通常不能同時(shí)接受大量請(qǐng)求,否則每個(gè)請(qǐng)求生成一個(gè)子進(jìn)程,就有可能把服務(wù)器擠爆。于是后來(lái)就有了fastcgi,fastcgi進(jìn)程可以將自己一直運(yùn)行在后臺(tái),并通過(guò)fastcgi協(xié)議接受數(shù)據(jù)包,執(zhí)行后返回結(jié)果,但自身并不退出。

php有一個(gè)叫php-cgi的sapi,php-cgi有兩個(gè)功能,一是提供cgi方式的交互,二是提供fastcgi方式的交互。也就說(shuō),我們可以像perl一樣,讓web容器直接fork一個(gè)php-cgi進(jìn)程執(zhí)行某腳本;也可以在后臺(tái)運(yùn)行 php-cgi -b 127.0.0.1:9000 (php-cgi作為fastcgi的管理器),并讓web容器用fastcgi協(xié)議和9000交互。

那我之前說(shuō)的fpm又是什么呢?為什么php有兩個(gè)fastcgi管理器?php確實(shí)有兩個(gè)fastcgi管理器,php-cgi可以以fastcgi模式運(yùn)行,fpm也是以fastcgi模式運(yùn)行。但fpm是php在5.3版本以后引入的,是一個(gè)更高效的fastcgi管理器,其諸多優(yōu)點(diǎn)我就不多說(shuō)了,可以自己去翻翻源碼。因?yàn)閒pm優(yōu)點(diǎn)更多,所以現(xiàn)在越來(lái)越多的web應(yīng)用使用php-fpm去運(yùn)行php。

回到本漏洞。CVE-2012-1823就是php-cgi這個(gè)sapi出現(xiàn)的漏洞,我上面介紹了php-cgi提供的兩種運(yùn)行方式:cgi和fastcgi,本漏洞只出現(xiàn)在以cgi模式運(yùn)行的php中。

這個(gè)漏洞簡(jiǎn)單來(lái)說(shuō),就是用戶請(qǐng)求的querystring被作為了php-cgi的參數(shù),最終導(dǎo)致了一系列結(jié)果。

探究一下原理, RFC3875 中規(guī)定,當(dāng)querystring中不包含沒(méi)有解碼的 = 號(hào)的情況下,要將querystring作為cgi的參數(shù)傳入。所以,Apache服務(wù)器按要求實(shí)現(xiàn)了這個(gè)功能。

但PHP并沒(méi)有注意到RFC的這一個(gè)規(guī)則,也許是曾經(jīng)注意并處理了,處理方法就是web上下文中不允許傳入?yún)?shù)。但在2004年的時(shí)候某個(gè)開(kāi)發(fā)者發(fā)表過(guò)這么一段言論:

From: Rasmus Lerdorf <rasmus <at> lerdorf.com>
Subject: [PHP-DEV] php-cgi command line switch memory check
Newsgroups: gmane.comp.php.devel
Date: 2004-02-04 23:26:41 GMT (7 years, 49 weeks, 3 days, 20 hours and 39 minutes ago)

In our SAPI cgi we have a check along these lines:

 if (getenv("SERVER_SOFTWARE")
  || getenv("SERVER_NAME")
  || getenv("GATEWAY_INTERFACE")
  || getenv("REQUEST_METHOD")) {
  cgi = 1;
 }

 if(!cgi) getopt(...)

As in, we do not parse command line args for the cgi binary if we are 
running in a web context. At the same time our regression testing system 
tries to use the cgi binary and it sets these variables in order to 
properly test GET/POST requests. From the regression testing system we 
use -d extensively to override ini settings to make sure our test 
environment is sane. Of course these two ideas conflict, so currently our 
regression testing is somewhat broken. We haven't noticed because we 
don't have many tests that have GET/POST data and we rarely build the cgi 
binary.

The point of the question here is if anybody remembers why we decided not 
to parse command line args for the cgi version? I could easily see it 
being useful to be able to write a cgi script like:

 #!/usr/local/bin/php-cgi -d include_path=/path
 <?php
  ...
 ?>

and have it work both from the command line and from a web context.

As far as I can tell this wouldn't conflict with anything, but somebody at 
some point must have had a reason for disallowing this.

-Rasmus

顯然,這位開(kāi)發(fā)者是為了方便使用類(lèi)似 #!/usr/local/bin/php-cgi -d include_path=/path 的寫(xiě)法來(lái)進(jìn)行測(cè)試,認(rèn)為不應(yīng)該限制php-cgi接受命令行參數(shù),而且這個(gè)功能不和其他代碼有任何沖突。

于是, if(!cgi) getopt(...) 被刪掉了。

但顯然,根據(jù)RFC中對(duì)于command line的說(shuō)明,命令行參數(shù)不光可以通過(guò) #!/usr/local/bin/php-cgi -d include_path=/path 的方式傳入php-cgi,更可以通過(guò)querystring的方式傳入。

這就是本漏洞的歷史成因。

那么,可控命令行參數(shù),能做些什么事。

通過(guò)閱讀源碼,我發(fā)現(xiàn)cgi模式下有如下一些參數(shù)可用:

-c 指定php.ini文件的位置
-n 不要加載php.ini文件
-d 指定配置項(xiàng)
-b 啟動(dòng)fastcgi進(jìn)程
-s 顯示文件源碼
-T 執(zhí)行指定次該文件
-h-? 顯示幫助

最簡(jiǎn)單的利用方式,當(dāng)然就是 -s ,可以直接顯示源碼:

但閱讀過(guò)我寫(xiě)的fastcgi那篇文章的同學(xué)應(yīng)該很快就想到了一個(gè)更好的利用方法:通過(guò)使用 -d 指定 auto_prepend_file 來(lái)制造任意文件讀取漏洞,執(zhí)行任意代碼:

注意,空格用 +%20 代替, = 用url編碼代替。

這個(gè)漏洞被爆出來(lái)以后,PHP官方對(duì)其進(jìn)行了修補(bǔ),發(fā)布了新版本5.4.2及5.3.12,但這個(gè)修復(fù)是不完全的,可以被繞過(guò),進(jìn)而衍生出CVE-2012-2311漏洞。

PHP的修復(fù)方法是對(duì) - 進(jìn)行了檢查:

if(query_string = getenv("QUERY_STRING")) {
 decoded_query_string = strdup(query_string);
 php_url_decode(decoded_query_string, strlen(decoded_query_string));
 if(*decoded_query_string == '-' && strchr(decoded_query_string, '=') == NULL) {
  skip_getopt = 1;
 }
 free(decoded_query_string);
}

可見(jiàn),獲取querystring后進(jìn)行解碼,如果第一個(gè)字符是 - 則設(shè)置skip_getopt,也就是不要獲取命令行參數(shù)。

這個(gè)修復(fù)方法不安全的地方在于,如果運(yùn)維對(duì)php-cgi進(jìn)行了一層封裝的情況下:

#!/bin/sh

exec /usr/local/bin/php-cgi $*

通過(guò)使用空白符加 - 的方式,也能傳入?yún)?shù)。這時(shí)候querystring的第一個(gè)字符就是空白符而不是 - 了,繞過(guò)了上述檢查。

于是,php5.4.3和php5.3.13中繼續(xù)進(jìn)行修改:

if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) {
 /* we've got query string that has no = - apache CGI will pass it to command line */
 unsigned char *p;
 decoded_query_string = strdup(query_string);
 php_url_decode(decoded_query_string, strlen(decoded_query_string));
 for (p = decoded_query_string; *p && *p <= ' '; p++) {
  /* skip all leading spaces */
 }
 if(*p == '-') {
  skip_getopt = 1;
 }
 free(decoded_query_string);
}

先跳過(guò)所有空白符(小于等于空格的所有字符),再判斷第一個(gè)字符是否是 - 。

這個(gè)漏洞在當(dāng)年的影響應(yīng)該說(shuō)中等。因?yàn)镻HP-CGI這個(gè)SAPI在漏洞出現(xiàn)的時(shí)間點(diǎn),因?yàn)槠湫阅艿葐?wèn)題,已經(jīng)在慢慢退出歷史舞臺(tái)了。但考慮到PHP這個(gè)在Web領(lǐng)域舉足輕重的語(yǔ)言,跨越多年,用量巨大,很多老的設(shè)備、服務(wù)器仍在運(yùn)行有漏洞的版本和PHP-CGI,所以影響也不能低估。

不過(guò),在2017年的今天,我分析這個(gè)漏洞當(dāng)然已經(jīng)不能談?dòng)绊懥?,只是其思路確實(shí)比較有意思,又讓我領(lǐng)會(huì)了一次閱讀RFC的重要性。

相關(guān)文章

  • PHP 源代碼分析 Zend HashTable詳解

    PHP 源代碼分析 Zend HashTable詳解

    在PHP的Zend引擎中,有一個(gè)數(shù)據(jù)結(jié)構(gòu)非常重要,它無(wú)處不在,是PHP數(shù)據(jù)存儲(chǔ)的核心,各種常量、變量、函數(shù)、類(lèi)、對(duì)象等都用它來(lái)組織,這個(gè)數(shù)據(jù)結(jié)構(gòu)就是HashTable。
    2009-08-08
  • php中Snoopy類(lèi)用法實(shí)例

    php中Snoopy類(lèi)用法實(shí)例

    這篇文章主要介紹了php中Snoopy類(lèi)用法,實(shí)例分析了使用Snoopy類(lèi)實(shí)現(xiàn)頁(yè)面抓取的相關(guān)技巧,需要的朋友可以參考下
    2015-06-06
  • php數(shù)據(jù)庫(kù)操作model類(lèi)(使用__call方法)

    php數(shù)據(jù)庫(kù)操作model類(lèi)(使用__call方法)

    這篇文章主要介紹了php數(shù)據(jù)庫(kù)操作model類(lèi),使用__call方法實(shí)現(xiàn)了數(shù)據(jù)的查詢功能,需要的朋友可以參考下
    2016-11-11
  • php mysql Errcode: 28 終極解決方法

    php mysql Errcode: 28 終極解決方法

    php mysql Errcode: 28 終極解決方法,碰到這類(lèi)問(wèn)題的朋友可以參考下。
    2009-07-07
  • 淺談PHP SHA1withRSA加密生成簽名及驗(yàn)簽

    淺談PHP SHA1withRSA加密生成簽名及驗(yàn)簽

    這篇文章主要介紹了PHP SHA1withRSA加密生成簽名及驗(yàn)簽,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • PHP中=賦值操作符對(duì)不同數(shù)據(jù)類(lèi)型的不同行為

    PHP中=賦值操作符對(duì)不同數(shù)據(jù)類(lèi)型的不同行為

    作為一個(gè)PHP的新手,一直對(duì)PHP的引用困惑了很久,今晚仔細(xì)看了用戶手冊(cè)和做了一些實(shí)驗(yàn),終于明白了其中的原理和細(xì)節(jié),特別是=操作符對(duì)于不同類(lèi)型的不同行為。
    2011-01-01
  • php 需要掌握的東西 不做浮躁的人

    php 需要掌握的東西 不做浮躁的人

    很多學(xué)PHP的人一直也搞不清楚,一個(gè)PHP程序員和Java程序員或者是.net程序員有什么不同,告訴你,其實(shí)都一樣!
    2009-12-12
  • PHP基于openssl實(shí)現(xiàn)非對(duì)稱(chēng)加密代碼實(shí)例

    PHP基于openssl實(shí)現(xiàn)非對(duì)稱(chēng)加密代碼實(shí)例

    這篇文章主要介紹了PHP基于openssl實(shí)現(xiàn)非對(duì)稱(chēng)加密代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • PHP 自動(dòng)加載類(lèi)原理與用法實(shí)例分析

    PHP 自動(dòng)加載類(lèi)原理與用法實(shí)例分析

    這篇文章主要介紹了PHP 自動(dòng)加載類(lèi)原理與用法,結(jié)合具體實(shí)例形式分析了PHP 自動(dòng)加載類(lèi)基本概念、原理、使用方法及操作注意事項(xiàng),需要的朋友可以參考下
    2020-04-04
  • php實(shí)現(xiàn)的美國(guó)50個(gè)州選擇列表實(shí)例

    php實(shí)現(xiàn)的美國(guó)50個(gè)州選擇列表實(shí)例

    這篇文章主要介紹了php實(shí)現(xiàn)的美國(guó)50個(gè)州選擇列表實(shí)例,可實(shí)現(xiàn)讓當(dāng)前州為選中狀態(tài)的功能,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-04-04

最新評(píng)論