SSH 是一種在不安全網路中提供安全遠端連線的協定,本文將探討如何使用 Python 的 Paramiko 函式庫建立 SSH 連線,並示範如何執行遠端命令和建立 SSH 隧道。這些技術在系統管理、網路安全測試等領域至關重要,能有效提升工作效率和安全性。透過學習本文,讀者可以瞭解 SSH 隧道的基本原理和應用技巧,並能根據實際需求搭建和使用 SSH 隧道。

SSH 遠端連線與隧道技術深度解析

SSH(Secure Shell)是一種廣泛使用的網路協定,主要用於在不安全的網路上進行安全的遠端登入和其他安全網路服務。在本文中,玄貓將探討如何利用 Paramiko 這個 Python 函式庫來建立 SSH 連線,並進行基本的命令執行和隧道技術應用。這些技術在實際的資安測試和系統管理中都非常有用。

基本 SSH 連線與命令執行

首先,讓我們來看看如何使用 Paramiko 建立一個簡單的 SSH 伺服器,並且能夠接收來自客戶端的命令。這個範例展示瞭如何在伺服器端接收客戶端的 SSH 連線,並且將輸入的命令傳送到客戶端執行。

import socket
import sys
import paramiko

HOSTKEY = paramiko.RSAKey(filename='test_rsa.key')

class Server (paramiko.ServerInterface):
    def __init__(self):
        self.event = threading.Event()

    def check_channel_request(self, kind, chanid):
        if kind == 'session':
            return paramiko.OPEN_SUCCEEDED
        return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED

    def get_allowed_auths(self, username):
        return 'password'

def check_auth_password(self, username, password):
    if (username == 'tim') and (password == 'sekret'):
        return paramiko.AUTH_SUCCESSFUL
    return paramiko.AUTH_FAILED

if __name__ == '__main__':
    server = '192.168.1.207'
    ssh_port = 2222
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind((server, ssh_port))
        sock.listen(100)
        print('[+] Listening for connection ...')
        client, addr = sock.accept()
    except Exception as e:
        print('[-] Listen failed: ' + str(e))
        sys.exit(1)
    else:
        print('[+] Got a connection!', client, addr)
        bhSession = paramiko.Transport(client)
        bhSession.add_server_key(HOSTKEY)
        server = Server()
        bhSession.start_server(server=server)
        chan = bhSession.accept(20)
        if chan is None:
            print('*** No channel.')
            sys.exit(1)
        print('[+] Authenticated!')
        print(chan.recv(1024))
        chan.send('Welcome to bh_ssh')
        try:
            while True:
                command= input("Enter command: ")
                if command != 'exit':
                    chan.send(command)
                    r = chan.recv(8192)
                    print(r.decode())
                else:
                    chan.send('exit')
                    print('exiting')
                    bhSession.close()
                    break
        except KeyboardInterrupt:
            bhSession.close()

內容解密:

  • 程式碼邏輯:這段程式碼首先建立了一個 SSH 伺服器,並在指定的 IP 地址和埠號上監聽連線。當有客戶端連線時,伺服器會接受該連線並進行身份驗證。
  • 身份驗證:在這個範例中,身份驗證是透過密碼進行的。如果使用者名稱和密碼正確,則認證成功。
  • 命令執行:在認證成功後,伺服器會接受來自客戶端的命令,並將這些命令傳送到客戶端執行。執行結果會被傳回給伺服器並顯示出來。
  • 技術原理:這段程式碼展示瞭如何使用 Paramiko 建立一個簡單的 SSH 伺服器,並且能夠接收來自客戶端的命令進行執行。這種技術在資安測試中非常有用,因為它允許測試人員遠端控制目標系統。

SSH 隧道技術

除了基本的命令執行外,SSH 還可以用來建立隧道(tunneling),將網路流量封裝在 SSH 連線中。這種技術可以用來避開防火牆和其他網路限制。

前向隧道(Forward Tunneling)

前向隧道是指將本地主機上的某個埠號轉發到遠端主機上的另一個埠號。例如,你可以將本地主機上的 8008 埠號轉發到遠端主機上的 80 埠號。這樣,當你存取本地主機的 8008 埠號時,實際上是存取了遠端主機的 80 埠號。

ssh -L 8008:web:80 justin@sshserver

此圖示展示了前向隧道的工作原理:

  graph TD;
    A[本地主機:Port 8008] --> B[SSH Client]
    B --> C[SSH Server]
    C --> D[Web Server]

