reverst:透過QUIC建立HTTP反向隧道的開源工具

banq發表於2024-05-26


Reverst 是一個基於 QUIC 和 HTTP/3 構建的(負載平衡)反向隧道伺服器和 Go 伺服器-客戶端庫。

  • Go Powered:使用quic-go以 Go 編寫
  • 相容性:Goclient包建立在net/http標準庫抽象之上
  • 負載平衡:在同一隧道後面執行多個服務例項
  • 效能卓越:基於 QUIC 和 HTTP/3 構建

Reverst 用於在受限網路(例如 NAT 閘道器後面)內公開公共網際網路上的服務。隧道二進位制檔案旨在部署在公共網際網路上。然後,客戶端伺服器撥出到隧道並在目標隧道組上註冊自己。隧道組是一組負載平衡的客戶端伺服器,透過 reverst 隧道 HTTP 介面公開。

Reverst是一個創新的反向隧道解決方案,利用QUIC和HTTP/3協議的高速和安全性,可以在受限網路內部向公網公開服務。它的主要特點包括:

  • 使用QUIC和HTTP/3協議,提供更快的連線建立和更高的吞吐量
  • 支援多路複用,單個連線可傳輸多個流
  • 支援連線遷移,可在網路環境變化時無縫切換網路
  • 支援0-RTT連線恢復,提高重連效率

反向隧道伺服器和客戶端庫
Reverst 是一個反向隧道伺服器,允許透過 QUIC 和 HTTP/3 從 NAT 或防火牆後面的客戶端機器到伺服器建立安全隧道。它還提供了一個 Go 客戶端庫,用於從 Go 應用程式建立這些反向隧道。

負載均衡
reverst 伺服器支援跨多個後端伺服器平衡傳入隧道連線的負載。這允許擴充套件伺服器端來處理更多的客戶端連線。

用 Go 編寫
整個 reverst 專案,包括伺服器和客戶端庫,都是用 Go 編寫的,並建立在 quic-go 庫之上以支援 QUIC。

其他專案:
1、GOST是一個功能豐富的Go反向代理和隧道工具。它支援多種代理和隧道協議,包括反向代理隧道。主要特點有:

  • 支援反向代理隧道將內網服務暴露到公網
  • 提供公共反向代理測試服務GOST.PLUS
  • 支援串列埠重定向,實現串列埠遠端通訊和資料監控
  • 支援各種代理協議:HTTP(S)、SOCKS5、Shadowsocks等

2、Supershell是一個基於反向SSH隧道的C2遠控平臺,透過在目標主機建立反向SSH隧道獲取完全互動式Shell。主要特點包括:

  • 支援多平臺架構Payload生成,整合壓縮和免殺
  • 支援全平臺完全互動式Shell,可分享Shell
  • 支援檔案管理、記憶體注入、安裝服務等功能
  • 支援客戶端監聽實現內網滲透

綜上所述,Go語言提供了多種優秀的反向隧道解決方案,可滿足不同場景的需求,如公網暴露內網服務、遠端控制、內網滲透等。

在Go中實現反向隧道思路
在Go中實現反向隧道的基本思路是:

  1. 客戶端主動連線公網伺服器,建立TCP連線。
  2. 服務端接受客戶端連線,作為代理伺服器。
  3. 客戶端透過該TCP連線將內網服務資料傳輸給代理伺服器。
  4. 代理伺服器接收資料後,將其轉發給需要訪問的公網客戶端。

具體實現步驟如下:
客戶端
  1. 使用net包建立與公網伺服器的TCP連線。
  2. 啟動本地HTTP服務,監聽內網埠。
  3. 將本地HTTP服務接收到的請求資料透過之前建立的TCP連線傳送給服務端。

<font>// 建立與服務端的連線<i>
conn, err := net.Dial(
"tcp", "伺服器IP:埠")
if err != nil {
   
// 錯誤處理<i>
}

// 啟動本地HTTP服務<i>
http.HandleFunc(
"/", func(w http.ResponseWriter, r *http.Request) {
   
// 讀取請求資料<i>
    data, err := ioutil.ReadAll(r.Body)
    if err != nil {
       
// 錯誤處理<i>
    }
    
   
// 透過TCP連線傳送資料給服務端<i>
    conn.Write(data)
})

