Does this site look plain?

This site uses advanced css techniques


Last Updated: Wed Aug 22 12:44:08 PDT 2001

Others have done a much more complete job of analyzing this, so we've left it in the incomplete state where we stopped working on it.

Latest News

22 Aug 2001 - There are reports of a new, minor variant of Code Red II, and its has only very small changes. The "CodeRedII" string was changed to "_________" so it could coexist with a "real" Code Red II worm, and it slightly changed the algorithm for scanning the network to less favor the local network. This is no big deal at all, and if you were protected before, you're still protected.

11 Aug 2001 - These reports of a new Code Red in Korea are false: it's the same Code Red II that we've come to know and love. It seems that nobody in Korea worked last weekend or read the newspapers. This is pretty lame reporting.

7 Aug 2001 1200 PST - there have been reports of a "new" Code Red II, but these are almost certainly false. It turns out that Code Red II has some interesting behavior on cable modem networks, but it's an accident and not intentional. In addition, there are reports from Reuters that suggest a second Code Red II infecting China and parts East, but this is almost certainly just very delayed reaction to the released-Saturday Code Red II. Why this is news on Tuesday instead of Saturday is curious.

Original Analysis of Code Red II

On Saturday morning, August 4 2001, a new variant of the Code Red worm started showing up in web server logfiles. The "real" work to analyze this is being done by the folks at eEye Digital Security, but until they release their big-time analysis I'd like to offer a minor-league running commentary. I believe this was the first comprehensive information available on this worm for the better part of Saturday.

Keep in mind that I am not an assembler guru, and though I've been doing C and Win32 programming for a long time, I am bound to get some of the details wrong here. Please let me know if you catch any of my mistakes.


The traditional signature of Code Red in web server logs has been:

%u0078%u0000%u00=a  HTTP/1.0

but the new strain is showing up as:

%u0078%u0000%u00=a  HTTP/1.0

The only difference here is the use of XXXX instead of NNNN, but this is only the initial infection code. The payload is a different story: I used my websnarf tool to capture the full text of the request, and instead of the usual 4039 or 4112 byte payloads, this one is 3818 and quite different. When running the "strings" command on this binary we see:

GET /default.ida?XX{220 x X}XX%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
Content-type: text/xml
Content-length: 3379
CodeRedII       <--- hence the name: Code Red II
hT @
hH @
hX @
SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
SYSTEM\CurrentControlSet\Services\W3SVC\Parameters\Virtual Roots
WinExec                  <--- troubling
RegSetValueExA           <--- troubling

In particular, there doesn't appear to be any "sample" HTML code that replaces the current home page, and the references to the registry API functions are troubling. I've not picked apart the code yet, but it would not surprise me if (unlike the previous variants), this one was actually persistent.

There are several threads going on in the DSLReports Security Forum, and there are reports of much higher than usual probe attempts. Based on many reports there, we have a very strong suspicion that Code Red II has a different algorithm for scanning its targets, and it favors machines "close" to itself (roughly: "same ISP").

Update (1700 PST) - I'm having a lot of trouble with my disassembler (not very good with it anyway), but this just doesn't look structurally at all like the first one that went around. This suggests that we will find very little in common with the first one.

In particular, the fact that it has "CodeRedII" inside means that it couldn't possibly be the original worm -- the name wasn't attached until after it was released.

Update: 1819 PST - The worm queries and sets a "global atom" of the name CodeRedII. I'm not sure what this is, but it's a form of interprocess communication that's (perhaps) used by IIS. Not yet sure what this implies.

It seems that the IP address calculation starts with the machine's local IP address, and this gives a bit of strength to the notion that scanning tends to be to "local" machines (something I'm very much seeing in my logs). It also skips IP addresses ending in dot-0 and dot-255.

If anybody else is trying to disassemble this, these values seem to reference the Win32 API functions used, plus a few additional special variables.

DWORD PTR [EBP-8]           FindLibrary
DWORD PTR [EBP-0CH]         LoadLibraryA
DWORD PTR [EBP-10H]         CreateThread
DWORD PTR [EBP-14H]         GetTickCount
DWORD PTR [EBP-18H]         Sleep
DWORD PTR [EBP-1CH]         GetSystemDefaultLangID
DWORD PTR [EBP-20H]         GetSystemDirectoryA
DWORD PTR [EBP-24H]         CopyFileA
DWORD PTR [EBP-28H]         GlobalFindAtomA
DWORD PTR [EBP-2CH]         GlobalAddAtomA
DWORD PTR [EBP-30H]         CloseHandle
DWORD PTR [EBP-34H]         _lcreat
DWORD PTR [EBP-38H]         _lwrite
DWORD PTR [EBP-3CH]         _lclose
DWORD PTR [EBP-40H]         GetSystemTime
DWORD PTR [EBP-44H]         WS2_32.DLL
DWORD PTR [EBP-48H]         socket
DWORD PTR [EBP-4CH]         closesocket
DWORD PTR [EBP-50H]         ioctlsocket
DWORD PTR [EBP-54H]         connect
DWORD PTR [EBP-58H]         select
DWORD PTR [EBP-5CH]         send
DWORD PTR [EBP-60H]         recv
DWORD PTR [EBP-64H]         gethostname
DWORD PTR [EBP-68H]         gethostbyname
DWORD PTR [EBP-6CH]         WSAGetLastError
DWORD PTR [EBP-70H]         USER32.DLL
DWORD PTR [EBP-74H]         ExitWindowsEx
DWORD PTR [EBP-7CH]         RandomSeed
DWORD PTR [EBP-80H]         socketFD
DWORD PTR DS:[0FFFFFE58]    my IP address

