Tuesday, May 24, 2005

Don Quixote of Paris, or Inverted Adaptations

I'm trying to think of adaptations (page to screen, page to stage, or any other) in which the adaptations does not merely alter some fundamental aspect of the original story, but indeed entirely inverts it.

Where this started: So Sunday Mrs. Prophet and I went to see the opera Don Quixote down here in Buenos Aires. We had a wonderful time marveling at the opera house and those within it, and by the fourth act really enjoyed the production as well.

But for the first twenty minutes, we both were simply shocked and dismayed. Because, as you probably remember, much of the action and humor of the book revolves around the fact that Quixote proclaims as his `beautiful beloved' a plain, coarse, peasant girl. Everyone else knows perfectly well that this woman is ugly, loose, ill-mannered and entirely undeserving of his title Dulcinea of Toboso. Yet Quixote composes endless love poems exalting her beauty, her refinement, and chasteness, while promising Sancho that he will share some part in her vast riches when Quixote has won her heart.

So we both a little, uh, surprised when the opera begins with Dulcinea, who is, in fact: rich, beautiful, chaste, and refined (though a bit mischievous). Apparently Jules-Émile-Frédéric Massenet, the silly Frenchmen who wrote the opera, had a favorite mezzo-soprano in mind for the part of Dulcinea, and it wouldn't do well to launch her career playing an ugly wench, would it? So he tidied up the part by inverting it.

Now there are plenty of lousy adaptations out there. But what others can you think of pull this kind of inversion?

A Widget-Toot

So thanks to that widget-post below (or actually the alternate here), yours truly found himself mentioned at length in this Boston Globe article condemning Apple for its security slipup. Both the true discoverer of the flaw, Stephan.com, and this elaborator upon its implications are quoted, though despite my protestations, my nom de birth rather than this nom de guerre is used.

Because the universe is propelled by an elaborate irony-generating engine, Apple released a 10.4.1 update addressing the flaw within hours of the publication of the article. (Hmmm, is that really ironic? Perhaps the engine also produces)

It is still my aim to see Mithras himself appear in a major publication, i.e. one that has actual deadlines. I shall not resort to acts of violence or property destruction, fear not.

In Which Mithras Does a Poor Imitation of Far Outliers

Greetings to all from the Southern of our hemispheres!

Today we have a pair of readings, illustrating the alternately generous and brutal, ultimately xenocidic mindset of 16th century Argentina.

This first excerpt is from the account Voyage to Río de Plata and Paraguay by Ulderico Schmidt, a German soldier and adventurer, published in 1554. Do be patient and read all the way to the end, as it gets rather interesting.

There we built a new town and called it Bonas Aeieres, that is, in German, Guter Wind.

We also brought from Hispania on board the fourteen ships seventy-two horses and mares.

Here, also, we found a place inhabited by Indian folk, named Querandíes, numbering about three thousand people, including wives and children, and they were clothed in the same way as the Charrúas, from the navel to the knees. They brought us fish and meat to eat. Those Querandíes have no houses, but wander about, as do the Gipsies with us at home, and in summer they oftentimes travel upwards of thirty miles on dry land without finding a single drop of water to drink.

And when they meet with deer and other wild beasts, when they have killed them they drink their blood. Also if they find a root, called Cardos, they eat it to slack their thirst. This — namely, that they drink blood — only happens because they cannot have any water, and that they might peradventure die of thirst.

These Querandíes brought us daily their provisions of fish and meat to our camp, and did so for a fortnight, and they did only fail once to come to us. So our captain, Pedro de Mendoza, sent to them, the Querandíes, a judge, named Johan Pabon, with two foot-soldiers, for they were at a distance of four miles from our camp. When our emissaries came near to the Indians, they were all three beaten black and blue, and were then sent back again to our camp. Pedro de Mendoza, hearing of this from the judge's report (who for this cause raised a tumult about it in our camp), sent Diego Mendoza, his own brother, against them with three hundred foot-soldiers and thirty well-armed mounted men, of whom I also was one, straightaway charging us to kill or take prisoners all these Indian Querandíes and to take possession of their settlement. But when we came near them there were now some four thousand men,for they had assembled all their friends. And when we were about to attack them, they defended themselves in such a way that we had that very day our hands full. They also killed our commander, Diego Mendoza, and six noblemen. Of our foot-soldiers and mounted men over twenty were slain, and on their side about one thousand. Thus did they defend themselves valiantly against us, so that indeed we felt it...

In due course God Almighty graciously gave us the victory, and allowed us to take possession of their place; but we did not take prisoner any of the Indians, and their wives and children also fled away from the place before we could seize them.


And when we returned again to our camp, our folk were divided into those who were to be soldiers, and the others workers, so as to have all of them employed. And a town was built there... The town wall was three foot broad, but that which was built today fell to pieces the day after, so that they suffered great poverty, and it became so bad that the horses could not go. Yea, finally, there was such want and misery for hunger's sake, that there were neither rats, nor mice, nor snakes to still the great dreadful hunger, and unspeakable poverty, and shoes and leather were resorted to for eating and everything else.

It happened that three Spaniards stole a horse, and ate it secretly, but when it was known, they were imprisoned and interrogated under the torture. Whereupon, as soon as they admitted their guilt, they were sentenced to death by the gallows, and all three were hanged.

Immediately afterwards, at night, three other Spaniards came to the gallows to the three hanging men, and hacked off their thighs and pieces of their flesh, and took them home to still their hunger.


After all this we remained still another month together in great poverty in the town of Bonas Aeieres, until ships could be prepared.

At this time the Indians came in great power and force, as many as twenty-three thousand men, against us and our town of Bonas Aeieres. There were four nations of them, namely, Querandíes, Charrúas, and Timbúes. They all meant to go about to destroy us all. But God Almighty preserved the greater part of us, therefore praise and thanks be to Him always and everlasting, for on our side not more than about thirty men, including commanders and ensign were slain.

— from The Argentina Reader, pp 22-25.

Tuesday, May 10, 2005

machine_info.pl: Get your bearings in a new machine

Continuing the spate of brain-dumps of scripts I use often, this is a favorite: machine_info.pl

I'm often hunting around for machines to run jobs on, and so log into a bunch of different machines to find one well-suited to my task. So when I log into a machine, I often want to get a quick sense of its capabilities. I got tired of trying to remember that Solaris uses /usr/sbin/psrinfo, while Linux uses /proc/cpuinfo, and which lines to grep for in each. So I wrote this script.

I also find this script handy for my duties as part-time administrator; it helps me keep tabs on which machines we've upgraded to how much RAM, etc.

Example output — at home:

[11:14 AM mithras@powerbook: ~] machine_info.pl cpu_count=1 cpu_speed_mhz=667 cpu_type=Power_Macintosh host_ip=xxx.xx.245.99 host_mac=00:03:93:a3:95:c2 host_name=powerbook.school.edu os=Darwin os_notes=Mac OS X 10.4 (build 8A428) os_version=8.0.0 ram_mbytes=768

And on a work server:

08:17 PM mithras@server6: src] machine_info.pl cpu_count=4 cpu_speed_mhz=3056 cpu_type=i686 host_ip=xxx.xx.22.98 host_mac=00:0E:0C:31:59:CF host_name=server6.school.edu os=Linux os_notes=Red Hat Linux release 9 (Shrike) os_version=2.4.20-31.9.progeny.6smp ram_mbytes=2016
[ Click to show the entire script ]
#!/usr/bin/perl -w use strict; # # FILE: machine_info.pl # AUTHOR: Mithras THe Prophet (mithras.the.prophet, which is a gmail account) # DATE: June 2004 # # This script runs on Linux, Solaris, and Mac OS X machines # to give you a quick rundown of the setup of the machine: # The processor type and speed, memory, disks, etc. # # I find this useful when I've just logged into an unfamiliar machine # and want to get my bearings. # # # #---- settings #----- # # # #----- globals my %info; #-- # # # Step 0: Determine platform # my $PLATFORM; { my $uname_s = `uname -s`; chomp $uname_s; my $uname_m = `uname -m`; chomp $uname_m; $uname_m =~ s/ /_/g; $PLATFORM=$uname_s . "-" . $uname_m; # print STDERR "## machineinfo\n"; # print STDERR "##\n"; # print STDERR "## running on $PLATFORM\n"; # print STDERR "##\n"; } # # os # { my $uname_s = `uname -s`; chomp $uname_s; $info{'os'} = $uname_s; } # # cpu_count # { my $cpu_num="1"; if ( $info{'os'} eq "Darwin" ) { my $command = '/usr/sbin/sysctl hw.ncpu | awk \'{print $2}\' '; $cpu_num = `$command`; chomp $cpu_num; } elsif ( $info{'os'} eq "Linux" ) { my $command = 'awk \'/processor/ { max_num=$3 } END { print max_num " +1"} \' /proc/cpuinfo | bc '; $cpu_num = `$command`; chomp $cpu_num; } elsif ( $info{'os'} eq "SunOS" ) { my $command = '/usr/sbin/psrinfo -v | perl -ne \' BEGIN { $count = 0 } if (/processor (\d+)/) { $count++ } END { print ($count) }\' '; $cpu_num = `$command`; chomp $cpu_num; } $info{'cpu_count'} = $cpu_num; } # # cpu_speed_mhz # { my $cpu_speed="unknown"; if ( $info{'os'} eq "Darwin" ) { my $command = '/usr/sbin/sysctl hw.cpufrequency | awk \'{print $2 "/ 1000000"}\' | bc'; $cpu_speed=`$command`; chomp $cpu_speed; # $cpu_speed .= " MHz"; } elsif ( $info{'os'} eq "Linux" ) { my $command = 'cat /proc/cpuinfo | perl -ne \'if (/cpu MHz\s+: (\d+)/) { print "$1\n" unless ($done); $done=1 } \' '; $cpu_speed=`$command`; chomp $cpu_speed; # $cpu_speed .= " MHz"; } elsif ( $info{'os'} eq "SunOS" ) { my $command = '/usr/sbin/psrinfo -v | perl -ne \'if (/(\d+) MHz/) { print "$1\n" unless ($done); $done=1 }\' '; $cpu_speed=`$command`; chomp $cpu_speed; # $cpu_speed .= " MHz"; } $info{'cpu_speed_mhz'} = $cpu_speed; } # # config_cpu_type # { my $uname_m = `uname -m`; chomp $uname_m; $uname_m =~ s/ /_/g; $info{'cpu_type'} = $uname_m; } # # hostname # { my $hostname=`hostname`; chomp $hostname; $info{'host_name'} = $hostname; } # # host_ip # { my $dig_command="dig " . $info{'host_name'} . " | awk 'BEGIN { ans=0 } /;;/ { if (ans==1) { ans=0 } } /ANSWER SECTION/ { ans=1 } /" . $info{'host_name'} . "/ { if (ans==1) { print \$5 } } '"; my $ip = `$dig_command`; chomp $ip; $info{'host_ip'} = $ip; } # # host_mac # { my $mac = "unknown"; if ( $info{'os'} eq "Darwin" ) { my $command = '/sbin/ifconfig | perl -ne ' . "'" . 'if (/en0/) { $next = 1; } ; if (/ether\s+(.*)/) { if ($next==1) { print $1; $next=0; } }' . "'"; $mac=`$command` } elsif ( $info{'os'} eq "Linux" ) { my $command = '/sbin/ifconfig | awk ' . "'" . '/eth0/ {print $5}' . "'"; $mac=`$command`; } elsif ( $info{'os'} eq "SunOS" ) { my $command = 'arp `hostname` | awk ' . "'" . '{print $4}' . "'"; $mac=`$command`; } chomp $mac; $info{'host_mac'} = $mac; } # # OS-version # { my $uname_r = `uname -r`; chomp $uname_r; $info{'os_version'} = $uname_r; } # # OS-notes # { my $os_flavor=""; if ($info{'os'} eq "Darwin") { if (-e '/usr/bin/sw_vers') { my $productName = `/usr/bin/sw_vers -productName`; chomp $productName; my $productVersion = `/usr/bin/sw_vers -productVersion`; chomp $productVersion; my $buildVersion = `/usr/bin/sw_vers -buildVersion`; chomp $buildVersion; $os_flavor = "$productName $productVersion (build $buildVersion)"; } } elsif ($info{'os'} eq "Linux") { if (-f '/etc/redhat-release') { $os_flavor=`cat '/etc/redhat-release'`; chomp $os_flavor; } else { $os_flavor="unknown Linux distro"; } } elsif ($info{'os'} eq "SunOS") { if (-f '/etc/release') { $os_flavor=`head -n 1 '/etc/release'`; chomp $os_flavor; $os_flavor =~ s/^\s+//; } } $info{'os_notes'} = $os_flavor; } # # ram # { my $mem="unknown"; if ($info{'os'} eq "Darwin") { my $command = 'sysctl hw.memsize | awk \'{print $2 " / (1024*1024)"}\' | bc'; $mem = `$command`; chomp $mem; # $mem .= " MB"; } elsif ( $info{'os'} eq "Linux" ) { my $command = 'cat /proc/meminfo | perl -ne \'if (/Mem:\s+(\d+)/) { print int( $1 / (1024 * 1024) ) }\' '; $mem = `$command`; chomp $mem; # $mem .= " MB"; } elsif ( $info{'os'} eq "SunOS" ) { my $command = '/usr/local/bin/sysinfo -msglevel terse -show "memory"'; $mem = `$command`; chomp $mem; if ($mem =~ m/(\d+) MB/) # chop out the ' MB' part { $mem = $1; } } $info{'ram_mbytes'} = $mem; } ######### # print out info foreach my $key (sort keys %info) { if ($key ne "") { my $value = $info{$key}; print "$key=$value\n"; } }

Monday, May 09, 2005

GNU screen hack-fu: running one command in multiple windows

I've long used the wonderful GNU screen text-windowing system, which allows you to do lots of neat things like detach a terminal session and reattach later, and switch between multiple terminal `windows' within a single session.

