Apache::CodeRedby Reuven M. Lerner
Like many programmers and system administrators, I like to know when
something goes wrong with my web site, no matter how trivial the problem is. So when
I moved my company's web server to mod_perl and
HTML::Mason last year,
one of the first things I did was write an automatic warning system
to send me an e-mail message whenever a visitor encounters a broken link.
I usually expect to receive two or three such e-mail messages on any given day,
with the majority coming from people who enter wrong URLs.
But on the night of Saturday, August 4, I had hundreds of
automatically generated "broken link" reports in my in-box, all of
which came from requests for
/default.ida. It didn't take long to
figure out that my server was under attack by the Code Red 2 worm.
Code Red 2, in case you haven't heard, attacks Windows 2000 systems
running Microsoft's IIS Web server. The worm enters via a very long
HTTP request beginning with
/default.ida. Once it has infected a
system, the worm opens some security holes, and then begins trying to
spread to other servers. The worm mostly attacks computers whose IP
addresses are similar to that of its current host, but sometimes it
chooses a new IP address at random.
Code Red 2 never should have infected more than a few dozen computers. A patch has been available from the Microsoft web site for some time, and the headlines that the original Code Red worm generated should have been enough to alert even the sleepiest system administrator. The unfortunate reality is that many system administrators, including those running Microsoft's own Hotmail system, didn't patch their systems in time, and were infected by this fast-spreading threat.
My own server wasn't vulnerable to these attacks, because it runs Apache and Linux. But Code Red 2 didn't check a server's identity when launching an attack; it sent the dangerous HTTP request to anyone who would listen, betting on the large number of IIS servers on the Internet. While I didn't have to worry about infection, I felt like I should do something to let people know that their computers had been infected.
My solution was to write a small module for mod_perl, originally
called "CodeRed" and eventually renamed
Apache::CodeRed. The module's
job is to intercept any request for
/default.ida, determine the
host name of the HTTP client, and send a warning e-mail message to the
administrator of that client.
Within a day of posting the first version of
Apache::CodeRed on the
mod_perl e-mail list, I began to get feedback, patches, and suggested
improvements. I added a cache of IP addresses, to ensure that each
system administrator would receive only one warning in a given 24-hour
period. I sent e-mail to the designated address at SecurityFocus.com,
which was keeping track of such things. And I added a list of IP
addresses for which warnings were unnecessary, avoiding the
embarrassing situation of turning a simple test into a false warning
of a Code Red 2 infection on a Unix system.
A quick primer on mod_perl
Writing Apache Modules with Perl and C
Before looking at
Apache::CodeRed, let's review how mod_perl works.
The goal of Apache, like any Web server, is to produce an HTTP
response for each incoming HTTP request. When Apache gets a request
foo.html, it will normally return the file verbatim. When it
gets a request for
foo.shtml in a directory that has been marked for
server-side includes, it will execute each of those server-side
includes, and then return the resulting file. And when it receives a
foo.pl in a directory marked for CGI programs, Apache will
execute the program, returning the program's output to the user's
browser. Each of these behaviors is controlled by a different
"handler." By assigning a handler to a directory (or to the files in a
directory), you change the way Apache behaves.
Mod_perl lets us go one step further by writing our own handlers that can be attached to files, directories, or URLs. We do this by telling Apache that mod_perl will be handling things, and then by telling mod_perl which specific Perl module should handle the HTTP request. By default, mod_perl looks for a subroutine named "handler" in the named module.
After I wrote and installed
Apache::CodeRed, I attached it to the URL
/default.ida by inserting the following into my Apache configuration
file, generally named
<Location /default.ida> SetHandler perl-script PerlHandler Apache::CodeRed </Location>
The above tells Apache that when an incoming HTTP request asks for
/default.ida. The response should be generated by the Perl subroutine
Our handler receives one input, an instance of the Apache
request/response object traditionally called
$r. It will also have to
return a value to its caller, indicating whether it handled the
OK), is passing the buck to another handler (
refuses to allow access to the requested URL (
that these constants, which are defined in the module
Apache::Constants, are not HTTP response codes, but rather indications
of whether Apache may return an HTTP response to the user.
Let's look through the overall logic of the code rather than
a line-by-line analysis. You'll notice that I often use the
method, which sends a warning to the Apache error log. Chatty
warnings in the error log allowed me to easily keep track of what was
happening, which is often a good idea when working on server-side
applications. I used
$r->warn, rather than
allow Apache administrators to turn off the chattiness by modifying
httpd.conf, rather than the module.