Scapy—SYN扫描
一、知识准备
TCP SYN
扫描以TCP
三次握手为基础,通过接受服务端返回的ACK
状态来判断端口的工作状态,此后SYN
扫描刻意建立不完整的TCP
连接,来逃避服务端的记录。具体SYN
连接流程如下
接着使用wireshark
进行抓包分析,可以看到流程与上面分析的一样,只是更具体了
二、构造数据包
SYN
扫描属于TCP
连接的一部分,于是需要涉及到TCP
部分,所以在使用的时候直接从网络层开始即可。
>>> p = IP(dst="10.87.51.19")/TCP(dport=22,flags="S")
>>> s = sr(p)
Begin emission:
..Finished sending 1 packets.
*
Received 3 packets, got 1 answers, remaining 0 packets
>>> s
(<Results: TCP:1 UDP:0 ICMP:0 Other:0>,
<Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)
在之前博客已经提到过,接收到的数据报一部分是没有得到回应的,一部分是有回应的,所以重新构造两个变量,分别接受两个部分
>>> p = IP(dst="10.87.51.19")/TCP(dport=22,flags="S")
>>> s,_ = sr(p)
Begin emission:
Finished sending 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
>>> s
<Results: TCP:1 UDP:0 ICMP:0 Other:0>
现在变量 s
只接受到了有回应的一部分数据包,因为只有一个包,所以直接查看第一个就行
>>> s[0]
(<IP frag=0 proto=tcp dst=10.87.51.19 |<TCP dport=ssh flags=S |>>,
<IP version=4 ihl=5 tos=0x0 len=44 id=0 flags=DF frag=0 ttl=64 proto=tcp chksum=0xbffa src=10.87.51.19 dst=10.87.51.17 |<TCP sport=ssh dport=ftp_data seq=4107690571
ack=1 dataofs=6 reserved=0 flags=SA window=14600 chksum=0x8cef urgptr=0 options=[('MSS', 1460)] |<Padding load='\x00\x00' |>>>)
可以看到,在这个数据包中还涉及到发送的部分跟接受的部分,这里只看接受到的部分
>>> s[0][1]
<IP version=4 ihl=5 tos=0x0 len=44 id=0 flags=DF frag=0 ttl=64 proto=tcp chksum=0xbffa src=10.87.51.19 dst=10.87.51.17 |<TCP sport=ssh dport=ftp_data seq=4107690571
ack=1 dataofs=6 reserved=0 flags=SA window=14600 chksum=0x8cef urgptr=0 options=[('MSS', 1460)] |<Padding load='\x00\x00' |>>>
上面已经提到了,连接成功的包是SYN
跟 ACK
,也就是 SA
,能够看到,在TCP
部分有相关内容,使用getlayer(TCP)
可以把TCP
部分拿出来,使用fields
可以看到这是一个字典形式的内容
>>> s[0][1].getlayer(TCP)
<TCP sport=ssh dport=ftp_data seq=4107690571 ack=1 dataofs=6 reserved=0 flags=SA window=14600 chksum=0x8cef urgptr=0 options=[('MSS', 1460)] |<Padding load='\x00\x00
' |>>
>>> s[0][1].getlayer(TCP).fields
{'sport': 22,
'dport': 20,
'seq': 4107690571,
'ack': 1,
'dataofs': 6,
'reserved': 0,
'flags': <Flag 18 (SA)>,
'window': 14600,
'chksum': 36079,
'urgptr': 0,
'options': [('MSS', 1460)]}
这里我们要判断是否连接成功也就是判断上面这个字典中的 flags
这个字段的内容是什么,从字典中取出内容只需要提取指定值对应的键
>>> s[0][1].getlayer(TCP).fields['flags']
<Flag 18 (SA)>
看到这里面有两个值,来判断一下,用哪个是对的
>>> if s[0][1].getlayer(TCP).fields['flags'] == "SA":
...: print("yes")
yes
>>> if s[0][1].getlayer(TCP).fields['flags'] == 18:
...: print("yes")
...:
yes
可以看到,这两个值都是可以用的,只不过是形式不同而已
三、代码编写
代码里面添加了装饰器用来查看代码所用时间,如果不喜欢的话可以删掉,在Linux
下面还可以使用 time -p
参数来查看代码运行时间
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# --author:valecalida--
# Edit time: 2020/4/23 12:20
from scapy.layers.inet import TCP, IP, ICMP, sr1
from threading import Thread
from random import randint
from time import time
import logging
import sys
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
def timeit(f):
def wrapper(*args, **kwargs):
start_time = time()
res = f(*args, **kwargs)
end_time = time()
print("%s函数运行时间为:%.8f" % (f.__name__, end_time - start_time))
return res
return wrapper
def random_id():
return randint(1, 65535)
def scan_single(host):
print("[..] Detecting whether the target is alive...")
packet = IP(dst=host, ttl=64, id=random_id())/ICMP(id=random_id(), seq=random_id())
response = sr1(packet, timeout=1, verbose=0)
if response:
return 2
@timeit
def syn_scan(target):
for port in range(1, 201):
response = sr1(IP(dst=target)/TCP(dport=port, flags="S"), timeout=1, verbose=False)
if response[0][1].getlayer(TCP).fields['flags'] == 18:
print("\t[+] Port: %s is opened!" % str(response[0][1].getlayer(TCP).fields['sport']))
def main():
if scan_single(sys.argv[1]) == 2:
print("[+] The target is alive!")
t = Thread(target=sys_scan, args=[sys.argv[1]])
t.start()
else:
print("[-] The target is unreachable!")
if __name__ == '__main__':
t1 = time()
main()
这里扫描200
个端口,看看速度怎么样,控制台输出如下:
root@kali:~/Desktop# python3 `SYN`_Scan.py 10.87.51.19
[..] Detecting whether the target is alive...
[+] The target is alive!
[+] Port: 22 is opened!
[+] Port: 80 is opened!
[+] Port: 111 is opened!
sys_scan函数运行时间为:2.89476037
可以看到程序扫描200
个端口就花费了约3秒
的时间,这是由于SYN
连接机制决定的,那么如果进行全端口扫描的话,这个数字可能要番几十番了,在次只作为学习的一点记录。