When you have lots of gnu screen windows open, sometimes it'd be nice to run the same command on several of them -- for example, cd several windows to the same directory, or ssh to the same server. I've always done this by copy-and-pasting, though there are some funky commands in screen for copying and pasting text between windows.

Now, thanks to the screen -X command and a short shell script, I can do this very easily.

I name the script screenex, and use it as follows:

[03:36 PM mithras@powerbook: ~] screenex 1 6 ls In windows 1 - 6, doing: ls

I simply pass the range of window numbers to run the command in, followed by the command itself. I can also omit the second number, to execute the command on just one other window:

[03:39 PM mithras@powerbook: ~] screenex 2 pine In windows 2 - 2, doing: pine

Neat! This becomes much more powerful, though, when you add substitution of the window number. When you include the token --INDEX-- in the command, the window number is substituted for --INDEX--. I use this to launch ssh connections to six machines at once, which is handy when I'm running a bunch of jobs in parallel. At work we have a bunch of servers, named server1.school.edu, server2.school.edu, etc. So to connect to a different machine in each window, I just run

[03:41 PM mithras@powerbook: ~] screenex 1 6 ssh server--INDEX--.school.edu In windows 1 - 6, doing: ssh server--INDEX--.school.edu

And I'm connected, ready to run my parallel jobs! You can also do cool stuff if you have environment variables that are indexed by number, for example WORKING_DIR_1, WORKING_DIR_2, etc. Anyway, I'll save more for later.

