網路安全威脅與防禦的重要性

在現代數位化的社會中,網路安全已經成為企業與個人都必須高度重視的關鍵議題。隨著網路技術的快速發展與應用範圍的不斷擴大,各種網路攻擊手法也日益精進與多樣化。攻擊者利用系統漏洞、配置缺陷或是人為疏失,試圖入侵目標系統、竊取敏感資料或是破壞正常服務。這些攻擊不僅可能造成直接的經濟損失,更可能損害企業聲譽、洩露客戶隱私,甚至影響國家安全。

暴力破解是最古老但仍然有效的攻擊手法之一。這種方法的核心概念是系統性地嘗試所有可能的組合,直到找到正確的答案為止。在網路安全領域中,暴力破解可以應用於多個層面,包括破解密碼、掃描有效的 IP 位址、尋找開放的網路服務埠號等。雖然這種方法看似簡單粗暴,但在某些情況下仍然能夠取得成效,特別是當目標系統缺乏適當的防護機制時。

Google Hacking 則代表了另一種截然不同的攻擊思維。攻擊者不是直接攻擊目標系統,而是利用搜尋引擎強大的索引與查詢能力,尋找目標組織無意中暴露在網際網路上的敏感資訊。這些資訊可能包括配置檔案、內部文件、資料庫備份、系統錯誤訊息等。透過精心設計的搜尋查詢語句,攻擊者可以在不直接接觸目標系統的情況下,收集大量有價值的情報,為後續的滲透攻擊做準備。

登入監控系統則是防禦端的重要工具。透過持續監控系統的登入活動,及時發現異常的登入嘗試模式,系統管理員可以在攻擊成功之前採取防禦措施。這種主動式的安全監控機制,能夠大幅提升系統抵禦暴力破解攻擊的能力。當系統偵測到多次失敗的登入嘗試時,可以自動觸發防禦機制,例如鎖定帳號、阻擋來源 IP 位址,或是發送告警通知給管理員。

本文將深入探討這些網路攻擊技術的原理與實作方法,同時也會詳細說明相應的防禦策略與最佳實踐。透過理解攻擊者的思維與手法,資訊安全從業人員能夠更有效地設計防禦機制,建立多層次的安全防護體系。文章將提供完整的 Python 程式碼範例,讓讀者不僅能夠理解理論概念,更能夠實際動手實作,在受控的測試環境中驗證這些技術的運作方式。

需要特別強調的是,本文介紹的攻擊技術僅供教育與研究用途,協助資訊安全從業人員理解潛在威脅,以便設計更有效的防禦機制。任何未經授權的攻擊行為都可能觸犯法律,讀者應該始終遵守相關法律規範,只在經過授權的環境中進行安全測試。對於台灣的資訊安全從業人員而言,了解這些攻防技術不僅有助於保護組織的資訊資產,更能夠提升整體的資安意識與防護能力。

暴力破解 IP 位址的技術原理與實作

在網路通訊中,IP 位址是每個連接到網路的裝置都必須具備的唯一識別碼。在正常的網路環境中,裝置通常透過 DHCP (Dynamic Host Configuration Protocol) 服務自動獲得 IP 位址配置,包括 IP 位址本身、子網路遮罩、預設閘道與 DNS 伺服器等資訊。這種自動化的配置機制大幅簡化了網路管理的複雜度,使用者只需要將裝置連接到網路,就能夠自動完成網路配置並開始通訊。

然而在某些特殊情況下,DHCP 服務可能無法正常運作或是被刻意停用。這可能是因為網路管理員出於安全考量採用靜態 IP 配置策略,也可能是因為 DHCP 伺服器故障或是網路配置錯誤。對於合法的網路使用者而言,這種情況下需要手動配置正確的網路參數才能連接網路。但對於攻擊者來說,如果無法透過正常管道獲得網路配置資訊,就可能採用暴力破解的方式來尋找有效的 IP 位址。

暴力破解 IP 位址的核心概念是系統性地嘗試特定 IP 範圍內的所有可能位址,透過發送網路封包來測試每個 IP 位址是否有效。最常見的測試方法是使用 ICMP (Internet Control Message Protocol) 的 echo request,也就是俗稱的 ping 指令。當攻擊者向某個 IP 位址發送 ping 封包時,如果該位址上存在活躍的主機且未被防火牆阻擋,就會收到回應封包,表示這是一個有效的 IP 位址。