內容解密:

  • 程式碼邏輯:這條命令將本地主機上的 8008 埠號轉發到 sshserver 上的 justin 帳號下,並且將流量轉發到 web 主機上的 80 埠號。
  • 應用場景:這種技術可以用來避開防火牆限制,例如當你無法直接存取內部網路中的某個伺服器時,可以透過 SSH 隧道來存取。
  • 技術原理:前向隧道透過將本地主機上的流量封裝在 SSH 連線中,並轉發到遠端主機上的特定埠號來實作。

反向隧道(Reverse Tunneling)

反向隧道是指將遠端主機上的某個埠號轉發到本地主機上的另一個埠號。例如,你可以將遠端主機上的 3389 埠號轉發到本地主機上的某個埠號。這樣,當你存取本地主機的該埠號時,實際上是存取了遠端主機的 3389 埠號。

ssh justin@sshserver -R 8008:webserver:80

此圖示展示了反向隧道的工作原理:

  graph TD;
    A[SSH Server] --> B[Port 8008]
    B --> C[SSH Client]
    C --> D[Web Server]

內容解密:

  • 程式碼邏輯:這條命令將 sshserver 上的 justin 帳號下的流量轉發到本地主機上的某個埠號,並且將流量轉發到 webserver 主機上的 80 埠號。
  • 應用場景:反向隧道可以用來讓外部系統能夠存取內部網路中的某些服務。例如,當你需要讓外部系統能夠存取內部網路中的 SQL 資料函式庫時,可以使用反向隧道。
  • 技術原理:反向隧道透過將遠端主機上的流量封裝在 SSH 連線中,並轉發到本地主機上的特定埠號來實作。

必須具體案例與資料支援

為了更好地理解這些概念,讓我們來看看一些具體的應用案例。

案例一:資安測試中的前向隧道

在資安測試中,測試人員經常需要存取內部網路中的某些系統。然而,由於防火牆和其他安全措施的限制,直接存取這些系統可能會非常困難。透過設定前向隧道,測試人員可以先連線到一台位於內部網路中的 SSH 機器,然後透過該機器存取其他內部系統。

例如:

ssh -L 8008:internal_server:443 test_user@internal_ssh_server

在此範例中,「internal_ssh_server」位於內部網路中,「internal_server」則是測試人員想要存取的一台資料函式庫伺服器。透過前向隧道,「internal_server」上執行之 HTTPS 網頁介面可透過 localhost 的「port 8008」進而存取。

案例二:系統管理中的反向隧道

在系統管理中,管理員可能需要從外部存取內部網路中的某些服務。然而,由於防火牆和其他安全措施的限制,直接存取這些服務可能會非常困難。透過設定反向隧道,「admin_user」可從外部「Admin_PC」透過「port_22」與「Internal_Server」產生一條反向隧道通訊管道,「Admin_PC」就可透過「localhost:port_3399」存取位於內部網路中之「Internal_Server」。

ssh admin_user@internal_ssh_server -R port_3399:internal_server:port_3399 -N -f

在此範例中,「internal_ssh_server」位於內部網路中,「internal_server」則是管理員想要存取的一台資料函式庫伺服器。「port_3399」則為「internal_server」之 Microsoft SQL Server Web Management 工具之預設 Port Number。

未來趨勢與合理預測

隨著雲端運算和分散式系統的普及,「玄貓」認為 SSH 隧道技術會變得更加重要。未來可能會出現更多根據雲端運算平台(如 AWS、Azure)之更新版本之新功能與引數設定選項等功能模組化產品。「玄貓」也預期未來會有一些更新或更複雜版本之「OpenSSH」等引數化設定應用產品方式出現;「玄貓」認為未來雲運算平台間之互通性必須有相互相容性之新產品出現;未來也會有更多根據「Open Source Community」研發新產品或功能版本之需求;至於新產品之專利問題以及應用授權問題也會隨著「Open Source Community」轉型產業化而受關注;全球性企業間之專利權合作分享與投資共同開發必須加強以促進產業鏈之健全與合理運作;最後未來必須有安全性與穩定性更高之新產品出現以滿足大多數一般使用者需求。

挑戰與改進點

