Previous Page
Next Page

BREAKING THE SILENCE

Quiet wireless networks, as previously presented, don't cut the cake when trying to gather sufficient unique IVs to mount a successful statistical attack. Several methods were described in previous chapters on how to stimulate traffic on idle networks via packet replaying and mangling. These methods, however, generate a lot of noise and, due to the synchronous nature of wireless adapters, aren't beneficial to the attack.

How can you generate traffic with minimal transmits on a network you can write to but can't quickly read from? From Chapter 6, you know that you can use incremental attacks to recover the keystream for a given IV, but this, in most cases, takes longer than the state timeout of most protocols. With a little preparation and a little protocol knowledge, however, you can generate large amounts of traffic with minimal injections.

The first requirement is to have a host on the Internet running a sniffer that listens for a specific trigger within packets sent to it. The host then reacts by sending many packets back to the source of the injected packet, thus generating encrypted data and, hopefully, many unique IVs. The high-level data flow, called Single Send-Multiple Response (SSMR), is illustrated in Figure 11-3.

There are a few other obstacles to overcome before an attacker can get a host to flood the local network with traffic using this technique. Most of them are related to determining what IP address range the local subnet is using. These issues are covered next.

Layer 2 and Layer 3 Resolution

On 802.11 networks, as with 802.3, layer 3 address resolution is done through the Address Resolution Protocol or ARP. As a result of this dependency on layer 3 to layer 2 resolution, the client must contain logic to identify and respond to ARP requests for the IP address it has obtained on the network. By utilizing Wireshark (formerly Ethereal), a tool mentioned in Chapter 6, you can see from Figure 11-4 that ARP packets have fairly static contents, and when bundled with the methodologies provided in the next section, you can easily identify the IP ranges used by the network.

IP

The IP protocol provides a few tasks that need to be overcome before proper communications can occur with the Internet host. First, you need to find an IP address with which to send; second, you need to know the IP address of the gateway to the target network; and third, you need to accommodate for the time-to-live decrements to accommodate for the Internet host being several hops away.

Address Discovery

Before you can send packets on an IP network, you need an IP address. You can either use the IP address of an existing machine on the network or find an unused address in a suitable range of IP addresses for the network. For Internet communications, it is best to choose a unique IP address, as you don't want the original IP's owner responding to packets it, of course, didn't send. Typically, these responses would reset and clear the router's state table entry for our new connection. You can discover a unique IP address in a number of ways, including monitoring, RARP/BOOTP/DHCP, and broadcast requests. All responses are decrypted using the incremental decryption attacks presented in Chapter 6.

Image from book
Figure 11-3: Single Send-Multiple Response (SSMR) data flow
Image from book
Figure 11-4: Using Wireshark to examine an ARP packet

RARP

Reverse Address Resolution Protocol (RARP) is basically, as the name indicates, the reverse protocol of ARP. Instead of resolving the layer 3 to layer 2 associations, you are resolving the layer 2 to layer 3 associations. Because WEP packets require that the source and destination layer 2 addresses be unencrypted, resolution is trivial. RARP daemons, however, are not so common, as RARP has been succeeded in modern architectures by BOOTP and DHCP. Traditional UNIX hardware platforms, such as Sun, HP, and SGI, often require RARP support for network-based installations, so finding active RARP daemons is a possibility.

Solution

Filter RARP traffic going in or out of a wireless network.

BOOTP/DHCP

BOOTP and DHCP are protocols used by modern platforms to request an IP address on a network. Most access points have DHCP servers built in to them, and most networks have DHCP as it provides ease of configuration for the end-user. This method is commonly the easiest one for obtaining a unique IP address, as you can see in Figure 11-5.

Solution

Restrict DHCP leases to a list of known MAC addresses. Deploy DHCP authentication as per RFC 3118 if the server and clients support that option.

Broadcast Addresses

