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 args… Arguments passed to script. Use — args when first argument –ini Show configuration file names –rf Show information about function . |
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’);”):
Let see which function references this string:
By deeper investigating the open_file_for_scanning() function you will see that the BF_decrypt() function constructs the executable PHP code:
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){ while (len– > 0) |
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 $login_name = rtrim(get_gpc(‘login_name’)); if ($session->getType() != IS_UNDEFINED && !($login_name && $passwd)) if (!headers_sent()) $session_created = false; |
This method is far from perfect but it is easy, quick and the result is the plain source code. Happy bug hunting! ☺