這種攻擊方法的效率取決於多個因素。首先是搜尋空間的大小,一個標準的 Class C 網路包含 254 個可用的主機位址,如果逐一測試可能需要相當長的時間。其次是網路的回應速度,如果網路延遲較高或是目標主機回應較慢,整個掃描過程會更加耗時。最後是防禦機制的存在與否,許多現代防火牆會限制或阻擋 ICMP 流量,使得這種簡單的 ping 掃描失效。

以下是使用 Python 實作的暴力破解 IP 位址掃描工具。這個工具展示了如何系統性地掃描 IP 範圍,並透過 ping 測試來識別活躍的主機。

#!/usr/bin/python3
import os
import re
import sys
from random import randint, shuffle

# 設定網路介面名稱
device = "wlp2s0"

# 生成 1 到 253 的 IP 範圍並隨機打亂順序
ips = list(range(1, 254))
shuffle(ips)

def ping_ip(ip):
    """
    向指定的 IP 位址發送 ping 封包並檢查回應
    
    參數:
        ip: 要測試的目標 IP 位址
    
    返回:
        如果收到回應則結束程式,表示找到有效的 IP
    """
    # 發送單次 ping 封包,等待時間為 1 秒
    fh = os.popen("ping -c 1 -W 1 " + ip)
    resp = fh.read()
    
    # 檢查回應中是否包含成功的標記
    if re.search("bytes from", resp, re.MULTILINE):
        print("成功從 " + ip + " 收到回應")
        print("找到有效的網路配置")
        sys.exit(0)

# 主要掃描迴圈
while len(ips) > 0:
    # 隨機選擇主機位址的最後一個八位元組
    host_byte = randint(2, 253)
    
    # 從打亂的列表中取出一個網路位址
    ip = ips.pop()
    
    print("正在掃描網路 192.168." + str(ip) + ".0")
    
    # 配置網路介面的 IP 位址
    cmd = "ifconfig " + device + " 192.168." + str(ip) + "." + str(host_byte) + " up"
    os.system(cmd)
    
    # 測試常見的閘道位址
    ping_ip("192.168." + str(ip) + ".1")
    ping_ip("192.168." + str(ip) + ".254")

print("掃描完成,未找到有效的網路配置")

這個程式碼的運作流程展現了暴力破解的典型特徵。程式首先匯入必要的模組,包括用於執行系統指令的 os 模組、用於正規表達式匹配的 re 模組、用於程式控制的 sys 模組,以及用於產生隨機數的 random 模組。程式定義了要使用的網路介面名稱,這個名稱需要根據實際系統的網路介面進行調整。

程式生成了一個包含 1 到 253 的數字列表,代表 IP 位址第三個八位元組的所有可能值。使用 shuffle 函數將這個列表隨機打亂,這樣做的目的是避免按照順序掃描可能引起的規律性流量模式,增加隱蔽性。定義的 ping_ip 函數負責實際的 IP 位址測試工作,它使用 os.popen 執行系統的 ping 指令,參數 -c 1 表示只發送一個封包,-W 1 表示等待回應的超時時間為一秒。

程式讀取 ping 指令的輸出結果,並使用正規表達式搜尋是否包含 “bytes from” 字串。這個字串是 ping 指令成功收到回應時的標準輸出格式,如果找到這個字串就表示目標 IP 位址是有效的,程式會印出成功訊息並結束執行。主要的掃描迴圈會持續進行,直到列表中的所有 IP 範圍都被測試完畢。

在每次迴圈中,程式隨機選擇一個 2 到 253 之間的數字作為主機位址的最後一個八位元組。避免使用 0、1、254 和 255 這些通常保留給網路位址、廣播位址或閘道的數值。從打亂的列表中取出一個網路位址,組合成完整的 IP 位址。使用 ifconfig 指令配置網路介面,將其 IP 設定為剛才組合的位址。

配置完成後,程式會測試兩個常見的閘道位址。在大多數網路環境中,預設閘道通常配置為子網路的第一個可用位址(.1)或最後一個可用位址(.254)。如果這兩個位址中的任何一個有回應,就表示找到了有效的網路配置,程式可以使用這個配置連接到網路。

這種暴力破解方法雖然簡單,但在某些情況下仍然有效。特別是在小型網路環境中,或是當攻擊者已經知道目標網路使用的 IP 範圍時,這種方法可以快速找到有效的網路配置。然而這種方法也有明顯的局限性。首先是效率問題,如果目標網路使用較大的 IP 範圍,掃描所有可能的位址可能需要很長時間。其次是隱蔽性問題,頻繁的網路配置變更與大量的 ping 封包很容易被網路監控系統發現。