Broadcast addresses are used to send one packet to multiple layer 3 hosts, providing exactly the same capabilities in their layer 2 form. Commonly, broadcast addresses are calculated by performing a logical AND on the IP address with the netmask and then performing a logical OR on the IP address with the netmask. However, you do not yet know the IP address range. One interesting quirk of some UNIX-based platforms, even embedded ones used in networking equipment, is that they will respond to the broadcast IP address of 255.255.255.255, regardless of their netmask:

[root@dokken ~]# ping -b 255.255.255.255
WARNING: pinging broadcast address
PING 255.255.255.255 (255.255.255.255) 56(84) bytes of data.
64 bytes from 172.16.200.125: icmp_seq=0 ttl=64 time=0.073 ms
  Linux 2.6
64 bytes from 172.16.200.1: icmp_seq=0 ttl=64 time=1.92 ms (DUP!)
 VXWorks
64 bytes from 172.16.200.106: icmp_seq=0 ttl=64 time=2.53 ms (DUP!)
  Linux 2.4
Image from book
Figure 11-5: Using Wireshark to monitor DHCP traffic

Solution

Use higher-level protocol authentication or endpoint security mechanisms to authenticate new clients on the network. Alternately, you could use strong layer 2 security (WPA) and stop attackers from reading or writing to your network to begin with.

Gateway Discovery

Gateway discovery can be done a number of ways. With the BOOTP/DHCP discovery method described previously, you can see that the responses contain gateway addresses as well (see Figure 11-5). In the case of using observation-based methods or the broadcast method just described, you can simply attempt to use the layer 2 addresses of the response packets as a gateway.

Solution

Disable IP forwarding on non-gateway hosts and further use higher-level protocol authentication or endpoint security mechanisms to authenticate new clients on the network.

TTL Accommodation

Another problem you encounter with the transmission of data to your Internet host and processing of its responses is with the TTL, or Time-To-Live, counter in the IP packet. To prevent packets from cycling forever in the event of a routing loop, the TTL is decremented by each host the packet passes through on its way from the sender to the receiver.

The existence of an unknown field value in the IP packet is a problem when you are trying to build up large keystreams (so you can read/inject larger packets). Since you don't know the value of the TTL, you don't know what value to XOR the encrypted field with to re-create the keystream. This isn't a problem when you are generating traffic for use with statistical attacks, which is what most attackers want to do.

One solution to not knowing the TTL in the response packet is to have the Internet host, which receives the packet from the attacker, send responses with a TTL that will decrement to a specific value by the time it reaches the wireless network. This technique, however, is somewhat unreliable because of a number of factors, including dynamic route adjustment, the unpredictability of the Internet, and things of this nature.

A more reliable solution is to brute-force the TTL, by starting at the predefined sending value and decrementing. Each time the decryption is done, the contents can be verified with the ICV value at the end of the packet. As mentioned previously, for statistical attacks using FMS methods, the full keystream is not required; however, using more advanced analysis methodologies requires larger keystream sequences.

 [root@dokken ~]# ping -c 1 172.16.200.1
PING 172.16.200.1 (172.16.200.1) 56(84) bytes of data.
64 bytes from 172.16.200.1: icmp_seq=0 ttl=64 time=1.62 ms
--- 172.16.200.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.622/1.622/1.622/0.000 ms, pipe 2
[root@dokken ~]# ping -c 1 yahoo.com

PING yahoo.com (216.109.112.135) 56(84) bytes of data.
64 bytes from yahoo.com (216.109.112.135): icmp_seq=0 ttl=47 time=90.8 ms
--- yahoo.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 90.805/90.805/90.805/0.000 ms, pipe 2

UDP

UDP is a good choice with regards to achieving high-yield SSMR due to its inherent stateless nature. However, layer 4 protocols that utilize UDP, such as DNS and LDAP, are often Single Send-Single Response.

TFTP

TFTP, defined by RFC 1350, allows a single UDP packet to be sent and an entry created for routing responses to the client. The protocol does define that the data packets be acknowledged with a response; however, testing reveals that most firewalls and routers, even enterprise-level equipment, allow the Internet host to send traffic through for anywhere from 30 seconds to indefinitely.