The script:

(Note that the ^M character is a literal newline character, which you can enter in vi by typing ctrl-V, then enter.)

[ Click to show the entire script ]
#!/bin/sh # # command to run the given command on multiple `GNU screen' windows # usage: screenex [begin-window] [end-window] command # # by Mithras The Prophet (mithras.the.prophet, which is a gmail account.) # # # returns 1 if arg is numeric, 0 otherwise # is_numeric() { var="$1" if [ -z "$var" ]; then echo "0" else [ "$var" -eq 0 ] 2> /dev/null if [ $? -eq 0 -o $? -eq 1 ]; then echo "1" else echo "0" fi fi } # # if argument 1, or arguments 1 and 2 are numeric, then # we apply the command to just that range of windows; # otherwise, to all windows # mycommand="" window_begin="1" # beginning of range of windows to send command to window_end="6" # and end arg1="$1" shift; if [ -z "$arg1" ]; then exit; fi if [ $(is_numeric "$arg1") -eq 1 ]; then # this is beginning of range window_begin="$arg1" # try to grab a second numeric arg? arg2="$1" shift; if [ $(is_numeric "$arg2") -eq 1 ]; then # we have a second number! window_end="$arg2" else # ahve second arg, but not a number # so it must be start of command window_end="$window_begin" # just do the one window mycommand="\"$arg2\"" fi else # arg1 -not- numeric, # so it must be start of command mycommand="$arg1" fi while [ ! -z "$1" ] do mycommand="$mycommand \"$1\"" # rest of line shift done echo "In windows $window_begin - $window_end, doing: $mycommand" # # now execute! # let "window_end = $window_end + 1" # so we can do strict -lt test index="$window_begin" while [ $index -lt $window_end ] do thiscommand=${mycommand/--INDEX--/$index} screen -X at ${index}# stuff "${thiscommand}^M" let "index = $index + 1" done

Safari / Dashboard vulnerability in OS X 10.4

This and the next few posts are full `geek_mode=on' posts. Sorry.

Safari / Dashboard vulnerability in OS X 10.4

This page discusses a security flaw resulting from a series of bad design choices in Safari and Dashboard in OS X 10.4, and links to a demonstration exploit.

You can make your machine safe by unchecking the ``automatically open `safe' files" option in Safari:

  • Update: Mac OS X 10.4.1 fixes this vulnerability, by changing Safari to present a dialog when downloading all widgets. Kudos to Apple for their speedy response to the issue.
  • Updated May 10 with a new, simpler, more deadly exploit.

1. Widget Auto-Install

Safari 2.0 in Mac OS X 10.4 Tiger has a dangerous new ``feature". By default, when asked to download a .zipped Dashboard widget, Safari not only unzips the widget, but also copies the widget into ~/Library/Widgets, and adds it to your Dashboard Bar. As first pointed out by Stephan.com, Safari does this not only for widgets you explicitly click on, but also for widgets that are silently, automatically downloaded by a web page's META refresh tag. So a malicious website can auto-install potentially malicious widgets without you even realizing the installation happened.

This is the rough equivalent of automatically installing auto-downloaded applications into the /Applications/ folder, and putting them on the Dock.

