Calling functions without their names in PHP

Strings. Ah juicy and precious strings! It is common for malware scanners and IDPS to look for suspicious strings in network traffic and files… but what if there are no strings to look for? (whaaa?)

Today I was wondering about some features found in many interpreted languages of listing its internal functions in some sort of list/array. This way we can enumerate the [index] => function_name relationship, replace all function_names on the code to their index and voilá!

You might ask: “But you can just put those indexes on the rulesets.” I answer: “Yes, that’s true”. But different server/PHP configurations might generate the different indexes. Besides, numbers are much more fun to obfuscate than strings.

PHP, because it’s widely used on the wild

I wasn’t remembering if PHP had this or not but I was pretty confident due to its nature. Turns out that get_defined_functions is there as expected. With the aid of call_user_func_array it enabled the calling of functions without writing down their names in any encoded form (but it is required that you enumerate them beforehand).

The Code

This snippet has fewer than 10 lines (and it’s optimized for readability) and is our indirect caller function.

Then you can call your functions like this:

The output

Just for illustration:

We can even make it a little more compact

But Jan, there are still tree other internal function names exposed in your code. (func_get_args, call_user_func_array and array_shift)

Heck, you’re right. Let’s make this better.

or even…

MOAR compact!

A simple silly backdoor

How the numbers could be obfuscated to bypass simple rules?

Well, with the very simple math stuff:

  • XOR/AND/OR all entries: $index^$key, $index&$key, $index|$key
  • SUM/SUB/DIV/MUL all entries: $index+$key,$index-$key, $index/$key, $index*$key

And so on.

So, what to do to prevent/catch those things?

Keep an eye for get_defined_functions.


Whitespace Esolang Covert Channel / Steganography

I’ve been always a fan of esoteric programming languages (esolangs). These programming languages are generally made just for fun, mostly in universities or for challenges. Wikipedia describes as the following:

An esoteric programming language (esolang, in short) is a programming language designed to test the boundaries of computer programming language design, as a proof of concept, or as a joke. The use of esoteric distinguishes these languages from programming languages that working developers use to write software.

You’ve probably seen some of the famous ones like brainfuck and shakespeare language. Some months ago I stumbled upon this funny one called “Whitespace“.

About Whitespace esolang

Whitespace is an esolang created by Edwin Brady and Chris Morris from the University of Durham in released in 2003. Their opcodes consists only of spaces, tabs and line-feeds. Interesting, huh?

Here’s a sample Hello, World! program in Whitespace (red = spaces, blue = tabs, black = VIM cursor):


This “feature” came into my mind as a very difficult pattern to detect through computational means since the language natively discards every other character permitting this language to be merged arbitrarily with any other text…. and even some code ;)

I’ve then remembered that HTML has cool workarounds with repeated spaces and tabs when it comes to parsing and rendering, so I could be able to inject Whitespace opcodes into HTML without breaking it and with minor rendering quirks.

So the weather was crappy, beer was over and I decided to make a covert shell with that.

Creating the covert shell

PHP is one of the most used languages in websites and power popular platforms like WordPress and MediaWiki. PHP has an output buffering system (ob_start, ob_get_contents etc) and a widely abused feature called auto_prepend_file. These would be enough to setup my covert shell.

I quickly spawned two files, one .htaccess to handle my shell injection via auto_prepend_file (very known and old trick) and one wcc.php to do the magic.

This would ensure my shell works on almost any php page on the target acessible through the browser.

First thing I had to do was ensure that the I’ve got the output buffer after all other possible output buffer handlers had processed it. There’s quite a time since I left web developing but I did remembered about register_shutdown_function that is used to, you know, register functions that will run before the script exits.

I could’ve stopped there, but I’ve wanted to prevent other register_shutdown_functions to win the race and alter the output after I did, so I did some research and saw that if you call register_shutdown_function from a register_shutdown_function, it will have high chances of being the last shutdown function (unless other register_shutdown_function did the same trick).

And further down…

That took care of ensuring that my wcc_merge_output function will run at the very end of any script.

