網路安全測試是保護系統免受攻擊的關鍵環節,而 Python 作為一門功能強大的程式語言,提供了豐富的工具和函式庫,能有效地協助安全測試工作。本文將探討如何使用 Python 進行 DNS 查詢、伺服器互動和 Fuzzing 等技術,並結合實際案例,展示如何在真實環境中應用這些技術。透過 DNSPython 取得 DNS 資訊、使用 Paramiko 與伺服器進行互動,以及利用 FuzzDB 輔助漏洞測試,都能有效提升網路安全測試的效率和準確性,並強化系統的防禦能力。

命令列執行範例

此指令碼接受目標和埠號作為引數,如下所示:

$ python3 get_banner_server.py -h
usage: get_banner_server.py [-h] --target TARGET --port PORT
取得伺服器banner
optional arguments:
  -h, --help       show this help message and exit
  --target TARGET  目標IP
  --port PORT      埠號

執行範例:

$ python3 get_banner_server.py --target www.python.org --port 80

輸出結果可能如下所示:

b'HTTP/1.1 301 Moved Permanently\r\nServer: Varnish\r\nRetry-After: 0\r\nLocation: https://www.python.org/\r\nContent-Length: 0\r\nAccept-Ranges: bytes\r\nDate: Tue, 23 Jun 2020 12:56:42 GMT\r\nVia: 1.1 varnish\r\nConnection: close\r\nX-Served-By: cache-lon4246-LON\r\nX-Cache: HIT\r\nX-Cache-Hits: 0\r\nX-Timer: S1592917002.308860,VS0,VE0\r\nStrict-Transport-Security: max-age=63072000; includeSubDomains\r\n\r\n'
HTTP/1.1 301 Moved Permanently
*****Server: Varnish*****
Retry-After: 0
Location: https://www.python.org/
Content-Length: 0
Accept-Ranges: bytes
Date: Tue, 23 Jun 2020 12:56:42 GMT
Via: 1.1 varnish
Connection: close
X-Served-By: cache-lon4246-LON
X-Cache: HIT
X-Cache-Hits: 0
X-Timer: S1592917002.308860,VS0,VE0
Strict-Transport-Security: max-age=63072000; includeSubDomains

內容解密:

  • 從輸出結果中,我們可以看到伺服器的banner資訊,包括伺服器名稱(Varnish)和其他HTTP頭資訊。
  • 這種資訊對於瞭解目標系統的組態和潛在漏洞非常有幫助。

DNS 協定與 DNSPython 模組:深入解析 DNS 伺服器資訊取得

DNS 協定基礎

DNS(Domain Name System)是將網域名稱與 IP 位址進行對映的全球分散式資料函式庫。它是一個開放且層級化的系統,許多組織選擇執行自己的 DNS 伺服器。這些伺服器允許其他機器解析來自內部網路的請求,以解析網域名稱。

DNS 協定用於不同的目的,其中最常見的有:

  • 名稱解析:給定主機的完整名稱,可以取得其 IP 位址。
  • 反向位址解析:與前者相反,可以根據 IP 位址取得相關聯的名稱。
  • 郵件伺服器解析:給定郵件伺服器網域名稱,可以取得用於通訊的伺服器。

DNS 伺服器詳解

人類更擅長記住與物件相關的名稱,而非長序列的數字。記住 google.com 網域名稱比記住其 IP 位址更容易。此外,當網路基礎設施發生變化時,IP 位址可能會改變,而網域名稱保持不變。

DNS 伺服器的運作根據使用分散式和層級化的資料函式庫,其中儲存了網域名稱和 IP 位址,以及提供郵件伺服器位置服務的能力。

DNS 伺服器位於應用層,通常使用埠 53(UDP)。當客戶端傳送 DNS 封包以執行某種型別的查詢時,必須指定要查詢的記錄型別。一些最常用的記錄型別包括:

  • A 記錄:允許查詢 IPv4 位址。
  • AAAA 記錄:允許查詢 IPv6 位址。
  • MX 記錄:允許查詢郵件伺服器。
  • SOA(授權起始)記錄:指定有關網域名稱區域的資訊。
  • NS 記錄:允許查詢名稱伺服器(Nameserver)。
  • TXT 記錄:允許查詢文字格式的資訊;TXT 記錄可以包含 DMARC 和 SPF 記錄,並可用於網域名稱驗證。

DNSPython 模組介紹

DNSPython 是用 Python 編寫的開源函式庫,允許對 DNS 伺服器執行記錄查詢操作。它提供了高階和低階的存取介面。在高階介面上,它允許查詢 DNS 記錄;在低階介面上,它允許直接操作區域、名稱和註冊。

安裝 DNSPython

可以使用 Python 套件儲存函式庫或從 GitHub 下載原始碼並執行 setup.py install 檔案來安裝 DNSPython。

