

...making Linux just a little more fun! Perl One-Liner of the Month: The Mystery of the Red Worm

By Ben Okopnik

- "It's just a little further along... right through here, Frink."

/var/log/apache

"Ah, here we are - there's ` /var/log/apache/access.log ' - and just in time from the looks of it. The poor thing is up to 400MB and it's nearly filling up the partition, and it's only been a few days since it was rolled over!"

- "What happened here, Woomert? I just came in to tell you about the latest story in the newspapers, praising you to the sky for your solution to the Missing Databases Mystery [1] at the Bigrich Bank, and you dragged me off without a word. Not that I mind, but..."

- "I do tend to get a bit concentrated while on the job, don't I? Oh well - there are worse things. All right, here is what's happening: the client, a small company that specializes in making horseshoe welding sprockets for accountants working in the napkin-fringing industry, has become suspicious of a few odd things happening with their web site. For example, their response time often spikes right through the roof, and they've been returning the ` Server busy ' message much too often as compared to normal operation. There hasn't been any huge jump in the amount of business they do - less, since the economy these days doesn't permit too many luxuries like their product - so..."

- "It sounds like a DoS (Denial of Service) attack, Woomert."

Here, let's test a few things. First, though, lets make a couple of copies of this file where it won't cram things quite as badly... There, I've put them both in ` /home/woomert '. We don't really want to lose any of the data if we should accidentally damage or destroy one file, do we? Now, let's zero out the actual log and restart the server... excellent. Now - on to exploring the files. Given that you suspect a DoS - I do, as well - what would you look for, Frink?"

- "I'm not sure, Woomert. I think I'd like to figure out the average hits per IP, and then maybe look at the sorted list of the same. That would tell us if someone is really slamming this server and from where, don't you think?"

- "Why, Frink, that sounds like an excellent idea! Yes, let's take a look at the average:

perl -wlne'/^(\S+)/;$h{$1}++}{$a=@a=values%h;map{$b+=$_}@a;print$b/$a' access.log

12.30830039525692



- "Hmm, interesting. Taking into account that the number is going to be higher due to the very large DoS entries - we're still assuming those, but it's a fair bet - that's not an unreasonable number. Most people will probably examine a few models before making their decision to buy; after all, it is a once-in-a-lifetime purchase. In fact, this company led the rest of the pack in offering lifetime warranties... All right - now let's look at that sorted list:

perl -wlne'/^(\S+)/;$h{$1}++}{print"$h{$}\t$"for sort{$h{$a}<=>$h{$b}}keys%h' access.log

...

22 users.osceola.k12.fl.us

26 152.31.2.221

26 modem-140.nyc-tc01a.fcc.net

28 62.84.228.7

31 209.106.1.124

103 bdsl.66.13.44.110.gte.net

112 24-164-141-122.si.rr.com

611 nyny01hsiapat.everestbroadband.com

1085 162.66.50.6

2817 web-05.segfl.ifl.net

55055 wsip66-210-242-2.ph.ph.cox.net

71031 205.213.111.53

85120 pc-80-193-117-84-cw.blueyonder.co.uk

97000 151.138.254.21

111092 168.11.225.251

122101 syr-24-92-242-3.twcny.rr.com

155017 212.85.1.1

175990 pool-68-161-90-99.ny325.east.verizon.net

181222 1cust185.tnt15.nyc9.da.uu.net

315078 pool-141-155-115-168.ny5030.east.verizon.net



Frink stared at the screen for a moment, then nodded. When he spoke, there was a confident note in his voice.

Woomert looked thoughtful, then nodded.

perl -lne'/^(\S+).*?"(.*?)"/;length$h{$1}>length$2or$h{$1}=$2}{print"@a"while@a=each%h' access.log

...

pool-68-161-90-99.ny325.east.verizon.net GET /default.ida?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090%u685

8%ucbd3%u7801%u9090%u9090%u8190%u00c3%u0003%u8b00%u531b%u53ff%u0078%u0000%u00=a

HTTP/1.0

syr-24-92-242-3.twcny.rr.com GET /default.ida?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090%u6858%

ucbd3%u7801%u9090%u9090%u8190%u00c3%u0003%u8b00%u531b%u53ff%u0078%u0000%u00=a

HTTP/1.0

1cust185.tnt15.nyc9.da.uu.net GET /default.ida?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090%u6

858%ucbd3%u7801%u9090%u9090%u8190%u00c3%u0003%u8b00%u531b%u53ff%u0078%u0000%u00=a

HTTP/1.0

212.85.1.1 GET /default.ida?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090%u90

90%u8190%u00c3%u0003%u8b00%u531b%u53ff%u0078%u0000%u00=a HTTP/1.0

...



Woomert and Frink looked at the screen, at each other, and exchanged a high-five salute, with Frink adding his "double twist with finger snaps" variation.

- "You did indeed, Frink, It looks like a modified version of a common worm, Code Red. The good news is that we're not dealing with a particularly sophisticated attacker, though: a Code Red infection attempt, which is what this is, is not the same thing as a Code Red DoS, which is just a network slam of a specific IP - and it only works against legacy operating systems, certainly nothing as modern as Linux - which is what this site runs. All these guys have going for them is bandwidth, and that's not particularly bad - and once the client blocks those IPs and notifies the relevant ISPs, it won't be an issue at all. In fact, there are analysis and response utilities that can track this sort of thing and do it automatically, and I'll recommend them to the client. Here - "