2. The Evil Twin

Apple's second major mistake in Dashboard is in how widgets are loaded.

First, it is important to emphasize that the auto-installed widgets do not execute automatically. It remains a `social engineering' task for a malicious attacker to get you to launch with widget by dragging it from your Dashboard Bar.

However, this is aided by Apple's second incredibly bad design decision. Widgets are identified by a `bundle identifier', such as com.apple.widget.stickies. When Dashboard encounters two or more widgets with the same bundle identifier, it only displays the last one loaded. And -- you guessed it -- widgets in ~/Library/Widgets are loaded after the system-supplied widgets in /Library/Widgets.

This means that our malicious widget can completely replace an Apple-supplied widget. Your ordinary Stickies widget simply will not appear in the Dashboard Bar -- but in its place, silently installed by Safari, is a malicious widget.

For example, here is the Dashboard Bar before visiting a malicious web page:

And here it is afterwards:

Can you tell the difference? If you can't, and try to create a new Sticky note...
...you have just launched a malicious program.

Note: It appears that an additional, unrelated bug in Dashboard may actually be a saving grace here. After ~/Library/Widgets has been updated, Dashboard sometimes garbles the contents of the Dashboard Bar, so when you try to drag a widget from the Bar, you get no widget, or an entirely different widget. Scrolling between pages of the bar resolves the issue. Nonetheless, your original safe Stickies is now gone, with a malicious one in its place.