Solution

Filter all outbound TFTP traffic and further filter any unneeded outbound services.

NFS

NFSv2, defined by RFC 1813, also behaves much like TFTP with regards to your task. This protocol also requires a response from the client; however, most networking equipment ignores this response for a reasonable period of time. Again, this is not the case for UDP SSSR protocols and not all SSMR. These two protocols have been personally verified in penetration tests and achieved a ratio of 1:100 sends to receives.

Solution

Filter outbound NFS traffic and further filter any unneeded outbound services.

TCP

The TCP protocol requires a bit more trickery than UDP. Similar to the ARP responder constructed previously, a replier must be constructed for the purpose of sending the appropriate ACK responses. Furthermore, the Internet host needs to reply with a hard-coded starting sequence number in the TCP header to aid in decryption. A typical data flow is illustrated in Figure 11-6.

In step 1 of Figure 11-6, the client, or the attacker, sends a TCP SYN packet. This packet contains a transmission sequence number that must be incremented every time new data is sent. In step 2, the server replies with a TCP SYN, ACK, thus acknowledging the request to initiate a connection. This packet contains an acknowledge sequence every time new data is received by the client. In step 3, the client responds with a TCP ACK packet, which contains both sequence numbers.

As previously mentioned, you can hard-code the acknowledge sequence number sent by the server so decrypting the packet is not required to reply properly with the ACK, thus creating a state table entry in the access point that acts as your router.

How do you optimize a protocol for SSMR that requires data acknowledgment? The TCP protocol allows negotiation of maximum TCP window size. This window size is a 16-bit unsigned value in the header that indicates the maximum number of bytes of data that the sender can transmit before waiting for the receiver to send an ACK. Most networking equipment will terminate the connection if this process is not followed, as it is fairly easy to track and fairly hard to get wrong in the implementation of IP Network Address Translation (NAT). Now the attack is SSMR, as indicated in Figure 11-7.

In step 1, the attacker forges a TCP SYN packet to the Internet host via the AP's network gateway. The Internet host then replies with a deterministic SYN, ACK using some form of priorly arranged sequence and data content. In step 3, the attacker forges a TCP ACK and thus completes the three-way TCP handshake. During the process of this handshake, the maximum segment size of both parties was negotiated as 65535, the maximum unsigned 16-bit value. Thus in steps 4–7, which have been reduced to three steps from the many possible for the sake of illustration, the Internet host can send multiple PSH, ACK packets. As a result of the Internet host sending this data, the AP then appropriately routes the packets to the wireless network, generating traffic and thus IVs. In step 8, you see the attacker forging another ACK packet to acknowledge the PSH, ACKs that were received, allowing this algorithm to loop infinitely back to step 4, provided the attacker completes step 8.

Image from book
Figure 11-6: TCP/IP handshake
Image from book
Figure 11-7: Generating additional IVs

Solution

Migrate existing WEP infrastructure to WPA. WPA resolves many of the deficiencies of 802.11 security. Further information can be found in Chapter 6.

Statistical Attacks Against WEP

FMS, an attack published by Scott Fluhrer, Itsik Mantin, and Adi Shamir, and known by the letters of the authors' last names, is the basis of most attacks against WEP. This attack exploits a weakness in the key scheduling and PGRA routines of RC4. As their publication indicated, when known IVs are prefixing the secret key, the first few bytes used to initialize the array used by the PGRA is the SNAP header. The output of the PGRA can be observed to determine the state of the KSA and recover portions of the secret key.

Attack Explained

Keep in mind that when using WEP, the Initialization Vector (IV) is prepended to the secret key before being fed to RC4. Knowing a few of the initial bytes that are input to RC4 forms the basis of this attack. The fact that the few known bytes are prepended to the secret key is important. As you might expect, RC4 processes the key fed to it in order. Because you know the first few bytes that RC4 will use as a key, you have partial knowledge of the state of RC4. You can use this partial knowledge to guess secret key bytes much more efficiently than if they were chosen at random.

