網絡安全-scapy學習筆記

目錄

scapy信息

函數彙總

數據包

Ether

IP

ICMP

TCP

內核的TCP堆棧

發送函數

send

sendp

發送接收函數

sr1

應用

地址段IP發現-Ping

端口掃描

Fuzz

缺點


scapy信息

官網:scapy

用戶手冊:scapy docs

一個python庫,用於發送Tcp消息、Udp消息,進行fuzz等。

函數彙總

scapy函數彙總
函數 作用

數據包

生成數據包
IP IP數據包
TCP TCP數據包,基於IP
ICMP ICMP數據包,基於IP
   
   
發送 發送數據包
send 發送數據包,三層
sendp 發送數據包,兩層
sr1 發送三層數據包並接收一個數據包

數據包

Ether

還沒填過參數

IP

IP數據包
參數 含義
version   = 4 版本
  ihl       = None  
  tos       = 0x0  
  len       = None  
  id        = 1  
  flags     =  
  frag      = 0  
  ttl       = 64  
  proto     = tcp 協議
  chksum    = None  
  src       = 39.156.69.79 源IP地址
  dst       = 121.17.123.130 目的IP地址

目的IP一般是我們想要發送數據包的,一般是指定的,我們可以僞造源IP地址,比如ping下百度的,以後進行主機發現之類的,源地址可以寫百度的ip。

ICMP

還沒填過參數

ping的ICMP

PING的時候是ICMP數據包,在應用的時候就寫了腳本,發送的ICMP,但是也沒填參數

TCP

TCP數據包
參數 含義

     sport     = ftp_data

源端口號
     dport     = http 目的端口號
     seq       = 0  
     ack       = 0  
     dataofs   = None  
     reserved  = 0  
     flags     = S  
     window    = 8192  
     chksum    = None  
     urgptr    = 0  
     options   = []  

內核的TCP堆棧

import socket
from scapy.packet import Raw
from scapy.sendrecv import sr1
from scapy.supersocket import StreamSocket


def tcpStack():
    s = socket.socket()
    s.connect(("www.httpbin.org", 80))
    ss = StreamSocket(s, Raw)
    ss.sr1(Raw("GET /get HTTP/1.1\r\n" + \
                     "Host: httpbin.org\r\n" + \
                     "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0\r\n" + \
                     "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" + \
                     "Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\r\n" + \
                     "Accept-Encoding: gzip, deflate\r\n" + \
                     "Connection: keep-alive\r\n" + \
                     "Upgrade-Insecure-Requests: 1\r\n" + \
                     "Cache-Control: max-age=0\r\n"))


if __name__ == "__main__":
    tcpStack()

 

發送函數

send

@conf.commands.register
def send(x, inter=0, loop=0, count=None,
         verbose=None, realtime=None,
         return_packets=False, socket=None, *args, **kargs):
    """Send packets at layer 3
send(packets, [inter=0], [loop=0], [count=None], [verbose=conf.verb], [realtime=None], [return_packets=False],  # noqa: E501
     [socket=None]) -> None"""
    need_closing = socket is None
    socket = socket or conf.L3socket(*args, **kargs)
    results = __gen_send(socket, x, inter=inter, loop=loop,
                         count=count, verbose=verbose,
                         realtime=realtime, return_packets=return_packets)
    if need_closing:
        socket.close()
    return results

 

send

 從圖中可以看出send函數是發送三層的數據包,例如IP()/TCP()/PACKET

syn泛洪攻擊時可以使用,只發送不接收,自然也不回覆,去消耗資源。

sendp

@conf.commands.register
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):
    """Send packets at layer 2
sendp(packets, [inter=0], [loop=0], [iface=None], [iface_hint=None], [count=None], [verbose=conf.verb],  # noqa: E501
      [realtime=None], [return_packets=False], [socket=None]) -> None"""
    if iface is None and iface_hint is not None and socket is None:
        iface = conf.route.route(iface_hint)[0]
    need_closing = socket is None
    socket = socket or conf.L2socket(iface=iface, *args, **kargs)
    results = __gen_send(socket, x, inter=inter, loop=loop,
                         count=count, verbose=verbose,
                         realtime=realtime, return_packets=return_packets)
    if need_closing:
        socket.close()
    return results

發送接收函數

sr1

@conf.commands.register
def sr1(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kargs):
    """Send packets at layer 3 and return only the first answer"""
    iface = _interface_selection(iface, x)
    s = conf.L3socket(promisc=promisc, filter=filter,
                      nofilter=nofilter, iface=iface)
    ans, _ = sndrcv(s, x, *args, **kargs)
    s.close()
    if len(ans) > 0:
        return ans[0][1]
    else:
        return None
sr1

發送,並只保存接收的第一個數據包。

from scapy.layers.inet import IP, TCP
from scapy.sendrecv import sr1

SRC_IP = "192.168.31.164"
DST_IP = "121.17.123.130"


def visitBaidu():
    pkt = IP(src=SRC_IP, dst=DST_IP) / TCP()
    print(pkt.show())
    res = sr1(pkt)
    print(res.summary())


if __name__ == "__main__":
    visitBaidu()
sr1抓包結果

應用

地址段IP發現-Ping

import ipaddress
import multiprocessing
import random
from scapy.layers.inet import IP, ICMP
from scapy.sendrecv import sr1

DIP = "121.17.123.1/24"
BNUM = 20
TNUM = 64


def getBytes(num):
    res = ''.join(random.sample('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567', num))
    return bytes(res, encoding='utf-8')


def ping(ip):
    pkt = IP(dst=ip) / ICMP() / getBytes(BNUM)
    res = sr1(pkt, timeout=5, verbose=False)
    if res:
        return True, ip
    else:
        return False, ip


def getIpList(ip):
    temp = ipaddress.ip_network(ip, False).hosts()
    ipList = []
    for i in temp:
        ipList.append(str(i))
    return ipList


def ipScan(ip, num):
    ipList = getIpList(ip)
    pool = multiprocessing.Pool(processes=int(TNUM))
    result = pool.map(ping, ipList)
    pool.close()
    pool.join()
    for res, ip in result:
        if res:
            print(ip)


if __name__ == "__main__":
    ipScan(DIP, TNUM)
ip發現

 採用了進程池,有時間再整理下進程(池),線程(池)

端口掃描

# /usr/bin/env python3
# _*_ coding:utf-8 _*_
# auther: saucerman
# project: https://github.com/saucer-man/penetration-script

"""
基於python-nmap的端口掃描器
pip install python-nmap
"""

import sys
import time
from colorama import init, Fore, Back, Style
import getopt

# 顏色定義
init(autoreset=True)


class Colored(object):
    def red(self, s):
        return Fore.RED + s + Fore.RESET

    def blue(self, s):
        return Fore.BLUE + s + Fore.RESET

    def yellow(self, s):
        return Fore.YELLOW + s + Fore.RESET


color = Colored()

try:
    import nmap
except:
    print("FATAL: Module nmap missing (python-nmap)")
    sys.exit(1)


# 使用說明
def usage():
    print(color.blue('Usage: port scanner'))
    print(color.blue('\t-h/--host:\tpoint the target to scan'))
    print(color.blue('\t-p/--port:\tpoint the port to scan(not nessesary)'))
    print(color.blue('Examples:'))
    print(color.blue('\tpython port_scanner.py -h 10.10.10.1'))
    print(color.blue('\tpython port_scanner.py -h 10.10.10.1 -p 80,443,8080'))
    print(color.blue('\tpython port_scanner.py -h 10.10.10.1 -p 1-1024'))
    print(color.blue('\nSEE THE MAN PAGE (https://github.com/saucer-man/saucer-frame) FOR MORE OPTIONS AND EXAMPLES'))
    sys.exit(0)


# 掃描
def scanner(host, ports):
    nm = nmap.PortScanner()
    try:
        print('Scanner report for %s\n' % host)
        if len(ports) == 0:
            result = nm.scan(host)
        else:
            result = nm.scan(host, ports)
        if result['nmap']['scanstats']['uphosts'] == '0':
            print(color.red('Host seems down'))
        else:
            print('Host is up')
            print("{:<7}\t{:<7}\t{:<7}\t{:<7}".format('PORT', 'STATE', 'SERVICE', 'VERSION'))
            for k, v in result['scan'][host]['tcp'].items():
                if v['state'] == 'open':
                    print(color.yellow("{:<7}\t{:<7}\t{:<7}\t{:<7}".format(str(k), v['state'], v['name'],
                                                                           v['product'] + v['version'])))
                else:
                    print(color.yellow("{:<7}\t{:<7}".format(str(k), v['state'])))
    except Exception as e:
        print(color.red("unhandled Option"))
        usage()


def main():
    start = time.time()

    # 解析命令行
    if not len(sys.argv[1:]):
        usage()
    try:
        opts, args = getopt.getopt(sys.argv[1:], "h:p:",
                                   ["host=", "port="])
    except:
        print(color.red("unhandled Option"))
        usage()

    ports = ''
    for o, a in opts:
        if o == "-h" or o == "--host":
            host = a
        elif o == "-p" or o == "--port":
            ports = a

    print("Starting port scanner...")
    scanner(host, ports)

    end = time.time()
    print('\n\nScanner down with %0.6f seconds.' % (end - start))


if "__main__" == __name__:
    main()
端口發現

這個腳本不是我寫的,使用了nmap包,我還沒看過命令解析的內容。

403

以爲誰寫的網站,結果給我403了

更多腳本查看:網絡安全-python腳本資源整理

嗅探(抓包與解析)

sniff

@conf.commands.register
def sniff(*args, **kwargs):
    sniffer = AsyncSniffer()
    sniffer._run(*args, **kwargs)
    return sniffer.results

 實例化了一個AsyncSniffer類,並運行

sniff參數
參數 含義
count 要捕獲的數據包數。 0表示無窮大。
store 存儲嗅探的數據包還是丟棄它們
prn 應用於每個數據包的功能。如果有返回則展示。
例如:prn = lambda x:x.summary()
session 一個會話,用於處理數據包流的流解碼器。
例如:IPSession(對流進行碎片整理)或NetflowSession
filter 要應用的BPF過濾器。
lfilter 適用於每個數據包的Python函數,以確定是否
可以採取進一步的措施。
例如:lfilter = lambda x:x.haslayer(填充)
offline PCAP文件(或PCAP文件列表),用於從中讀取數據包。
timeout 在給定時間後停止嗅探(默認值:None)
L2socket 使用提供的L2socket(默認值:use conf.L2listen)
open_socket 提供一個準備使用的對象(或對象列表).recv()開啓
stop_filter

將Python函數應用於每個數據包以確定是否
我們必須在此數據包之後停止捕獲。

例如:stop_filter = lambda x:x.haslayer(TCP)

iface 接口或接口列表(默認值:None for sniffing on all interfaces)。
monitor 使用監視模式。可能並非在所有操作系統上都可用
starts_callback 嗅探器開始嗅探後立即調用(默認值:None)
from scapy.all import *


def tcpSniff():
    packets = sniff(filter="ip.src == 192.168.31.164 and ip.dst==192.168.31.174 and tcp and tcp.dstport==6633", count=20)
    for pkt in packets:
        pkt.show()
        # print(pkt)


def main():
    print('1.tcp sniff')
    choice = int(input('please input number:'))
    if choice == 1:
        tcpSniff()


if __name__ == '__main__':
    main()

 開啓了python-網絡編程之socket中寫的Tcp服務器和客戶端,然後運行了上面的代碼

抓包結果

但是

部分抓包

這個UDP是什麼鬼????在這個filter和wireshark的還不一樣,效果不是很好

wireshark

wireshark同樣的過濾器,顯示的效果就很好。

Fuzz

def Tcpfuzz(dip, dport, payload):
    try:
        pkt = fuzz(IP(dst=dip) / TCP(dport=dport) / payload)
        pkt1 = IP(dst=dip) / fuzz(TCP(dport=dport) / payload)
        # fuzz 參數必須是scapy中的Packet對象
        # pkt2 = IP(dst=dip) / TCP(dport=dport) / fuzz(payload)
        send(pkt)
        send(pkt1)
        # send(pkt2)
    except Exception as e:
        print(e)


if __name__ == "__main__":
    for i in range(10):
        Tcpfuzz('192.168.31.164', 6633, bytes("fuzz".encode(encoding='utf-8')))
抓包

我開了自己寫的TCP服務器,python-網絡編程之socket中有提到,不然的話抓包都是紅色是Tcp錯誤。

只對數據進行fuzz時會出現'bytes' object has no attribute 'copy',根據官方用戶手冊

fuzz舉例
fuzz參數

 

 fuzz只能對Packet的對象進行默認值的替換,copy函數是Packet的。到應用層,把數據包作爲http協議的參數進行fuzz時可能可以,還未嘗試。

缺點

無法進行持續Tcp連接,在發送連接函數中有socket.close(),Tcp層傳輸的數據無法fuzz

可能更新syn掃描,http等內容

未完待續...

更多內容查看:網絡安全-自學筆記

數據結構專欄:數據結構(嚴蔚敏版)與算法的實現(含全部代碼)

STL專欄:C++ STL容器用法示例大全

OpenGL專欄:現代OpenGL入門教程

有問題請下方評論,轉載請註明出處,並附有原文鏈接,謝謝!如有侵權,請及時聯繫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章