/    Sign up×
Community /Pin to ProfileBookmark

Help with some PHP Security

Ok, over the last few days my site has been hacked twice on a shared hosting account I am using to store all my static media such as video, images, sound, etc. The only PHP script on the server is a little thing we wrote to resize and cache images for display on the page.

How it works is in the htaccess I have the following:

[code]
RewriteEngine on
RewriteCond %{query_string} ^width=([^&]+)&height=([^&]+) [NC]
RewriteRule ^(.*).jpg imageLoader.php?width=%1&height=%2&target=$1.jpg [L]
RewriteCond %{query_string} ^width=([^&]+)&height=([^&]+) [NC]
RewriteRule ^(.*).png imageLoader.php?width=%1&height=%2&target=$1.png [L]
RewriteCond %{query_string} ^width=([^&]+)&height=([^&]+) [NC]
RewriteRule ^(.*).gif imageLoader.php?width=%1&height=%2&target=$1.gif [L]
[/code]

Basically the idea is, if an image is called on the server with width and height queries attached it get re-routed to this image loader.

The image loader then checks in a cache database to see if an image of that size already has been cached. If it has, that image is served up. If not one is created and cached, then served up.

Anyway I am sure they are injecting into it somehow but I have no idea how? Yesterday they managed to delete every file on the server and my host says it has to have been done through a php file.

The image loader has its permissions set to 644 as it needs write access to create the cached files.

Here is the script within the image loader: (i swapped out the database credentials with XXX)

[code]
<?php

scaleImageFileToBlob($_GET[‘target’],$_GET[‘width’],$_GET[‘height’]);

