Beware of Format Injection
So, first things first, I’ve moved on again. And it was quite a move, all the way across the country to the Philadelphia area, home of the Liberty Bell, the cheesesteak, and the baseball team with possibly the least imaginative name in the history of professional sports. (Original names don’t seem to be Pennsylvania’s strong suit. We’ve got a Pottstown, Pottsgrove and Pottsville all within about 30 miles of each other, for example. On the other hand, there’s also this place, also nearby, so… yeah.)
I’m at Gateway Ticketing Systems now, the place where Nick Hodges was working when he made that “rock-star developer” video. (Which appears to no longer be available, unfortunately.) It’s a nice place to work, even if they do use Mercurial.
Anyhoo, yesterday one of my coworkers sent around a very interesting email. There’s nothing sensitive in it, so I’m going to copy it here, verbatim:
Something I’ve been meaning to mention: use care when constructing format strings using string data not generated under your control.
The following code could fail if the exception isn’t generated by us explicitly:
except on e: Exception do begin e.Message := Format('A terrible error occurred: %s', [e.Message]) raise; end; end;We don’t always have control over the text in e.Message. That text could contain a format specifier, which could cause Format to fail.
I encountered this error in a Win32 call, which generated the error message “%1 is not a valid Win32 application” when loading a library. That caused the call to Format to fail, obscuring the original exception.
Such constructs should be written as:
except on e: Exception do begin e.Message := 'A terrible error occurred: ' + e.Message; raise; end; end;Or, if using an inline translation facility as we Delphi programmers do:
except on e: Exception do begin e.Message := Translate('A terrible error occurred:’) + ' ' + e.Message; raise; end; end;We could get complicated and write a function to strip out format specifiers from text not generated by our system.
That’s something I’d never run into before: parameters to format() having unexpected % characters in them! In the email title, he called it “format injection”, which makes it sound a lot like a SQL Injection vulnerability. That actually makes me glad I’m a Delphi developer, though, because such an injection vulnerability does exist… in C. It’s known as a Format String Attack, and two types exist. One takes advantage of the way that C has no bounds checking, and can be used to reveal information. The other takes advantage of the fact that C’s string formatting is actually not read-only! The %n token will actually write a value to an arbitrary memory location! (Not to be confused with the %n token in Delphi’s format syntax, which interprets an input value as a number.)
Obscure little details like that are one of the reasons it’s good to be working in Delphi, where the worst that can happen if some user screws up your format string is that it’ll raise an exception. (And possibly stomp the exception you were trying to handle.)