Now it’s time to play with the output buffer and do the whitespace magic. I’ve placed ob_start on my wcc_init function that is prepended by .htaccess so as soon as script starts, output buffering is enabled.

If the magic cookie key is set, it will run the command with exec (for testing purposes, you can change to whichever method you want), gzdeflate-it and convert to whitespace language.

I won’t comment on the actual code responsible to converting from ASCII to Whitespace but FYI, it basically converts each char to binary, pushes each character to Whitespace Stack, then adds “print char from stack” N times, where N = len(string) and finally adds the “end program” sentence.

You can read more about the internals of Whitespace Language on the official page’s tutorial. If you’re interested, you can check the wcc_whitespace_print_string function source-code.

The wcc_init function ended up looking like this:

When the original script finishes its job and execution is passed to my shutdown function, wcc_merge_output will grab the contents of the output buffer, merge, display and exit.

The caveat

Of course, nothing comes easy. I decided to test the WCC on a real (little old) WordPress install so I pulled up an old VM that I had it installed.

My buffer was being ignored or duplicated, depending on the page. Turns out I did not account the race condition that other codes might introduce when handling output buffer and they were flushing the buffer before I could act, so I created a little trap to prevent others from messing with it.

As from the documentation for the callback parameter from ob_start:

The function will be called when the output buffer is flushed (sent) or cleaned (with ob_flush(), ob_clean() or similar function) or when the output buffer is flushed to the browser at the end of the request.

That did the trick.

The sanitization and tokenization process is quite simple.

For content, first I replace all tabs to spaces, place each line in an array with respective line number as index. Then I remove the linefeeds from the each line and break the line into tokens separated by spaces.

For whitespace, I just convert a string to an array of characters. Dead simple.

Merging content

This is quite simple also. For each whitespace token (character/opcode), I add one of the content parts (tokens) followed by the whitespace token.

If whitespace payload is less than content, when I finish adding whitespace tokens, I just add the remaining pieces glued toghether by spaces and keep their line feeds.

If whitespace payload is greater than content, I just add raw whitespace payload to the end of the HTML content. This brings up some issues on detection but it’s better than no output at all. Just choose a page with more content.

You can check it out the source-code of this routine.

Issuing requests to the shell

In this PoC I’ve used the classic Cookie command input trick.

So through a shell, you can do something like

Then parse with any Whitespace interpreter and inflate the payload again

inflate.php reads input from stdin and passes to gzinflate.


Visual Differences

For some content you might get some quirks, for others, not.

Here’s a screenshot from both pages sources. First with embedded Whitespace command output and the second is the original.

Whitespace HTML Source Comparison

Here’s a screenshot from both pages rendered HTML. First with embedded Whitespace command output and the second is the original. (Don’t mind the images, this theme has random header images)

Whitespace Rendered HTML Comparison

Size discrepancies

Since we’re mostly changing stuff than adding, our file size ends up very close to the original if you have enought HTML to fit your command output. Below is the comparison of the original HTML with one with the output from some commands.

I’ve ran id, ip a show, ps aux and ls -al. id was the only one that fit entirely in the HTML I’ve got (pretty short page). The others resulted in raw whitespace appended to the end of the original HTML.

LOL. In some cases it even “minifies” a little…

Final notes

  • I did not put any effort on encoding/encrypting the cookie or commands or whatever. This is only to test the covert response channel.
  • I also know that cookie based command passing is easily detectable. And are many other better ways to do that.
  • Of course there are better methods than exec to run code. This is also not the point of this research.

Source code & files

All files are available on my github repo. Feel free to download, play, fork, whatever.

Shared-hosting accounts victims of lame directory permissions

From a few months some clients started to complain about their shared-hosting accounts being amazingly owned even if their PHP applications (WordPress mostly) were totally up to date.

The malware got into most of the PHP files including a PHP snippet that would eval and decode a base64-encoded string containing a script declaration loading Javascript from outside. This Javascript usually initiates download of .exe’s and stuff.

A friend’s account got hacked a few days back by this very same technique. He told me the attacker had left some files over so I asked him to hand me over so I could play a little and reverse-it.


