44 comments

[ 3.4 ms ] story [ 81.8 ms ] thread
How exactly is this a bug in Win32? We can argue about whether or not this was a good design choice, but it wasn’t Win32 that opened an HTTP connection, wrote the response stream to disk and then executed the file without any kind of validation.
Linden probably deserves the bulk of the blame, but it is a little weird that, if Windows sees a file with an .exe extension that but no PE structure, it will just happily take whatever bits it gets and run them directly. But I guess if COM executables genuinely had no structure than there was nothing else that could be done. Yet again, back-compatibility is Microsoft's savior and curse at once.
Fortunately this can never happen on linux. The kernel would never interpret a header less executable file as a shell script and invoke it with sh.
The Linux kernel won't, but most shells happily will.

    echo 'ls' > test.sh
    chmod +x test.sh
    ./test.sh  #  Works just fine, no header
and since the C library routine "system()" executes the command via /bin/sh, system("./test.sh") works fine too.
As much as I want to blame Windows for it, this seems like reasonable behavior if the updater software is passing bytes straight from the HTTP stream. Unfortunate that the 404 page happened to have a byte sequence that would be interpreted as a COM, but the Updater should have been validating the downloaded exe in the first place. Not just for 404 but for actual .exe files that may be malicious
> Unfortunate that the 404 page happened to have a byte sequence that would be interpreted as a COM

That's the thing. Any byte sequence which didn't have an EXE header was interpreted as a COM executable. And in the (NT)VDM environment, there aren't a lot of illegal operations, so executing random data (like an HTML error page) can cause quite a bit of unpredictable behavior before it crashes or gets caught in a loop.

COM files indeed had no structure. DOS would load the file contents to the bottom 64k of memory starting at 0x100, set code, data and stack segments to zero, the stack pointer to zero (so the first push would wrap around to 64k), and jump to 0x100. And that was as much complexity at the linkage had.

It's almost hard to remember how easy it was to deal with the hardware back then. We view things as simpler now, but just getting a toolchain installed via rustup or npm or whatever is multiple blog posts of complexity, vs. the paragraph above plus a half-sheet of paper required to scribble down the instruction set and BIOS hooks.

COM files have no structure; they're loaded directly into a segment and control transferred to the first byte. I think the smallest valid COM file is a single return instruction.

Or you can have one file interpreted multiple ways: http://www.nyx.net/~gthompso/poly/polyglot.txt

Certainly the bulk of the blame falls on Second Life and the printer manufacturers for not validating inputs.

But there's another valuable lesson about interpreting binary/string blobs: don't make guesses about the format of the data from its value. You'll eventually get data that looks like one thing but isn't, and do the wrong thing with it.

I run into this all the time. Just a couple weeks ago I spent a lot of time trying to track down a bug where a programmer had decided to use a de-serialization function that would take the value of a JSON field, look for an internal object with that ID and either return the object it found or initialize a new object using the string as a value (and a random ID). So guess what happened when I tried to create a new object with a value that happened to be the same as the ID of an existing object?

The Win32 issue is just looking at the first few bytes of the input to determine its format (and how to try executing it) - it's somewhat hard to avoid here for legacy reasons, but a better design would use metadata associated with the input to determine its format rather than guessing.

>but a better design would use metadata associated with the input to determine its format rather than guessing.

how would that have prevented this incident? if the metadata is stored in the filesystem (as opposed to the file contents), chances are, the updater is going to blindly mark whatever it downloaded as "an executable". if it's part of the file contents, it's essentially the same as magic bytes.

> But there's another valuable lesson about interpreting binary/string blobs: don't make guesses about the format of the data from its value.

You could say the same about the file extension, meta data or any other means.

The best thing you can do is write your code to validate the input and to fail early, explicitly, and safely if it runs into a problem.

There's a fabulous article from Raymond Chen about EXE headers in general: https://blogs.msdn.microsoft.com/oldnewthing/20060130-00/?p=...

>A Win32 executable file begins with a so-called "MZ" header, followed by a so-called "PE" header. If the "PE" header cannot be found, then the loader attempts to load the program as a Win16 executable file, which consists of an "MZ" header followed by an "NE" header.