function scaleImageFileToBlob($file,$max_width,$max_height) {

$db = “XXXX”;
$table = “XXXX”;
$user = “XXXX”;
$pass = “XXXX”;

mysql_connect(“localhost”,$user,$pass) or die (mysql_error());
mysql_select_db($db) or die (mysql_error());

$query = “SELECT * FROM “.$table.” WHERE url = ‘”.$file.”‘ AND width = ‘”.$max_width.”‘ AND height = ‘”.$max_height.”‘ LIMIT 1″;
$result = mysql_query($query) or die(mysql_error);

while ($row = mysql_fetch_array($result)){
$found = true;
header(“Location:http://media.domain.com/cache/”.$row[‘id’].”.”.$row[‘type’]);
}

if($found != true){
$source_pic = $file;

list($width, $height, $image_type) = getimagesize($file);

switch ($image_type)
{
case 1: $src = imagecreatefromgif($file); break;
case 2: $src = imagecreatefromjpeg($file); break;
case 3: $src = imagecreatefrompng($file); break;
default: return ”; break;
}

$x_ratio = $max_width / $width;
$y_ratio = $max_height / $height;

if( ($width <= $max_width) && ($height <= $max_height) ){
$tn_width = $width;
$tn_height = $height;
}elseif (($x_ratio * $height) < $max_height){
$tn_height = ceil($x_ratio * $height);
$tn_width = $max_width;
}else{
$tn_width = ceil($y_ratio * $width);
$tn_height = $max_height;
}

$tmp = imagecreatetruecolor($tn_width,$tn_height);

/* Check if this image is PNG or GIF to preserve its transparency */
if(($image_type == 1) OR ($image_type==3))
{
imagealphablending($tmp, false);
imagesavealpha($tmp,true);
$transparent = imagecolorallocatealpha($tmp, 255, 255, 255, 127);
imagefilledrectangle($tmp, 0, 0, $tn_width, $tn_height, $transparent);
}
imagecopyresampled($tmp,$src,0,0,0,0,$tn_width, $tn_height,$width,$height);

switch ($image_type)
{
case 1: $fileType=”gif”; break;
case 2: $fileType=”jpg”; break;
case 3: $fileType=”png”; break;
default: break;
}

$query = “INSERT INTO “.$table.” (url,width,height,type) VALUES (‘”.$file.”‘,'”.$max_width.”‘,'”.$max_height.”‘,'”.$fileType.”‘)”;
mysql_query($query) or die (mysql_error());

$fileLoc = $_SERVER[‘DOCUMENT_ROOT’].”/cache/”;
$fileId = mysql_insert_id();

switch ($image_type)
{
case 1: imagegif($tmp,$fileLoc.$fileId.”.gif”); break;
case 2: imagejpeg($tmp, $fileLoc.$fileId.”.jpg”, 80); break;
case 3: imagepng($tmp, $fileLoc.$fileId.”.png”, 4); break;
default: echo ”; break;
}

header(“Location:http://media.domain.com/cache/”.$fileId.”.”.$fileType);

imagedestroy($tmp);
};

}

?>
[/code]

thanks so much any suggestions

to post a comment
PHP

8 Comments(s)

Copy linkTweet thisAlerts:
@ehimeNov 19.2010 — Never store plain text password data in your file, include it, encrypt it, and check it against a salt.

As for your upload value I believe they are feeding you php information and evaling everything

between ?> and <? and echoing it out. Try stripping prior to doing anything with your data.
Copy linkTweet thisAlerts:
@aj_nscNov 19.2010 — You're not sanitizing your database input.

Do this:
<i>
</i>$table = mysql_real_escape_string($table);
$file = mysql_real_escape_string($file);
$max_width = mysql_real_escape_string($max_width);
$max_height = mysql_real_escape_string($max_height);
$query = "SELECT * FROM ".$table." WHERE url = '".$file."' AND width = '".$max_width."' AND height = '".$max_height."' LIMIT 1";


If you look at your script, the variables go straight from a query string to arguments in a function to SQL - no sanitizing in between......well there's your problem...
Copy linkTweet thisAlerts:
@NogDogNov 19.2010 — Don't use external values in your DB query without escaping them or forcing them to be numeric:
[code=php]
$query = "SELECT * FROM ".$table." WHERE url = '"
. mysql_real_escape_string($file) . "' AND width = '"
. (int) $max_width . "' AND height = '" . (int) $max_height
. "' LIMIT 1";
[/code]
Copy linkTweet thisAlerts:
@mortalgodauthorNov 19.2010 — Alright guys thanks, that all makes sense and I will implement it, however, the database isn't being attacked at all. Somehow they are using the script to delete and/or corrupt actual files and their directories.

I am assuming they cannot do that with a mysql injection or by hacking the database credentials?
Copy linkTweet thisAlerts:
@NogDogNov 19.2010 — What evidence do you have that they broke in via that script? A poorly set up shared host could be allowing someone to run a script (in any language supported by the host) under another user account (perhaps after hacking that user's site) that could be affecting your files/directories. Or they could have stolen login/FTP credentials for your site (so be sure to change all your passwords).

Also, make sure all directories/files are only writable by your account unless absolutely necessary -- e.g., an image upload directory might have to be writable by all so that Apache can access it, but if you put it above the web root directory and create an image server script to access them, then you can lessen the risk.
Copy linkTweet thisAlerts:
@mortalgodauthorNov 19.2010 — Only evidence I have is that when all the files were corrupt the logs show increased traffic to that php script and the host said that the "only way it could happen is due to an insecure php script" and that is the only one on the server.

The only file with write permissions is the php file in question and only because it needs that permission to create the cached images.

And ya all the passwords were changed between the first and second hack. They are randomly generated with symbols, numbers, and letters so should be quite tough to crack.

I guess not a lot i can do save switching hosts if it is a host problem but I imagine the fact that that php file got hit hard at the same time as the hack suggests it was used somehow?
Copy linkTweet thisAlerts:
@NogDogNov 19.2010 — Only other thing I can see that might be a problem is the $_GET['target']. Someone could use ".." to point to a file elsewhere on the server, though I don't see how that could matter unless they've found some way to embed something in an image file. You might want to apply basename() to the value to strip out any directory data:

[code=php]
<?php
scaleImageFileToBlob(basename($_GET['target']), (int) $_GET['width'], (int) $_GET['height']);
[/code]

This assumes the image files are in the same directory as the script. If not, you may have to add the necessary path info after applying basename().

PS:
...the host said that the "only way it could happen is due to an insecure php script"...[/quote]
That's probably a bit naive, or at least overly optimistic, on their part. ?
Copy linkTweet thisAlerts:
@mortalgodauthorNov 21.2010 — Only other thing I can see that might be a problem is the $_GET['target']. Someone could use ".." to point to a file elsewhere on the server, though I don't see how that could matter unless they've found some way to embed something in an image file. You might want to apply basename() to the value to strip out any directory data:

[code=php]
<?php
scaleImageFileToBlob(basename($_GET['target']), (int) $_GET['width'], (int) $_GET['height']);
[/code]

This assumes the image files are in the same directory as the script. If not, you may have to add the necessary path info after applying basename().

PS:

That's probably a bit naive, or at least overly optimistic, on their part. ?[/QUOTE]


Ya I know they aren't completely secure, but then again no one is. So i just want to be sure it isn't me before taking anymore steps

And ya the files will all be in different directories. Perhaps I could check the target against ".." as a sub-string and remove it if present? Could that offer the same protection while maintaining the directory path?

Also perhaps add something in to check to ensure the path is relative and that they are not using it to load in an image on another server that may have something bad embedded?

thanks!
×

Success!

Help @mortalgod spread the word by sharing this article on Twitter...

Tweet This
Sign in
Forgot password?
Sign in with TwitchSign in with GithubCreate Account
about: ({
version: 0.1.9 BETA 5.18,
whats_new: community page,
up_next: more Davinci•003 tasks,
coming_soon: events calendar,
social: @webDeveloperHQ
});

legal: ({
terms: of use,
privacy: policy
});
changelog: (
version: 0.1.9,
notes: added community page

version: 0.1.8,
notes: added Davinci•003

version: 0.1.7,
notes: upvote answers to bounties

version: 0.1.6,
notes: article editor refresh
)...
recent_tips: (
tipper: @AriseFacilitySolutions09,
tipped: article
amount: 1000 SATS,

tipper: @Yussuf4331,
tipped: article
amount: 1000 SATS,

tipper: @darkwebsites540,
tipped: article
amount: 10 SATS,
)...