Quick and dirty security
Checking the date on my last guff entry shows I’ve been AWOL for nearly a month. To the uninitiated: I do that from time to time. No apologies. The reason for posting now is that over a 14 hour span (of which 6 were spent asleep), I went through two security updates to my plugin-related script, Quick & Dirty PHPSource Printer (or as I refer to it in my overly-complicated little head, Q&DPHPSP). It’s possible I could push out a third before the day is out, if I’m still missing something. And on this, I really don’t want to be missing something.
It began after 11pm Sunday when I checked my email and saw a message from one Seth Alan Woolley. Don’t know him–or at least I don’t recognize his name. During the months I’ve been hanging around the WordPress support fora, it’s not unusual to receive email with unfamiliar monikers in the header. 99 times out a 100 these turn out to be requests for help on some problem, or to expand on a solution I’ve posted, or…well, try to imagine. These I tend to drop into my email ‘slush’ pile that 99 times out of a 100 I get around to answering, but just not RIGHT NOW. However, Seth’s email stood out a bit due to the subject line: directory traversal for Quick & Dirty Source PHP Source Printer. Not how I expect an email asking for help to read. There are two areas in the act of code writing that really really annoy me personally: 1. I make the same error in coding logic over and over again, refusing to see it until it hits me in the face with a frying pan, and 2. I perform a major blunder due to my own lack of knowledge, or perhaps failing in the due diligence of testing a feature or component of my work. In regards to the topic of Seth’s email to me, a glance at the subject line told me there’d be a decent helping of both portions to be found.
As noted, Q&DPHPSP is a script. And as one might deduce from the name, it’s job is to display the source of PHP files, with syntax highlighting. Though designed with WordPress plugins in mind, you can submit any filename through the script’s “file” query and have its innards output to the browser, all colorful (if there’s PHP code in it). Thing is, you probably don’t want to allow people to point Q&DPHPSP at just any file, like an “include” holding your SQL login and password, or the server’s passwd file. That wouldn’t be good, you’d probably tell me. And since I’d have to agree, I stuck code in limiting which directory is accessed; by default: wp-content/plugins/. That’s not enough, though. I can’t restrict what someone enters in a browser’s URL (location or address) field, which means you can provide more than a filename. For example, you could provide a (directory) path along with it. Well not just a path, since you’d rarely get lucky with wp-content/plugins/etc/passwd. Rather a path–or directory–traversal, like say ../../../do_not_read/this_file. That would take you three directories up and to this_file in the do_not_read/ directory. Traversed paths can be quite useful in relation to relative links to images or other material on a web page. But for the purposes of such as my script, they strike a big security bell.
So what to do? Well, all you need do is watch for attempts at path traversal, and “filter” them out before they can cause any harm. And that’s what I did with this line of code:
$file = str_replace('../', '', $file);
Looks good, eh? (Right now there’s a few readers shaking their heads. I won’t work for their willing suspension of disbelief on this story.) It compares the file’s name ($file) against “../“, and where it appears strips it out (replaces it with “”) and invalidates the path. I must have thought at the time that it was a simple solution to a very big problem.
If only…
Subject: directory traversal for Quick & Dirty Source PHP Source Printer
From: Seth Alan Woolley
Date: 7/3/2005 9:50 PMkaf,
url?file=…/…//…/…//…/…//…/…//…/…//…/…//etc/passwd
…
Uh, oops. Yes, the above worked with Q&DPHPSP. And the moment I saw it the 180 my brain did in my skull hurt like you wouldn’t believe. So as I said, two security updates to the same script in less than one day. First attempt worked nominally, but turned out to be hiding an error brought to my attention by Chew Keong Tan at Secunia, a security services and advisory company. Didn’t I mention my little security bug is appearing on advisory lists? That’s what happens when you release your work to the wild. Anyway, it’s my first time… The second repair (putting Q&DPHPSP at R1.1.1) has been pounded on in a number of ways, and goes further in shoring up against path traversal attacks. Basically, don’t think about naming a file something like script[dot][dot]php (not that you would) if you want to use it with Q&DPHPSP. I’ve chosen paranoid over smart as the best method of protection.
On a side note, for those who happen to know about my outgoing mail problems for (apparently) the past month or more, it’s fixed. I’m again sending over my own stuff (i.e. szub.net). Thank the Illuminati for small favors.
Author: Kaf Oseo
Categories: Technical Folly
Comments: (3) · Leave a comment · Comments RSS2 · Trackback URL
some errors :)…
Warning: show_source(xfiles/.): failed to open stream: No such file or directory in /home/szub/www/guff/wp-content/sourceprt.php on line 37
Warning: show_source(): Failed opening ‘xfiles/.’ for highlighting in /home/szub/www/guff/wp-content/sourceprt.php on line 37
Fatal error: Call to undefined function: load_plugin_textdomain() in /home/szub/www/guff/wp-content/xfiles/download-mgr.php on line 62
Fatal error: Call to undefined function: add_filter() in /home/szub/www/guff/wp-content/xfiles/entity2ncr.php on line 307
Fatal error: Call to undefined function: _e() in /home/szub/www/guff/wp-content/xfiles/fragment-17565.php on line 33
Warning: main(wp-blog-header.php): failed to open stream: No such file or directory in /home/szub/www/guff/wp-content/xfiles/fragment-21009.php on line 3
Warning: main(wp-blog-header.php): failed to open stream: No such file or directory in /home/szub/www/guff/wp-content/xfiles/fragment-21009.php on line 3
Fatal error: main(): Failed opening required ‘wp-blog-header.php’ (include_path=’.:/usr/local/lib/php’) in /home/szub/www/guff/wp-content/xfiles/fragment-21009.php on line 3
Warning: main(./wp-blog-header.php): failed to open stream: No such file or directory in /home/szub/www/guff/wp-content/xfiles/fragment-21761.php on line 2
Warning: main(./wp-blog-header.php): failed to open stream: No such file or directory in /home/szub/www/guff/wp-content/xfiles/fragment-21761.php on line 2
Fatal error: main(): Failed opening required ‘./wp-blog-header.php’ (include_path=’.:/usr/local/lib/php’) in /home/szub/www/guff/wp-content/xfiles/fragment-21761.php on line 2
Fatal error: Call to a member function on a non-object in /home/szub/www/guff/wp-content/xfiles/fragment-26440.php on line 2
Fatal error: Call to undefined function: get_header() in /home/szub/www/guff/wp-content/xfiles/home-dbc.php on line 12
Fatal error: Call to undefined function: add_filter() in /home/szub/www/guff/wp-content/xfiles/my-tags.php on line 40
Fatal error: Call to undefined function: add_action() in /home/szub/www/guff/wp-content/xfiles/post-css.php on line 36
Warning: main(./wp-blog-header.php): failed to open stream: No such file or directory in /home/szub/www/guff/wp-content/xfiles/redirect2post.php on line 11
Warning: main(./wp-blog-header.php): failed to open stream: No such file or directory in /home/szub/www/guff/wp-content/xfiles/redirect2post.php on line 11
Fatal error: main(): Failed opening required ‘./wp-blog-header.php’ (include_path=’.:/usr/local/lib/php’) in /home/szub/www/guff/wp-content/xfiles/redirect2post.php on line 11
Warning: main(./wp-blog-header.php): failed to open stream: No such file or directory in /home/szub/www/guff/wp-content/xfiles/sample-23205.php on line 20
Warning: main(./wp-blog-header.php): failed to open stream: No such file or directory in /home/szub/www/guff/wp-content/xfiles/sample-23205.php on line 20
Fatal error: main(): Failed opening required ‘./wp-blog-header.php’ (include_path=’.:/usr/local/lib/php’) in /home/szub/www/guff/wp-content/xfiles/sample-23205.php on line 20
Fatal error: Call to undefined function: add_filter() in /home/szub/www/guff/wp-content/plugins/w-p.php on line 82
Fatal error: Call to undefined function: add_filter() in /home/szub/www/guff/wp-content/plugins/theme-switcher-tv.php on line 163
Fatal error: Call to undefined function: add_action() in /home/szub/www/guff/wp-content/plugins/singular.php on line 50
Fatal error: Call to undefined function: add_filter() in /home/szub/www/guff/wp-content/plugins/post-templates-by-cat.php on line 39
Fatal error: Call to undefined function: add_filter() in /home/szub/www/guff/wp-content/plugins/paginate.php on line 266
Fatal error: Call to undefined function: add_action() in /home/szub/www/guff/wp-content/plugins/head-meta-desc.php on line 39
Fatal error: Call to undefined function: add_action() in /home/szub/www/guff/wp-content/plugins/enhanced-views.php on line 840
Fatal error: Call to undefined function: is_plugin_page() in /home/szub/www/guff/wp-content/plugins/dofollow.php on line 43
zip viewing :)…
http://guff.szub.net/wp-content/sourceprt.php?file=/downloads/home-dbc.zip
To track down the warning it would help to know what you were doing when you got it, drZ (and when).
On the fatal errors: if you were accessing those files directly, that would do it. If not, same as above.
On files in /downloads/…I have things set up a little strange here. The /downloads/ directory doesn’t really exist. It’s a subdirectory under wp-contents/(xfiles/), which is why you can access that file (the sourceprt.php script I run is a little different than the released Q&DPHPSP so I can use it on files outside plugins/); the initial slash is ignored.
Oh, and if someone wants to use Q&DPHPSP to display the source of zip files, who am I to stop them?