libnet使用举例(9)
作者:小四 (scz@nsfocus.com)
主页:http://www.nsfocus.com
日期:2000-08-15
今次以IGMP攻击为例继续介绍libnet库编程。IGMP补丁我没有用过,对于Pwin98来说,
IGMP实在没有什么用途,可以考虑袁哥的这个办法:
用ultraedit搜索6A 02 E8,修改成6A F2 E8。这里02对应IGMP协议,这样处理过后,
F2才对应IGMP,所以呢,一般的针对标准IGMP协议的攻击就全部失效了,当然你也无
法正常使用IGMP协议。完全可以不用F2,换用其他值。
可能需要复习一点IP协议、IP分片的基础知识:
1) tos应该只指定其中一个bit,同时指定几个是没有意义的。至于是不是允许的或
者说接收者如何处理这种报文依赖于具体实现,有可能出错。
2) IP分片和完整IP报文几乎拥有同样的IP头,id域对于每个分片都是一致的,这样
才能在重组的时候识别出来自同一IP报文的分片。flags占用了最高的3bit,按从
左到右表示从高到低,最左bit保留,应该置零,如果非零有什么后果不清楚;中
间bit置1表示不得对该IP报文分片,如果路由因为MTU缘故必须分片才能发送这种
不可分片IP报文的时候,就先废弃该IP报文然后用ICMP通知源主机废弃原因,如
果不是特殊需要不应该置1;最右bit置1表示该报文不是最后一个IP分片。
3) 完整单包IP报文的flags最右bit为零,同时fragment offset为零。第一个IP分片
的flags最右bit为1,同时fragment offset为零。最后一个IP分片的flags最右
bit为0,此时fragment offset必不为零。fragment offset与flags共用两个字节,
前者占用了低13bit。fragment offset给出的偏移是相对完整IP报文数据区而言
的,以8bytes为单位。
4) 重组发生在最终目的主机上,中间路由不对分片重组。重组发生在所有分片到达
之后。一般最终目的主机在收到一个IP分片(不一定是第一个分片)的时候启动一
个定时器,如果超时而分片仍未到齐,则废弃具有相同id的所有已到达分片,意
味着丢失一个分片就丢失了完整IP报文,此时IP层本身不会负责重传,需要上层
协议自己意识到需要重传了。
可以想象,故意发送部分IP分片而不是全部,导致目标主机总是等待分片消耗占
用系统资源。某些分片风暴攻击就是这种原理。
5) IP分片的total_len给出的是这个IP分片的总长度,而不是完整IP报文总长度。
6) 使用UDP很容易导致IP分片,而很难强迫TCP发送一个需要进行分片的报文。
早期操作系统实现对IP分片的边界判断不够完善,总是存在这样那样的问题,经过近
年各种DoS攻击的研究、分析、对抗,已经日趋完善,很不容易找到关于分片边界处
理上的漏洞了。为什么要复习IP分片,下面介绍的一种IGMP攻击涉及到IP分片,不复
习一下,怕有些朋友看晕菜掉。
另外一个不太相关的问题,raw_socket可以发送IP分片,但是永远接收不到IP分片,
内核在重组完成之前是不会给raw_socket一个IP分片的,记住这点很重要。可能有
些朋友看到利用raw_socket发送IP分片的源代码,而误以为IP分片对于收发
raw_socket都是可见的。
不打算重复IGMP的整个RFC,下面就IGMP协议的几个要点回顾一下:
1) IGMP版本目前为1,类型只有两种,1表示组播路由器发出的查询,2表示组内主机
发出的响应。校验和是针对8字节的IGMP报文的。未用域必须清零。
2) 两个有效IGMP报文例子
IGMP响应(2)
TTL = 1
所加入的组地址
目的IP = 组地址
源IP = 本机IP
IGMP查询(1)
TTL = 1
组地址 = 0
目的IP = 224.0.0.1
源IP = 组播路由器IP
3) 主机上某个进程在本机某个接口上加入某个组时,如果前面没有本机其他进程加
入该组,则发送一个IGMP响应。本机会维护相关信息,直到所有本机进程退出该
组。
进程离开某个组时并不发送IGMP响应。当本机所有进程退出所有组后,如果有组
播路由器发送查询报文,本机不做IGMP响应。
组播路由器定时发送查询,这个查询报文的组地址固定为0,目标IP为224.0.0.1。
4) 224.0.0.0 - 239.255.255.255的D类地址属于组播地址。但是224.0.0.0不能用于
任何组。224.0.0.1表示局域网内所有拥有组播能力的主机、路由器,每个网络接
口初始化后如果拥有组播能力,会自动加入该组,即使没有本机进程明确加入该
组,不会因为加入该组而发送IGMP响应。组播地址可以作为目标IP出现,但不能
作为源IP出现。
224.0.0.0 - 224.0.0.255之间的组播地址如果出现在目标IP上,无论TTL多大,
组播路由器也不会转发这种报文,这种报文只能出现在局域网内。显然这里包括
了224.0.0.1。
5) TTL为0的组播报文根本就无法出主机,为1表示组播报文只能在局域网内传送。如
果想被组播路由器转发出去,必须设置更大的TTL。
上述描述是W.Richard.Stevens对4.x BSD实现的描述,现在可能有变化。版本和类型
共用一个字节,版本占用高4bit,所以很多头文件里定义0x11、0x12这样的宏,至于
0x16、0x17,注释解释得比较清楚。
组内成员主机收到IGMP查询后,会在随机时间段内作出响应,因为组播导致组内其他
成员主机均收到响应,不仅仅是发出查询报文的主机,所以其余成员主机不再响应,
避免了响应风暴。非组内成员主机可以发送查询报文,但它一定收不到相应的IGMP响
应。不是只有组播路由器才可以发送IGMP查询,其他IGMP查询报文的数据有所区别,
比如目的IP不一定是224.0.0.1,可以是某个其他组播地址,组地址也不一定是0,可
以是某个确定的组地址。
libnet库中有如下函数原型:
int libnet_build_igmp ( u_char type, u_char code, u_long ip,
const u_char * payload, int payload_s,
u_char * buf );
该函数用于构造IGMP报文,type取值在/usr/include/libnet/libnet-headers.h中有
如下宏定义:
#define IGMP_MEMBERSHIP_QUERY 0x11 /* membership query */
#define IGMP_V1_MEMBERSHIP_REPORT 0x12 /* Ver. 1 membership report */
#define IGMP_V2_MEMBERSHIP_REPORT 0x16 /* Ver. 2 membership report */
#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */
这里0x17似乎意味着以后离开组需要发送IGMP报文通知大家,不清楚。0x16更让人迷
糊,既然是版本2的,就应该是0x26,不懂。反正我们不用它们,懒得深究。
形参code应该指定未用域的,那就只能是零了。形参ip指定D类组播地址,payload为
NULL,payload_s为零。形参buf需要指向一个已分配好的数据区,IGMP头从该指针开
始。
校验和计算套用libnet_do_checksum( packet, IPPROTO_IGMP, LIBNET_IGMP_H )函
数,当然,如果非正常IGMP报文,带了负载的话就需要调整参数值。
遗憾的是,此次攻击程序并没有真正利用上述函数,仅仅是IP头的部上层协议域指明
负载是IGMP报文。
下面将要介绍的这个IGMP攻击程序有很多地方异常:
1) IP头部上层协议指明IP数据区出现IGMP报文,但是IP头部的目标地址不是组播地
址而是单播地址(也是被攻击的目标地址),所以这样的报文能不能称做IGMP报文
值得推敲,是否作为IGMP报文被处理值得怀疑。
2) IGMP报文总共8字节,没有其他负载。但这个攻击程序有其他负载,并且负载很大,
刻意制造了IP分片。攻击程序在发送IP分片的时候采用倒序,先发送最后一个分
片,最后发送第一个分片。这里采用raw_socket人为制造的异常分片,不是让IP
协议栈自行分片,事实上在发送方始终没有出现过那个分片前的超大完整IP报文。
3) IGMP报文版本、类型、校验和、组地址全部为零,显然异常。
4) 如果以完整的一批IP分片为单位,原来的程序循环发送了两次。在测试过程感觉
还是依赖于发送速度和发送数量的,缺省使用两次,不是每次都攻击成功,这种
情况下重复攻击效果也不是很好。在程序中把循环次数调整成200,一次攻击就成
功,明显和攻击速度、报文数量有关。
源IP不要求等于目标IP,可以是任意伪造的源IP。目标IP地址并不是组播地址,
对于只看到IP层的路由器来说,就是普通单播IP报文,并不会意识到正在路由一
个IGMP报文(如果这个报文能被称做IGMP报文的话)。以前有朋友说不能在广域网
上进行IGMP攻击,而又有一些朋友却说远程攻击奏效。据我这次分析,远程攻击
有些时候失效,并不是因为常规路由不转发组播报文,前面解释过了,路由看到
的是单播IP报文,而是因为分片丢失。这个攻击程序每次循环制造11个分片,远
程攻击不能保证11个分片都能按时到达目标IP进而重组。这样看来,还是重组后
巨大的异常IGMP报文带来麻烦。
总有朋友问这类DoS攻击原理何在,也总有朋友回答就是发送什么什么样的报文,
感觉回答的只是表象,真正解释为什么蓝屏、为什么死机,需要用softice跟踪目
标主机对这种报文的处理过程。一个有效DoS报文本身说明不了问题,通过协议分
析软件抓取报文、通过阅读源代码分析报文都仅仅是搞清楚什么样的报文会导致
DoS,为什么这样的报文会导致DoS不是网络数据本身能给出答案的。前阵子那个
jot2.c,我只能知道那种类型的报文可能导致DoS,但为什么导致,不清楚,不分
析具体操作系统在这些协议处理上的实现,只看网络数据,能得到什么答案,隔
靴搔痒。不大喜欢别人用这种方式讨论这种问题,有夸夸其谈的嫌疑。也没有必
要过深了解这类DoS的真实原因,除非你有能力修改系统,象Linux那样能修理源
代码的另当别论。
这个攻击首先导致目标蓝屏,回车后IP栈基本废掉了,从远程无法ping通目标,
需要重启动恢复。但没有导致死机,还可以做其他非网络工作。如果存在
softice,情况不大一样,我对此并不熟悉,不多描述。
5) IP头部的id域始终没有改变,后一轮循环中的分片和前一轮循环中的分片如何区
分,我看就无法区分,对接收方分片重组带来什么样的影响?
个人觉得这个攻击完全可以远程进行,而且不要求源IP等于目标IP,机会很大。命令
行上指定伪造的源IP、攻击目标IP、IGMP报文数目(以一批分片为单位)。
--------------------------------------------------------------------------
void igmpSend ( u_long srcIp, u_long dstIp )
{
u_short ipDataLen;
u_short frag;
u_short bit;
bit = 0;
ipDataLen = 200; /* 200字节的负载,总共15000字节的负载 */
frag = 1850;
do
{
/* 构造IP头 */
libnet_build_ip( ipDataLen, /* IP数据区长度 */
IPTOS_LOWDELAY, /* IP tos */
19774, /* IP ID */
frag | bit, /* frag stuff */
255, /* TTL */
IPPROTO_IGMP, /* 上层协议 */
srcIp, /* big-endian序 */
dstIp, /* 目标IP */
NULL, /* 无选项 */
0, /* 选项长度零 */
packet ); /* 指向IP头 */
Libnet_write_ip( rawSocket, packet, LIBNET_IP_H + ipDataLen );
if ( frag == 0 )
{
break;
}
ipDataLen = IPDATALEN;
bit = 0x2000; /* 非最后分片 */
frag -= 185;
} while ( 1 ); /* 总共11个分片发送出去 */
return;
} /* end of igmpSend */
--------------------------------------------------------------------------
<待续>
作者:小四 (scz@nsfocus.com)
主页:http://www.nsfocus.com
日期:2000-08-15
今次以IGMP攻击为例继续介绍libnet库编程。IGMP补丁我没有用过,对于Pwin98来说,
IGMP实在没有什么用途,可以考虑袁哥的这个办法:
用ultraedit搜索6A 02 E8,修改成6A F2 E8。这里02对应IGMP协议,这样处理过后,
F2才对应IGMP,所以呢,一般的针对标准IGMP协议的攻击就全部失效了,当然你也无
法正常使用IGMP协议。完全可以不用F2,换用其他值。
可能需要复习一点IP协议、IP分片的基础知识:
1) tos应该只指定其中一个bit,同时指定几个是没有意义的。至于是不是允许的或
者说接收者如何处理这种报文依赖于具体实现,有可能出错。
2) IP分片和完整IP报文几乎拥有同样的IP头,id域对于每个分片都是一致的,这样
才能在重组的时候识别出来自同一IP报文的分片。flags占用了最高的3bit,按从
左到右表示从高到低,最左bit保留,应该置零,如果非零有什么后果不清楚;中
间bit置1表示不得对该IP报文分片,如果路由因为MTU缘故必须分片才能发送这种
不可分片IP报文的时候,就先废弃该IP报文然后用ICMP通知源主机废弃原因,如
果不是特殊需要不应该置1;最右bit置1表示该报文不是最后一个IP分片。
3) 完整单包IP报文的flags最右bit为零,同时fragment offset为零。第一个IP分片
的flags最右bit为1,同时fragment offset为零。最后一个IP分片的flags最右
bit为0,此时fragment offset必不为零。fragment offset与flags共用两个字节,
前者占用了低13bit。fragment offset给出的偏移是相对完整IP报文数据区而言
的,以8bytes为单位。
4) 重组发生在最终目的主机上,中间路由不对分片重组。重组发生在所有分片到达
之后。一般最终目的主机在收到一个IP分片(不一定是第一个分片)的时候启动一
个定时器,如果超时而分片仍未到齐,则废弃具有相同id的所有已到达分片,意
味着丢失一个分片就丢失了完整IP报文,此时IP层本身不会负责重传,需要上层
协议自己意识到需要重传了。
可以想象,故意发送部分IP分片而不是全部,导致目标主机总是等待分片消耗占
用系统资源。某些分片风暴攻击就是这种原理。
5) IP分片的total_len给出的是这个IP分片的总长度,而不是完整IP报文总长度。
6) 使用UDP很容易导致IP分片,而很难强迫TCP发送一个需要进行分片的报文。
早期操作系统实现对IP分片的边界判断不够完善,总是存在这样那样的问题,经过近
年各种DoS攻击的研究、分析、对抗,已经日趋完善,很不容易找到关于分片边界处
理上的漏洞了。为什么要复习IP分片,下面介绍的一种IGMP攻击涉及到IP分片,不复
习一下,怕有些朋友看晕菜掉。
另外一个不太相关的问题,raw_socket可以发送IP分片,但是永远接收不到IP分片,
内核在重组完成之前是不会给raw_socket一个IP分片的,记住这点很重要。可能有
些朋友看到利用raw_socket发送IP分片的源代码,而误以为IP分片对于收发
raw_socket都是可见的。
不打算重复IGMP的整个RFC,下面就IGMP协议的几个要点回顾一下:
1) IGMP版本目前为1,类型只有两种,1表示组播路由器发出的查询,2表示组内主机
发出的响应。校验和是针对8字节的IGMP报文的。未用域必须清零。
2) 两个有效IGMP报文例子
IGMP响应(2)
TTL = 1
所加入的组地址
目的IP = 组地址
源IP = 本机IP
IGMP查询(1)
TTL = 1
组地址 = 0
目的IP = 224.0.0.1
源IP = 组播路由器IP
3) 主机上某个进程在本机某个接口上加入某个组时,如果前面没有本机其他进程加
入该组,则发送一个IGMP响应。本机会维护相关信息,直到所有本机进程退出该
组。
进程离开某个组时并不发送IGMP响应。当本机所有进程退出所有组后,如果有组
播路由器发送查询报文,本机不做IGMP响应。
组播路由器定时发送查询,这个查询报文的组地址固定为0,目标IP为224.0.0.1。
4) 224.0.0.0 - 239.255.255.255的D类地址属于组播地址。但是224.0.0.0不能用于
任何组。224.0.0.1表示局域网内所有拥有组播能力的主机、路由器,每个网络接
口初始化后如果拥有组播能力,会自动加入该组,即使没有本机进程明确加入该
组,不会因为加入该组而发送IGMP响应。组播地址可以作为目标IP出现,但不能
作为源IP出现。
224.0.0.0 - 224.0.0.255之间的组播地址如果出现在目标IP上,无论TTL多大,
组播路由器也不会转发这种报文,这种报文只能出现在局域网内。显然这里包括
了224.0.0.1。
5) TTL为0的组播报文根本就无法出主机,为1表示组播报文只能在局域网内传送。如
果想被组播路由器转发出去,必须设置更大的TTL。
上述描述是W.Richard.Stevens对4.x BSD实现的描述,现在可能有变化。版本和类型
共用一个字节,版本占用高4bit,所以很多头文件里定义0x11、0x12这样的宏,至于
0x16、0x17,注释解释得比较清楚。
组内成员主机收到IGMP查询后,会在随机时间段内作出响应,因为组播导致组内其他
成员主机均收到响应,不仅仅是发出查询报文的主机,所以其余成员主机不再响应,
避免了响应风暴。非组内成员主机可以发送查询报文,但它一定收不到相应的IGMP响
应。不是只有组播路由器才可以发送IGMP查询,其他IGMP查询报文的数据有所区别,
比如目的IP不一定是224.0.0.1,可以是某个其他组播地址,组地址也不一定是0,可
以是某个确定的组地址。
libnet库中有如下函数原型:
int libnet_build_igmp ( u_char type, u_char code, u_long ip,
const u_char * payload, int payload_s,
u_char * buf );
该函数用于构造IGMP报文,type取值在/usr/include/libnet/libnet-headers.h中有
如下宏定义:
#define IGMP_MEMBERSHIP_QUERY 0x11 /* membership query */
#define IGMP_V1_MEMBERSHIP_REPORT 0x12 /* Ver. 1 membership report */
#define IGMP_V2_MEMBERSHIP_REPORT 0x16 /* Ver. 2 membership report */
#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */
这里0x17似乎意味着以后离开组需要发送IGMP报文通知大家,不清楚。0x16更让人迷
糊,既然是版本2的,就应该是0x26,不懂。反正我们不用它们,懒得深究。
形参code应该指定未用域的,那就只能是零了。形参ip指定D类组播地址,payload为
NULL,payload_s为零。形参buf需要指向一个已分配好的数据区,IGMP头从该指针开
始。
校验和计算套用libnet_do_checksum( packet, IPPROTO_IGMP, LIBNET_IGMP_H )函
数,当然,如果非正常IGMP报文,带了负载的话就需要调整参数值。
遗憾的是,此次攻击程序并没有真正利用上述函数,仅仅是IP头的部上层协议域指明
负载是IGMP报文。
下面将要介绍的这个IGMP攻击程序有很多地方异常:
1) IP头部上层协议指明IP数据区出现IGMP报文,但是IP头部的目标地址不是组播地
址而是单播地址(也是被攻击的目标地址),所以这样的报文能不能称做IGMP报文
值得推敲,是否作为IGMP报文被处理值得怀疑。
2) IGMP报文总共8字节,没有其他负载。但这个攻击程序有其他负载,并且负载很大,
刻意制造了IP分片。攻击程序在发送IP分片的时候采用倒序,先发送最后一个分
片,最后发送第一个分片。这里采用raw_socket人为制造的异常分片,不是让IP
协议栈自行分片,事实上在发送方始终没有出现过那个分片前的超大完整IP报文。
3) IGMP报文版本、类型、校验和、组地址全部为零,显然异常。
4) 如果以完整的一批IP分片为单位,原来的程序循环发送了两次。在测试过程感觉
还是依赖于发送速度和发送数量的,缺省使用两次,不是每次都攻击成功,这种
情况下重复攻击效果也不是很好。在程序中把循环次数调整成200,一次攻击就成
功,明显和攻击速度、报文数量有关。
源IP不要求等于目标IP,可以是任意伪造的源IP。目标IP地址并不是组播地址,
对于只看到IP层的路由器来说,就是普通单播IP报文,并不会意识到正在路由一
个IGMP报文(如果这个报文能被称做IGMP报文的话)。以前有朋友说不能在广域网
上进行IGMP攻击,而又有一些朋友却说远程攻击奏效。据我这次分析,远程攻击
有些时候失效,并不是因为常规路由不转发组播报文,前面解释过了,路由看到
的是单播IP报文,而是因为分片丢失。这个攻击程序每次循环制造11个分片,远
程攻击不能保证11个分片都能按时到达目标IP进而重组。这样看来,还是重组后
巨大的异常IGMP报文带来麻烦。
总有朋友问这类DoS攻击原理何在,也总有朋友回答就是发送什么什么样的报文,
感觉回答的只是表象,真正解释为什么蓝屏、为什么死机,需要用softice跟踪目
标主机对这种报文的处理过程。一个有效DoS报文本身说明不了问题,通过协议分
析软件抓取报文、通过阅读源代码分析报文都仅仅是搞清楚什么样的报文会导致
DoS,为什么这样的报文会导致DoS不是网络数据本身能给出答案的。前阵子那个
jot2.c,我只能知道那种类型的报文可能导致DoS,但为什么导致,不清楚,不分
析具体操作系统在这些协议处理上的实现,只看网络数据,能得到什么答案,隔
靴搔痒。不大喜欢别人用这种方式讨论这种问题,有夸夸其谈的嫌疑。也没有必
要过深了解这类DoS的真实原因,除非你有能力修改系统,象Linux那样能修理源
代码的另当别论。
这个攻击首先导致目标蓝屏,回车后IP栈基本废掉了,从远程无法ping通目标,
需要重启动恢复。但没有导致死机,还可以做其他非网络工作。如果存在
softice,情况不大一样,我对此并不熟悉,不多描述。
5) IP头部的id域始终没有改变,后一轮循环中的分片和前一轮循环中的分片如何区
分,我看就无法区分,对接收方分片重组带来什么样的影响?
个人觉得这个攻击完全可以远程进行,而且不要求源IP等于目标IP,机会很大。命令
行上指定伪造的源IP、攻击目标IP、IGMP报文数目(以一批分片为单位)。
--------------------------------------------------------------------------
void igmpSend ( u_long srcIp, u_long dstIp )
{
u_short ipDataLen;
u_short frag;
u_short bit;
bit = 0;
ipDataLen = 200; /* 200字节的负载,总共15000字节的负载 */
frag = 1850;
do
{
/* 构造IP头 */
libnet_build_ip( ipDataLen, /* IP数据区长度 */
IPTOS_LOWDELAY, /* IP tos */
19774, /* IP ID */
frag | bit, /* frag stuff */
255, /* TTL */
IPPROTO_IGMP, /* 上层协议 */
srcIp, /* big-endian序 */
dstIp, /* 目标IP */
NULL, /* 无选项 */
0, /* 选项长度零 */
packet ); /* 指向IP头 */
Libnet_write_ip( rawSocket, packet, LIBNET_IP_H + ipDataLen );
if ( frag == 0 )
{
break;
}
ipDataLen = IPDATALEN;
bit = 0x2000; /* 非最后分片 */
frag -= 185;
} while ( 1 ); /* 总共11个分片发送出去 */
return;
} /* end of igmpSend */
--------------------------------------------------------------------------
<待续>