Hacker News new | ask | show | jobs
by okdana 2922 days ago
>Simple trick behind this technique is that when using shell wildcards, especially asterisk (...), Unix shell will interpret files beginning with hyphen (-) character as command line arguments to executed command/program.

To be clear, the shell isn't really 'interpreting' anything here — it knows absolutely nothing about the argument conventions of whatever program you're running[0]. All it's doing is passing a list of arguments to an executable; how the executable deals with that is up to it.

And this isn't only a shell problem — it's an issue when you pass arbitrary arguments to ANY external utility in ANY language. For example, maybe you have some kind of tool that calls grep:

    # Perl
    exec '/usr/bin/grep', '-R', $input, 'mydir';

    # PHP (with Symfony Process)
    (new Process(['/usr/bin/grep', '-R', $input, 'mydir']))->run();

    # Python
    subprocess.run(['/usr/bin/grep', '-R', input, 'mydir'])

    # C
    execv("/usr/bin/grep", (char *[]) {"grep", "-R", input, "mydir", NULL});
All of these are safe in the sense that there's no risk of shell command injection (in fact, only the PHP one even calls the shell), but NONE of them are safe against this problem, where the input value might begin with a hyphen. In this grep scenario it's probably not a security issue, but it can produce confusing results for the user if nothing else.

As others mentioned, you must ALWAYS use '--' to mark the end of option processing when you do something like this.

[0] Technically the shell knows about the conventions of its built-ins, but even those mostly handle arguments in a similar fashion to external utilities, where an arbitrary argv is fed to a function and then parsed using some getopts-like method.

4 comments

> In this grep scenario it's probably not a security issue, but it can produce confusing results for the user if nothing else.

I tried to think of an example where it could create a problem and the best I could do so far is injecting -f /sensitive-file/that-the-user/otherwise-cannot-read, supposing that mydir contains instances of the sensitive things, or that the contents of mydir are under the user's control.

So for example, suppose that the user has SFTP access to upload stuff to mydir, and suppose that there's a file /etc/spynames containing a list of spies (but it's mode 660 so the user normally can't read it). Maybe the grep command gets run with user-provided input and with the same group as /etc/spynames, and so then the user could SFTP upload a file containing suspected spies, and then inject causing the execution of

/usr/bin/grep -R -f /etc/spynames mydir

The output will be lines that are also present in /etc/spynames.

Edit: apparently -f/etc/spynames will be parsed as -f /etc/spynames, and so it isn't even necessary to be able to inject a space. I bet some form of this attack works on hundreds of deployed Internet services.

As others mentioned, you must ALWAYS use '--' to mark the end of option processing when you do something like this.

Relatedly, GNU getopt() (the most common one in use) has a dubious and non-standard "feature" --- enabled by default --- where it will automatically reorder the option arguments to come before the non-option ones. In other words, if you use it with something like

    foo -a -b somearg $input
according to POSIX there cannot possibly be any more options after somearg, and thus you can't inject any options via $input, but GNU will reorder them and pick up options from there too. Fortunately it respects --, but this feature has personally caused me some grief in the past, and while it sounds intuitive at first, it actually complicates things because it violates the "left-to-right"-ness of (mentally or otherwise) parsing a command line.
You shouldn't assume grep is in /usr/bin. On many systems it's in /bin.

Perl's exec and Python's subprocess already search for executables in PATH, so you don't need to provide the full path.

In C, you can use execvp() instead of execv().

(No idea about PHP…)

Right, it was just a random example that popped into my head. You would probably also want to think twice about using -R (at least with GNU grep), about hard-coding the search directory, &c.

All of PHP's process-execution methods (except one, which is unusable on many systems) go through the shell, so by virtue of that it also searches the PATH.

It’s a shell problem in the sense that the shell itself expands wildcards. It’s cumbersome for every windows utility to do it (via a library call), but in this sense it is an obvious benefit.