《Python绝技:运用Python成为顶级黑客》 用Python分析网络流量

1、IP流量将何去何从?——用Python回答:

使用PyGeoIP关联IP地址和物理地址:

需要下载安装pygeoip,可以pip install pygeoip

或者到Github上下载安装https://github.com/appliedsec/pygeoip

同时需要下载用pygeoip操作的GeoLiteCity数据库来解压获得GeoLiteCity.dat数据库文件:

http://dev.maxmind.com/geoip/legacy/geolite/

将GeoLiteCity.dat放在脚本的同一目录中直接调用即可。

#!/usr/bin/python
#coding=utf-8
import pygeoip

# 查询数据库相关的城市信息并输出
def printRecord(tgt):
    rec = gi.record_by_name(tgt)
    city = rec['city']
    # 原来的代码为 region = rec['region_name'],已弃用'region_name'
    region = rec['region_code']
    country = rec['country_name']
    long = rec['longitude']
    lat = rec['latitude']
    print '[*] Target: ' + tgt + ' Geo-located. '
    print '[+] '+str(city)+', '+str(region)+', '+str(country)
    print '[+] Latitude: '+str(lat)+ ', Longitude: '+ str(long)

gi = pygeoip.GeoIP('GeoLiteCity.dat')
tgt = '173.255.226.98'
printRecord(tgt)

运行结果:

使用Dpkt解析包:

需要安装dpkt包:pip install dpkt

dpkt允许逐个分析抓包文件里的各个数据包,并检查数据包中的每个协议层。

#!/usr/bin/python
#coding=utf-8
import dpkt
import socket

def printPcap(pcap):
    # 遍历[timestamp, packet]记录的数组
    for (ts, buf) in pcap:
        try:
            # 获取以太网部分数据
            eth = dpkt.ethernet.Ethernet(buf)
            # 获取IP层数据
            ip = eth.data
            # 把存储在inet_ntoa中的IP地址转换成一个字符串
            src = socket.inet_ntoa(ip.src)
            dst = socket.inet_ntoa(ip.dst)
            print '[+] Src: ' + src + ' --> Dst: ' + dst
        except:
            pass

def main():
    f = open('geotest.pcap')
    pcap = dpkt.pcap.Reader(f)
    printPcap(pcap)

if __name__ == '__main__':
    main()

因为抓取的流量不多,直接使用书上的数据包来测试即可:

 接着添加retGeoStr()函数,返回指定IP地址对应的物理位置,简单地解析出城市和三个字母组成的国家代码并输出到屏幕上。整合起来的代码如下:

#!/usr/bin/python
#coding=utf-8
import dpkt
import socket
import pygeoip
import optparse

gi = pygeoip.GeoIP('GeoLiteCity.dat')

# 查询数据库相关的城市信息并输出
def printRecord(tgt):
    rec = gi.record_by_name(tgt)
    city = rec['city']
    # 原来的代码为 region = rec['region_name'],已弃用'region_name'
    region = rec['region_code']
    country = rec['country_name']
    long = rec['longitude']
    lat = rec['latitude']
    print '[*] Target: ' + tgt + ' Geo-located. '
    print '[+] '+str(city)+', '+str(region)+', '+str(country)
    print '[+] Latitude: '+str(lat)+ ', Longitude: '+ str(long)

def printPcap(pcap):
    # 遍历[timestamp, packet]记录的数组
    for (ts, buf) in pcap:
        try:
            # 获取以太网部分数据
            eth = dpkt.ethernet.Ethernet(buf)
            # 获取IP层数据
            ip = eth.data
            # 把存储在inet_ntoa中的IP地址转换成一个字符串
            src = socket.inet_ntoa(ip.src)
            dst = socket.inet_ntoa(ip.dst)
            print '[+] Src: ' + src + ' --> Dst: ' + dst
            print '[+] Src: ' + retGeoStr(src) + '--> Dst: ' + retGeoStr(dst)
        except:
            pass

# 返回指定IP地址对应的物理位置
def retGeoStr(ip):
    try:
        rec = gi.record_by_name(ip)
        city = rec['city']
        country = rec['country_code3']
        if city != '':
            geoLoc = city + ', ' + country
        else:
            geoLoc = country
        return geoLoc
    except Exception, e:
        return 'Unregistered'

def main():
    parser = optparse.OptionParser('[*]Usage: python geoPrint.py -p <pcap file>')
    parser.add_option('-p', dest='pcapFile', type='string', help='specify pcap filename')
    (options, args) = parser.parse_args()
    if options.pcapFile == None:
        print parser.usage
        exit(0)
    pcapFile = options.pcapFile
    f = open(pcapFile)
    pcap = dpkt.pcap.Reader(f)
    printPcap(pcap)

if __name__ == '__main__':
    main()

还是使用之前测试用的数据包:

 

使用Python画谷歌地图:

这里修改一下代码,将kml代码直接写入一个新文件中而不是直接输出到控制台。

#!/usr/bin/python
#coding=utf-8
import dpkt
import socket
import pygeoip
import optparse

gi = pygeoip.GeoIP('GeoLiteCity.dat')

# 通过IP地址的经纬度构建kml结构
def retKML(ip):
    rec = gi.record_by_name(ip)
    try:
        longitude = rec['longitude']
        latitude = rec['latitude']
        kml = (
            '<Placemark>\n'
            '<name>%s</name>\n'
            '<Point>\n'
            '<coordinates>%6f,%6f</coordinates>\n'
            '</Point>\n'
            '</Placemark>\n'
            ) %(ip,longitude, latitude)
        return kml
    except:
        return ' '

def plotIPs(pcap):
    kmlPts = ''
    for (ts, buf) in pcap:
        try:
            eth = dpkt.ethernet.Ethernet(buf)
            ip = eth.data
            src = socket.inet_ntoa(ip.src)
            srcKML = retKML(src)
            dst = socket.inet_ntoa(ip.dst)
            dstKML = retKML(dst)
            kmlPts = kmlPts + srcKML + dstKML
        except:
            pass
    return kmlPts

def main():
    parser = optparse.OptionParser('[*]Usage: python googleearthPrint.py -p <pcap file>')
    parser.add_option('-p', dest='pcapFile', type='string', help='specify pcap filename')
    (options, args) = parser.parse_args()
    if options.pcapFile == None:
        print parser.usage
        exit(0)
    pcapFile = options.pcapFile
    f = open(pcapFile)
    pcap = dpkt.pcap.Reader(f)

    kmlheader = '<?xml version="1.0" encoding="UTF-8"?>\
    \n<kml xmlns="http://www.opengis.net/kml/2.2">\n<Document>\n'
    kmlfooter = '</Document>\n</kml>\n'
    kmldoc = kmlheader + plotIPs(pcap) + kmlfooter
    # print kmldoc
    with open('googleearthPrint.kml', 'w') as f:
        f.write(kmldoc)
        print "[+]Created googleearthPrint.kml successfully"

if __name__ == '__main__':
    main()

运行结果:

查看该kml文件:

接着访问谷歌地球:https://www.google.com/earth/

在左侧选项中导入kml文件:

导入后点击任一IP,可以看到该IP地址的定位地图:

2、“匿名者”真能匿名吗?分析LOIC流量:

LOIC,即Low Orbit Ion Cannon低轨道离子炮,是用于压力测试的工具,通常被攻击者用来实现DDoS攻击。

使用Dpkt发现下载LOIC的行为:

一个比较可靠的LOIC下载源:https://sourceforge.net/projects/loic/

由于下载源站点已从HTTP升级为HTTPS,即已经无法直接通过抓包来进行请求头的分析了。

#!/usr/bin/python
#coding=utf-8
import dpkt
import socket

def findDownload(pcap):
    for (ts, buf) in pcap:
        try:
            eth = dpkt.ethernet.Ethernet(buf)
            ip = eth.data
            src = socket.inet_ntoa(ip.src)
            # 获取TCP数据
            tcp = ip.data
            # 解析TCP中的上层协议HTTP的请求
            http = dpkt.http.Request(tcp.data)
            # 若是GET方法,且请求行中包含“.zip”和“loic”字样则判断为下载LOIC
            if http.method == 'GET':
                uri = http.uri.lower()
                if '.zip' in uri and 'loic' in uri:
                    print "[!] " + src + " Downloaded LOIC."
        except:
            pass

f = open('download.pcap')
pcap = dpkt.pcap.Reader(f)
findDownload(pcap)

这里直接使用书上提供的数据包进行测试:

解析Hive服务器上的IRC命令:

下面的代码主要用于检测僵尸网络流量中的IRC命令:

#!/usr/bin/python
#coding=utf-8
import dpkt
import socket

def findHivemind(pcap):
    for (ts, buf) in pcap:
        try:
            eth = dpkt.ethernet.Ethernet(buf)
            ip = eth.data
            src = socket.inet_ntoa(ip.src)
            dst = socket.inet_ntoa(ip.dst)
            tcp = ip.data
            dport = tcp.dport
            sport = tcp.sport
            # 若目标端口为6667且含有“!lazor”指令,则确定是某个成员提交一个攻击指令
            if dport == 6667:
                if '!lazor' in tcp.data.lower():
                    print '[!] DDoS Hivemind issued by: '+src
                    print '[+] Target CMD: ' + tcp.data
            # 若源端口为6667且含有“!lazor”指令,则确定是服务器在向HIVE中的成员发布攻击的消息
            if sport == 6667:
                if '!lazor' in tcp.data.lower():
                    print '[!] DDoS Hivemind issued to: '+src
                    print '[+] Target CMD: ' + tcp.data
        except:
            pass

f = open('hivemind.pcap')
pcap = dpkt.pcap.Reader(f)
findHivemind(pcap)

同样直接用案例的数据包来测试:

 

实时检测DDoS攻击:

主要通过设置检测不正常数据包数量的阈值来判断是否存在DDoS攻击。

#!/usr/bin/python
#coding=utf-8
import dpkt
import socket

# 默认设置检测不正常数据包的数量的阈值为1000
THRESH = 1000

def findAttack(pcap):
    pktCount = {}
    for (ts, buf) in pcap:
        try:
            eth = dpkt.ethernet.Ethernet(buf)
            ip = eth.data
            src = socket.inet_ntoa(ip.src)
            dst = socket.inet_ntoa(ip.dst)
            tcp = ip.data
            dport = tcp.dport
            # 累计各个src地址对目标地址80端口访问的次数
            if dport == 80:
                stream = src + ':' + dst
                if pktCount.has_key(stream):
                    pktCount[stream] = pktCount[stream] + 1
                else:
                    pktCount[stream] = 1
        except:
            pass

    for stream in pktCount:
        pktsSent = pktCount[stream]
        # 若超过设置检测的阈值,则判断为进行DDoS攻击
        if pktsSent > THRESH:
            src = stream.split(':')[0]
            dst = stream.split(':')[1]
            print '[+] ' + src + ' attacked ' + dst + ' with ' + str(pktsSent) + ' pkts.'

f = open('attack.pcap')
pcap = dpkt.pcap.Reader(f)
findAttack(pcap)

同样直接用案例的数据包来测试:

然后将前面的代码整合到一起:

#!/usr/bin/python
#coding=utf-8
import dpkt
import socket
import optparse

# 默认设置检测不正常数据包的数量的阈值为1000
THRESH = 1000

def findDownload(pcap):
    for (ts, buf) in pcap:
        try:
            eth = dpkt.ethernet.Ethernet(buf)
            ip = eth.data
            src = socket.inet_ntoa(ip.src)
            # 获取TCP数据
            tcp = ip.data
            # 解析TCP中的上层协议HTTP的请求
            http = dpkt.http.Request(tcp.data)
            # 若是GET方法,且请求行中包含“.zip”和“loic”字样则判断为下载LOIC
            if http.method == 'GET':
                uri = http.uri.lower()
                if '.zip' in uri and 'loic' in uri:
                    print "[!] " + src + " Downloaded LOIC."
        except:
            pass

def findHivemind(pcap):
    for (ts, buf) in pcap:
        try:
            eth = dpkt.ethernet.Ethernet(buf)
            ip = eth.data
            src = socket.inet_ntoa(ip.src)
            dst = socket.inet_ntoa(ip.dst)
            tcp = ip.data
            dport = tcp.dport
            sport = tcp.sport
            # 若目标端口为6667且含有“!lazor”指令,则确定是某个成员提交一个攻击指令
            if dport == 6667:
                if '!lazor' in tcp.data.lower():
                    print '[!] DDoS Hivemind issued by: '+src
                    print '[+] Target CMD: ' + tcp.data
            # 若源端口为6667且含有“!lazor”指令,则确定是服务器在向HIVE中的成员发布攻击的消息
            if sport == 6667:
                if '!lazor' in tcp.data.lower():
                    print '[!] DDoS Hivemind issued to: '+src
                    print '[+] Target CMD: ' + tcp.data
        except:
            pass