>If neither a "PE" nor an "NE" header can be found after the "MZ" header, then the loader attempts to load the program as an MS-DOS relocatable executable. If not even an "MZ" header can be found, then the loader attempt to load the program as an MS-DOS non-relocatable executable (aka "COM format" since this is the format of CP/M .COM files).

I'm curious what actually happened to physically break that printer.
Didn't you see the picture? It smashed itself to pieces!
I have so many questions, and the article is so shallow.
If you run arbitrary code from the internet arbitrary things can happen.
This is also true for running arbitrary non-code.
Most arbitrary data is actually valid x86 code.
Random data is very likely to give you e.g. “invalid instruction” runtime error when executing.
If true, we can add this to the infamous PET race condition as the second way that software can physically break a computer setup.
It is easy to "physically break" a thermal printer. Simply stop the servo motor while continuing to provide the power to the printer element heater. Your printer will soon catch fire. I've done it a few times myself while writing printer drivers.
> In the event of a printing stall, and occasionally during normal operation, the fusing oven would heat paper to combustion. This fire risk was aggravated by the fact that if the printer continued to operate, it would essentially stoke the oven with fresh paper at high speed.

Ouch. Talk about failing open.

HCF seems relevant in this case - Halt (servo motor) and Catch Fire
There’s a fun story out there about software physically breaking a hard drive. At the time, this was actually quite difficult for drivers to avoid doing. So, when a certain manufacturer came out with a drive it finally considered safe against driver errors, it proclaimed a bounty for anyone who could prove them wrong.

The winner put the drive on a table, connected with a cable, but not attached to a case. Then he wrote a routine that slammed the read head to one side, then slowly moved it back. Each time the routine looped, the drive would scoot across the table until it finally fell off the edge.

That's really funny, and something I want to see. It sounds like a SIGBOVIK paper
Back in 1993 I was thumbing through a book about assembly programming for MS-DOS, and it seemed that you could theoretically cause all kinds of hardware problems. IIRC the method that stood out to me at the time was intentionally accelerating disk drive motors. Were there built-in limits? On some drives or all? The book didn't say.

I'm still not sure I read that right, but man was it fascinating to think about all the control one could wield over the head of one's high school electronics teacher and his room full of new PC's.

That reminds me of the IBM "Black Team" QA group story where a guy writes a perfect tape drive driver, formally verified and everything. The "Black Team" was annoyed they couldn't find a problem with it, so they wrote a program to spin the tape at the cabinet's resonant frequency, until the cabinet eventually fell over.

http://www.penzba.co.uk/GreybeardStories/TheBlackTeam.html

When I was 13 I wrote a C program that wrote random data to a file. I had the idea to try executing these files. Most of them did nothing, but I remember that one did end up causing the printer to print out pages of gibberish.

Years later I found myself wondering how that even worked in the first place. Surely the chance of generating a valid executable was basically zero. Now I know why.

I wish I had kept that executable, it would've been interesting to disassemble it.

Nothing quite as fun as piping a postscript to a printer in you college Unix lab and watching garbage being spewed out. Meanwhile due to misconfigured permissions, you cannot cancel the job. So you quietly shut of the printer and walk out of the lab.
I tried to teach myself postscript by writing directly to the new laserprinters at school .. and was met the next day by a very angry grad student sysadmin waving two reams of mostly postscript error pages. Something about not being in his accounting budget since I was an undergrad annoyed him. Fortunately, he decided that the only solution was to give me access to his departments new Sun workstations running NeWS.
You should have installed the PostScript error handler that prints up giant posters of your error messages and stack dumps scaled and tiled across many pages in a gargantuan font. Maybe they would have given you a really big printer instead of a Sun running NeWS.
We had 8086 IBM machines in our typing class. I remember writing a program to send random bits to the big dot matrix printer. I'd keep a buffer and when the printer did something strange I'd save the buffer and replay it till I isolated the sequence. When a substitute would show up the printer would put on a show ;)
TLDR:

-lets eval() this random piece of bytes we got from non secure connection

-lets curl | sudo sh

At one point, HP2000's would crash if you tried to rewind the disk drive.
Was it really necessary to add to the article the picture of a printer that clearly had been smashed by a sledgehammer or fell from the third floor?
So disappointing. I was expecting a story about a bug in both 2nd life and Windows that somehow caused an action in the virtual world to affect he host one a la 13th floor. Maybe next time. [Please fix the title.]