$ pip3 install dnspython

使用 DNSPython 取得 DNS 資訊

DNSPython 的主要套件包括 dnsdns.resolver。對於特定網域名稱,我們可以取得以下資訊:

  • 郵件伺服器記錄:response_MX = dns.resolver.query('domain', 'MX')
  • 名稱伺服器記錄:response_NS = dns.resolver.query('domain', 'NS')
  • IPv4 位址記錄:response_ipv4 = dns.resolver.query('domain', 'A')
  • IPv6 位址記錄:response_ipv6 = dns.resolver.query('domain', 'AAAA')

以下是一個使用 query() 方法取得多個主機網域名稱 IP 位址列表的範例(位於 dnspython 資料夾中的 dns_resolver.py 檔案):

import dns.resolver

hosts = ["example.com", "yahoo.com", "google.com", "microsoft.com", "cnn.com"]

for host in hosts:
    print(host)
    try:
        ip = dns.resolver.resolve(host, "A")
        for i in ip:
            print(i)
    except dns.resolver.NoAnswer:
        print(f"No A record for {host}")
    except dns.resolver.NXDOMAIN:
        print(f"Domain {host} does not exist")

檢查網域名稱關係

我們可以使用 is_subdomain() 方法檢查一個網域名稱是否是另一個網域名稱的子網域名稱(位於 dnspython 資料夾中的 check_domains.py 檔案):

import dns.name

def main(domain1, domain2):
    domain1 = dns.name.from_text(domain1)
    domain2 = dns.name.from_text(domain2)
    print(f"domain1 is subdomain of domain2: {domain1.is_subdomain(domain2)}")
    print(f"domain1 is superdomain of domain2: {domain1.is_superdomain(domain2)}")

if __name__ == '__main__':
    domain1 = 'python.org'
    domain2 = 'docs.python.org'
    main(domain1, domain2)

取得網域名稱與 IP 位址的對應關係

我們可以使用 dns.reversename 子模組和 from_address() 方法從 IP 位址取得網域名稱:

import dns.reversename

def get_domain_from_ip(ip_address):
    try:
        domain = dns.reversename.from_address(ip_address)
        return str(domain)
    except Exception as e:
        return f"Failed to get domain: {e}"

ip_address = "8.8.8.8"
print(get_domain_from_ip(ip_address))

同樣地,我們可以使用 dns.reversename.to_address() 方法從網域名稱取得 IP 位址,但這通常不是直接需要的,因為 DNS 解析通常是從網域名稱到 IP 位址。

使用DNSPython進行DNS查詢與解析

DNSPython是一個強大的Python函式庫,用於處理DNS相關操作,包括查詢、解析和管理DNS記錄。在本文中,我們將探討如何使用DNSPython來取得DNS記錄資訊以及進行反向查詢。

反向查詢

反向查詢是一種透過IP地址取得其對應的網域名稱的過程。以下是一個使用DNSPython進行反向查詢的例子:

import dns.reversename

def reverse_lookup(ip_address):
    try:
        domain = dns.reversename.from_address(ip_address)
        print(domain)
        print(dns.reversename.to_address(domain))
    except Exception as e:
        print("反向查詢失敗:", e)

if __name__ == '__main__':
    reverse_lookup("45.55.99.72")

內容解密:

  1. dns.reversename.from_address(ip_address):將IP地址轉換為反向DNS名稱。
  2. dns.reversename.to_address(domain):將反向DNS名稱轉換回IP地址。
  3. 錯誤處理:捕捉並列印任何在反向查詢過程中發生的異常。

取得DNS記錄

DNSPython允許我們查詢多種DNS記錄型別,包括’A’、‘AAAA’、‘NS’、‘SOA’、‘MX’和’TXT’等。以下是一個查詢這些記錄的例子:

import dns.resolver

