socks5 結合抓包詳解

圖片拍攝於 2023 年 04 月 28 日 日本奈良

SOCKS5(Socket Secure 5)是一種網絡協議,用於在客戶端和代理服務器之間進行通信。它是 SOCKS 協議的第五個版本,SOCKS5 協議支持 TCP 和 UDP 協議,並提供了認證和加密的功能。

SOCKS5 協議廣泛用於代理服務器、xx 上網、匿名訪問、負載均衡等場景。它提供了一種通用的、靈活的代理解決方案,可以在各種網絡環境和應用中使用。

RFC 1928[1] 是關於 SOCKS5 的協議文檔。

協議解析

不想看這部分可以直接跳到下面的抓包分析。

SOCKS5 工作在應用層和傳輸層之間,首先客戶端需要和代理服務器進行 tcp 連接。

連接完成後,客戶端和代理服務器之間進行協商確定認證方式。

具體就是客戶端發送認證方法請求,

代理服務器在收到客戶端消息,從客戶端提供的 METHODS 字段中選擇一種支持的認證方法,並將該方法的標識填充到 METHOD 字段中。代理服務器通過這個應答消息告知客戶端選擇的認證方法。

目前的 METHOD 包含以下幾個值:

如果返回的方法是 X 'FF',意味着失敗了,那麼客戶端需要關閉連接。

一旦協商完畢,客戶端會發送請求的詳細信息,主要包括實際要請求的目的地址和端口號。

其中各字段含義如下:

代理服務器收到請求後,會發起到 DST.ADDR:PORT 的連接,並響應客戶端結果,

其中的值有:

REP:

RSV:預留位,必須設置成 X'00'。

BND.ADDR: 服務器綁定的地址

BND.PORT: 服務器綁定的端口(網絡字節序)

當連接建立後,客戶端就可以和正常一樣訪問目標地址 "通信" 了。

此時通信的數據除了目的地址是發往代理服務器以外,所有內容都是和普通連接一樣。對代理服務器而言,後面所有收到的來自客戶端的數據都會原樣轉發到目標服務。

抓個包看看

實驗涉及到的兩個庫:go-shadowsocks2[2] 和 proxychains[3]

假設我現在有兩臺服務器,服務器 A(上海) 和服務器 B(倫敦), 服務器 B 上部署了 Shadowsocks 服務,Shadowsocks 是一個安全的 socks5 代理。

我想把機器 A 對外請求的流量都通過 B 來代理,並且指定只有發送到 A 機器的 1080 端口流量才能被代理。

通過 go-shadowsocks2 啓動程序,

參數說明,

-c: 設置 Shadowsocks2 客戶端連接到 B 機器 Shadowsocks 的配置。

-socks:  指定 Shadowsocks2 客戶端在本地監聽的 SOCKS5 代理服務器的地址。在這個例子中,客戶端在 1080 端口上監聽代理請求。

myip.ipip.net 是一個用來查詢本機公網 ip 的服務。假設我們直接在服務器 A 上 curl myip.ipip.net,那麼請求的結果,

現在我們要通過上面描述的達到輸出的是 B 服務器倫敦的 ip 地址。

我們使用了工具 proxychains,配置 proxychains,

執行之前,我們先開啓 tcpdump,我們只抓本地 1080 端口的流量,畢竟它是作爲本地其他客戶端 socks5 代理服務器。

然後執行,

8OTpkB

最終獲取抓到的包。

前三個包是 tcp 的三次握手,這裏就不說了。

包 4 也就是客戶端發送協商認證方法請求給代理服務器,其中包含支持的認證方法列表,這裏只支持 1 種方法: 0。也就是客戶端無需進行任何認證,可以直接進行連接或操作。

包 6 服務端響應,

此時協商完畢。

包 8 是客戶端發起請求的詳細信息,裏面包含了要訪問的目標地址和端口。這裏的 address type=3 表示域名類型。

包 9 爲服務端響應:

可以看到 RSV 的值表示成功。

這裏要注意的是包 8 中的 CMD 參數。上面說到有三個值。

當客戶端發往代理服務器的數據包的 CMD 字段的值爲 0x01 時, 代表 CONNECT, 此時 DST.ADDR 和 DST.PORT 表示客戶端所想要訪問的目標主機的地址和端口, 代理服務器在收到該請求後建立代理服務器到目標主機的 TCP 連接, 並將代理服務器分配的 IP 地址和端口在返回的數據包中的 BIND.ADDR 和 BIND.PORT 表示。

其他 CMD 值含義自行查看 rfc。

但是包 9 中在對 CONNECT 請求回覆中,BIND.PORT 和 BIND.ADDR 都不是預期的值。看了 shadowsocks2 裏面的代碼,

它是直接把 ATYP 設置成 1,BND.ADDR 和 BND.PORT 設置爲 0。

因爲此時的本地 socks5 並不是最終的代理服務器,實際請求目標地址的是我們在啓動 shadowsocks2 配置的 B 機器上的 ss 服務。本身 BND.ADDR 和 BND.PORT 字段是可選的,如果服務器不提供這些信息,客戶端可以根據實際情況進行處理。

當響應成功,客戶端開始請求傳輸數據了,也就是包 10,發送了一個 http 請求。包 12 是對請求的響應。

所以實際的流轉是,

在 Shadowsocks2 中,主要通過這個函數來實現雙向數據傳輸:

也就是,

主要靠的是標準庫的 io.Copy。

最後 13-16 包就是 tcp 的 4 次揮手了。

上圖這組包還有一個很基礎的 tcp 知識點,

首先你圖上看到的 seq 都是從 0 開始的,那是因爲 wireshark 有個可以開啓 Relative Sequence Numbers 的選項。

上面的包 4 截圖可以看到包信息: Seq =1,Len=3。再看包 5,是對包 4 的 Ack, 包 6 包 7 同理。

但是你再看包 8:Seq:4,Len=20。

你在包 9 看到的也是一個 Socks 協議的包,並沒有看到專門對包 8 的 Ack。

在 TCP 通信中,並不一定每個請求都會立即收到相應的 ACK 確認包。TCP 使用了一種稱爲 "累積確認"(Cumulative Acknowledgment)的機制。ACK 確認包可能會延遲發送,或者包含在接收方發送的其他數據包中,這是 TCP 協議的一種優化機制,可以減少網絡中的包數量和傳輸開銷。

那麼我們就知道對包 8 的確認,其實是隨着包 9 的數據一起發送了。Ack=Seq(4)+Len(20)=24

完結。

[1]https://www.rfc-editor.org/rfc/rfc1928

[2]https://github.com/shadowsocks/go-shadowsocks2

[3]https://github.com/haad/proxychains

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/5n7_x52VHY2U0EX6DIEvig