10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * Cryptographic attack detector for ssh - source code 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. 50Sstevel@tonic-gate * 60Sstevel@tonic-gate * All rights reserved. Redistribution and use in source and binary 70Sstevel@tonic-gate * forms, with or without modification, are permitted provided that 80Sstevel@tonic-gate * this copyright notice is retained. 90Sstevel@tonic-gate * 100Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 110Sstevel@tonic-gate * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE 120Sstevel@tonic-gate * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR 130Sstevel@tonic-gate * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS 140Sstevel@tonic-gate * SOFTWARE. 150Sstevel@tonic-gate * 160Sstevel@tonic-gate * Ariel Futoransky <futo@core-sdi.com> 170Sstevel@tonic-gate * <http://www.core-sdi.com> 180Sstevel@tonic-gate */ 190Sstevel@tonic-gate 200Sstevel@tonic-gate #include "includes.h" 210Sstevel@tonic-gate RCSID("$OpenBSD: deattack.c,v 1.18 2002/03/04 17:27:39 stevesk Exp $"); 220Sstevel@tonic-gate 230Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 240Sstevel@tonic-gate 250Sstevel@tonic-gate #include "deattack.h" 260Sstevel@tonic-gate #include "log.h" 270Sstevel@tonic-gate #include "crc32.h" 280Sstevel@tonic-gate #include "getput.h" 290Sstevel@tonic-gate #include "xmalloc.h" 300Sstevel@tonic-gate #include "deattack.h" 310Sstevel@tonic-gate 32*3102Sjp161948 /* 33*3102Sjp161948 * CRC attack detection has a worst-case behaviour that is O(N^2) over 34*3102Sjp161948 * the number of identical blocks in a packet. This behaviour can be 35*3102Sjp161948 * exploited to create a limited denial of service attack. 36*3102Sjp161948 * 37*3102Sjp161948 * However, because we are dealing with encrypted data, identical 38*3102Sjp161948 * blocks should only occur every 2^35 maximally-sized packets or so. 39*3102Sjp161948 * Consequently, we can detect this DoS by looking for identical blocks 40*3102Sjp161948 * in a packet. 41*3102Sjp161948 * 42*3102Sjp161948 * The parameter below determines how many identical blocks we will 43*3102Sjp161948 * accept in a single packet, trading off between attack detection and 44*3102Sjp161948 * likelihood of terminating a legitimate connection. A value of 32 45*3102Sjp161948 * corresponds to an average of 2^40 messages before an attack is 46*3102Sjp161948 * misdetected 47*3102Sjp161948 */ 48*3102Sjp161948 #define MAX_IDENTICAL 32 49*3102Sjp161948 500Sstevel@tonic-gate /* SSH Constants */ 510Sstevel@tonic-gate #define SSH_MAXBLOCKS (32 * 1024) 520Sstevel@tonic-gate #define SSH_BLOCKSIZE (8) 530Sstevel@tonic-gate 540Sstevel@tonic-gate /* Hashing constants */ 550Sstevel@tonic-gate #define HASH_MINSIZE (8 * 1024) 560Sstevel@tonic-gate #define HASH_ENTRYSIZE (2) 570Sstevel@tonic-gate #define HASH_FACTOR(x) ((x)*3/2) 580Sstevel@tonic-gate #define HASH_UNUSEDCHAR (0xff) 590Sstevel@tonic-gate #define HASH_UNUSED (0xffff) 600Sstevel@tonic-gate #define HASH_IV (0xfffe) 610Sstevel@tonic-gate 620Sstevel@tonic-gate #define HASH_MINBLOCKS (7*SSH_BLOCKSIZE) 630Sstevel@tonic-gate 640Sstevel@tonic-gate 650Sstevel@tonic-gate /* Hash function (Input keys are cipher results) */ 660Sstevel@tonic-gate #define HASH(x) GET_32BIT(x) 670Sstevel@tonic-gate 680Sstevel@tonic-gate #define CMP(a, b) (memcmp(a, b, SSH_BLOCKSIZE)) 690Sstevel@tonic-gate 700Sstevel@tonic-gate static void 710Sstevel@tonic-gate crc_update(u_int32_t *a, u_int32_t b) 720Sstevel@tonic-gate { 730Sstevel@tonic-gate b ^= *a; 740Sstevel@tonic-gate *a = ssh_crc32((u_char *) &b, sizeof(b)); 750Sstevel@tonic-gate } 760Sstevel@tonic-gate 770Sstevel@tonic-gate /* detect if a block is used in a particular pattern */ 780Sstevel@tonic-gate static int 790Sstevel@tonic-gate check_crc(u_char *S, u_char *buf, u_int32_t len, 800Sstevel@tonic-gate u_char *IV) 810Sstevel@tonic-gate { 820Sstevel@tonic-gate u_int32_t crc; 830Sstevel@tonic-gate u_char *c; 840Sstevel@tonic-gate 850Sstevel@tonic-gate crc = 0; 860Sstevel@tonic-gate if (IV && !CMP(S, IV)) { 870Sstevel@tonic-gate crc_update(&crc, 1); 880Sstevel@tonic-gate crc_update(&crc, 0); 890Sstevel@tonic-gate } 900Sstevel@tonic-gate for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) { 910Sstevel@tonic-gate if (!CMP(S, c)) { 920Sstevel@tonic-gate crc_update(&crc, 1); 930Sstevel@tonic-gate crc_update(&crc, 0); 940Sstevel@tonic-gate } else { 950Sstevel@tonic-gate crc_update(&crc, 0); 960Sstevel@tonic-gate crc_update(&crc, 0); 970Sstevel@tonic-gate } 980Sstevel@tonic-gate } 990Sstevel@tonic-gate return (crc == 0); 1000Sstevel@tonic-gate } 1010Sstevel@tonic-gate 1020Sstevel@tonic-gate 1030Sstevel@tonic-gate /* Detect a crc32 compensation attack on a packet */ 1040Sstevel@tonic-gate int 1050Sstevel@tonic-gate detect_attack(u_char *buf, u_int32_t len, u_char *IV) 1060Sstevel@tonic-gate { 1070Sstevel@tonic-gate static u_int16_t *h = (u_int16_t *) NULL; 1080Sstevel@tonic-gate static u_int32_t n = HASH_MINSIZE / HASH_ENTRYSIZE; 1090Sstevel@tonic-gate u_int32_t i, j; 110*3102Sjp161948 u_int32_t l, same; 1110Sstevel@tonic-gate u_char *c; 1120Sstevel@tonic-gate u_char *d; 1130Sstevel@tonic-gate 1140Sstevel@tonic-gate if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) || 1150Sstevel@tonic-gate len % SSH_BLOCKSIZE != 0) { 1160Sstevel@tonic-gate fatal("detect_attack: bad length %d", len); 1170Sstevel@tonic-gate } 1180Sstevel@tonic-gate for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2) 1190Sstevel@tonic-gate ; 1200Sstevel@tonic-gate 1210Sstevel@tonic-gate if (h == NULL) { 1220Sstevel@tonic-gate debug("Installing crc compensation attack detector."); 1230Sstevel@tonic-gate n = l; 1240Sstevel@tonic-gate h = (u_int16_t *) xmalloc(n * HASH_ENTRYSIZE); 1250Sstevel@tonic-gate } else { 1260Sstevel@tonic-gate if (l > n) { 1270Sstevel@tonic-gate n = l; 1280Sstevel@tonic-gate h = (u_int16_t *) xrealloc(h, n * HASH_ENTRYSIZE); 1290Sstevel@tonic-gate } 1300Sstevel@tonic-gate } 1310Sstevel@tonic-gate 1320Sstevel@tonic-gate if (len <= HASH_MINBLOCKS) { 1330Sstevel@tonic-gate for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) { 1340Sstevel@tonic-gate if (IV && (!CMP(c, IV))) { 1350Sstevel@tonic-gate if ((check_crc(c, buf, len, IV))) 1360Sstevel@tonic-gate return (DEATTACK_DETECTED); 1370Sstevel@tonic-gate else 1380Sstevel@tonic-gate break; 1390Sstevel@tonic-gate } 1400Sstevel@tonic-gate for (d = buf; d < c; d += SSH_BLOCKSIZE) { 1410Sstevel@tonic-gate if (!CMP(c, d)) { 1420Sstevel@tonic-gate if ((check_crc(c, buf, len, IV))) 1430Sstevel@tonic-gate return (DEATTACK_DETECTED); 1440Sstevel@tonic-gate else 1450Sstevel@tonic-gate break; 1460Sstevel@tonic-gate } 1470Sstevel@tonic-gate } 1480Sstevel@tonic-gate } 1490Sstevel@tonic-gate return (DEATTACK_OK); 1500Sstevel@tonic-gate } 1510Sstevel@tonic-gate memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE); 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate if (IV) 1540Sstevel@tonic-gate h[HASH(IV) & (n - 1)] = HASH_IV; 1550Sstevel@tonic-gate 156*3102Sjp161948 for (c = buf, same = j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) { 1570Sstevel@tonic-gate for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED; 1580Sstevel@tonic-gate i = (i + 1) & (n - 1)) { 1590Sstevel@tonic-gate if (h[i] == HASH_IV) { 1600Sstevel@tonic-gate if (!CMP(c, IV)) { 1610Sstevel@tonic-gate if (check_crc(c, buf, len, IV)) 1620Sstevel@tonic-gate return (DEATTACK_DETECTED); 1630Sstevel@tonic-gate else 1640Sstevel@tonic-gate break; 1650Sstevel@tonic-gate } 1660Sstevel@tonic-gate } else if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE)) { 167*3102Sjp161948 if (++same > MAX_IDENTICAL) 168*3102Sjp161948 return (DEATTACK_DOS_DETECTED); 1690Sstevel@tonic-gate if (check_crc(c, buf, len, IV)) 1700Sstevel@tonic-gate return (DEATTACK_DETECTED); 1710Sstevel@tonic-gate else 1720Sstevel@tonic-gate break; 1730Sstevel@tonic-gate } 1740Sstevel@tonic-gate } 1750Sstevel@tonic-gate h[i] = j; 1760Sstevel@tonic-gate } 1770Sstevel@tonic-gate return (DEATTACK_OK); 1780Sstevel@tonic-gate } 179