- "...shall we? I have a Paglia e Fieno con Pollo e Funghi that should only take a few minutes to finish preparing, a tiramisu made to my own recipe for desert, and a really great '97 Rosso di Cerbaiona wine that should go well with it all. My girlfriend, the lovely Priority Interrupt, is going to join us."

- "Nonsense, Frink; we'd love your company."





After dinner, Frink lounged in the big armchair and Priority curled up in Woomert's lap and lit a huge Toscano cigar, which she used to produce beautuful double and sometimes triple rings of smoke. At Woomert's inquiring glance, she reached up and placed the cigar between his lips.

Frink grinned at the two of them across the room.

perl -wlne'/^(\S+)/;$h{$1}++}{$a=@a=values%h;map{$b+=$_}@a;print$b/$a' access.log

/^(\S+)/

127.0.0.1 - - [09/Mar/2003:22:14:46 -0500] "GET / HTTP/1.0" 200 50000 "http://localhost/" "Lynx/2.8.4rel.1 libwww-FM/2.14" webcache-01.segfl.ifl.net - - [01/Apr/2003:05:45:27 -0500] "GET / HTTP/1.0" "-" 200 5238

$h{$1}++

At Woomert's encouraging nod and smile, Frink went on.

%h

Next... um. Next, there's a closing brace all by itself... and I don't understandand what it does - or even why the code works. Shouldn't that fail with a syntax error?"

"Normally, it would. However - go ahead and pull up `` perldoc perlrun '' again, and take a look at the entry for `-p':

# From ``perldoc perlrun''

while (<>) {

... # your program goes here

} continue {

print or die "-p destination: $!

";

}



Frink concentrated on the code. Suddenly, his face lit up.

END{}

All right, since we have that, the rest isn't too tough. Let's see:

$a=@a=values%h;

All right, you extract the list of values - all the counts - from the hash and set `$a' to the number of values returned; that's what you get when you look at a list in a scalar context (it's a bit more complex than that, but that's the part that's important right now.) Next, you sum up all those values -

map{$b+=$_}@a;

print$b/$a

you print out the ratio of that sum over the count of the elements - thus dividing the total hits by the number of IPs. How's that?"

- "Thank you, thank you... I guess spending all that time studying under Woomert's direction is starting to pay off - thanks, Woomert! The rest of them are somewhat similar:

perl -wlne'/^(\S+)/;$h{$1}++}{print"$h{$_}\t$_"for sort{$h{$a}<=>$h{$b}}keys%h' access.log

The first part we already know - do a frequency count of the IPs. In the end block, however, you do something different; we'll parse it right to left, just as Woomert taught me:

sort{$h{$a}<=>$h{$b}}keys%h

for ( values %h ){ ... }

sort

perldoc -f sort

$a

$b

sort

print"$h{$_}\t$"for ...

for

$_

$h{$_}

Last but not least, we have this:

perl -lne'/^(\S+).*?"(.*?)"/;length$h{$1}>length$2or$h{$1}=$2}{print"@a"while@a=each%h' access.log



/^(\S+).*?"(.*?)"/



Woomert lazily extracted a laser pointer from his shirt pocket and pointed.

length$h{$1}>length$2or$h{$1}=$2

@a

perldoc perllexwarn

$2

or

or

||

Can you do the rest?"

- "Yes; it looks fairly easy.

print"@a"while@a=each%h

I've seen you do this before... oh yes. It's a `` while each '' loop that retrieves a key-value pair from a hash; you're assigning them to an array and printing the array. Since you've interpolated it by using double quotes around the array name, you'll get a space between the elements - which makes it nicely readable. All together, this prints out our hash - in more-or-less random order, but we don't really care since we just want to see what's in it. Right?"

- "I... I hope so, Woomert." Frink looked up, proud as can be. "I believe so. I'll certainly do my best. I'll head off for home then, and leave you two alone. Have a great night."

- "You've made Frink's week, you know. That's quite a compliment."

[1] In regard to this, my mysterious correspondent notes: "This is a case where continued secrecy is necessary to the Bank's security arrangements. Perhaps one day, the world will be apprised of the brilliant, decisive, and above all courageous actions of the Great Detective and his assistant."



[2] Woomert, as my correspondent noted, does not take credit for this particular Perl hack; it was created by Abigail in

comp.lang.perl.misc and seems to have become an idiom, at least to a degree. In fact, Abigail's brilliant one-liners have been known to stump Woomert on occasion...



Ben is a Contributing Editor for Linux Gazette and a member of The Answer Gang.

Ben was born in Moscow, Russia in 1962. He became interested in electricity at age six--promptly demonstrating it by sticking a fork into a socket and starting a fire--and has been falling down technological mineshafts ever since. He has been working with computers since the Elder Days, when they had to be built by soldering parts onto printed circuit boards and programs had to fit into 4k of memory. He would gladly pay good money to any psychologist who can cure him of the resulting nightmares. Ben's subsequent experiences include creating software in nearly a dozen languages, network and database maintenance during the approach of a hurricane, and writing articles for publications ranging from sailing magazines to technological journals. Having recently completed a seven-year Atlantic/Caribbean cruise under sail, he is currently docked in Baltimore, MD, where he works as a technical instructor for Sun Microsystems. Ben has been working with Linux since 1997, and credits it with his complete loss of interest in waging nuclear warfare on parts of the Pacific Northwest.