First, you need to examine how RC4 schedules keys. The algorithm is as follows:

for(i = 0; i < 256; i++)
 S[i] = i;
for(i = 0, j = 0; i < 256; i++)
{
 j = (j + S[i] + key[i % key_length_in_bytes]) % 256;
 /* swap S[i] and S[j] */
  tmp = S[i];
 S[i] = S[j];
  S[j] = tmp;
}
i = 0;
j = 0;

Next is the PGRA, which uses the following algorithm:

i = (i + 1) % 256;
j = (j + S[i]) % 256;
/* swap S[i] and S[j] */
tmp = S[i];
S[i] = S[j];
S[j] = tmp;
 V = S[(S[i] + S[j]) % 256];

V is the generated value of the PGRA stream. With these two algorithms in mind, let's use the secret key of {72, 73, 77, 79, 78} and an IV of {3, 255, 7} to encrypt the first byte of the SNAP header, which is 170. The cipher text output is 255.

You can obtain the first output value of the PGRA algorithm by performing a logical XOR of the cipher text and the known plaintext byte of the SNAP header. The value obtained from this is 85.

Next, you perform the first three steps of the KSA algorithm:

for(i = 0; i < 256; i++)
   S[i] = i;
for(i = 0, j = 0; i < 3; i++)
{
   j = (j + S[i] + IV[i]) % 256;
   /* swap S[i] and S[j] */
    tmp = S[i];
   S[i] = S[j];
    S[j] = tmp;
}

Now you note if the value of j is less than 2; if so, then the first two entries will be disturbed by a future swap during the key scheduling and the value will become unreliable.

The matrix obtained for S in this example is

   0 : 03 00 0c 01 04 05 06 07 08 09 0a 0b 02 0d 0e 0f
  16 : 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
  32 : 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
  48 : 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
  64 : 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
  80 : 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
  96 : 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
 112 : 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f
 128 : 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f
 144 : 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f
 160 : a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af
 176 : b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf
 192 : c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf
 208 : d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df
 224 : e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef
 240 : f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff

From this, you see that j = 12 and S [3] = 1 at the third step of the key scheduling algorithm. Using the following equation, you can guess at the potential key position 0 value:

Guess = PGRA - J - S[3]
Guess = 85 - 12 - 1
Guess = 72

In this case, your guess is correct, but it won't always be. This is where statistics come into play. If you enumerate through the captured IVs and locate the ones that are in acceptable form for the attack, which is {3, 255, X}, you can keep a count of the guess values. This attack then acts like a voting system, whereby you select the candidate that has the highest number of guesses.

Now this example focuses on the first byte of the secret key, but it can be expanded to solve for other bytes with the following IV pattern:

{ B + 3, 255, X }

In this pattern, B is the byte index you are solving for; thus, in the previous example, 0 + 3 = 3 and X is a random value. Assuming you have guessed at previous key bytes, you assemble them with the IV preceding the secret key component. Further guesses can be calculated with the following algorithm:

for(i = 0; i < 256; i++)
 S[i] = i;
for(i = 0, j = 0; i < (B + 3); i++)
{
 j = (j + S[i] + K[i]) % 256;
  tmp = S[i];
 S[i] = S[j];
  S[j] = tmp;
}
Guess = PGRA - j - S[B + 3];

Attacks can be performed on all bytes of the initial key in this manner.

Modified Statistical Attacks

IVs in the form of { B + 3, 255, X } allowed key recovery more efficiently than the FMS attack projected. A security researcher from Dachb0den Labs, David Hulton, discovered that this equation held true for the second byte of the PGRA output, but with less accuracy. This attack, however, could then utilize packets that were otherwise detrimental to the attack and effectively lowered the number of unique IVs that needed to be captured to perform a successful attack.