The code ended up being two times rendering 3 total ‘stages’.

Stage 1

Starts with two vars with random names containing hex-encoded strings that ended up being ‘create_function’ and ‘base64_decode’, two regular PHP functions.

Followed by that we have these vars called as functions

And it ends calling this assigned variable as a function…

The base64-encoded code is presented next at

Stage 2

What was base64-encoded is now a function that decodes content from another variable creating a payload string. The very first two variables are the most interesting because they are the “key” used to encode the payload, and the encoded payload.

All the characters of the payload string are presented as numbers that when put to the power of “key” will become the corresponding ascii character of the payload.

A very simple for loop makes the math and appends the string.

Stage 3

Well, what can I say, the decoded and evaluated string is a very simple PHP shell. It can run unix commands from a variety of techniques (popen, exec, system, passthru — the first available), can upload files, run php commands or connect to database hosts and run sql queries from the victim account.

Artifacts missing

The script actually used for the infection of the PHP file is missing. Unfortunately as it is a shared-hosting I can’t reverse the disk for deleted items but it’s not something very simple. What I deduce is a simple find looking for PHP files and then piping them to a custom-built parser that would add the evil line at the end or after some specific code.

I’ve seen variants that only infected WordPress’ footer.php template file (as it is included in every other page). This revealed to be a true application-targeted attack.

If there are no holes in the app, how the hell did he got here?

After some manual auditing on the client’s application I’ve stated that the compromise couldn’t be started by the application so I started probing the environment.

Nothing seemed really bad at first but then, when talking to a friend I’ve noticed the most primary mistake: Directory permissions. So I present you…

The attack vector

Some WordPress plugin developers make their plugins chmod upload directories world writable (chmod 777) to avoid problems. This is quite stupid but you might not know that most security-unaware developers do this quite often while having permissions issues.

We ran a quick find to see which directories where with write bit set on ‘other’.

Then, as expected, we got some entries…

Just having an account on the same machine is enough. The attacker simply copied the files over our writable directory and accessed via his browser.

As a hosting account with SSH access enabled is around 10 bucks/month, this is a really victim-untargeted, cost-effective attack.

Enumerating Victims

After we discovered the vector I jumped to my account and tried to ls /home but I was, as expected, denied.

So I though for a while and realized that /etc/passwd is readable. A little for function with some cut and the find above I was able to scratch down nearly 15.000 (YES, FIFTEEN THOUSAND!!!) world writable folders on this client shared-hosting machine.

Just stopped there because I’m on the light side ;)

Some nasty artifacts

The infection also affects .htaccess files. Even if you haven’t one, if you got owned this way, you probably have one now for each site you’ve hosted.

Google started to mark some sites as attack sites. The pointed out offending url is So I started grepping like there’s no tomorow. In instants my less become populated by some good eggs.

grep was pointing to many .htaccess files with strange RewriteRules statements. Then I opened the first one and there it was:

Apparently this malicious snippet is trying to hide itself from the site owner and direct hits, focusing only on hits arrived through search engines like Google, Bing, Yahoo! etc.

What the malware author missed and stood out like a shoot in the foot was that GoogleBot also got into that rule and started pointing out and alerting out users.

Just grep all your files for the URL mentioned on the report, or access your site from a search engine and see if it acts weird.


The instant-fix is just runing an extended version of the above command.

The main reason this happens and have so many occurrences is that application developers often fail to properly configure the user account of the running application and the application’s filesystem folders so the application can only write to public directories or its user’s home (which the application is not located there).

Give the application’s folder owner and group permissions according to the application’s user and group, then give the application’s group to the accounts that need to rights to write on it (ex: developers, deploy daemons etc). Then, set up the permissions of the folders accordingly.

It could be worse

If the whole home directory is 777 (if this wasn’t bad enough by itself) (and we found some home directories like that on our probe) the attacker could put his key under ~/.authorized_keys and gain direct shell access to the account. And then it could be even worse…

I strongly recommend everyone that has a shared-hosting account to do this little test. It may save your life.