def query_dns_records(domain):
    records = ['A', 'AAAA', 'NS', 'SOA', 'MX', 'TXT']
    for record in records:
        try:
            responses = dns.resolver.query(domain, record)
            print("\n記錄型別:", record)
            print("
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
")
            for response in responses:
                print(response)
        except Exception as e:
            print("無法解析查詢記錄:", record)
            print("錯誤訊息:", e)

if __name__ == '__main__':
    query_dns_records('google.com')

內容解密:

  1. dns.resolver.query(domain, record):對指定的網域名稱和記錄型別進行DNS查詢。
  2. 迴圈遍歷多種記錄型別,列印查詢結果。
  3. 錯誤處理:對於無法解析的記錄型別,捕捉並列印錯誤訊息。

Fuzzing與FuzzDB專案

Fuzzing是一種透過輸入大量隨機或惡意資料來測試系統安全性的技術。FuzzDB是一個包含已知攻擊模式和可預測資源位置模式的專案,可以用於輔助漏洞測試。

使用FuzzDB進行登入頁面識別

以下是一個使用FuzzDB中的可預測URL列表來識別登入頁面的例子:

import requests

def identify_login_pages(domain, login_urls):
    for login_url in login_urls:
        try:
            url = f"{domain}/{login_url.strip()}"
            response = requests.get(url)
            if response.status_code == 200:
                print(f"發現登入頁面:{url}")
        except Exception as e:
            print(f"請求{url}失敗:{e}")

if __name__ == '__main__':
    domain = "http://example.com"
    with open("login_urls.txt", "r") as f:
        login_urls = f.readlines()
    identify_login_pages(domain, login_urls)

內容解密:

  1. requests.get(url):對指定的URL傳送GET請求。
  2. 檢查回應狀態碼,如果是200,則表示發現了登入頁面。
  3. 錯誤處理:捕捉並列印請求過程中發生的任何異常。

與伺服器互動:FTP、SFTP與SSH

在本章中,我們將學習如何使用Python模組與FTP、SFTP和SSH伺服器進行互動。這些模組將使開發者能夠在進行與伺服器上執行的服務相關的安全測試時,更輕鬆地連線到不同型別的伺服器。

使用ftplib與FTP伺服器互動

首先,我們來探討如何使用ftplib模組與FTP伺服器進行互動。ftplib是Python內建的一個模組,用於實作FTP客戶端。

連線至FTP伺服器

要連線至FTP伺服器,我們需要使用ftplib.FTP()類別,並呼叫其connect()方法。

from ftplib import FTP

def connect_to_ftp(host, username, password):
    try:
        ftp = FTP(host)
        ftp.login(user=username, passwd=password)
        print("Connected to FTP server")
        return ftp
    except Exception as e:
        print(f"Failed to connect to FTP server: {e}")
        return None

# 使用範例
ftp = connect_to_ftp('ftp.example.com', 'username', 'password')
if ftp:
    ftp.quit()

#### 內容解密:

此程式碼片段展示瞭如何使用ftplib連線至FTP伺服器。關鍵步驟包括:

  • 匯入ftplib模組中的FTP類別。
  • 定義一個函式connect_to_ftp(),該函式接受主機名稱、使用者名稱和密碼作為引數。
  • 在函式內,建立一個FTP物件並使用login()方法登入FTP伺服器。
  • 若連線成功,傳回ftp物件;否則,傳回None
  • 使用範例展示瞭如何呼叫此函式並在完成後離開FTP連線。

使用Paramiko與SFTP和SSH伺服器互動

Paramiko是一個Python實作的SSHv2協定,提供客戶端和伺服器功能。它支援SFTP、SSH等協定。

使用Paramiko連線至SFTP伺服器

import paramiko

def connect_to_sftp(host, username, password):
    try:
        transport = paramiko.Transport((host, 22))
        transport.connect(username=username, password=password)
        sftp = paramiko.SFTPClient.from_transport(transport)
        print("Connected to SFTP server")
        return sftp, transport
    except Exception as e:
        print(f"Failed to connect to SFTP server: {e}")
        return None, None

# 使用範例
sftp, transport = connect_to_sftp('sftp.example.com', 'username', 'password')
if sftp:
    sftp.close()
    transport.close()

#### 內容解密:

此程式碼片段展示瞭如何使用Paramiko連線至SFTP伺服器。主要步驟包括:

  • 匯入Paramiko模組。
  • 定義一個函式connect_to_sftp(),該函式接受主機名稱、使用者名稱和密碼作為引數。
  • 在函式內,建立一個Transport物件並使用其connect()方法建立連線。
  • Transport物件建立一個SFTP客戶端。
  • 若連線成功,傳回SFTP客戶端和傳輸物件;否則,傳回None
  • 使用範例展示瞭如何呼叫此函式並在完成後關閉SFTP連線和傳輸。

實作SSH客戶端和伺服器

AsyncSSH是一個用於在Python中建立SSHv2客戶端和伺服器的非同步框架。

使用AsyncSSH建立SSH伺服器

import asyncio
import asyncssh

async def handle_client(process):
    # 處理客戶端請求的邏輯
    pass

async def main():
    await asyncssh.create_server(handle_client, '', 8022, authorized_client_keys='authorized_keys')

# 啟動事件迴圈
asyncio.get_event_loop().run_until_complete(main())

#### 內容解密:

此程式碼片段展示瞭如何使用AsyncSSH建立一個SSH伺服器。主要步驟包括:

  • 匯入必要的模組,包括asyncioasyncssh
  • 定義一個協程函式handle_client()來處理客戶端請求。
  • 定義另一個協程函式main(),在其中使用asyncssh.create_server()建立SSH伺服器。
  • 指定處理客戶端請求的函式、監聽的地址和埠,以及授權的客戶端金鑰。
  • 使用asyncio.get_event_loop().run_until_complete(main())啟動事件迴圈。