對於網路管理員而言,防範這種暴力破解攻擊需要採取多層次的防護措施。最基本的是確保 DHCP 服務正常運作,並且只允許已註冊的 MAC 位址獲得 IP 配置。使用 DHCP Snooping 技術可以防止未經授權的 DHCP 伺服器,確保裝置只能從合法的 DHCP 伺服器獲得配置。實施網路存取控制 (Network Access Control, NAC) 可以在裝置連接網路前進行身份驗證與合規性檢查,拒絕未經授權的裝置接入。

使用 VLAN (Virtual Local Area Network) 技術將網路分割成多個邏輯子網,可以限制攻擊者即使獲得某個子網的 IP 位址,也無法存取其他敏感的網路區段。部署入侵偵測系統 (Intrusion Detection System, IDS) 可以監控異常的網路流量模式,例如短時間內大量的 ping 請求或是頻繁的網路配置變更,及時發現潛在的攻擊行為。配置防火牆規則限制 ICMP 流量,可以阻止或限制 ping 掃描,雖然這可能影響某些正常的網路診斷功能。

Google Hacking 技術的原理與應用

Google Hacking,也稱為 Google Dorking,是一種利用 Google 搜尋引擎的進階搜尋功能來尋找網際網路上敏感資訊與系統漏洞的技術。這種技術的獨特之處在於,攻擊者不需要直接與目標系統互動,而是透過 Google 已經索引的資訊來收集情報。由於 Google 的爬蟲程式會持續掃描與索引網際網路上的內容,許多組織無意中暴露的敏感資訊可能已經被 Google 收錄,攻擊者只需要使用適當的搜尋語法就能找到這些資訊。

Google 提供了豐富的進階搜尋運算子,這些運算子原本是為了幫助使用者更精確地找到所需資訊而設計的,但同時也成為攻擊者強大的偵察工具。常用的搜尋運算子包括 intitle 用於搜尋網頁標題中包含特定關鍵字的頁面、inurl 用於搜尋 URL 中包含特定字串的頁面、filetype 用於搜尋特定類型的檔案、site 用於限制搜尋範圍在特定網域內,以及 intext 用於搜尋網頁內容中包含特定文字的頁面。

Google Hacking Database (GHDB) 是由資訊安全研究者 Johnny Long 建立的資料庫,收錄了大量經過驗證的 Google 搜尋查詢語句,這些查詢可以用來發現各種類型的安全問題。GHDB 將這些查詢分類整理,包括尋找敏感目錄、暴露的配置檔案、資料庫備份、登入頁面、錯誤訊息等。資訊安全從業人員可以使用這些查詢來測試自己組織的資訊暴露程度,及時發現並修正問題。

攻擊者可能使用的典型 Google Hacking 查詢包括 intitle index of 搜尋未受保護的目錄列表,這些目錄可能包含敏感檔案或系統備份。filetype pdf confidential 搜尋標記為機密的 PDF 文件,這些文件可能包含商業機密或內部資訊。inurl admin 或 inurl login 搜尋系統的管理介面或登入頁面,作為後續攻擊的入口點。site example.com filetype sql 搜尋特定網站的 SQL 檔案,這些可能是資料庫備份或配置檔案。

以下是一個簡單的 Google Hacking 工具實作,展示如何自動化地使用搜尋查詢來收集資訊。

#!/usr/bin/env python3

import re
import sys
import time
from googlesearch import search

def google_hacking_scan(query_file):
    """
    讀取查詢檔案並執行 Google 搜尋
    
    參數:
        query_file: 包含搜尋查詢語句的檔案路徑
    """
    if not query_file:
        print("使用方式: " + sys.argv[0] + " <查詢檔案>")
        print("查詢檔案應包含每行一個 Google 搜尋語句")
        sys.exit(1)
    
    try:
        with open(query_file, 'r', encoding='utf-8') as fh:
            queries = fh.readlines()
    except FileNotFoundError:
        print(f"錯誤: 找不到檔案 {query_file}")
        sys.exit(1)
    except Exception as e:
        print(f"錯誤: 讀取檔案時發生問題 - {str(e)}")
        sys.exit(1)
    
    print(f"載入了 {len(queries)} 個搜尋查詢")
    print("開始執行 Google Hacking 掃描...\n")
    
    for query in queries:
        query = query.strip()
        if not query or query.startswith('#'):
            # 跳過空行和註解行
            continue
        
        print(f"\n正在搜尋: {query}")
        print("-" * 60)
        
        try:
            # 執行 Google 搜尋
            results = search(query, num_results=10, sleep_interval=2)
            
            result_count = 0
            for link in results:
                # 過濾掉 YouTube 等不相關的結果
                if not re.search(r"(youtube|facebook|twitter)", link, re.IGNORECASE):
                    print(f"  發現: {link}")
                    result_count += 1
            
            if result_count == 0:
                print("  未找到相關結果")
            else:
                print(f"\n  總計找到 {result_count} 個相關結果")
            
            # 延遲以避免被 Google 封鎖
            time.sleep(3)
            
        except Exception as e:
            print(f"  搜尋時發生錯誤: {str(e)}")
            continue

