Decrypting and analyzing HTTPS traffic without MITM

Author: dnet

Sniffing plaintext network traffic between apps and their backend APIs is an important step for pentesters to learn about how they interact. In this blog post, we’ll introduce a method to simplify getting our hands on plaintext messages sent between apps ran on our attacker-controlled devices and the API, and in case of HTTPS, shoveling these requests and responses into Burp for further analysis by combining existing tools and introducing a new plugin we developed. So our approach is less of a novel attack and more of an improvement on current techniques.

Of course, nowadays, most of these channels are secured using TLS, which provides encryption, integrity protection and authenticates one or both ends of the figurative tube. In many cases, the best method to overcome this limitation is man-in-the-middle (MITM), where a special program intercepts packets and acts as a server to the client and vice versa.

For well-written applications, this doesn’t work out-of-the-box, and it all depends on the circumstances, how many steps must be taken to weaken the security of the testing environment for this attack to work. It started with adding MITM CA certificates to OS stores, recent operating systems require more and more obscure confirmations and certificate pinning is gaining momentum. Latter can get to a point, where there’s a big cliff: either you can defeat it with automated tools like Objection or it becomes a daunting task, where you know that it’s doable but it’s frustratingly difficult to actually do it.


The curious case of encrypted URL parameters

Author: dnet

As intra-app URLs used in web applications are generated and parsed by the same code base, there’s no external force pushing developers towards using a human-readable form of serialization. Sure, it’s easier to do debugging and development, but that’s why I used the word “external”. Many frameworks use custom encodings, but one of the most extreme things a developer can do in this regard is completely encrypting request parameters. We encountered such a setup during a recent web app security assessment, let’s see how it worked out.


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”);

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!

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”

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/ /usr/lib/php/modules/ 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:


int i=0;

void *memcpy(void *dst,const void *src,size_t len){
  if((strstr(src,”);”) > 0) && (i==0)){
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.o

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

[root@localhost ~]# LD_PRELOAD=/tmp/ /usr/bin/sw-engine /usr/local/psa/admin/htdocs/login_up.php3

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

if ($session->getType() != IS_UNDEFINED && !($login_name && $passwd))

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! ☺