Discussion:
Second-Order Symlink Vulnerabilities
(too old to reply)
c***@mitre.org
2005-06-07 06:41:48 UTC
Permalink
Introduction
------------

Recently, Eric Romang of ZATAZ Audits reported several symlink issues
that are different than the usual symlink vulnerabilities [1] [2].
There are probably a large number of applications that are safe with
respect to traditional symlink problems, but vulnerable to this
particular variant.

Specifically, the problem arises when one program, "PARENT," invokes
another program, "CHILD," in which:

- the CHILD has a "standalone" design, i.e. its normal mode of
operation is to be run by an interactive user, or a script on
behalf of the user.

- the CHILD does not run with more privileges than the user that
invokes it, e.g. it is not setuid.

- the CHILD program assumes that the user calling the program has
control over all files that are specified as arguments, i.e. the
specified filenames are trusted.

- the CHILD program follows symlinks.

- the PARENT uses filenames that are passed as arguments to the
CHILD.

- the filenames as used by the PARENT are:
- controllable, or predictable, by the attacker, and
- in a directory that's writable by the attacker;

The attacker could then use a symlink attack on the filename arguments
that the PARENT passes to the CHILD.

This variant might be referred to as a "Second Order Symlink
Vulnerability," with apologies to NGS Software [3] for my slightly
different usage of the "second order" term.


Discussion
----------

Note that the four conditions for the CHILD, if treated alone, are not
normally regarded as a security vulnerability: there aren't any
privilege boundaries being crossed, and the filenames are under the
control of the user.

It's the interaction between the PARENT and the CHILD that becomes the
problem. There is a strong argument that it is the PARENT's
responsibility to protect against the second-order symlink
vulnerability, since "fixing" the child could significantly reduce
functionality in common standalone uses. However, there may be some
cases in which the PARENT does not have intrinsic knowledge of which
CHILD will be invoked at runtime.

Note that from the CHILD's perspective, the attack vectors do NOT
involve temporary files.

Current code scanning tools may not locate second-order symlink
vulnerabilities when scanning the PARENT, because the affected
fopen/create/etc. function call may not be in the PARENT at all, but
in the CHILD.



Examples
--------

Romang's reports for everybuddy and LutelWall both deal with the case
in which wget is the CHILD, and the PARENT specifies an output file to
wget using the "-O" argument.

It must be emphasized that the vulnerability is NOT in wget; as
previously discussed, it is in the interaction between wget and the
process that invokes it.


For LutelWall, we have:

echo `wget -C off -O $tmp-newfeat -q -t 1 -T 3 -w 3
http://firewall.lutel.pl/FEATURES-${new_ver}`

For everybuddy, we have:

258 g_snprintf(buf, 2048, "rm /tmp/.eb.%s.translator -f ; wget -O \
/tmp/.eb.%s.translator \
'http://world.altavista.com/sites/gben/pos/babelfish/tr?tt=urltext&lp=%s_%s&urltext=%s'",
259 getenv("USER"), getenv("USER"), from, to, string);
...
...
263 if(system(buf)!=0)




References
----------


[1] "everybuddy <= 0.4.3 insecure temporary file creation"
Bugtraq mailing list, June 6, 2005
http://marc.theaimsgroup.com/?l=bugtraq&m=111809118405393&w=2
http://www.zataz.net/adviso/everybuddy-06062005.txt

[2] "LutelWall <= 0.97 insecure temporary file creation"
Full-Disclosure mailing list, June 6, 2005
http://lists.grok.org.uk/pipermail/full-disclosure/2005-June/034424.html
http://www.zataz.net/adviso/lutelwall-05222005.txt

[3] "Second Order Code Injection Attacks"
NGS Software
http://www.nextgenss.com/papers/SecondOrderCodeInjection.pdf

_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.grok.org.uk/full-disclosure-charter.html
Hosted and sponsored by Secunia - http://secunia.com/
Graham Reed
2005-06-07 15:07:11 UTC
Permalink
Post by c***@mitre.org
258 g_snprintf(buf, 2048, "rm /tmp/.eb.%s.translator -f ; wget -O \
/tmp/.eb.%s.translator \
'http://world.altavista.com/sites/gben/pos/babelfish/tr?tt=urltext&lp=%s_%s&urltext=%s'",
259 getenv("USER"), getenv("USER"), from, to, string);
...
...
263 if(system(buf)!=0)
Any program which removes a file by doing system("rm ...") has much, much
worse problems than any kind of symlink attack.

But the absolute WORST aspect of this example is the use of an environment
variable to form a command line which is then dumped into the shell.

The second worst thing is the use of the ";" to separate commands. If you
have batch code which does this, you almost always have bad code.
Especially if the commands are run with user-provided input (and that
includes EVERYTHING in the environment). Even transient errors can cause
"cd ${TARGET}; rm -rf *" to be a really, really, really bad idea. Or the
typical "cd ${SUBDIR}; make"--I've seen that blow the per-uesr process limit
when SUBDIR doesn't exist, can't be accessed, or the fileserver is sulking.

And it's unnecessary.

int old_umask=umask(0600);
char tmpnam[]="/tmp/whateverXXXXXX";
int tmpfd=mkstemp(tmpnam);
char url[2048];

umask(old_umask);
if(snprintf(url,sizeof url,"http://.....",from,to,string)>=sizeof url)
{ abort(); }
fork()...execlp("wget","wget","-O",tmpnam,url,0);
wait()...
...use file...
unlink(tmpnam);
close(tmpfd);

Or anything but a plain-vanilla "system()" with URLs and user-provided input
in it at the same time.

As far as I'm concerned, it is simply too difficult to predict all the
possible quoting rules that may be required to safely invoke the shell via
system(). So I don't. And yes, that means I sometimes do a bit of
fork/exec coding that system() saves me from. Similary with redirecting to
files--it's a little more work to do your own redirection (fork/open/exec),
but you don't have to worry about some new shell putting meaning on
something that you relied on.

Actually, why don't we have "systeml", "systemlp", "systemv", and "systemvp"
or something, so you get the fork/exec/wait done (and debugged) by the
library, but you've got the reliability of argument passing of exec*?

I think it's rather ironic that both your example problems seem to be in
that annoying "check for new versions" thing that so many programs have
added--each their own way.

_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.grok.org.uk/full-disclosure-charter.html
Hosted and sponsored by Secunia - http://secunia.com/

Loading...