if __name__ == "__main__":
    if len(sys.argv) < 2:
        google_hacking_scan(None)
    else:
        google_hacking_scan(sys.argv[1])

這個工具的設計展現了自動化 Google Hacking 的基本概念。程式首先檢查是否提供了查詢檔案作為命令列參數,如果沒有則顯示使用說明。使用 with 語句安全地開啟檔案,並設定編碼為 UTF-8 以支援中文查詢。加入錯誤處理機制,當檔案不存在或讀取發生錯誤時提供清楚的錯誤訊息。

程式逐行讀取查詢檔案,每一行代表一個 Google 搜尋查詢。使用 strip 方法移除行首行尾的空白字元,跳過空行與以井號開頭的註解行,讓查詢檔案更具可讀性與可維護性。對於每個有效的查詢,程式呼叫 googlesearch 模組執行實際的搜尋操作。設定 num_results 參數限制返回的結果數量,避免產生過多的流量。設定 sleep_interval 參數在每次請求之間加入延遲,避免因請求過於頻繁而被 Google 識別為機器人並封鎖。

程式過濾搜尋結果,排除 YouTube、Facebook、Twitter 等社群媒體網站的連結,因為這些通常不是安全測試的目標。印出找到的相關連結,並統計結果數量提供回饋。在每個查詢之間加入額外的延遲時間,進一步降低被偵測與封鎖的風險。加入異常處理機制,當某個查詢失敗時不會中斷整個掃描流程,而是繼續處理下一個查詢。

Google Hacking 的威力在於它能夠快速且大規模地收集情報,而且完全不需要直接接觸目標系統,大幅降低被偵測的風險。然而這種技術也提醒組織必須重視資訊在網際網路上的暴露程度。防範 Google Hacking 攻擊需要從多個層面著手。

最基本的是禁用網站伺服器的目錄瀏覽功能。許多網站伺服器預設允許使用者瀏覽沒有索引檔案的目錄內容,這可能暴露敏感檔案的存在。透過適當的伺服器配置,可以確保所有目錄都需要明確的索引檔案才能存取。使用 robots.txt 檔案可以指示搜尋引擎爬蟲不要索引特定的目錄或檔案,雖然這不是絕對的防護機制,但可以減少資訊被搜尋引擎收錄的機會。

實施嚴格的存取控制,確保敏感檔案與目錄需要身份驗證才能存取。即使這些資源的 URL 被洩露或被搜尋引擎索引,未經授權的使用者也無法實際存取內容。定期進行安全審計,使用 Google Hacking 技術主動搜尋自己組織可能暴露的資訊,及時發現並修正問題。可以使用 site 運算子限制搜尋範圍在自己的網域,檢查是否有不應該公開的資訊被索引。

對於已經被 Google 索引的敏感資訊,可以使用 Google Search Console 提交 URL 移除請求,要求 Google 從索引中移除特定的頁面。然而這只是治標的方法,根本的解決方案還是要從源頭防止敏感資訊暴露在網際網路上。培訓員工的資訊安全意識,讓他們了解什麼資訊不應該放在公開可存取的位置,避免因為疏忽而造成資訊洩露。

SMB 分享掃描工具的開發與應用

SMB (Server Message Block) 協定是 Windows 作業系統中用於檔案分享、列印服務與其他網路通訊的核心協定。在 Windows 網路環境中,使用者可以輕鬆地透過 SMB 協定分享資料夾與印表機,讓網路中的其他使用者存取這些資源。然而許多使用者在設定 SMB 分享時,可能因為不了解安全設定的重要性,或是為了方便而採用過於寬鬆的權限設定,導致敏感資料暴露在網路上。

最常見的安全問題是設定了不需要密碼驗證的匿名分享。使用者可能將整個 C 磁碟機或包含重要文件的資料夾設定為分享,並且允許所有人讀取甚至寫入,而沒有要求任何身份驗證。這種配置在區域網路環境中可能看似方便,但實際上為攻擊者提供了輕鬆存取敏感資料的機會。攻擊者只需要掃描網路中的 SMB 服務,就能發現這些開放的分享資源,並可能取得機密文件、個人資料或系統配置資訊。

以下是一個 SMB 分享掃描工具的實作,展示如何自動化地掃描 IP 範圍內的 SMB 分享資源。

#!/usr/bin/env python3

import sys
import os
import subprocess
from random import randint

def get_ips(start_ip, stop_ip):
    """
    計算 IP 範圍內的所有 IP 位址
    
    參數:
        start_ip: 起始 IP 位址 (例如: "192.168.1.1")
        stop_ip: 結束 IP 位址 (例如: "192.168.1.254")
    
    返回:
        包含所有 IP 位址的列表
    """
    ips = []
    tmp = []
    
    # 將起始 IP 的每個八位元組轉換為十六進位
    for octet in start_ip.split('.'):
        tmp.append("%02X" % int(octet))
    
    # 將十六進位字串轉換為十進位數字
    start_dec = int(''.join(tmp), 16)
    tmp = []
    
    # 對結束 IP 執行相同的轉換
    for octet in stop_ip.split('.'):
        tmp.append("%02X" % int(octet))
    
    stop_dec = int(''.join(tmp), 16)
    
    # 生成範圍內的所有 IP 位址
    while start_dec <= stop_dec:
        bytes_list = []
        
        # 計算每個八位元組的值
        bytes_list.append(str(int(start_dec / 16777216)))
        rem = start_dec % 16777216
        
        bytes_list.append(str(int(rem / 65536)))
        rem = rem % 65536
        
        bytes_list.append(str(int(rem / 256)))
        rem = rem % 256
        
        bytes_list.append(str(rem))
        
        # 組合成完整的 IP 位址字串
        ips.append(".".join(bytes_list))
        start_dec += 1
    
    return ips

def smb_share_scan(ip):
    """
    掃描指定 IP 位址的 SMB 分享資源
    
    參數:
        ip: 要掃描的目標 IP 位址
    """
    print(f"\n正在掃描 {ip} 的 SMB 分享...")
    print("-" * 60)
    
    try:
        # 使用 smbclient 嘗試列出分享資源
        # -q: 安靜模式
        # -N: 不使用密碼
        # -L: 列出伺服器的分享資源
        cmd = ["smbclient", "-q", "-N", "-L", ip]
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
        
        if result.returncode == 0:
            output = result.stdout
            if "Sharename" in output:
                print("發現 SMB 分享:")
                print(output)
                
                # 分析分享名稱
                shares = []
                for line in output.split('\n'):
                    if line.strip() and not line.startswith('\t'):
                        parts = line.split()
                        if len(parts) >= 1 and parts[0] not in ['Sharename', 'Server', 'Workgroup']:
                            shares.append(parts[0])
                
                if shares:
                    print(f"\n找到 {len(shares)} 個分享:")
                    for share in shares:
                        print(f"  - {share}")
            else:
                print("未找到可存取的分享")
        else:
            print(f"連接失敗或主機無回應")
    
    except subprocess.TimeoutExpired:
        print("連接超時")
    except FileNotFoundError:
        print("錯誤: 找不到 smbclient 命令")
        print("請安裝 samba-client 套件")
    except Exception as e:
        print(f"掃描時發生錯誤: {str(e)}")

def main():
    """
    主程式進入點
    """
    if len(sys.argv) < 2:
        print("使用方式:")
        print(f"  {sys.argv[0]} <IP 位址>")
        print(f"  {sys.argv[0]} <起始IP>-<結束IP>")
        print("\n範例:")
        print(f"  {sys.argv[0]} 192.168.1.100")
        print(f"  {sys.argv[0]} 192.168.1.1-192.168.1.254")
        sys.exit(1)
    
    target = sys.argv[1]
    
    # 檢查是否為 IP 範圍
    if '-' in target:
        start_ip, stop_ip = target.split("-")
        start_ip = start_ip.strip()
        stop_ip = stop_ip.strip()
        
        print(f"開始掃描 IP 範圍: {start_ip}{stop_ip}")
        ips = get_ips(start_ip, stop_ip)
        print(f"總計 {len(ips)} 個 IP 位址")
        
        # 隨機順序掃描以增加隱蔽性
        while len(ips) > 0:
            i = randint(0, len(ips) - 1)
            lookup_ip = ips[i]
            del ips[i]
            smb_share_scan(lookup_ip)
    else:
        # 單一 IP 位址掃描
        smb_share_scan(target)

if __name__ == "__main__":
    main()

這個 SMB 掃描工具的設計展現了網路掃描工具的典型架構。程式定義了 get_ips 函數來計算指定範圍內的所有 IP 位址,這個函數使用位元運算的方式處理 IP 位址,確保能夠正確處理跨越多個子網的範圍。smb_share_scan 函數負責實際的 SMB 分享掃描工作,它使用系統的 smbclient 命令來嘗試連接目標主機並列出可用的分享資源。

程式使用 subprocess 模組安全地執行外部命令,並設定超時時間避免因為主機無回應而長時間等待。capture_output 參數讓程式能夠捕獲命令的輸出結果進行分析,text 參數確保輸出以文字格式處理而非位元組流。程式分析 smbclient 的輸出,提取分享名稱並統計數量,為使用者提供清楚的掃描結果摘要。

加入完善的錯誤處理機制,包括處理超時、找不到必要工具、網路錯誤等各種可能的異常情況。主程式支援兩種使用模式,可以掃描單一 IP 位址,也可以掃描指定的 IP 範圍。當掃描 IP 範圍時,程式採用隨機順序而非順序掃描,這樣可以減少被入侵偵測系統識別的機會,提高掃描的隱蔽性。

SMB 分享掃描工具對於網路管理員而言是重要的安全審計工具,可以幫助發現網路中存在的不當分享配置。然而對於攻擊者來說,這也是偵察階段的重要工具,可以快速找到潛在的攻擊目標。防範 SMB 分享相關的安全威脅需要採取多項措施。

最重要的是教育使用者正確配置 SMB 分享權限,確保只有必要的使用者能夠存取分享資源,並且所有存取都需要經過身份驗證。避免使用 Guest 帳號或允許匿名存取,除非有明確的業務需求且經過適當的風險評估。定期審查網路中的 SMB 分享配置,使用類似本文介紹的掃描工具主動發現不當的分享設定,及時修正問題。

在網路層面,可以透過防火牆規則限制 SMB 協定的流量,例如只允許特定的 IP 範圍存取 SMB 服務,阻擋來自網際網路的 SMB 連接嘗試。使用網路分段技術將敏感系統隔離在獨立的網路區段,即使攻擊者在某個區段獲得立足點,也無法輕易橫向移動到其他區段。部署入侵偵測系統監控異常的 SMB 流量模式,例如短時間內大量的 SMB 連接嘗試,及時發現潛在的掃描或攻擊行為。

登入監控系統的設計與實作

在資訊安全領域中,監控與偵測是防禦策略的重要組成部分。雖然我們努力強化系統的防禦措施,但沒有任何系統是絕對安全的。因此建立有效的監控機制,能夠及時發現異常行為並採取應對措施,是確保系統安全的關鍵。登入監控系統就是這種防禦思維的具體實現,透過持續監控系統的登入活動,可以及早發現暴力破解攻擊或其他未經授權的存取嘗試。

暴力破解登入攻擊是最常見的攻擊方式之一。攻擊者使用自動化工具系統性地嘗試各種可能的使用者名稱與密碼組合,希望最終能夠猜中正確的認證資訊並獲得系統存取權限。雖然現代系統通常都有密碼複雜度要求,但許多使用者仍然使用簡單或常見的密碼,或是在多個系統使用相同的密碼,這為暴力破解攻擊提供了成功的機會。

許多線上服務系統實施了帳號鎖定機制,當偵測到連續多次失敗的登入嘗試時,會暫時或永久鎖定該帳號,直到管理員解鎖或使用者透過其他管道重新驗證身份。然而在某些本地系統或是未實施這種保護機制的系統中,攻擊者可以無限制地嘗試登入,只要有足夠的時間與耐心,最終可能成功破解。

以下是一個登入監控系統的實作,展示如何監控系統登入日誌,偵測暴力破解攻擊,並自動執行防禦措施。

#!/usr/bin/env python3

import os
import re
import sys
import time
from collections import defaultdict

# 配置參數
LOG_FILE = "/var/log/auth.log"  # 認證日誌檔案位置
MAX_FAILED_ATTEMPTS = 3  # 允許的最大失敗嘗試次數
LOCKOUT_DURATION = 300  # 鎖定時間(秒)
CHECK_INTERVAL = 1  # 檢查間隔(秒)

# 儲存失敗登入記錄
failed_logins = defaultdict(lambda: {'count': 0, 'first_attempt': 0, 'last_attempt': 0})

# 成功登入的正規表達式模式
success_patterns = [
    re.compile(r"Accepted password for (?P<user>\S+) from (?P<host>\S+)"),
    re.compile(r"session opened for user (?P<user>\S+)"),
]

# 失敗登入的正規表達式模式
failed_patterns = [
    re.compile(r"Failed password for (?P<user>\S+) from (?P<host>\S+)"),
    re.compile(r"FAILED LOGIN .* FOR '(?P<user>\S+)'"),
    re.compile(r"authentication failure.*user=(?P<user>\S+)"),
]

def log_message(message):
    """
    記錄訊息到標準輸出和系統日誌
    
    參數:
        message: 要記錄的訊息
    """
    timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
    formatted_message = f"[{timestamp}] {message}"
    print(formatted_message)
    
    # 同時記錄到系統日誌
    os.system(f'logger -t LoginWatcher "{message}"')

def play_warning(message):
    """
    播放警告訊息
    
    參數:
        message: 要播放的警告訊息
    """
    log_message(f"警告: {message}")
    
    # 使用文字轉語音工具播放警告
    try:
        os.system(f'echo "{message}" | festival --tts 2>/dev/null')
    except:
        pass

def handle_successful_login(user, host):
    """
    處理成功登入事件
    
    參數:
        user: 登入的使用者名稱
        host: 來源主機 IP 位址
    """
    key = (user, host)
    
    if key in failed_logins:
        log_message(f"使用者 {user}{host} 成功登入,清除先前的失敗記錄")
        del failed_logins[key]

def handle_failed_login(user, host):
    """
    處理失敗登入事件
    
    參數:
        user: 嘗試登入的使用者名稱
        host: 來源主機 IP 位址
    """
    key = (user, host)
    current_time = time.time()
    
    # 檢查是否在鎖定期內
    if key in failed_logins:
        time_since_first = current_time - failed_logins[key]['first_attempt']
        
        # 如果超過鎖定時間,重置計數器
        if time_since_first > LOCKOUT_DURATION:
            failed_logins[key] = {
                'count': 1,
                'first_attempt': current_time,
                'last_attempt': current_time
            }
            log_message(f"使用者 {user}{host} 登入失敗 (1/{MAX_FAILED_ATTEMPTS})")
            return
    
    # 更新失敗記錄
    if key not in failed_logins:
        failed_logins[key]['first_attempt'] = current_time
    
    failed_logins[key]['count'] += 1
    failed_logins[key]['last_attempt'] = current_time
    
    count = failed_logins[key]['count']
    log_message(f"使用者 {user}{host} 登入失敗 ({count}/{MAX_FAILED_ATTEMPTS})")
    
    # 檢查是否達到閾值
    if count >= MAX_FAILED_ATTEMPTS:
        handle_attack_detected(user, host, count)

def handle_attack_detected(user, host, count):
    """
    處理偵測到的攻擊
    
    參數:
        user: 攻擊目標使用者
        host: 攻擊來源 IP
        count: 失敗嘗試次數
    """
    warning_message = f"偵測到暴力破解攻擊!使用者 {user}{host} 已失敗 {count} 次"
    play_warning(warning_message)
    
    # 執行防禦措施
    log_message("執行防禦措施...")
    
    # 選項 1: 阻擋來源 IP (需要 root 權限)
    try:
        os.system(f"iptables -A INPUT -s {host} -j DROP")
        log_message(f"已阻擋 IP 位址: {host}")
    except:
        log_message("無法執行 iptables 命令,可能需要 root 權限")
    
    # 選項 2: 發送告警郵件 (需要配置郵件系統)
    try:
        email_body = f"偵測到來自 {host} 對帳號 {user} 的暴力破解攻擊\\n失敗嘗試次數: {count}"
        os.system(f'echo "{email_body}" | mail -s "安全警告: 暴力破解攻擊" root')
    except:
        pass

def monitor_log_file():
    """
    監控日誌檔案
    """
    log_message("開始監控登入活動...")
    log_message(f"監控檔案: {LOG_FILE}")
    log_message(f"失敗閾值: {MAX_FAILED_ATTEMPTS} 次")
    log_message(f"鎖定時間: {LOCKOUT_DURATION} 秒")
    
    try:
        # 開啟日誌檔案並移動到檔案結尾
        with open(LOG_FILE, 'r') as log_file:
            # 移動到檔案結尾
            log_file.seek(0, 2)
            
            while True:
                line = log_file.readline()
                
                if not line:
                    # 沒有新內容,等待後重試
                    time.sleep(CHECK_INTERVAL)
                    continue
                
                # 檢查成功登入
                for pattern in success_patterns:
                    match = pattern.search(line)
                    if match:
                        user = match.group('user')
                        # 某些模式可能沒有 host 群組
                        host = match.group('host') if 'host' in match.groupdict() else 'local'
                        handle_successful_login(user, host)
                        break
                
                # 檢查失敗登入
                for pattern in failed_patterns:
                    match = pattern.search(line)
                    if match:
                        user = match.group('user')
                        host = match.group('host') if 'host' in match.groupdict() else 'local'
                        handle_failed_login(user, host)
                        break
    
    except FileNotFoundError:
        log_message(f"錯誤: 找不到日誌檔案 {LOG_FILE}")
        sys.exit(1)
    except PermissionError:
        log_message(f"錯誤: 沒有權限讀取日誌檔案 {LOG_FILE}")
        log_message("請使用 root 權限執行此程式")
        sys.exit(1)
    except KeyboardInterrupt:
        log_message("\n監控已停止")
        sys.exit(0)
    except Exception as e:
        log_message(f"發生錯誤: {str(e)}")
        sys.exit(1)

if __name__ == "__main__":
    # 檢查是否以 root 權限執行
    if os.geteuid() != 0:
        print("警告: 此程式需要 root 權限才能執行某些防禦措施")
        print("建議使用 sudo 執行此程式")
    
    monitor_log_file()

這個登入監控系統的設計展現了安全監控工具的核心要素。程式定義了多個可配置的參數,包括日誌檔案位置、允許的失敗嘗試次數、鎖定時間與檢查間隔,這些參數可以根據不同的安全策略進行調整。使用 defaultdict 儲存失敗登入記錄,自動初始化新的鍵值,簡化程式碼邏輯。

程式定義了多個正規表達式模式來匹配不同格式的登入日誌。由於不同的 Linux 發行版與認證機制可能產生不同格式的日誌訊息,使用多個模式可以提高相容性。log_message 函數不僅將訊息輸出到標準輸出,也記錄到系統日誌,方便後續的審計與分析。play_warning 函數使用文字轉語音工具播放警告訊息,這種聲音告警可以立即引起管理員的注意。

handle_successful_login 函數處理成功登入事件,當使用者成功登入時清除該使用者的失敗記錄,避免誤判。handle_failed_login 函數處理失敗登入事件,實作了基於時間窗口的計數機制,如果距離第一次失敗嘗試已經超過鎖定時間,則重置計數器,這避免了因為偶爾的輸錯密碼而觸發防禦機制。

handle_attack_detected 函數在偵測到攻擊時執行防禦措施,包括使用 iptables 阻擋攻擊來源的 IP 位址,以及發送電子郵件告警通知管理員。monitor_log_file 函數是程式的核心,它持續監控日誌檔案的新增內容,使用 seek 方法移動到檔案結尾,然後持續讀取新增的行,實現即時監控。

程式加入完善的錯誤處理機制,包括處理檔案不存在、權限不足、使用者中斷等各種情況,確保程式能夠優雅地處理異常。主程式在啟動時檢查執行權限,如果不是以 root 權限執行則顯示警告訊息,因為某些防禦措施如修改 iptables 規則需要 root 權限。

這個登入監控系統提供了基本但有效的暴力破解防護機制。然而在實際部署時還有許多可以改進的地方。可以加入白名單機制,對於信任的 IP 位址不進行監控或使用較寬鬆的閾值。實作更智慧的攻擊偵測演算法,例如分析失敗嘗試的時間模式,區分人為輸錯密碼與自動化攻擊。整合威脅情報資料庫,當偵測到來自已知惡意 IP 的登入嘗試時提高警戒等級。

對於台灣的系統管理員而言,部署這類監控系統需要考慮本地的法規與隱私保護要求。日誌的收集與儲存必須符合個人資料保護法的規範,確保敏感資訊得到適當的保護。在實施自動化防禦措施如 IP 阻擋時,需要建立適當的審核與解除機制,避免誤判造成合法使用者無法存取系統。定期檢視與分析監控日誌,可以幫助了解系統面臨的威脅態勢,持續改進安全防護策略。