Subscribe to PHP Freaks RSS

Preventing remote file include attacks with mod rewrite

Print
by Thomas Johnson on May 27, 2008 7:52:29 PM - 56,761 views

I have seen many attempted rfi attacks and almost all of these are basically the same. PHPfreaks has seen thousands of these attacks and most have a url somewhere in the query string. The good news is that we can use a simple rewrite to prevent these attacks.

Here we check our query string for http://, https:// or ftp://

RewriteCond %{QUERY_STRING} (.*)(http|https|ftp):\/\/(.*)

If you are using this rewrite within a .htaccess all you have left is to deny access from all matching requests.

RewriteRule ^(.+)$ - [F]

If you have access to your vhost you could also log those requests like this:

<IfModule mod_rewrite.c>
   RewriteEngine on
   RewriteCond %{QUERY_STRING} (.*)(http|https|ftp):\/\/(.*)
   RewriteRule ^(.+)$ - [env=rfi:true]
</IfModule>
CustomLog /path/to/logs/rfi.log combined env=rfi

You will also have to deny access from requests that have been caught by the above rewrite

Deny from env=rfi

Comments

Daniel May 28, 2008 3:11:40 AM

In case anyone is interested, the log file we have for this gets a new entry at least once per second. Sometimes even quicker.

Corbin Hughes May 28, 2008 4:35:59 AM

Entirely offtopic, but... Do you mean for this tutorial or someone trying to do an attack like this?

Anyway, a real comment:

If someone doesn't have access to mod_rewrite, he/she could always just use PHP (like they should in the first place!).

Methods of protecting file paths can come in many forms...

<?php
//pretend $path is some user input or something
$path = str_replace(array('..', '/',), '', $path); //this way is as plain as it gets...

if(preg_match('/some_pattern/', $path)) { //only allow certain things...

}

//Or...  This is a fun little way if you want to give access to subdirectories, but no where else....  (To be honest, I've never extensively tested this or thought of ways around it or anything.... so...)

$here = realpath('.');
$path = realpath($path);

if(strpos($path, $here) === 0) {
    //$path starts with $here and all relative directives are removed....  therefore (excluding symbolic links and what not), $path must be under $here
}

?>

Anyway, just a few ways to do it in PHP....

(Sorry if something doesn't make sense or if there's a type.... 3:30 AM.

Daniel May 28, 2008 5:03:21 AM

This is not about directory traversal, but remote file inclusion attacks ;)

Wasim Ilyas May 28, 2008 6:38:09 AM

But cant you just use regex to check whether the $_GET contains someting dangerous? It might be possible to stop remote file inclusion that way right?

Daniel May 28, 2008 6:46:15 AM

@Wasim Ilyas: Yes, but this works on server level whereas using PHP would work on application level. We implemented this on our server to protect SMF.

Thomas Johnson May 28, 2008 8:24:38 AM

@Wasim llyas: As Daniel said, this works on a server level instead of an application level. We literally see thousands of these attacks each day and we would see a large drop in performance if we processed each of those requests with php. This way those requests never touch our applications. This approach really makes sense when working with third party applications(wordpress, drupal, smf ect) as you would most likely have no idea that a vulnerability existed in the first place.

Corbin Hughes May 28, 2008 11:28:17 AM

Yeah I guess my script wasn't specifically RFI, but it could help protect at a PHP level against that. (If you can't traverse a directory, chances are you can't include a remote file ;p.) (If you try to find examples you probably will be able to lol)

@ Thomas' last comment:
I didn't really think of it that way.... I was thinking it would be slower because each and every request would have to be checked, but in php each and every request (that involved an include) would have to be checked too.... And I can see where it would be extremely useful for third party apps like you said.

