WAF bypass made easy

Author: pz

In this post I will share my testing experiences about a web application protected by a web application firewall (WAF). The investigation of the parameters of web interfaces revealed that I can perform XSS attacks in some limited ways. The target implemented blacklist-based filtering that provided some HTML tag and event handler restriction. Since this restriction appeared at quite unusual places I suspected that there might be a WAF in front of the application. To verify my suspicion:

  • I tested the interfaces by random HTTP parameters which contained XSS payload. Most of the time a WAF checks all of the input parameters but the server side validation only checks the expected ones;
  • I used the Wafit tool.

These tests underpinned my assumption: there was indeed some application level firewall in front of my target.

In order to map the filter rules I created a list of HTML4 and HTML5 tags and event handlers based on the information of w3.org. After this I could easily test the allowed tags and event handlers with Burp Suite Professional:

HTML eventsHTML events (click to enlarge)

Untitled2

HTML tags (click to enlarge)

Based on the results we can perform only some browser specific attacks typically against IE/6.0. However, many web applications handle parameters passed by GET and POST methods in the same way. I performed the tests again, now submitting the crafted parameters in the HTTP POST body and found that the WAF didn’t interrupt my request and the application processed my input through the same vulnerable code path. This way I could perform XSS without limitation and surprisingly was able to find an SQL injection issue also.

When you try implement a WAF in your infrastructure the most important key concepts are:

  • Know your system: Collect the applications and HTTP parameters to be protected and understand how they are used (data types, expected format, etc.);
  • Consult somebody with an offensive attitude (like a penetration tester) before you implement the defense;
  • Be aware of the limits of blacklist-based filtering;

Improper WAF implementation can lead to false sense of security that can result in more damage than operating applications with known security issues.


Plesk panel decryption

Author: pz

After I read the description of the Plesk vulnerability CVE-2012-1557 I decided to investigate the application a bit deeper. You can download a fully installed VMware image from the internet so you can skip the install and save some time. The PHP files which belong to the PLESK application are encrypted:

[root@localhost tmp]# cat /usr/local/psa/admin/htdocs/index.php
die(“The file {$_SERVER[‘SCRIPT_FILENAME’]} is part of Plesk 9 distribution. It cannot be run outside of Plesk 9 environment.\n”);
__sw_loader_pragma__(‘P’);
?>
1^:??Zl??9??W??RK?3??^Om??n.#’?N”??64?Z??W?[>?p?vW???`J?1?R?y?E?i38???

First of all I investigated the /usr/local/psa/bin/sw-engine-pleskrun executable which is a modified PHP interpreter:

[root@localhost ~]# /usr/local/psa/bin/sw-engine-pleskrun –help
Usage: sw-engine [options] [-f] [–] [args…]
sw-engine [options] -r [–] [args…]
sw-engine [options] [-B <begin_code>] -R [-E <end_code>] [–] [args…]
sw-engine [options] [-B <begin_code>] -F [-E <end_code>] [–] [args…]
sw-engine [options] — [args…]
sw-engine [options] -a

-a Run interactively
-c | Look for php.ini file in this directory
-n No php.ini file will be used
-d foo[=bar] Define INI entry foo with value ‘bar’
-e Generate extended information for debugger/profiler
-f Parse and execute .
-h This help
-i PHP information
-l Syntax check only (lint)
-m Show compiled in modules
-r Run PHP without using script tags
-B <begin_code> Run PHP <begin_code> before processing input lines
-R Run PHP for every input line
-F Parse and execute for every input line
-E <end_code> Run PHP <end_code> after processing all input lines
-H Hide any passed arguments from external tools.
-s Output HTML syntax highlighted source Output source with stripped comments and whitespace.
-z Load Zend extension .

args… Arguments passed to script. Use — args when first argument
starts with – or script is read from stdin

–ini Show configuration file names

–rf Show information about function .
–rc Show information about class .
–re Show information about extension .
–ri Show configuration for extension .

I tried to inspect the syscalls of the Plesk binary:

[root@localhost tmp]# strace ./sw-engine-pleskrun ../admin/htdocs/index.php
execve(“./sw-engine-pleskrun”, [“./sw-engine-pleskrun”, “../admin/htdocs/index.php”], [/* 23 vars */]) = 0
brk(0) = 0x8dc9000
.
.
.
getppid() = 17921
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb77967e8) = 17923
waitpid(17923, DEBUGGER DETECTED… Bye!
Killed

I did a quick search for the error message in the binary:

[root@localhost tmp]# strings /usr/local/psa/bin/sw-engine-pleskrun | grep “DEBUGGER DETECTED”
[root@localhost tmp]#

Found nothing, so it seems that the sw-engine-pleskrun binary is just a framework executable and another one does the actual PHP processing. Let’s see which files are accessed by the binary:

[root@localhost tmp]# strings /usr/local/psa/bin/sw-engine-pleskrun | grep -i “/usr”
/usr/bin/sw-engine
/usr/sbin/rblsmtpd
/usr/local/psa/bin/php-cli
/usr/share/awstats
/usr/share/tomcat6
/usr/lib/mailman
/usr/bin
/usr/bin/webalizer
/usr/local/psa

The sw-engine and the php-cli binaries seem to be good targets from the list above. When I ran the php-cli application I got the following message:

[root@localhost tmp]# /usr/local/psa/bin/php-cli /usr/local/psa/admin/htdocs/index.php
Failed loading /usr/lib/php/modules/ioncube_loader_lin_5.3.so: /usr/lib/php/modules/ioncube_loader_lin_5.3.so: cannot open shared object file: No such file or directory
The file /usr/local/psa/admin/htdocs/index.php is part of Plesk 9 distribution. It cannot be run outside of Plesk 9 environment.

Let’s see what our first contestant says when we ask it to parse an encrypted PHP file:

[root@localhost tmp]# /usr/bin/sw-engine /usr/local/psa/admin/htdocs/index.php

Fatal error: Call to undefined function get_gpc() in /usr/local/psa/admin/htdocs/index.php on line 2

Sounds good! Fire up the IDA Pro Disassembler and collect some starting information: Look for the plain strings from the encrypted PHP files in the binary (like “__sw_loader_pragma__(‘P’);”):

ida1

Let see which function references this string:

ida2

By deeper investigating the open_file_for_scanning() function you will see that the BF_decrypt() function constructs the executable PHP code:

ida3

For the quick and effective result – bypass the ptrace, debug the decrypt function, collect the crypto key, etc… – I used the following method:
The open_file_for_scanning() function uses memcpy() to construct the unencrypted PHP code. I created a shared library which redeclares the memcpy function, I used it with the LD_PRELOAD technique and voila, readable source code:

#include
#include

int i=0;

void *memcpy(void *dst,const void *src,size_t len){
  if((strstr(src,”);”) > 0) && (i==0)){
  printf(“%s\n”,src);
  i=1;
}
register char *src_c, *dst_c;
src_c = (char *)src;
dst_c = (char *)dst;

while (len– > 0)
  *dst_c++ = *src_c++;
return dst;
}

 Compile the shared library with the following commands:

[root@localhost ~]# gcc -fPIC -c ld.c -o ld.o
[root@localhost ~]# gcc -shared -o ld.so ld.o

Set up the LD_PRELOAD environment variable and get the PHP source code:

[root@localhost ~]# LD_PRELOAD=/tmp/ld.so /usr/bin/sw-engine /usr/local/psa/admin/htdocs/login_up.php3
require_once(’cmd_loginup.php’);
require_once(‘sso/RelyingParty.php’);

$login_name = rtrim(get_gpc(‘login_name’));
$passwd = get_gpc(‘passwd’);

if ($session->getType() != IS_UNDEFINED && !($login_name && $passwd))
go_to(‘/logout.php3’);

if (!headers_sent())
header(‘X-Plesk: PSA-Key/’ . getKeyProp(‘plesk_key_id’));

$session_created = false;
$ssoAuthentication = null;
….

This method is far from perfect but it is easy, quick and the result is the plain source code. Happy bug hunting! ☺