Re: Re: [Fwd: (SRADV00001) Arbitrary file disclosurethrough PHP file upload]
| From: | Ron Chmara | Date: | Mon, 04 Sep 2000 23:44:41 +0000 |
| Subject: | Re: Re: [Fwd: (SRADV00001) Arbitrary file disclosurethrough PHP file upload] | ||
| References: | 1 | Groups: | php.dev php.general |
| Request: | Send a blank email to php-dev+get-31998@lists.php.net to get a copy of this message | ||
Stanislav Malyshev wrote:
> RC>> Uh, any program which allows user input *must be checked* for the validity
> RC>> of that input, and the same goes for returned output based on that input.
> True. But! PHP gives you no good means to check if that variable
> $uploaded_file is from valid user upload or not. *That's* the problem.
If you are relying on user input to be safe, and sane, then it's a problem,
regardless of source. I code for files that could be _anything_ improper.
I agree that it's inconvenient to code all of your uploads for checking
the actual data (rather than assuming a good file based on name), but
if coding is done with some decent forethought of possible attacks (and
just plain incompetance), it's not much of an issue compared to rebuilding
a compromised server.
Here's an exmaple:
<?php //check-master.inc
/* pgsessionid contains the unique session number so rename the file
with that to keep everything connected */
$fixname = $pgsessionid;
$xchgdir = "/var/spool/uploads";
$newfileorig = "$xchgdir/$fixname".".orig" ;
/* start out by making a backup copy, never work on live files */
copy ("$userfile", "$newfileorig");
/* the tempfile is the starting file for record reads, clean records
are then put into a brand new file */
$tempfile = "$xchgdir/$fixname" . ".temp";
$newfile = "$xchgdir/$fixname";
/* open temp for read,make newfile for write */
$fp = fopen($tempfile, "r") or die;
$fp2 = fopen($newfile, "w+") or die;
$buffer = fgets($fp, 1000);
$data = split($tab, $buffer);
$fields = count($data);
rewind($fp);
/* Go through all records to count number of rows in file, and make sure
all rows have the same number of fields.... get record zero to start
the comparison */
$buffer = fgets($fp, 1000);
$data = split($tab, $buffer);
$fields = count($data);
rewind($fp);
/* now count the rows and compare the fields to the first one */
while(!feof($fp))
{
$buffer = fgets($fp, 1000);
$data = split($tab, $buffer);
$fieldnewcount = count($data);
if ($fieldnewcount != $fields )
{
$row = $row++; /*increase one row to make up for PHP counting from zero */
print "<B>The file you sent contains an inconsistant number of fields
";
print "with the header.</B>\n";
/* nuke the temp and new files. Note the .orig is kept for diagnosis*/
fclose($fp) or die;
fclose($fp2) or die;
unlink("$tempfile");
unlink("$newfile");
exit;
}
$row++;
}
...
This same script also checks to make sure that fields in the file are
of a maximum length, that the file uses the proper delimiters for data,
indeed, the entire structure of the file is _determined and evaluated_.
In such a case, it wouldn't matter if they fed server paths to the scripts,
because a given file would have to match, exactly, the structure of the
*expected* file. This is a standard security model, everything which
is not explicitly allowed is forbidden. The coding style of *every*
programmer must include basic security handling, shouldn't it?
> The
> only way now to check if the filename is prefixed by your temp upload
> directory, which means you should remember that, update it on all your
> scripts every time it changes and generally requires a lot of PITA work.
Well, if you _assume_ the name is meaningless in the first place, and
dont rely on it, and check your files for valid _data_, it doesn't
matter what they feed to it, server based or local user file. I am
talking about a security model where it is assumed that the server
files themselves may be uploaded.
> RC>> Repeat after me:
> RC>> A PUBLIC WEBSERVER IS ALREADY 0WN3D BY THE NET.
> Wrong. I won't go into detailed explanation, but note that "every file
> readable by httpd user is publicly accessible to the web" is just
> wrong.
This is more about mindset than it is about today's current software
practices. On a web-server level, sure, you can "request" files to not be
readable, but on Unix systems, all security is file-level, _regardless_
of the application running on top of it. If a file is readable by "o"
users, that file is readable by _anybody_. The applications which act as
public interfaces can help to obscure the access to it, can prevent
different ways of getting at it, but do not completely deny access
to it. This is the security balance inherent in web servers..
> Think about it, if you still disagree, I can explain it to you
> privately.
No need. We're talking about different things. I understand that
there are application level access-control layers built in. BUT:
There's a reason why standard two-tier firewalling is used. The machines
in the DMZ already have accounts accessable by anyone on the net. Those
accounts can be secured and obscured to a greater level, but they are
already at severe risk. I know that it's not supposed to be easy. :-)
But that wasn't my point. My point was to combat the FUD of "anybody
can get to /etc/passwd" with a rousing "well, duh, any user who has
a login can, too...". Sealing off an easy route to it is *always* a
good thing, but the mindset needed when dealing with systems that may
be compromised by any one of a thousand software bugs is to approach
the problem at *all* levels, rather than just assuming that "It's a secure
program" because an application/process has added some access control,
or tried to code around a bad OS security model.
The access control of any *nix program could be circumvented all the way down
to the file level, which is where the buck stops. (And then they have
to gain physical access). It's just plain good practice to assume that
such a thing *may* happen, intentionally or otherwise, and code for
it as much as possible.
> RC>> And you sure as hell don't allow your webserver user access to
> RC>> anything important, regardless of how hard your webserver
> /etc/passwd is important to you?
Not on a server designed for public use. Everything you could
find in there is already well known (a few standard system users).
If you're mixing safety zones, for whatever reason, you have increased
your risk. This is not rocket science, it's just plain good practice
to not put any secure accounts on the same box as your public accounts,
as your public services. Let's take, for example, a machine running
BIND, SMTP (sendmail), Apache+PHP3, and has 10K users logging in via
telnet or ssh. Each one of those accounts, or account sets, has access
to all of your world readable files. If you break the application level
security (indeed, this is the most *commonly* reported break), then you
are down to the security provided by your machine's filesystem itself.
If you code for this, your system is much less vulnerable.
> How about your
> webserver-protected files (like, private user directories)?
Private users? On a public server? Er.... there's no privacy in public,
for a reason. The application-level security overlay is not a fool-proof
means of protecting files. (Locks keep honest people honest, so hide
the entire house, lock it up, and don't use a public street address...)
I guess I'm arguing for better coding practices in the first place, so
when minor bugs are found, their impact is primarlily cosmetic until
they are fixed.
> How about
> various passwords in user scripts?
Putting passwords in user scripts is *creating additional risk*.
Period.
How one gets at them is a minor detail... it's like hiding the key to
your front door under the mat, over the stoop, under a rock. You are
putting the keys in a publicly readable file.
> I remember the (righteuos) wave of rage against Microsoft when it was
> discovered that you can steal any ASP file source. Now you telling me it's
> small change?
I was amused by the tirade because MS was selling people on a secure "product".
Products, in and of themselves, are *not* secure. They have locks, but locks
don't stop theft.
> We have real problem here, and we need to think what to do with
> it - or telling "PHP is not secure here, you need to go additional mile
> for it" or doing changes. Just putting your head in the sand while
> incantating "variables mucst be checked" won't do you any good.
We should be doing _both_. Spreading FUD isn't helping (well, maybe it's
helping folks to realize that you can't *buy* a secure, hackproof, server,
nor can you build one.), and for the new coder, trying to rapidly learn
security is not an easy thing.... so we should try to teach them not
accedentally shoot themselves in the foot (by using defaults).
> RC>> What next, a "security hole" where a PHP hosting site has
> RC>> allowed their users access to /etc/passwd, and users can run
> RC>> fread()?
> This hole allows _script user_, not PHP developer, to get any file. Please
> read bug report before going into flames.
Script users, on the file level, have access to everything that httpd
does, as that is the user they access the machine with. httpd has access
to /etc/passwd. Whether coder or user, that file is available to them.
If you are using a webserver that will deny access to certain files,
as apache has done, it helps a little.
-Bop
--
Brought to you from iBop the iMac, a MacOS, Win95, Win98, LinuxPPC machine,
which is currently in MacOS land. Your bopping may vary.