通過前面幾篇關(guān)于有效ip發(fā)現(xiàn)的文章介紹后,接下來就是對于這些活著的主機(jī)進(jìn)行端口掃描的介紹了。
端口對應(yīng)了網(wǎng)絡(luò)服務(wù)及應(yīng)用端程序,服務(wù)的程序的漏洞通過端口攻入;通過開放的端口,可以進(jìn)行更具體、更高效的攻擊。
UDP端口掃描
一般都會假設(shè)ICMP Port Unreachable響應(yīng)代表端口關(guān)閉,不過當(dāng)目標(biāo)系統(tǒng)設(shè)置了不響應(yīng)ICMP Port Unreachable時,就會存在誤判的情況。
當(dāng)然還可以通過完整的UDP應(yīng)用層請求來進(jìn)行端口掃描,雖然能提升準(zhǔn)確性,但是會消耗大量時間。
UDP端口掃描:scapy
最好去了解每一種基于UDP的應(yīng)用層包結(jié)構(gòu),對于使用udp來進(jìn)行端口掃描的工作會很有幫助。
直接上代碼吧:
#!/usr/bin/Python3
import logging
logging.getLogger('scapy.runtime').setLevel(logging.ERROR)
import time
import sys
from scapy.all import *
if len(sys.argv) != 4:
print('Usage ./udp_port_scan.py [Target-IP] [First Port] [Last Port]')
print('Example ./udp_port_scan.py 172.18.14.1 1 100')
print('Example will UDP port scan ports 1 through 100 on 172.18.14.1')
sys.exit()
ip = str(sys.argv[1])
start = int(sys.argv[2])
end = int(sys.argv[3])
for port in range(start, end):
a = sr1(IP(dst=ip)/UDP(dport=port), timeout=5, verbose=0)
time.sleep(1)
if a == None:
print(port)
else:
pass
執(zhí)行結(jié)果就是這樣:
執(zhí)行結(jié)果
可以看到結(jié)果顯示沒有一個端口是開放的,這個結(jié)果可能有問題,將端口范圍加大一些再試試:
./udp_port_scan.py 192.168.31.168 1 150
掃出一個開放的端口,可以參考一下,但不要完全相信。
UDP端口掃描:nmap
使用nmap進(jìn)行udp的端口掃描,需要傳的參數(shù)是-sU,不指定端口的話以前默認(rèn)是掃描1000個常用的端口,如果通過-p指定端口的話,就只會對指定端口進(jìn)行掃描:
nmap -sU 192.168.31.168
當(dāng)然通過-p也是可以傳遞端口范圍的,比如這樣:
nmap -sU 192.168.31.168 -p1-100
也能通過-iL傳遞指定文檔里的ip進(jìn)行端口掃描:
nmap -iL ip.txt -sU -p1-100
TCP端口掃描
使用tcp端口掃描,主要是基于連接協(xié)議,基于三次握手的。可以實(shí)現(xiàn)隱蔽掃描、僵尸掃描、全連接掃描,所有的tcp掃描方式,都是基于三次握手的變化來判斷目標(biāo)端口狀態(tài)。
隱蔽掃描是只發(fā)syn包,不建立完整連接,應(yīng)用日志不記錄掃描行為,所以相對比較隱蔽,在網(wǎng)絡(luò)層還是會留下記錄。
僵尸掃描比前一個隱蔽掃描更加隱蔽,實(shí)施條件比較苛刻(現(xiàn)在偽造來源地址比較困難),可偽造來源地址,選擇僵尸機(jī)(閑置系統(tǒng)、系統(tǒng)使用遞增的IPID,IPID不可以是0或者隨機(jī)產(chǎn)生的)
僵尸掃描目標(biāo)端口打開
1.掃描者向僵尸機(jī)發(fā)送一個SYN/ACK,僵尸機(jī)回復(fù)一個RST,并取得僵尸機(jī)的IPID,假設(shè)為x;
2.掃描者偽造來源為僵尸機(jī),向目標(biāo)主機(jī)發(fā)送一個SYN,然后目標(biāo)主機(jī)給僵尸機(jī)返回一個SYN/ACK的RST,僵尸機(jī)收到后給目標(biāo)主機(jī)返回一個RST,并且IPID加1;
3.然后掃描者再次向僵尸機(jī)發(fā)送一個SYN/ACK,如果收到僵尸機(jī)返回的RST中IPID比第1步中收到IPID大2,就是目標(biāo)機(jī)器端口處于打開狀態(tài),如果大1則處于關(guān)閉狀態(tài)。
僵尸掃描目標(biāo)端口關(guān)閉
隱蔽的端口掃描:scapy
隱蔽端口掃描的定義已經(jīng)在前面說過了,這里直接用scapy來演示具體操作:
a = sr1(IP(dst='36.152.44.96')/TCP(flags='S',dport=80), timeout=1, verbose=0)
從結(jié)果來看,目標(biāo)機(jī)器的80端口是開放的。
然后通過抓包工具看一下:
wireshark抓包
可以看到我的機(jī)器又回來一個rst包給目標(biāo)主機(jī),這個不是scapy發(fā)的,是當(dāng)前機(jī)器的系統(tǒng)內(nèi)核自動發(fā)送的,后面會講到如何處理這種rst包,避免對后續(xù)安全工作的干擾。
如果遇到端口沒有開放會有兩種可能,第一種就是啥都不回復(fù),另一種就是回復(fù)一個flags為RA的數(shù)據(jù)包。
用前面的內(nèi)容寫一個python腳本:
#!/usr/bin/python3
import logging
logging.getLogger('scapy.runtime').setLevel(logging.ERROR)
import sys
from scapy.all import *
if len(sys.argv) != 4:
print('Usage ./syn_port_scan.py [Target-IP] [First Port] [Last Port]')
print('Example ./syn_port_scan.py 172.18.14.1 1 100')
print('Example will TCP SYN port scan ports 1 through 100 on 172.18.14.1')
sys.exit()
ip = str(sys.argv[1])
start = int(sys.argv[2])
end = int(sys.argv[3])
for port in range(start, end):
a = sr1(IP(dst=ip)/TCP(dport=port, flags='S'), timeout=1, verbose=0)
if a == None:
pass
else:
if int(a[TCP].flags) == 18:
print(port)
else:
pass
腳本中最后用18作為判斷依據(jù)的原因可以看抓包中的數(shù)據(jù):
SYN,ACK的抓包數(shù)據(jù)
可以看出來SYN,ACK轉(zhuǎn)換成10進(jìn)制后就是18。
執(zhí)行方式還是一樣,先賦予權(quán)限,然后傳入正確的參數(shù):
./syn_port_scan.py 36.152.44.96 50 200
隱蔽的端口掃描:nmap
scapy畢竟還要寫好幾行代碼,實(shí)際工作中都是怎么簡單高效怎么來。所以我們可以選擇nmap來做隱蔽的端口掃描。
nmap使用syn做端口掃描只需要使用參數(shù)-sS:
nmap -sS 36.152.44.96 -p 1-100
有時候nmap的掃描結(jié)果里會有一些fail、close、filtered,如果不想要這些結(jié)果,只想知道open的端口的話,可以加個參數(shù)--open。
nmap也可以通過傳遞指定端口集合來掃描指定的端口:
nmap 36.152.44.238 -sS -p 50,80,88,90
隱蔽的端口掃描:hping3
hping3可以通過參數(shù)-S來使用syn進(jìn)行掃描:
hping3 36.152.44.238 --scan 1-100 -S
hping3有一個偽裝來源地址的參數(shù)--spoof:
hping3 -c 100 -S --spoof 192.168.10.13 -p ++1 36.152.44.238
其中-c表示發(fā)多少數(shù)量的包,-p ++1表示端口號每次加一,但是由于偽裝了源地址,目標(biāo)機(jī)器就算端口開放,發(fā)出來的synack數(shù)據(jù)包,也不會是我的主機(jī),肯定是僵尸機(jī)。
全連接的端口掃描:scapy
全連接掃描只有在特殊情況下使用,很容易被防火墻發(fā)現(xiàn)。
scapy做全連接的端口掃描其實(shí)是有點(diǎn)麻煩的。因?yàn)閟yn掃描不需要raw packets,系統(tǒng)內(nèi)核認(rèn)為目標(biāo)機(jī)器返回的syn/ack是非法包,直接發(fā)送rst終斷連接。
在不解決系統(tǒng)內(nèi)核自動發(fā)送RST時,用下面的腳本嘗試一下全連接的端口掃描:
#!/usr/bin/python3
import logging
logging.getLogger('scapy.runtime').setLevel(logging.ERROR)
import sys
from scapy.all import *
SYN = IP(dst='36.152.44.238')/TCP(dport=80, flags='S')
print('--- SENT ---')
SYN.display()
print('nn ---RECEIVED---')
response = sr1(SYN, timeout=1, verbose=0)
response.display()
if int(response[TCP].flags) == 18:
print('nn --- SENT')
A = IP(dst='36.152.44.238')/TCP(dport=80, flags='A', ack=(response[TCP].seq + 1))
A.display()
print('nn ---RECEIVED---')
response2 = sr1(A, timeout=1, verbose=0)
response2.display()
else:
print('SYN-ACK not returned')
抓包的結(jié)果:
未關(guān)閉系統(tǒng)內(nèi)核發(fā)送RST時的執(zhí)行結(jié)果
從抓包結(jié)果里看出這臺目標(biāo)服務(wù)器雖然沒有回復(fù)RST,但是也沒有真正建立連接。不過有些服務(wù)器會將收到RST包后過來的ACK包拒絕掉,直接返回一個RST過來。
可以通過iptables的命令將去往指定目標(biāo)ip的數(shù)據(jù)包過濾掉:
iptables -A OUTPUT -p tcp --tcp-flags RST RST -d 36.152.44.238 -j DROP
執(zhí)行上面這個命令后,在執(zhí)行腳本,就是這樣的結(jié)果:
內(nèi)核沒有在發(fā)送RST數(shù)據(jù)包
雖然系統(tǒng)內(nèi)核的確是沒有發(fā)送RST數(shù)據(jù)包了,但是從執(zhí)行結(jié)果看也是有問題的。可能是目標(biāo)機(jī)器的防火墻功能比較強(qiáng)大,也可能是腳本的邏輯存在問題,如果有同學(xué)對這個感興趣,可以私信我一起研究一下,這里就先這樣吧。
全連接的端口掃描:nmap
通過傳遞參數(shù)-sT來使用nmap的全連接端口掃描:
nmap 36.152.44.238 -sT -p 70-90
在抓包工具中可以看到:
80端口建立起了連接
全連接的端口掃描:nc
nc也是可以做掃描工具的,只需要使用參數(shù)-z:
nc -nv -w 1 -z 36.152.44.238 70-90
僵尸掃描
僵尸掃描的定義已經(jīng)在前面說過了,這里直接來介紹具體實(shí)現(xiàn),分別用scapy和nmap。
需要先假設(shè)有了一臺可以使用的僵尸機(jī)。
僵尸掃描:scapy
直接上腳本吧,應(yīng)該都能看懂,如果有疑問,可以私信咨詢我。腳本里主要是提供了兩個方法,一個是校驗(yàn)不清楚僵尸機(jī)是否符合使用條件時調(diào)用,也就是最開始輸入1的時候,另一個就是直接指定僵尸機(jī)和目標(biāo)機(jī)器,直接進(jìn)行掃描。
#!/usr/bin/python3
import logging
logging.getLogger('scapy.runtime').setLevel(logging.ERROR)
from scapy.all import *
def ipid(zombie):
reply1 = sr1(IP(dst=zombie)/TCP(flags='SA'), timeout=2, verbose=0)
send(IP(dst=zombie)/TCP(flags='SA'), verbose=0)
reply2 = sr1(IP(dst=zombie)/TCP(flags='SA'), timeout=2, verbose=0)
if reply2[IP].id == (reply1[IP].id+2):
print('IPID sequence is incremental and target Appears to be idle. ZOMBIE LOCATED')
response = input('Do you want to use this zombie to perform a scan?(Y or N): ')
if response == 'Y':
target = input('Enter the IP address of the target system: ')
zombie_scan(target, zombie)
else:
print('Either the IPID sequence is not incremental or the target is not idle. NOT A GOOD ZOMBIE')
def zombie_scan(target, zombie):
print('n Scanning target ' + target + ' with zombie ' + zombie)
print('n ------ Open Ports on Target ------ n ')
for port in range(1, 10000):
try:
start_val = sr1(IP(dst=zombie)/TCP(flags='SA', dport=port), timeout=2, verbose=0)
send(IP(src=zombie, dst=target)/TCP(flags='S', dport=port), verbose=0)
end_val = sr1(IP(dst=zombie)/TCP(flags='SA'), timeout=2, verbose=0)
if end_val[IP].id == (start_val[IP].id + 2):
print(port)
except:
pass
print('------Zombie Scan Suite------n')
print('1 - Identify Zombie Host n')
print('2 - Perform Zombie Scan n')
ans = input('Select an Option(1 or 2): ')
if ans == '1':
zombie = input('Enter IP address to test IPID sequence: ')
ipid(zombie)
else:
if ans == '2':
zombie = input('Enter IP address for zombie system: ')
target = input('Enter IP address for scan target: ')
zombie_scan(target, zombie)
我在網(wǎng)上找了一些機(jī)器,一時半會沒找到合適的僵尸機(jī),這里就不演示效果了。
僵尸掃描:nmap
nmap提供了專門用來檢測指定機(jī)器是否是否作為僵尸機(jī):
nmap -p端口 僵尸主機(jī)IP地址 --script=ipidseq.nse
如果能正常使用的話,在執(zhí)行結(jié)果里會返回ipdi是否是遞增的。
然后在有了合適的僵尸機(jī)以后,可以通過-sI參數(shù)來指定使用的僵尸機(jī):
nmap 目標(biāo)主機(jī)IP -sI 僵尸主機(jī)IP -Pn -p 端口號