20199105 实验:网络嗅探与协议分析

作业要求:
(1)根据教材参考代码,编写一个简单网络抓包工具。要求核心代码和运行结果截图1-2张。(3分)代码push到码云(1分)

(2)找一个网站或者搭建一个本地网站,登录网站,并嗅探,分析出账号和密码,结果截图1-2张。(3分)可以用邮箱、各类博客、云班课,只能分析自己的账号,严禁做各类攻击,否则后果自负。

(3)加分项2分:抓取手机App的登录过程数据包,分析账号和密码。可以用邮箱、各类博客、云班课,只能分析自己的账号,严禁做各类攻击,否则后果自负。

(1)、编写一个简单网络抓包工具

1.1 编译环境与工具介绍

语言: python

编译环境: pycharm 2019.3.4

抓包库: scapy是python中一个可用于网络嗅探的非常强大的第三方库,可以用它来做 packet 嗅探和伪造 packet,还具有发送、捕获、匹配请求和响应这些 packet 以及更多的功能。scapy已经在内部实现了大量的网络协议,如DNS、ARP、IP、TCP、UDP等等,可以用它来编写非常灵活实用的工具

解包库: dpkt快速简单的包创建/解析工具

1.2 实验过程

抓包

  • 使用scapy库里的sniff函数进行网络嗅探
    参数说明
    filter指定过滤语句,过滤规则使用(BPF)
    prn指定回调函数,每当一个符合filter的报文被探测到时,就会执行回调函数
    count指定最多嗅探多少个报文
    iface用来指定要在哪个网络接口上进行抓包(通常不指定即所有网络接口)
    timeout在给定的时间后停止嗅探,默认为None
    代码如下:
from scapy.all import *

#sniff回调函数
def packet_callback(packet):
    print(packet.show())

pkts = sniff(filter="ip.src == 192.168.1.104 ", prn=lambda x:x.summary(), count=20)

#将数据存储在demo.pcap文件中
wrpcap("demo.pcap", pkts)
  • pkts是一个PacketList对象,pkts.res是一个由packet组成的list,在后面解包对数据包组进行遍历时会用到

  • 回调函数设置为lambda x:x.summary()时的输出,比较简洁

  • 回调函数设置为packet_callback时的输出,信息较全面

  • 关于sniff()函数,我简单的学习了一下接口源码: /python/site-packages/scapy/sendrecv.py
    比如说下面这个函数,其实和外部的sniff函数很相似,用户接收数据包,传一些设置的参数。但是时间有限,忙着实现了,还没达到清晰的把sniff原理梳理的地步

  • 在学习源码的过程中,我发现scapy的发包功能也很方便,源码中的函数定义
#发送网络层包
def send(x, inter=0, loop=0, count=None, verbose=None, realtime=None, return_packets=False, socket=None,*args,**kargs)

发送数据链路层包
def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, realtime=None,return_packets=False,socket=None,*args,**kargs)

def sendpfast(x, pps=None, mbps=None, realtime=None, loop=0, file_cache=False, iface=None,*args,**kargs)
  • 发包的包格式: 层的协议(参数)/上一层的协议(参数) /要发送的数据,尝试了一个简单的发包
from dpkt.ip import IP
from dpkt.tcp import TCP
from dpkt.udp import UDP
from scapy.all import *
from scapy.layers.l2 import Ether

send(IP(src='192.168.1.104',dst='192.168.1.101')/TCP(sport=80))

pkt=IP(dst="192.168.1.101")/UDP(dport=53)

send(pkt,inter=1,count=3)  #每隔一秒发包,发3次

sendp(Ether()/IP(dst="192.168.1.101")/UDP(dport=53))

...有点扯远了,回归正题,继续实验...


解包

  • 使用dpkt库进行数据包的解析,解包的过程其实就是基于OSI五层网络模型(物理层、数据链路层、网络层、传输层、应用层),按层逐步解析数据
    以太网规定,一组电信号构成一个数据包,叫做"帧"(Frame),网络嗅探到的就是在数据链路层传输的信息,而每一帧分成两个部分:标头(Head)和数据(Data)

  • 具体到dpkt库中,我们将数据链路层的数据存储在eth对象中,eth的data部分对应ip包(网络层),ip包的data部分对应tcp报文(传输层,也可以是udp等协议),tcp报文的data部分是应用层数据,就是这样一层层把head(标头)拿掉的过程

  • 在主函数中,打开pcap文件,调用自己写的解析函数Analysis_Pcap()
    代码如下:
def main():

    #'rb'按照二进制位进行读取
    f =open('demo.pcap','rb')

    dpcap = dpkt.pcap.Reader(f)

    Analysis_Pcap(dpcap)

    f.close()

Analysis_Pcap()函数编写介绍:

  • 传递数据包给dpkt的Ethernet类,解析和解码到eth对象。因为dpkt的Ethernet类同样包括一些额外功能去解析已知的高层次的协议,其实这一步IP层和TCP层的信息也被解码了
#迭代pcap对象
for timestamp, buf in pcap:

    print('Arrival Time: ', str(datetime.datetime.utcfromtimestamp(timestamp)))  # 包的抓取时间

    eth = dpkt.ethernet.Ethernet(buf)  # 解析以太网帧
  1. 首先,获取源IP地址与目标IP地址,使用自己写的mac_addr()函数将其转换为MAC地址形式并输出
#将MAC地址转换为可打印的字符串
def mac_addr(address):
    return ':'.join('%02x' % compat_ord(b) for b in address)

# 获得MAC地址并输出
            src_mac = mac_addr(eth.src)
            dst_mac = mac_addr(eth.dst)
            print('MAC: src( %s )   dst( %s )   Type( %s )' % (src_mac, dst_mac, hex(eth.type)))
  1. 解析网络层数据包(即IP数据包),使用socket.inet_ntoa()函数把4字节IP地址转化成点分十进制的可读形式并输出,提取MD\DF\OFFSET三个标志位信息并输出
  • 在解析IP数据包时,我回顾了一下IP包的格式,选择了一些重要的信息进行输出(当年计算机网络课的知识突然涌现)

#解析ip包
ip = eth.data

# 把4字节IP地址转化成点分十进制的可读形式
src = socket.inet_ntoa(ip.src)
dst = socket.inet_ntoa(ip.dst)

# 提取标志信息
do_not_fragment = bool(ip.off & dpkt.ip.IP_DF)   #不分片位
more_fragments = bool(ip.off & dpkt.ip.IP_MF)   #更多分片位
fragment_offset = ip.off & dpkt.ip.IP_OFFMASK   #该片偏移原始数据包起始位置
  1. 使用dpkt.http.Request()函数解析http数据信息,如运营商、版本、http与服务器交互方式(GET or POST)
if len(tcp.data):
    request = dpkt.http.Request(tcp.data)
    print("user-agent:" + request.headers['user-agent'])
    print("request method:%s  request version:%s" % (request.method, request.version))
  1. 最终最数据包的分析输出如下图,可以看到时间戳、MAC地址、IP类型(0x800表示IPv4)、IP地址及数据包流向、源端口、目标端口、传出曾协议等信息,参考了wireshark里的输出信息的模式。

完整代码如下:

import dpkt
import socket
import datetime
from dpkt.compat import compat_ord

#将MAC地址转换为可打印的字符串
def mac_addr(address):
    return ':'.join('%02x' % compat_ord(b) for b in address)

#判断http
def checkIfHTTPRes(data):
    if data[:4] == str.encode('GET'):
        return True
    else:
        return False

def httpPacketParser(http):
    if checkIfHTTPRes(http):
        try:
            request = dpkt.http.Request(http)
            print(request.status)

        except Exception as e:
            pass

def tcpPacketParser(tcp):
    http = tcp.data
    if len(http):
        httpPacketParser(http)