def findAttack(pcap):
    pktCount = {}
    for (ts, buf) in pcap:
        try:
            eth = dpkt.ethernet.Ethernet(buf)
            ip = eth.data
            src = socket.inet_ntoa(ip.src)
            dst = socket.inet_ntoa(ip.dst)
            tcp = ip.data
            dport = tcp.dport
            # 累计各个src地址对目标地址80端口访问的次数
            if dport == 80:
                stream = src + ':' + dst
                if pktCount.has_key(stream):
                    pktCount[stream] = pktCount[stream] + 1
                else:
                    pktCount[stream] = 1
        except:
            pass

    for stream in pktCount:
        pktsSent = pktCount[stream]
        # 若超过设置检测的阈值,则判断为进行DDoS攻击
        if pktsSent > THRESH:
            src = stream.split(':')[0]
            dst = stream.split(':')[1]
            print '[+] ' + src + ' attacked ' + dst + ' with ' + str(pktsSent) + ' pkts.'

def main():
    parser = optparse.OptionParser("[*]Usage python findDDoS.py -p <pcap file> -t <thresh>")
    parser.add_option('-p', dest='pcapFile', type='string', help='specify pcap filename')
    parser.add_option('-t', dest='thresh', type='int', help='specify threshold count ')
    (options, args) = parser.parse_args()
    if options.pcapFile == None:
        print parser.usage
        exit(0)
    if options.thresh != None:
        THRESH = options.thresh
    pcapFile = options.pcapFile
    # 这里的pcap文件解析只能调用一次,注释掉另行修改
    # f = open(pcapFile)
    # pcap = dpkt.pcap.Reader(f)
    # findDownload(pcap)
    # findHivemind(pcap)
    # findAttack(pcap)
    with open(pcapFile, 'r') as f:
        pcap = dpkt.pcap.Reader(f)
        findDownload(pcap)
    with open(pcapFile, 'r') as f:
        pcap = dpkt.pcap.Reader(f)
        findHivemind(pcap)
    with open(pcapFile, 'r') as f:
        pcap = dpkt.pcap.Reader(f)
        findAttack(pcap)

if __name__ == '__main__':
    main()

由于这部分作者没有给示例数据包,那就自己来合并上述几个pcap文件,使用命令:

mergecap -a -F pcap -w traffic.pcap download.pcap hivemind.pcap attack.pcap

-a参数指定按照命令顺序来合并各个pcap文件(不添加-a参数则默认按照时间的顺序合并),-F参数指定生成的文件类型,-w参数指定生成的pcap文件。

运行结果:

3、H.D.Moore是如何解决五角大楼的麻烦的:

理解TTL字段:

TTL即time-to-live,由8比特组成,可以用来确定在到达目的地之前数据包经过了几跳。当计算机发送一个IP数据包时会设置TTL字段为数据包在到达目的地之前所应经过的中继跳转的上限值,数据包每经过一个路由设备,TTL值就自减一,若减至0还未到目的地,路由器会丢弃该数据包以防止无限路由循环。

Nmap进行伪装扫描时,伪造数据包的TTL值是没有经过计算的,因而可以利用TTL值来分析所有来自Nmap扫描的数据包,对于每个被记录为Nmap扫描的源地址,发送一个ICMP数据包来确定源地址与目标机器之间隔了几跳,从而来辨别真正的扫描源。

Nmap的-D参数实现伪造源地址扫描:nmap 192.168.220.128 -D 8.8.8.8

Wireshark抓包分析,加上过滤器的条件“ip.addr==8.8.8.8”,发现确实是有用伪造源地址进行扫描:

点击各个数据包查看TTL值:

可以看到默认扫描的Nmap扫描,其ttl值是随机的。

添加-ttl参数指定值为13之后,可以看到发送的数据包的ttl值都为13:

用Scapy解析TTL字段的值:

这里使用Scapy库来获取源地址IP及其TTL值。

#!/usr/bin/python
#coding=utf-8
from scapy.all import *

# 检查数据包的IP层,提取出源IP和TTL字段的值
def testTTL(pkt):
    try:
        if pkt.haslayer(IP):
            ipsrc = pkt.getlayer(IP).src
            ttl = str(pkt.ttl)
            print "[+] Pkt Received From: " + ipsrc + " with TTL: " + ttl
    except:
        pass