Update: 1900 PST -- it seems that the worm will exit if the current month is 10/2001 or later. EDIT: - Not true: Starting at 10/2001 it reboots immediately upon being probed. Slight difference!

Update: 1945 PST -- minor tidbit: whether the machine is installed with Chinese as its language seems to be a factor in the random number generator. How curious.

Update: 2053 PST -- the worm sets the CodeRedII global atom, and if it finds that this is already set, it simply goes to sleep forever without touching anything else. It's still presumably eating up a thread, but at least it won't walk on its siblings.

Update: 2137 PST -- it seems that the worm spawns 18 threads for most systems, but spawns 36 if the current language is Chinese. What an interesting turn. Edit: - it's 300 and 600, respectively.

Update: 2244 PST -- it's clear that the worm has an interesting pattern of scanning that favors the local network. The worm generates a target address that depends on a random number and the user's IP address.

[SEE BELOW - got the details wrong]

Localhost (127.X.X.X), multicast (224.X.X.X) and the machine itself are never scanned.

BUT: if a machine inside a firewall is cracked through NAT, it will simply go nuts inside the company firewall as it tries to scan the private range (192.168.X.X or 10.X.X.X). For a company with lots of NT systems inside, they will positively go nuts trying to infect each other.

Update: 2341 PST -- pretty sure I got the scanning pattern backwards. Am still hand-checking the code, but don't bet the farm on four-out-of-eight scanning the local subnet :-(

Update: 0010 PST -- got the algorithm backwards, see the next section for the details.

Update: 0120 PST -- The top-level controlling loop works roughly like this (not literal translation of the asm, but conveys the same idea):

bool is_Chinese = (GetSystemDefaultLangID() matches CHINESE)

nthreads  = is_Chinese ? 600    : 300;
sleeptime = is_Chinese ? 2 Days : 1 Day;

while ( nthreads-- > 0 )



So these machines will all reboot a day after infection, at which time they are available for new infections (but the Chinese go two days between reboots). I suspect that this reboot business has something to do with the Windows File Protection scheme that is subverted by the worm. I believe that disabling WFP requires a reboot in some circumstances, so this may take care of this.

Update: = This is now old news, but I'm including a note here in case people use this as their "standard reference" for Code Red II. It has a nasty back door that allows the bad guy to get into the machine via HTTP even if it's been rebooted. See the eEye advisory for information on this. It's ugly.

Pattern scanning algorithm for Code Red II variant

The algorithm used by this new variant is designed to infect lots of "local" machines that are presumably more fully populated than picking random IPs out there. A blend of the local IP and a random number is used, and the number of octets in each depends on yet another random number that can go eight ways:

The code to do this starts with a table of masks, but remember that on the Intel platform, "network" byte order is the inverse of the native CPU order, so we have to think backwards. This made it much more difficult for a non-assembler guy like me to follow :-(

mtable[] = {  0xFFFFFFFF       // go anywhere
              0xFFFFFF00       // stay in class A
              0xFFFFFF00       // stay in class A
              0xFFFFFF00       // stay in class A
              0xFFFFFF00       // stay in class A
              0xFFFF0000       // stay in class B
              0xFFFF0000       // stay in class B
              0xFFFF0000 };    // stay in class B

# start with a random number that will be our new IP address.
# I presume the random number generator is "random enough".

newip = random();

# zero the UPPER octets of the random IP, which means that the
# random number won't participate in the class A or class B
# address
mask  = mtable[ random() & 0x7 ];   // locate a mask
newip &= mask;                      // throw away rightmost bits

# flip the mask around to operate on LOWER octets
mask = ~mask;                       // flip the mask around
myip = LOCAL_IP & mask;             // throw away leftmost bits

# newip contains the upper bits
# myip  contains the lower bits
# join them:
newip |= myip;

if (newip starts with 127)  try again    // localhost
if (newip starts with 224)  try again    // multicast
if (newip matches LOCAL_IP) try again

Connect to "newip" and try to infect

This algorithm certainly has been effective in hammering the poor cable modem users...

When did this start?

At the moment I am not working terribly hard to track down when all this started happening, but I do want to record what I know. People have been sending me logfile (please don't!) that shows earlier timestamps. but the first public report I know of on DSL Reports in this posting. Member dwmorris at reported this at 0730 PST on August 4, 2001.

I'm no longer tracking earlier log entries -- others will do that -- but I am curious about earlier public reports.

Protecting Yourself

All indications are that this worm has the same infection mechanism as the original Code Red, which means that it exploits the same vulnerability in IIS. The different payload only comes into play if the infection attempt succeeds: otherwise it's ignored. This means that machines patched to protect itself from Code Red are safe from this variant.

Update: - there are credible reports that this worm also infects the Personal Web Server in Windows 2000 Professional. Not sure if the same patches apply.

Observations of behavior

Over the last couple of days there has been much better analysis of the worm that I've been able to do myself. This is a compilation of the collective wisdom of the net at large.

Download the Worm

The binary code captured via websnarf can be found in the binary file CodeRedII.bin. You can see a text dump of the binary worm at CodeRedII.txt.


Here I'll put links to other things I found interesting. Note that all of these leave my site, and unless my link is actually broken, kindly contact the remote site owners if you have issues with their pages: