Posts Tagged ‘htaccess’

Making things a little more difficult to run exploits on compromised WordPress sites

Monday, September 30th, 2013

I was called in to fix a number of WordPress sites that had been hacked. Many were running older versions of WordPress and thankfully weren’t running SetUID, so, the damage was limited to exploit scripts running in some of the world writeable directories.

After cleaning up the sites, upgrading them to the latest version of WordPress and scanning for additional exploits, I added a number of rules to each of the Apache VirtualHost configs on his server.

<Directory /var/www/domain.com/wp-content/uploads/>
AllowOverride none
RemoveHandler .cgi .pl .py
<FilesMatch "\.(php|p?html?)$">
  SetHandler none
</FilesMatch>
</Directory>

<Directory /var/www/domain.com/wp-content/cache/>
AllowOverride none
RemoveHandler .cgi .pl .py
<FilesMatch "\.(php|p?html?)$">
  SetHandler none
</FilesMatch>
</Directory>

These rules need to be placed in the VirtualHost configuration and prevent PHP, cgi scripts, Perl and Python files from being executed in the two directories that WordPress is allowed to write to. To prevent other tampering, we disallow Overrides which prevents hackers from creating a directory and including their own .htaccess that would enable PHP or CGI to be parsed.

Since making these changes, we’ve seen a few files dropped into the uploads directory, but, none have been executable.

Hackers bypass .htaccess security by using GETS rather than GET

Friday, December 10th, 2010

Last night I received an urgent message from a client. My machine has been hacked, someone got into the admin area, I need all of the details from this IP.

So, I grepped the logs, grabbed the appropriate entries and saw something odd.

1.2.3.4 - - [09/Dec/2010:22:15:41 -0500] "GETS /admin/index.php HTTP/1.1" 200 3505 "-" "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12"
1.2.3.4 - - [09/Dec/2010:22:17:09 -0500] "GETS /admin/usermanagement.php HTTP/1.1" 200 99320 "-" "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12"
1.2.3.4 - - [09/Dec/2010:22:18:05 -0500] "GETS /admin/index.php HTTP/1.1" 200 3510 "-" "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12"

A modified snippet of the .htaccess file:

AuthUserFile .htpasswd
AuthName "Protected Area"
AuthType Basic

<Limit GET POST>
require valid-user
</Limit>

Of course, we know GETS isn’t valid, but, why is Apache handing out status 200s and content lengths that appear to be valid? We know the area was password protected behind .htaccess and with some quick keyboard work we’ve got a system that properly prompts for Basic Authentication with a properly formed HTTP/1.0 request. Removing the <Limit> restriction from the .htaccess protects the site, but, why are these other methods able to pass through? Replacing GETS with anything other than POST, PUT, DELETE, TRACK, TRACE, OPTIONS, HEAD results in Apache treating those requests as if GET had been typed.

Let’s set up a duplicate environment on another machine to figure out what Apache is doing.

tsavo:~ mcd$ telnet devel.mia 80
Trying x.x.x.x...
Connected to xxxxxxx.xxx.
Escape character is '^]'.
GET /htpasstest/ HTTP/1.0    

HTTP/1.1 401 Authorization Required
Date: Fri, 10 Dec 2010 21:29:58 GMT
Server: Apache
WWW-Authenticate: Basic realm="Protected Area"
Vary: Accept-Encoding
Content-Length: 401
Connection: close
Content-Type: text/html; charset=iso-8859-1

Let’s try what they did:

tsavo:~ mcd$ telnet devel.mia 80
Trying x.x.x.x...
Connected to xxxxxxx.xxx.
Escape character is '^]'.
GETS /htpasstest/ HTTP/1.0

HTTP/1.1 501 Method Not Implemented
Date: Fri, 10 Dec 2010 21:53:58 GMT
Server: Apache
Allow: GET,HEAD,POST,OPTIONS,TRACE
Vary: Accept-Encoding
Content-Length: 227
Connection: close
Content-Type: text/html; charset=iso-8859-1

Odd, this is the behavior we expected, but, not what we are experiencing on the client’s machine. Digging a little further we look at the differences and begin to suspect the machine may have been compromised. The first thing that struck was mod_negotiation – probably not. mod_actions, maybe, but, no. DAV wasn’t loaded, but, Zend Optimizer was on the machine that appeared to have been exploited. Testing the above script on the client’s machine resulted in…. exactly the same behavior — method not supported. Testing the directory that was exploited results in the GETS request served as if it was a GET request.

So, now we’ve got a particular domain on the machine that is not behaving as the config files would suggest. A quick test on the original domain, and as expected, GETS responds with the data and bypasses the authorization clause in the .htaccess. Lets try one more test:


# telnet xxxxxx.mia 80
Trying x.x.x.x...
Connected to xxxxxx.xxx.
Escape character is '^]'.
GETS /zend.php HTTP/1.1
Host: xxxxxx.xxx

HTTP/1.1 200 OK
Date: Fri, 10 Dec 2010 22:02:33 GMT
Server: Apache
Vary: Accept-Encoding
Content-Length: 602
Connection: close
Content-Type: text/html

Bingo. A Zend encoded file handed us an error 200 even though it contained an invalid request method.

The solution in this case was simple, remove the <Limit> clause from the .htaccess.

The question is, is Zend Optimizer actually doing the proper thing here. Watching Apache with gdb, Zend Optimizer does appear to hook the Apache request handler a bit higher, but why is it attempting to correct an invalid request?

One of the first rules in input validation is validate and reject on error. Never try to correct the data and accept it. If you try to correct it and make a mistake, you’re just as vulnerable and hackers will try to figure out those patterns and add extra escaping into their url request. In this case, only a few pages were able to be displayed as there were checks to make sure forms were POSTed. But, the Limit in .htaccess that should have protected the application, didn’t work as expected because the invalid methods weren’t specified.

As so many applications on the web generate .htpasswd files with the Limit clause, it makes me wonder how many Zend Encoded applications are vulnerable. Take a minute to check your systems.

Entries (RSS) and Comments (RSS).
Cluster host: li