#数据包分析输出
def Analysis_Pcap(pcap):
    try:
        #迭代pcap对象
        for timestamp, buf in pcap:

            print('Arrival Time: ', str(datetime.datetime.utcfromtimestamp(timestamp)))  # 包的抓取时间

            eth = dpkt.ethernet.Ethernet(buf)  # 解析以太网帧

            # 获得MAC地址并输出
            src_mac = mac_addr(eth.src)
            dst_mac = mac_addr(eth.dst)
            print('MAC: src( %s )   dst( %s )   Type( %s )' % (src_mac, dst_mac, hex(eth.type)))

            #解析ip包
            ip = eth.data

            # 把4字节IP地址转化成点分十进制的可读形式
            src = socket.inet_ntoa(ip.src)
            dst = socket.inet_ntoa(ip.dst)

            # 提取标志信息
            do_not_fragment = bool(ip.off & dpkt.ip.IP_DF)   #不分片位
            more_fragments = bool(ip.off & dpkt.ip.IP_MF)   #更多分片位
            fragment_offset = ip.off & dpkt.ip.IP_OFFMASK   #该片偏移原始数据包起始位置

            print('IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)' % (src, dst, ip.len, ip.ttl, do_not_fragment, more_fragments, fragment_offset))

            print("sport= %d,dport= %d" % (eth.data.data.sport, eth.data.data.dport))

            #传输层协议
            if eth.data.data.__class__.__name__ == 'TCP':
                # 解析tcp数据
                tcp = ip.data
                print("protocol:" + tcp.__class__.__name__)
                # 解析tcp上层协议http
                tcpPacketParser(tcp)
            else:
                print("protocol:" + eth.data.data.__class__.__name__)

                request = dpkt.http.Request(tcp.data)
                print("user-agent:" + request.headers['user-agent'])
                print("request method:%s  request version:%s" % (request.method, request.version))

            #print("\n")

            if not isinstance(eth.data, dpkt.ip.IP):

                print('Non IP Packet type not supported %s' % ip.__class__.__name__)

                continue

    except:
        pass

def main():

    #'rb'按照二进制位进行读取
    f =open('demo.pcap','rb')

    dpcap = dpkt.pcap.Reader(f)

    Analysis_Pcap(dpcap)

    f.close()

if __name__ =='__main__':
    main()

 

  • 代码已经上传到码云

Tips:创建github账号时我遇到一个小问题,最后一步邮箱认证失败。解决方法是:在邮箱中找到设置 ——> 反垃圾,将github.com添加到白名单里,就可以认证成功了 ~ ~ ~ /// (v) \\ ~ ~ ~


存在问题

dpkt进行数据包解析,要求只有在符合单个包的情况下才会正确解析,否则会报错,尝试在对应用层数据(http数据为例)分析时,利用if-else语句判断数据分了两个包发送,dpkt无法进行正确解析。
自己尝试编写了http解析过程,但未成功。

 


(2)、找一个网站,登录并嗅探,分析出账号和密码

http协议是明文传输的,所以在解析数据包过程中可以嗅探到登录操作的账号密码,实验使用的工具Wireshark也没有session key,不能解密https的数据流。

But!! 现在稍微正规点的网站都改成了https协议

一开始我选择了人人网,这个曾经辉煌一时网站,惊讶的发现它使用的是http

  • 重新注册了一个账号,使用wireshark在注册和登录时进行抓包,结果如下图,账号就是手机号183********可以看到,但是密码password和验证码rkey都进行了加密(hash)

  • 于是,我使用hash-identifier工具来确定上面的密码哈希值到底是什么类型的哈希,尝试解密。打开kali终端,然后输入“hash-identifier”并将上面的哈希值粘贴到终端,回车之后hash-identifier将会给出可能的匹配值。

  • 可以看到推测可能是SHA256或Haval256,利用在线哈希解密网站,对SNA256进行解密,发现不对,Haval256没有找到解密方法,故只能放弃。等我回去在补补密码学知识。

 