Further development by a researcher named KoreK revealed that other patterns existed with different IVs; however, many of these are not proven or stable. The jcaircrack component of airbase provides a fairly legible implementation of 17 different attacks. Another interesting aspect of the jc-aircrack implementation is a per-attack set of counters, so you can measure the number of hits a certain attack gets versus another to weigh the reported effectiveness against the actual effectiveness.

Consider the following program:

/* simple statistical attack finder
 * compile: gcc -O3 -o rc4finder rc4finder.c
 * usage: ./rc4finder <keybyte>
 */
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rc4.h>


typedef struct val_s {
 unsigned long key[8];    /* key that was used */
 unsigned long S[256];    /* S state permutation up to key_byte */

 unsigned long Si[256];   /* inverse of S state */
 unsigned long y[8];      /* Y values up to key_byte */
  unsigned long o1;  /* PGRA byte #1 */
  unsigned long o2;  /* PGRA byte #2 */
} val_t;


/* values we are going to gather statistics from */
val_t ent[0x400];
unsigned long key_byte;


/* Used for debugging, also useful for printing matches to determine
 * criteria for eliminating bad votes.  However this is a manual process.
 */
void dump_ent(val_t *val)
{
 size_t i;


 printf("key:\n");
 for(i = 0; i < 7; i++)
       printf("%02x:", val->key[i]);
 printf("%02x\n", val->key[i]);
  printf("S:\n");
  for(i = 0; i < 256; i++)
  {
       printf("%02x", val->S[i]);
        if((i & 0xf) == 0xf)
              printf("\n");
        else
              printf(":");
  }
 printf("Si:\n");
  for(i = 0; i < 256; i++)
  {
        printf("%02x", val->Si[i]);
        if((i & 0xf) == 0xf)
              printf("\n");
        else
              printf(":");
  }
  printf("y:\n");
  for(i = 0; i < key_byte - 1; i++)
        printf("%02x:", val->y[i]);
 printf("%02x\n", val->y[i]);
  printf("o:\n");

 printf("%02x:%02x\n", val->o1, val->o2);
  return;
}


/* Computes a permutation of key into val */
void compute_value(unsigned char *key, val_t *val)
{
  unsigned long x, y, tmp;
  unsigned char data[2];
 RC4_KEY ctx;


  memset(val, 0, sizeof(val_t));
  /* copy key into structure */
  for(x = 0; x < 8; x++)
        val->key[x] = key[x];
  /* initialize S */
  for(x = 0; x < 256; x++)
        val->S[x] = x;
 /* perform first key_byte number of permutations of the
   * key scheduling algorithm
  */
 for(x = 0, y = 0; x < key_byte; x++)
  {
       y = (y + val->S[x] + key[x]) & 0xff;
       /* save Y value into Y array */
        val->y[x] = y;
        /* swap S[x] and S[y] */
        tmp = val->S[x];
       val->S[x] = val->S[y];
        val->S[y] = tmp;
  }


  /* invert array S and store it into Si */
  for(x = 0; x < 256; x++)
        val->Si[val->S[x]] = x;


  /* do a full key schedule with OpenSSL */
 RC4_set_key(&ctx, 8, key);
  memset(data, 0, sizeof(data));
 /* get the first to bytes of the PGRA output */
 RC4(&ctx, 2, data, data);
  val->o1 = data[0];
  val->o2 = data[1];
  return;

}


typedef struct tst_s {
 unsigned char (*tst)(val_t *);
 int cnt;
} tst_t;


/* FMS attack */
unsigned char logic_0(val_t *val)
{
  return val->Si[val->o1] - val->S[key_byte] - val->y[key_byte - 1];
}


/* h1kari - FMS attack */
unsigned char logic_1(val_t *val)
{
  return val->Si[val->o2] - val->S[key_byte] - val->y[key_byte - 1];
}


/* KoreK #1 attack */
unsigned char logic_2(val_t *val)
{
 return val->Si[0] - val->S[key_byte] - val->y[key_byte - 1];
}


