作者: BG7YWL
公眾號: 數智安全研究院

前言

Shadowsocks(簡稱SS)是一款科學上網工具,基于Socks5代理方式的加密傳輸協議,但是近年來隨著GFW墻的日益增高,一些 Shadowsocks 流量已經可以被很好的識別出來。

雖然“協議可以被識別”已經眾所周知,但我們依舊認為,Shadowsocks 的加密做的不錯,中間人應該破解不出明文信息。

然而,近期一份來自于奇虎 360 核心安全團隊于披露的論文顯示:Shadowsocks 的 steam 加密存在漏洞,導致數據包頭部可被修改。攻擊者可以利用修改過后的數據包進行「重定向」,從而進行 MITM 攻擊。

目前受影響的包括:shadowsocks-py, shadowsocoks-go, shadowsocoks-nodejs.

socks5 協議基礎

雖然 Shadowsocks 使用的底層協議是 SOCKS5,但對于本文而言,底層的 SOCKS5 并不是重點,我們只需要關注 Shadowsocks 的客戶端與服務器之間是如何傳輸數據的。

根據官方文檔所說,客戶端向服務器發送的數據,一開始是流密碼的 IV(也就是說,IV 由客戶端生成,并直接扔進數據包中),之后就是一段加密數據,它的明文格式是這樣的:[目標地址][數據]

其中,類型是 1 字節的枚舉值:

  • 0x01:主機名是 IPv4 地址;

  • 0x03:主機名是變長字符串,首字節表示長度(最大 255),后面是數據;

  • 0x04:主機名是 IPv6 地址。

一次代理的過程如下:

  1. 客戶端將這些數據加密后發到服務器;
  2. 服務器收到后將其解密,會得到 [1 字節類型][主機名][2 字節端口][數據];
  3. 服務器會將數據部分直接發送給 主機名:端口;
  4. 服務器將主機返回的數據直接使用同樣的算法加密(如果加密算法用了流密碼,則會生成并使用一個新的 IV,并將其放在包的最前面),發送給客戶端;
  5. 客戶端解密后即可得到主機返回的數據。

論文中提到,如果攻擊者抓到了一個 Shadowsocks 服務器返回的包,并且已知數據部分的開頭七個字節,那么有可能在不知道密碼的情況下,利用 Shadowsocks 服務器來解出包的絕大部分內容(最多損失 16 字節)。

作者的思路

假設有一臺 Shadowsocks 服務器,攻擊者通過嗅探或其它方式抓到了這個 Shadowsocks 服務器返回的一個包。

為了知道明文內容,攻擊者要么暴力破解密碼(幾乎不可行),要么想辦法利用這臺 Shadowsocks 服務器幫忙解密。

作者選擇了后者,即想辦法把這個包變成客戶端發的包,讓服務器解密后代理到自己指定的服務器,這被稱為重定向攻擊。

CFB 模式

前面說了,Shadowsocks 客戶端的發包格式是 :[1 字節類型][主機名][2 字節端口][數據]。如果攻擊者可以利用加密算法的缺陷來篡改明文數據,就可以把主機名改成攻擊者的服務器地址,Shadowsocks 服務器就會以為客戶端想訪問攻擊者的服務器,于是就把解密后的包中的數據部分發了過去。

先考慮如何篡改數據。假設這臺 Shadowsocks 服務器的加密算法使用的是 AES-256-CFB。

CFB模式的全稱是 Cipher FeedBack模式(密文反饋模式),在CFB模式中前一密文分組會被送到密碼算法的輸入端,進行下一分組的加密。

加密的流程如下圖所示:

相反的解密流程如下所示:

只看解密流程,如果我們知道了 明文分組1 和 密文分組1,接下來就可以通過構造一個假的密文分組1,讓 Shadowsocks 服務器解密來偽造一個任意的 明文分組1。

'明文分組1' xor '密文分組1' = 'enc_iv''假密文分組1' xor 'enc_iv' = '假明文分組1''enc_iv' xor' 假明文分組1' = '假密文分組1'

通過這樣的方式控制 假密文分組1 就可以構造任意的 假明文分組1 了。

漏洞利用過程

通過上面協議的分析,可以得出 sslocal 發送給 ssserver 的數據格式為:

隨機IV + encrypt([ 1-byte type][variable-length host][2-byte port][payload])

ssserver 發送給 sslocal 的數據格式為:

隨機IV + encrypt([payload])

如果拿到 ssserver 發送給 sslocal 的數據,使用常規的非暴力手段是無法解密的,但是如果我們知道此數據的前7個字節,那么就可以利用CFB明文偽造攻擊將 假明文分組1 的前7個字節偽造為 [ 1-byte type][variable-length host][2-byte port],然后把此數據包做為 sslocal 發送給 ssserver 的數據,發給 ssserver。

因為數據 [ 1-byte type][variable-length host][2-byte port]的內容被我們控制,所以將目標地址修改為我們自己的服務器,然后 ssserver 就會把解密完的數據發送到我們自己的服務器上,工作過程如下所示:

ss-local(fake one) <--[encrypted]--> ss-remote <---> target(controlled)

那關鍵問題是怎么知道加密數據的前7個字節的明文呢?論文中提供了一種方法,如果用戶使用 Shadowsocks 進行 http 通信,那么響應的前7個字節是HTTP/1,我們可以利用這7個字節來解密整個數據包。

直接用 LeadroyaL 寫好的 exp 進行測試 https://github.com/LeadroyaL/ss-redirect-vuln-exp。由于攻擊者修改了 密文分組1 ,而 密文分組1 在 CFB 模式中又用來解密 明文分組2 ,因此收到的 明文分組2 這 16 個字節是亂碼。

攻擊者最終可以還原出 明文分組2 以外的所有數據。論文中的命令行截圖也說明了這點,獲取到的數據的第一個字節是之前包的明文的第 8 個字節(前 7 個是 HTTP/1.),然后有 9 個字節是正確的,之后 16 個字節是亂碼,再之后是完全正確的。

防御措施

  1. 禁用 shadowsocks-py、shadowsocks-go、go-shadowsocks2、shadowsocks-nodejs

  2. 只用 shadowsocks-libev,并且只使用 AEAD 加密

原因如下:shadowsocks-libev 的實現很久之前就已經禁止了 IV 重用,可以在一定程度上防止這種攻擊;只要加密算法帶有 AEAD 特性,那么數據就無法被篡改,本文的攻擊方式也是無效的。

參考鏈接

  1. https://github.com/edwardz246003/shadowsocks

  2. https://wonderkun.cc/2020/02/18/shadowsocks

  3. https://github.com/LeadroyaL/ss-redirect-vuln-exp

  4. https://jiajunhuang.com/articles/2019_06_06-socks5.md.html


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1134/