// 監聽本地埠<i>
http.ListenAndServe(
":8080", nil)

服務端

  1. 監聽公網埠,接受客戶端連線。
  2. 對每個連線,啟動一個goroutine處理。
  3. 在goroutine中,從TCP連線讀取客戶端發來的資料。
  4. 將讀取到的資料作為請求傳送給需要訪問的公網服務。
  5. 將公網服務的響應資料透過TCP連線返回給客戶端。

<font>// 監聽埠<i>
listener, err := net.Listen(
"tcp", ":8000")
if err != nil {
   
// 錯誤處理<i>
}

// 迴圈接受連線<i>
for {
    conn, err := listener.Accept()
    if err != nil {
       
// 錯誤處理<i>
        continue
    }
    
   
// 啟動goroutine處理連線<i>
    go handleConn(conn)
}

func handleConn(conn net.Conn) {
    defer conn.Close()
    
   
// 讀取客戶端發來的資料<i>
    data := make([]byte, 1024)
    n, err := conn.Read(data)
    if err != nil {
       
// 錯誤處理<i>
        return
    }
    
   
// 將資料作為請求傳送給公網服務<i>
    resp, err := http.Post(
"http://公網服務地址", "binary/data", bytes.NewReader(data[:n]))
    if err != nil {
       
// 錯誤處理<i>
        return  
    }
    defer resp.Body.Close()
    
   
// 讀取公網服務響應<i>
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
       
// 錯誤處理<i>
        return
    }
    
   
// 將響應返回給客戶端<i>
    conn.Write(body)
}

在Go中實現內網穿透
客戶端執行在內網中,主要職責是:

  1. 主動連線公網伺服器,建立TCP連線作為隧道。
  2. 啟動本地HTTP服務,監聽需要穿透的內網埠。
  3. 將本地HTTP服務接收到的請求資料透過隧道TCP連線傳送給服務端。
  4. 定期傳送心跳包保持與服務端的連線。
  5. 如果與服務端連線斷開,則重新連線。

<font>// 建立與服務端的連線<i>
conn, err := net.Dial(
"tcp", "伺服器IP:埠")
if err != nil {
   
// 錯誤處理<i>
}

// 啟動本地HTTP服務<i>
http.HandleFunc(
"/", func(w http.ResponseWriter, r *http.Request) {
   
// 讀取請求資料<i>
    data, err := ioutil.ReadAll(r.Body)
    if err != nil {
       
// 錯誤處理<i>
    }
    
   
// 透過TCP連線傳送資料給服務端<i>
    conn.Write(data)
})

// 監聽本地埠<i>
http.ListenAndServe(
":8080", nil)

// 定期傳送心跳包<i>
go keepAlive(conn)

// 重連機制<i>
go reconnect(conn)

服務端
服務端執行在公網伺服器上,主要職責是:

  1. 監聽公網埠,接受客戶端連線。
  2. 對每個連線,啟動一個goroutine處理。
  3. 在goroutine中,從TCP連線讀取客戶端發來的資料。
  4. 將讀取到的資料作為請求傳送給需要訪問的公網服務。
  5. 將公網服務的響應資料透過TCP連線返回給客戶端。
  6. 處理客戶端斷開連線的情況。

<font>// 監聽埠<i>
listener, err := net.Listen(
"tcp", ":8000"
if err != nil {
   
// 錯誤處理<i>
}

// 迴圈接受連線<i>
for {
    conn, err := listener.Accept()
    if err != nil {
       
// 錯誤處理<i>
        continue
    }
    
   
// 啟動goroutine處理連線<i>
    go handleConn(conn)
}

func handleConn(conn net.Conn) {
    defer conn.Close()
    
   
// 讀取客戶端發來的資料<i>
    data := make([]byte, 1024)
    n, err := conn.Read(data)
    if err != nil {
       
// 錯誤處理<i>
        return
    }
    
   
// 將資料作為請求傳送給公網服務<i>
    resp, err := http.Post(
"http://公網服務地址", "binary/data", bytes.NewReader(data[:n]))
    if err != nil {
       
// 錯誤處理<i>
        return  
    }
    defer resp.Body.Close()
    
   
// 讀取公網服務響應<i>
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
       
// 錯誤處理<i>
        return
    }
    
   
// 將響應返回給客戶端<i>
    conn.Write(body)
}

 

相關文章