/* KoreK #2 attack */
unsigned char logic_3(val_t *val)
{
  return 1 - val->S[key_byte] - val->y[key_byte - 1];
}


/* KoreK #3 attack */
unsigned char logic_4(val_t *val)
{
  return 2 - val->S[key_byte] - val->y[key_byte - 1];
}


/* KoreK #4 attack */
unsigned char logic_5(val_t *val)
{
 return val->Si[(val->S[1] - val->S[2]) & 0xff] - val->S[key_byte]
       - val->S[key_byte - 1];
}

tst_t tests[] = {
  { &logic_0, 0 },
  { &logic_1, 0 },
  { &logic_2, 0 },
  { &logic_3, 0 },
  { &logic_4, 0 },
  { &logic_5, 0 }
};


int main(int argc, char *argv[])
{
  size_t i, j, k;
 unsigned char key[8];
  unsigned long perm[256];


 if(argc < 2)
  {
       fprintf(stderr, "usage: %s <key_byte>\n");
        return 1;
  }


 srandom(time(NULL));
 key_byte = strtoul(argv[1], NULL, 0);
  if(key_byte < 3 || key_byte > 8)
  {
        fprintf(stderr, "key byte needs to be >= 3 and <= 8\n");
        return 1;
  }


 for(i = 0; i < 0x10000; i++)
  {
        key[1] = i & 0xff;
        key[0] = (i >\> 8) & 0xff;
        for(j = 0; j < sizeof(ent) / sizeof(val_t); j++)
        {
             for(k = 2; k < 8; k++)
                   key[k] = random();
             compute_value(key, &ent[j]);
        }
       for(j = 0; j < sizeof(tests) / sizeof(tst_t); j++)
             tests[j].cnt = 0;
        for(j = 0; j < sizeof(ent) / sizeof(val_t); j++)
        {
              for(k = 0; k < sizeof(tests) / sizeof(tst_t); k++)

              {
                   /* Check our test functions to see if
                    * they produce a correct guess
                    * If so increment the counter on the test */
                   if(tests[k].tst(&ent[j])
                         == ent[j].key[key_byte])
                    {
                          tests[k].cnt++;
                    }
              }
        }
        for(j = 0; j < sizeof(tests) / sizeof(tst_t); j++)
        {
             /* check to see if any of the tests have a counter
              * higher than number of values / 256 * 3
              */
             if(tests[j].cnt > (((sizeof(ent)
                   / sizeof(val_t)) / 256) * 3))
              {
                    /* print the first two bytes of the IV
                    * and the test number and the counter value */
                   printf("iv[0]=%u iv[1]=%u tst=%u cnt=%u\n",
                          key[0], key[1],
                         j, tests[j].cnt);
              }
        }
 }
 return 0;
}

By modifying the logic routines and analyzing the different correct votes versus incorrect votes, you can find new and interesting weak IVs. The dump_ent function is not linked to by any of the example listing; however, it can easily be modified to dump the entries accordingly, but be warned it creates a fair amount of data. This data, however, is required in most cases to determine the dependencies for eliminating IVs that would cast incorrect votes.

Here's the example usage:

[root@dokken ~]# ./rc4finder 3
iv[0]=2 iv[1]=22 tst=1 cnt=13
iv[0]=3 iv[1]=31 tst=5 cnt=13
iv[0]=3 iv[1]=255 tst=0 cnt=54 <-- FMS attack with correct IV
iv[0]=4 iv[1]=53 tst=2 cnt=17
iv[0]=10 iv[1]=243 tst=3 cnt=14

iv[0]=19 iv[1]=35 tst=1 cnt=14
iv[0]=19 iv[1]=212 tst=0 cnt=14
iv[0]=21 iv[1]=56 tst=2 cnt=15
iv[0]=21 iv[1]=95 tst=2 cnt=13
iv[0]=21 iv[1]=115 tst=2 cnt=13

Statistical Attack Countermeasure

Avoid using WEP mode if possible. If not, use a strong passphrase.


Previous Page
Next Page