Edit: (P.S... Sorry about the lack of code tags in my earlier post who ever editted them in [or are they automatic?]. I tried PHP tags, but it didn't work, and then I was lazy and gave up.)

Daniel May 28, 2008 11:34:33 AM

Re the formatting of posts. I have created this page. It's aimed towards tutorial and blog authors, but some of it is applicable for comments as well.

Edit: Seeing as you're a guru, it is actually for you as well :P

Corbin Hughes May 28, 2008 11:38:42 AM

"

code

" (hopefully that doesn't parse when I submit)

Hrmmm didn't think of that x.x. Better than 10 diff languages in brackets I guess lol...

I glanced through that page last night, but I didn't look through the BBCode... Guess I should've ;p.

*Blushes* (Hrmm that was kinda girly.... Oh well.)

Edit: And I should've looked when I didn't know the BBCode. Laziness at 3AM ftl :(.

fry2010 Apr 26, 2009 9:27:13 AM

hi can someone explain to me breifly what an rfi attack does? If I have shared hosting do I still need to protect against it? Also how do I actually use that code in the tutorial, do I have to create a php file in the .htaccess directory? Thanks.

gizmola Jun 22, 2009 7:47:27 PM

Very simply, it might surprise you to find that php include() will accept a url as a parameter. If, on my site, I have some maliciious php code, and I can get an application on your site to include my code, I can make bad things happen. Typically the way this happens is someone writes some code where they include a variable like so:

include( $fname );

So if I can get the variable $fname to be: http://www.mybadsite.ru/exploit.txt, your server will go ahead and open the url and bring that code in, which the server will very helpfully run.

The trick is finding a hole that allows you to inject your url into the application so that the include occurs. Exploiters pore over popular open source apps looking for these errors, and then they will often use google to help find sites running apps that have these holes, and use scripts to screw them up.

As to how you use the code, you put the rewrite condition and rule in your .htaccess file at the root of your site. It needs to be configured to allow for this.

Well that very much depends on your php version. In php 5.2 they added a setting to defeat RFI's: allow_url_include, and this disables the remote inclusion feature in php, and this comes set to Off now.

Previously you could disable the feature by setting allow_url_fopen = Off, but sometimes people want the ability to be able to open foreign websites using fopen() and similar functions. It took a long time and a lot of arguing, but not unlike Register globals, which was a great enabler of RFI exploits, the php devs finally took steps to help shut down the exploit.

Regardless, this is a nice technique for sending 403's to the script kiddies who write spiders that just suck up your bandwidth attempting to find these exploits on your site, not to mention preventing them from reaching any applications you might be running that have exploits in them. Even with version 5.2, this is a nice idea.

fry2010 Jun 28, 2009 7:17:09 AM

thanks gizmola, that is loud and clear.

Hashim Amla Mar 20, 2010 3:04:47 AM

Minimal httpd.conf or .htaccess sample from source
<IfModule mod_security.c>

# Enable ModSecurity
SecFilterEngine On

# Reject requests with status 403
SecFilterDefaultAction "deny,log,status:403"

# Some sane defaults
SecFilterScanPOST On
SecFilterCheckURLEncoding On
SecFilterCheckUnicodeEncoding Off

# Accept almost all byte values
SecFilterForceByteRange 1 255

# Server masking is optional
# SecServerSignature "Microsoft-IIS/5.0"

# Designate a directory for temporary files
# storage. It is a good idea to change the
# value below to a private directory, just as
# an additional measure against race conditions
SecUploadDir /tmp
SecUploadKeepFiles Off

# Only record the interesting stuff
SecAuditEngine RelevantOnly
# Uncomment below to record responses with unusual statuses
# SecAuditLogRelevantStatus ^5
SecAuditLog logs/modsec_audit.log

# You normally won't need debug logging
SecFilterDebugLevel 0
SecFilterDebugLog logs/modsec_debug.log

# Only accept request encodings we know how to handle
# we exclude GET requests from this because some (automated)
# clients supply "text/html" as Content-Type
SecFilterSelective REQUEST_METHOD "!^(GET|HEAD)$" chain
SecFilterSelective HTTP_Content-Type "!(^application/x-www-form-urlencoded$|^multipart/form-data;)"

# Do not accept GET or HEAD requests with bodies
SecFilterSelective REQUEST_METHOD "^(GET|HEAD)$" chain
SecFilterSelective HTTP_Content-Length "!^$"

# Require Content-Length to be provided with
# every POST request
SecFilterSelective REQUEST_METHOD "^POST$" chain
SecFilterSelective HTTP_Content-Length "^$"

# Don't accept transfer encodings we know we don't handle
SecFilterSelective HTTP_Transfer-Encoding "!^$"

regards,

Hashim Amla
---------------------
water war is a future war!

Add Comment

Login or register to post a comment.