儘管 SSH 隧道技術非常強大,「玄貓」認為還存在一些挑戰和改進點:

  1. 效能問題:由於所有流量都經過 SSH 隧道,「玄貓」認為在高流量情況下可能會出現效能瓶頸問題。「玄貓」認為未來需要更高效率之壓縮演算法與流量分析演算法。
  2. 安全性問題:儘管 SSH 本身非常安全,「玄貓」認為在一些特殊情況下仍然可能存在安全漏洞。「玄貓」認為未來需要更多防範非法攻擊演算法與風險評估分析演算法。
  3. 易用性問題:對於一些非專業使用者而言,「玄貓」認為組態和使用 SSH 隧道可能會比較複雜。「玄貓»認為未來需要提供更簡單易懂之 GUI 應用軟體以及說明手冊。
  4. 相容性問題:不同作業系統和平台間可能存在相容性問題。「玄貓 」認為未來需要提供更多跨平台支援以及更新版本之相容性模組化軟體包檔案更新模組。

SSH 隧道技術與實務應用

SSH 隧道是一種透過 SSH 協定建立的安全通道,用於轉發網路流量。這種技術在資安測試、遠端管理及資料加密傳輸中具有重要應用。以下玄貓將詳細介紹如何使用 Paramiko 函式庫在 Python 中建立 SSH 隧道,並探討其實務應用。

反向 SSH 隧道

反向 SSH 隧道是一種將本地埠的流量轉發到遠端伺服器的技術。這在某些情況下特別有用,例如當您需要從內網連線到外網時。以下是如何使用 Paramiko 建立反向 SSH 隧道的步驟。

1. 建立 SSH 連線

首先,我們需要建立與 SSH 伺服器的連線。這裡使用 Paramiko 的 SSHClient 類別來完成。

import paramiko
import socket
import select
import threading

def create_ssh_client(host, port, username, password):
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname=host, port=port, username=username, password=password)
    return client

2. 建立反向隧道

接著,我們需要建立反向隧道並啟動監聽。這裡使用 request_port_forward 方法來實作。

def start_reverse_tunnel(client, remote_host, remote_port, local_port):
    transport = client.get_transport()
    transport.request_port_forward(address='', port=local_port)
    thr = threading.Thread(
        target=handler, args=(transport.open_channel('direct-tcpip', (remote_host, remote_port), ('', local_port)), remote_host, remote_port)
    )
    thr.setDaemon(True)
    thr.start()

網路流量管理

在反向隧道中,我們需要管理每個連線的流量。這裡定義一個 handler 函式來處理每個執行緒的通訊。

def handler(chan, host, port):
    sock = socket.socket()
    try:
        sock.connect((host, port))
    except Exception as e:
        print(f'Forwarding request to {host}:{port} failed: {e}')
        return
    print(f'Connected! Tunnel open {chan.origin_addr} -> {chan.getpeername()} -> {host}:{port}')
    while True:
        r, w, x = select.select([sock, chan], [], [])
        if sock in r:
            data = sock.recv(1024)
            if len(data) == 0:
                break
            chan.send(data)
        if chan in r:
            data = chan.recv(1024)
            if len(data) == 0:
                break
            sock.send(data)
    chan.close()
    sock.close()
    print(f'Tunnel closed from {chan.origin_addr}')

內容解密:

  • socket.socket():建立一個新的 socket 物件。
  • sock.connect((host, port)):嘗試連線到指定的主機和埠。
  • select.select([sock, chan], [], []):使用 select 函式監聽 socket 和 channel 的讀取事件。
  • data = sock.recv(1024) / data = chan.recv(1024):從 socket 或 channel 接收資料。
  • chan.send(data) / sock.send(data):將資料傳送到對應的 channel 或 socket。
  • chan.close() / sock.close():關閉 channel 和 socket。

實務應用與測試

在實務中,我們可以使用上述程式碼來建立反向 SSH 隧道。例如,將本地埠 8081 的流量轉發到遠端伺服器的埠 3000。

if __name__ == "__main__":
    ssh_client = create_ssh_client('192.168.1.203', 22, 'tim', 'password')
    start_reverse_tunnel(ssh_client, '192.168.1.207', 3000, 8081)

增強安全性

在資安測試中,SSH 隧道可以用來增加攻擊者的匿名性和安全性。例如,攻擊者可以透過 SSH 隧道避免直接暴露自己的 IP 地址,從而降低被追蹤和阻止的風險。

未來趨勢

隨著雲端運算和遠端工作的普及,SSH 隧道技術將變得越來越重要。未來可能會出現更多自動化工具和框架來簡化隧道建立和管理過程。