3. The Sandbox That Isn't

And now we come to Apple's third major mistake. Apple went to great lengths to develop a security model for Dashboard widgets. By default, Widgets do not have access to local files, your network connection, or the ability to run native code, other than opening applicications and web pages.

Widgets can request additional privileges, including the ability to run arbitrary shell commands with the widget.system() call. The first time you double-click to run a widget that requests extra privileges, in most cases Dashboard presents an `are you sure?' prompt before allowing it to run.

Ought to appear, but does not.

However -- incredibly, amazingly, stupidly -- Dashboard does not present a prompt before running a privileged widget that is one of the Library/Widgets folders, including our auto-installed widgets. So now your auto-installed replacement look-alike widget has complete access to your system, and could do nasty things like delete your home folder.

If a widget contains a native Mach-O executable, Safari will present a warning before downloading the widget. However, because widgets in ~/Library/Widgets can run shell commands with the widget.system() call, this protection is easily defeated. The demonstration exploit below includes a native Mach-O application with its bits reversed, so Safari does not realize it's an application. A call to a Perl script unreverses the bits and makes the file executable, allowing full native code to be run.

Or, in a self-propagating attack, the widget could start a user-level Apache (on a user-accessible port like 8080), and email everyone in your addressbook with the URL to visit. They need only click on the URL to install the widget on their own Macs running Tiger, etc. etc.

4. Privilege Escalation

Our malicious widget has all of the capabilities of an ordinary application launched by your user. This is bad enough, but it could also take advantage of privilege escalation vulnerabilities to run as root, where the damage could be more widespread. If the widget acquired root it could, for example, start Apache for you and proceed as described above in the self-propagation.

One such privilege escalation that is known to work on Tiger is this sudo-piggyback method, in which the widget waits silently in the background for an admin user to run sudo, then piggy-backs onto the sudo grace period and acquires root. I'm sure there are a few other buffer-overflow vulnerabilities lying around.

Demonstration Exploit

WARNING! This example exploit page will:
1. download a single .zip file
2. In Safari on OS X 10.4, auto-install a Stickies widget to your Dashboard Bar that replaces the Apple-supplied widget.
3. Upon dragging the `Evil Stickies' widget out of the Dashboard Bar, request (and receive) full system access, unpacks a binary executable, and runs a Perl script to speak some greetings.

I have received feedback that due to an unrelated Dashboard bug, you might have to scroll the Dashboard Bar once or twice before you can drag new widgets from it.

None of these widgets does anything actually malicious. (My school would kill me if I were distributing malware from their servers :-)

To get rid of the evil `Stickies' widget, simply go to ~/Library/Widgets, and delete the widget named `Stickies.wdgt'. It will be gone -- and the normal Apple widget back again -- the next time you invoke the Dashboard.

If and only if you understand those removal instructions, you may now try the example exploit:


I'd appreciate feedback on how these widgets behaved on your system. Did the Stickies widget require confirmation to run? Did the widget displace the Apple one from your Dashboard Bar?

Please write with comments to mithras.the.prophet@gmail.com. Thanks.