📄 writeworm.txt
字号:
This is an old paper of mine, released in 1999 or 2000, about an interesting
project that laid out some general suggestions about the possible future
development of Internet worms. While most of it might appear more than
obvious nowadays, back then, it not necessarily was.
The text has to offer some bad grammar and other wonders, but I never had
enough time and good will to review and edit it, now that I am marginally
more fluent with this language ;-)
=============================================================================
"I don't think I really love you"
=============================================================================
or writing internet worms for fun and profit
(C) 1998-2000 Michal Zalewski <lcamtuf@tpi.pl>
0x00: Preface
-----------------------------------------------------------------------------
The media, kindly supported by AV "experts", have drawn an apocalyptical vision
of destruction caused by a little MS Outlook / VisualBasic worm, called
"ILOVEYOU". Rough estimations - $10M lost for "defeating the disease" -
fueled AV companies' stock prices, but made many people wonder - is this really
the worst that could happen? Or is that just another lame VBS application that
is hardly able to spread without user "click-me" interaction, and is limited
to one desk-end operating system? An application than in its vicious rage
of destruction goes as far as deleting MP3 files on your disk [1]?
This article is a report of a study on another type of Internet worms.
Over a year ago, with a couple of friends, I started a project, called
'Samhain' (with no relation whatosever to the other similarly named security
project that surfaced later on). We wanted to see if it's difficult to write a
worm that would have a potential to have effects that are more serious
and longer-lasting than we were dealing with on a daily basis. In theory,
of course. In practice, we wanted to find out how hard it'd be. First, we
agreed on some theoretical guidelines for such an application:
1: Portability - the worm must be architecture-independent, and should work on
many different operating systems (we focused on Unix/Unix-alikes,
but also looked at the Windows/DOS platform).
2: Invisibility - the worm must implement stealth/masquerading techniques,
being able to hide its own code in live system and stay undetected as
long as possible or necessary.
3: Independence - the worm must be able to spread autonomically, with no user
interaction, using a built-in exploit database.
4: Learning - the worm should be able to learn new exploits and techniques
instantly; by launching one instance of an 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 - the worm should be fully polymorphic, with no constant
unique signatures, to avoid detection.
7: Usability - the worm should be able to complete choosen mission objectives -
eg. infect choosen system, then download instructions, and, when its
mission is completed, simply disappear from all systems.
With those seven simple principles, we started the work on an implementation
to test the feasibility of building such a program. It took approximately
two months, and the remaining part of this text describes our ideas and
the implementation.
This is not a terrorist's cookbook, and the worm never made it to the real
world. It's just a study of technical possibilities and an insight in the
future, should more sophisticated worms ever become a reality.
It's written to show a serious potential risk which can be hardly avoided
or stopped. Its main purpose is to show that the task is not as difficult
as it seems, and only the laziness of the malicious coders keeps us
fairly secure.
Winter 1998, three bored people somewhere in the middle of Europe.
0x01: Portability
-----------------------------------------------------------------------------
This is probably the most important thing - we don't want a code that can run
only on Windows, Linux or Solaris, or - worse - can run only on x86 or
Red Hat. The task is quite easy to complete if you decide to distribute
the code in a platform-independent form, and spend enough time on testing
and porting. Most systems do have a C compiler, so we decided to use a
source code with a simple bootstrap.
But the question is - what about systems that do not have a C compiler?
Would the coverage of the worm be limited? Not quite. Since, per the original
plan, our worms would be interconnected, it'd be trivial for one to query for
a binary that is suitable for this specific architecture (more details about
the wormnet are described in the section 0x04). The binary, should one be
necessary, is always transferred accompanied with the source code itself,
and more details about the infection scheme are given in section 0x03 of
this writeup.
Early version of our decryptor for the "source" infection, which we decided t
o disclose, looked the following way:
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<i;)a[x++]-=atoi(v[1]"
");write(1,a,i);}\" >$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 with
custom increment value (decryptor has been modified accordingly to choosen
value - \x01, \x02, \x03 and \x04 are changed by encryptor routine). Also,
this constant decryptor has been every time re-written using simple
polymorphic engine (per section 0x06) to avoid constant strings. This
was the simplest static signature avoidance mechanism, but later on
we migrated towards a better algorithm (using a logistic equation in
a chaos window); the purpose was not to remain absolutely undetectable,
this is not possible, but to make it difficult to detect the code using
common signature checkers.
The decryptor listed above is not very portable itself, since it depends
on a bash shell, a compiler, gzip and such - but the rule was quite simple,
if the new instance fails to report within a short timeframe after the
compromise attempt, a precompiled binary is tried instead.
On a side (but not unrelated) note, there are some interesting examples of
code that can compile and run in C, bash, csh, perl and other languages
at the same time. You might be interested in seeing IOCCC archives for
that purpose [2].
Sebastian authored a virus that could spread both on Windows/DOS platforms
with a compiler, and Unix systems with no modifications and no user
interaction. It could perform cross-platform infections and install itself
as a compiler trojan (by modifying system library headers). The virus
got named Califax and has been developed while writing Samhain as an
exercise to demonstrate that it's fairly trivial to implement cross-system
jumps even in a source form, and combine viral and worm activities. I
do not include any parts of his code without a permission, but the source
was surprisingly short - around 400 lines of code - and will be likely
made available on the net with time.
0x02: Invisibility
-----------------------------------------------------------------------------
After breaking into a remote system, the worm would not always have root
privledges, so first of all, we wanted to implement some techniques to hide it,
make it look like any other process in system, and make it hard to kill until
there's a chance to gain higher privledges. We also made sure it is hard to
debug/trace running or even inactive worm (see section 0x05).
Our non-privledged process masquerading code consisted of the 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 if
one of them is catched, we have 'mirror' project. Of course, at first we
should avoid such attempts by camouflage. This comment comes from libworm
README for Unices:
-- snip from README --
a) Anti-scanning routines
Following 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 patiently
int 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 operation
int 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 operation
int 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 routines
These routines are designed to masquerade and hide current
process:
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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -