Python中的Scapy初探之六--找出非法的DHCP Server
記得以前在公司時,經常碰到未經授權的DHCP伺服器接入網路,可能是做實驗,沒有把虛擬機器的網路與真實網路遮蔽,這樣就很難搞。而且還遇到一特邪門的事情,VOIP的DHCPServer躥入了使用者網路,導致使用者拿到的是IP電話的IP,通過DHCP獲取IP,就有這個麻煩,誰快就拿誰的。那麼,如何通過Scapy找到DHCP李鬼呢?可以自己寫,也可以使用內建的函式dhcp_request()
這個工具非常簡單,就是建立一個DHCP請求包,在網路中傳送廣播,如果接受到DHCPACK,就會返回這個ACK包,這樣就可以分析到底是哪個DHCP傳送的IP地址
dhcp_request()需要兩個引數,iface表示使用哪個網絡卡介面,如果沒有賦值,那麼使用conf.iface所指定的網絡卡,這種情況對多個網絡卡好用,還有一個引數是conf.checkIPaddr,如果為1,那麼會有提示,可能會檢查IP地址,還沒仔細看這個引數是幹嘛用的。設定好之後,就可以直接使用了
conf.iface=0
dhcp_request(iface='wlan0')
如果留空,那麼就是conf.iface指定的網口,一般留空就好了
>>>dhcp_request()
Begin emission:
.Finished to send 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
<Ether dst=00:0c:29:3e:0f:dasrc=5c:63:bf:76:90:20 type=0x800|<IP version=4L ihl=5L tos=0x0len=576 id=57084 flags= frag=0L ttl=64 proto=udp chksum=0x15f7src=192.168.1.1 dst=192.168.1.104 options=[]|<UDP sport=bootps dport=bootpclen=556 chksum=0x5266 |<BOOTP op=BOOTREPLY htype=1 hlen=6 hops=0 xid=0 secs=0 flags=ciaddr=0.0.0.0 yiaddr=192.168.1.104 siaddr=0.0.0.0 giaddr=0.0.0.0chaddr='\x00\x0c)>\x0f\xda\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'sname='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'file='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'options='c\x82Sc' |<DHCP options=[message-type=offer server_id=192.168.1.1 lease_time=7200subnet_mask=255.255.255.0 router=192.168.1.1name_server=192.168.1.1 end pad ... pad]|>>>>>
>>> a=_
>>>a.summary()
'Ether / IP / UDP 192.168.1.1:bootps >192.168.1.104:bootpc / BOOTP / DHCP'
>>>a.sprintf('%Ether.src%:%IP.src%')
'5c:63:bf:76:90:20:192.168.1.1'
這樣就可以看出DHCP伺服器的IP地址與MAC地址了,可以看看DHCP應答的包結構
>>>a[BOOTP].show()
###[ BOOTP ]###
op= BOOTREPLY
htype= 1
hlen= 6
hops= 0
xid= 0
secs= 0
flags=
ciaddr= 0.0.0.0
yiaddr= 192.168.1.104
siaddr= 0.0.0.0
giaddr= 0.0.0.0
chaddr='\x00\x0c)>\x0f\xda\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
sname='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
file='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
options= 'c\x82Sc'
###[ DHCP options ]###
options= [message-type=offer server_id=192.168.1.1 lease_time=7200subnet_mask=255.255.255.0 router=192.168.1.1name_server=192.168.1.1 end pad ...pad]
這個工具有一定的侷限性,就是太模擬客戶端了,如果有一個最快的DHCP返回了DHCPRequest,那麼就顯示這個最快的資料包
如何能找到所有的DHCP伺服器呢?這個需要新增srp()的引數multi=True
等到接收到所有的DHCP應答,才返回接收到的結果
這個就不能用方便的dhcp_request()了
改寫一下指令碼
>>>conf.checkIPaddr = False
>>> fam,hw =get_if_raw_hwaddr(conf.iface)
>>> dhcp_discover =Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67)/BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"),"end"])
>>> ans, unans =srp(dhcp_discover,multi=True) # Press CTRL-C after several seconds
Begin emission:
Finished to send 1 packets.
.*...*..
Received 8 packets, got 2 answers, remaining 0 packets
>>>ans.summary()
Ether / IP / UDP 0.0.0.0:bootpc >255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP/ UDP 192.168.1.1:bootps > 255.255.255.255:bootpc /BOOTP / DHCP
Ether / IP / UDP 0.0.0.0:bootpc >255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP/ UDP 192.168.1.11:bootps > 255.255.255.255:bootpc /BOOTP / DHCP
>>> for p in ans:print p[1][Ether].src, p[1][IP].src
...
00:de:ad:be:ef:00 192.168.1.1
00:11:11:22:22:33 192.168.1.11
第二步參考
def dhcp_request(iface=None,**kargs):
ifconf.checkIPaddr != 0:
warning("conf.checkIPaddr is not 0, I may not be able to match theanswer")
if iface isNone:
iface = conf.iface
fam,hw =get_if_raw_hwaddr(iface)
returnsrp1(Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67)
/BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"),"end"]),iface=iface,**kargs)