于是我换了一个网站。
这里我拿鲜果网(http://www.xianguo.pro/)进行测试。
它的登陆界面长这个样子,买瓜果的主人应该叫“王大叔”

  • 我将用户名设置为shuiguo(就是‘水果’啦),密码不能告诉你,注册时还要写手机号13051607197,这里我用了一个副号,专门抵抗各种信息骚扰的号。

  • 使用wireshark在注册和登录时进行抓包。首先分析注册时抓的包,使用http.request.method=="POST"命令筛选,即客户机与服务器直接以post请求方式交互,其实就是我给网站传送信息的数据包。可以在下面信息栏看到用户名shuiguo和手机号,但是密码又被加密了。

  • 于是我采用刚才的方法,在kali终端检测,发现使用的是MD5加密(这里忘记截图了),利用在线哈希解密获得了正确的密码

密文:708a9c84b47404c5524405e5Cbd910b8

密码:qazwsx123

  • 在对登录操作抓包的数据包分析中,也可以看到账号和使用MD5加密后的密文,而且在wireshark中登录时数据包信息会显示“login”字段,便于大家找到登录传输的数据包

Tips:考虑到成本问题,并不是每个网站都会使用SSL来保证密码的安全。但是大部分为使用SSL的网站所有者通常在登录操作中进行哈希值加密密码的操作,这样至少在攻击者破解网站密码的时候不会那么轻而易举,能够多一道防护屏障。

 

(3)、加分项:抓取手机App的登录过程数据包,分析账号和密码

  • 如果要使用wireshark抓取手机APP的数据包,那要将手机连接电脑的网络中,我通过手机连入电脑热点的方式,对手机数据进行抓包解析
    具体步骤如下顺序,很简单。
  • 首先,点击电脑左下角的微软按钮,点击设置,进入设置后,选择网络和Internet

  • 跳转界面,在左侧栏选择移动热点,会看到如下图所示。右侧栏内打开移动热点,选择WLAN,会自动给出网络名称和网络密码

  • 在手机上选择网络加入

至此,就完成了手机点解电脑热点的操作,可以开始抓包啦!!

 


 

  • 使用wireshark嗅探手机访问浏览器进行账号登录操作,还是选择鲜果网,手机浏览器选择safari。(注意这个时候wireshark嗅探到的手机产生的数据包IP地址是之前电脑的IP地址,这里是192.168.1.104)

通过http.request.method=="POST"命令过滤,在信息栏可以看到访问的网址

找到传输账号密码的数据包,登录操作通常在“Info”栏后面会有“login”字段,还是可以看到明文的用户名,MD5加密的密码。

 

使用wireshark嗅探手机APP进行账号登录操作,没有成功分析,感觉wireshark也不好使,但是在分析数据包时看到好多TLS、SSL协议的包,因此查阅了一些资料,这里简单介绍一下我嗅探到的SSL/TLS握手过程。

 


SSL/TLS握手过程可以分成两种类型:
1)SSL/TLS 双向认证,双方都会互相认证,也就是两者之间将会交换证书。
2)SSL/TLS 单向认证,客户端会认证服务器端身份,而服务器端不会去对客户端身份进行验证。

握手阶段分成五步:

  • 第一步,客户端给出协议版本号、生成的随机数(Client random),以及客户端支持的加密方法。在wireshark中显示为Client Hello
  • 第二步,服务器确认双方使用的加密方法,并给出数字证书、以及一个服务器生成的随机数(Server random),在wireshark中分为两个包,你一个包显示为 Server Hello,第二个包显示为 certificate,server_key_exchange,server hello done
  • 第三步,客户端确认数字证书有效,生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给服务端。在wireshark中显示为client key exchange,change cypher spec
  • 第四步,服务端使用自己的私钥,获取客户端发来的随机数(Premaster secret),在wireshark中显示为hange_cypher_spec
  • 第五步,客户端和服务端根据约定的加密方法,使用前面的三个随机数,生成"对话密钥"(session key),用来加密接下来的整个对话过程。

-在wireshark中抓到的SSL/TLS握手过程

前两个随机数都是明文传送的,我们以第二步sever hello为例查看Server random
可以看到随机数和服务端支持的加密方法,例如RSA、AES等


参考资料

  1. HTTP数据包头解析

  2. 用Python实现windows下简单的抓包与解析

  3. dpkt教程(英文)

  4. 如何利用wireshark破解网站密码——hash类型

  5. SSL/TLS握手过程

posted @ 2020-03-29 16:00  FLwww  阅读(977)  评论(0编辑  收藏  举报