def main():
    sniff(prn=testTTL, store=0)

if __name__ == '__main__':
    main()

运行脚本监听后,启动Nmap伪造源地址扫描即可看到如下结果:

接着添加checkTTL()函数,主要实现对比TTL值进行源地址真伪判断:

#!/usr/bin/python
#coding=utf-8
from scapy.all import *
import time
import optparse
# 为避免IPy库中的IP类与Scapy库中的IP类冲突,重命名为IPTEST类
from IPy import IP as IPTEST

ttlValues = {}
THRESH = 5

# 检查数据包的IP层,提取出源IP和TTL字段的值
def testTTL(pkt):
    try:
        if pkt.haslayer(IP):
            ipsrc = pkt.getlayer(IP).src
            ttl = str(pkt.ttl)
            checkTTL(ipsrc, ttl)
    except:
        pass

def checkTTL(ipsrc, ttl):
    # 判断是否是内网私有地址
    if IPTEST(ipsrc).iptype() == 'PRIVATE':
        return

    # 判断是否出现过该源地址,若没有则构建一个发往源地址的ICMP包,并记录回应数据包中的TTL值
    if not ttlValues.has_key(ipsrc):
        pkt = sr1(IP(dst=ipsrc) / ICMP(), retry=0, timeout=1, verbose=0)
        ttlValues[ipsrc] = pkt.ttl

    # 若两个TTL值之差大于阈值,则认为是伪造的源地址
    if abs(int(ttl) - int(ttlValues[ipsrc])) > THRESH:
        print '\n[!] Detected Possible Spoofed Packet From: ' + ipsrc
        print '[!] TTL: ' + ttl + ', Actual TTL: ' + str(ttlValues[ipsrc])

def main():
    parser = optparse.OptionParser("[*]Usage python spoofDetect.py -i <interface> -t <thresh>")
    parser.add_option('-i', dest='iface', type='string', help='specify network interface')
    parser.add_option('-t', dest='thresh', type='int', help='specify threshold count ')
    (options, args) = parser.parse_args()
    if options.iface == None:
        conf.iface = 'eth0'
    else:
        conf.iface = options.iface
    if options.thresh != None:
        THRESH = options.thresh
    else:
        THRESH = 5

    sniff(prn=testTTL, store=0)

if __name__ == '__main__':
    main()

运行脚本监听后,启动Nmap伪造源地址扫描即可看到如下结果:

4、“风暴”(Storm)的fast-flux和Conficker的domain-flux:

你的DNS知道一些不为你所知的吗?

下面用nslookup命令来进行一次域名查询:

Wireshark抓包如下:

可以看到客户端发送DNSQR请求包,服务器发送DNSRR响应包。

使用Scapy解析DNS流量:

一个DNSQR包含有查询的名称qname、查询的类型qtype、查询的类别qclass。

一个DNSRR包含有资源记录名名称rrname、类型type、资源记录类别rtype、TTL等等。

 

用Scapy找出fast-flux流量:

解析pcap文件中所有含DNSRR的数据包,提取分别含有查询的域名和对应的IP的rrname和rdata变量,然后建立一个索引字典并对字典中未出现的IP添加到数组中。

#!/usr/bin/python
#coding=utf-8
from scapy.all import *

dnsRecords = {}

def handlePkt(pkt):
    # 判断是否含有DNSRR
    if pkt.haslayer(DNSRR):
        rrname = pkt.getlayer(DNSRR).rrname
        rdata = pkt.getlayer(DNSRR).rdata
        if dnsRecords.has_key(rrname):
            if rdata not in dnsRecords[rrname]:
                dnsRecords[rrname].append(rdata)
        else:
            dnsRecords[rrname] = []
            dnsRecords[rrname].append(rdata)

def main():
    pkts = rdpcap('fastFlux.pcap')
    for pkt in pkts:
        handlePkt(pkt)
    for item in dnsRecords:
        print "[+] " + item + " has " + str(len(dnsRecords[item])) + " unique IPs."
        # for i in dnsRecords[item]:
        #     print "[*] " + i
        # print

if __name__ == '__main__':
    main()

猜你喜欢

转载自www.cnblogs.com/LyShark/p/9100005.html