File this under a "Lessons Learned" catagory:

Apple recenetly released a new version of iTunes, a program for Mac users to make their own music, to the world last weekend (11/2). However, those that rushed to install this update found that not only did the update not work, but it completely wiped out their hard drives. Apple quickly pulled this version, and rereleased a new version that behaved as expected.

The problem? According to posts on the Slashdot report, it had to do with the Unix underpinings of Mac OS X. Specifically, the installed ran a unix batch file which contained the following:

#!/bin/sh # if current iTunes pkg exists, delete it b/c of Installer bug if [ -e $1Library/Receipts/iTunes.pkg ] ; then rm -rf $1Library/Receipts/iTunes.pkg 2> /dev/null fi # if iTunes application currently exists, delete it if [ -e $2Applications/iTunes.app ] ; then rm -rf $2Applications/iTunes.app 2> /dev/null fi
The values of $1 and $2 were filled in with path information based the OS finding the location of the previous version of iTunes. Certainly nothing terribly wrong with this, but remember that on Macs, it's very common to name folders with spaced embedded in them. If either $1 or $2 had a space, the script would execute normally but could easily delete much of the system.

The solution was easy: simply quote the file names to check existance and deletion on.

Relating this to perl, whenever you do system/exec commands in which you are specifying file names that might come from an tainted source, or even if they aren't untained, it's typically always a good idea to quote the filename to prevent any special characters from getting in the way. For example:

$file = "testdir /"; #oops, typo! system( "rm -rf $file" ); #double oops system( "rm -rf '$file'" ); #will probably get an error # from the command, but your # root dir is still there.
And it's nearly always better to use any perl builtin functions over system commands if they perform the same task (eg unlink $file vs system("rm -rf $file")

-----------------------------------------------------
Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
"I can see my house from here!"
It's not what you know, but knowing how to find it if you don't know that's important

Replies are listed 'Best First'.
Re: Apple, quoting, and system()
by blakem (Monsignor) on Nov 07, 2001 at 03:04 UTC
    Good advice. Security can also be improved by using the safer "flavor" of system. If you pass system a string, it will use a sub-shell to figure out what that string means. This can easily be abused and is almost always overkill anyway.

    It's safer to pass system a list. The first element will be treated as the program name to fork, the rest are params to said program. Perhaps a concrete example is best:

    #!/usr/bin/perl -w use strict; # malicious input my $dir = '/tmp; echo "GOT YA"'; # system using shell print "AS STRING\n"; system("ls $dir"); # system w/o the shell print "\nAS LIST\n"; system('ls', $dir); =OUTPUT AS STRING [snip -- same as `ls /tmp`] GOT YA AS LIST ls: /tmp; echo "GOT YA": No such file or directory
    Notice how 'ls /tmp; echo "GOT YA"' is parsed into two separate commands by the shell. By calling system in a safer way we can force everything in $dir to be treated as a filename.

    -Blake

      Actually, Perl's system and exec only invoke a shell if you pass them a single string and that string contains shell meta characters.

      Note that this can still break things similar to the original story:

      my $root= "/usr/local "; # oops system( "rm -rf $root/lib" ); # goodbye /lib (and /usr/local) # However, if you instead do: system( "rm", "-rf", "$root/lib" ); # then nothing gets deleted unless # "/usr/local /lib" exists.
      Note that no shell is involved in the above problem.

              - tye (but my friends call me "Tye")