Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shadowsocks AEAD 加密方式设计存在严重漏洞,无法保证通信内容的可靠性 #183

Open
RPRX opened this issue Feb 28, 2021 · 108 comments

Comments

@RPRX
Copy link

RPRX commented Feb 28, 2021

这个问题是昨天发现的,本来我只是想先给 Xray-core 的 Shadowsocks 明文结构加个时间戳以彻底解决对服务端的重放问题。

顺便研究了一下 SS AEAD 的安全性,我脑洞比较大,考虑得比较多,比如是否可以造成 对客户端的攻击,简述如下:

  1. 对于 TCP,Shadowsocks AEAD 往返是完全一样的加密结构,HKDF 的 info 也相同,且响应的加密完全独立于请求
  2. 对于 UDP,同样存在上面的问题(甚至可以与 TCP 杂交),更糟糕的是,UDP 往返连明文结构都是完全一样的

以上“特性”的利用方式太多了,真的很难排列组合完,还是主要说对客户端的攻击:

在不需要密码的情况下,可以随意对客户端接收的数据进行移花接木或重放等操作,使得被代理程序收到的数据没有任何可靠性。

比如将 A、B 两条 TCP 连接返回的数据对调,Shadowsocks AEAD 客户端是无法发现异常的,只会成功解密并传给被代理程序。

一些 SS 实现是全局共用 IV 过滤器,只能在单一客户端且不重启(which is impossible)的前提下防御重放,防不了移花接木。
Shadowsocks 流加密也存在此问题,有兴趣的可以研究一下 SSR 的 auth_chain_* 系列是否也存在此问题。

建议先用 VMess AEAD(实际上它有其它问题,但不是这种严重漏洞),最后感谢某个开发者群成员参与讨论与验证。


需要说明,这里主要是描述存在的漏洞,而对漏洞的利用有很多方式,比如 移花接木、重放、自交、杂交 等,欢迎补充测试结果。

@ghost
Copy link

ghost commented Feb 28, 2021

开发者群成员进行了 本地 echo 的 PoC,并根据测试结果得出 TCP / UDP 移花接木猜想,后者尚未进行 PoC

注意⚠️:下文提及的 “问题” 仅限于本地 echo 自交,不包含其他问题的受影响程度

注意⚠️:没发出 PoC 不代表没有问题,不受 echo 攻击影响的不代表没有问题

什么是 本地 echo

本地 echo:将 Client 原本要发给 Server 的流量重新发回给 Client,但 Client 解密成功发回给上层应用
Client 收到了返回来的包,成功解密后发给上层应用
但他没有意识到,这包其实是不久前自己发出的,原封不动地又被发了回来

经过实践验证,echo 方式影响的软件 / 实现:

  • V2Ray / Xray 内 Shadowsocks 任意 method (AEAD / 非 AEAD)
  • SSR 任意 method + origin + plain (怕不是没人用了) 对不起我高估了,还有好多人在用
  • ss-libev 非 AEAD 加密受影响 (AEAD 仅仅不受 echo 影响,其他攻击向量仍然可行)

复现过程(仅本地 echo 方式,其他攻击方式后续发布)

  1. 搭建本地 echo 环境
    socat UDP4-LISTEN:2000,fork EXEC:'cat'socat TCP4-LISTEN:2000,fork EXEC:'cat'
  2. 启动任意上述受影响的实现
  3. 使用 proxychains 或 cgproxy (因为 proxychains 不能处理 UDP 流量) 代理 nc 进行连接
    proxychains nc 任意IP地址 任意端口号
    cgproxy nc -u 任意IP地址 任意端口号
  4. 输入任意内容, 即可发现内容被 echo 返回

参考复现结果 (仅参考)

TCP

image

UDP

image

需要说明的是,这种情况下 nc 收到的回包内包含目标地址块(即图一中的 y 和图二中的 ):
image

@zonyitoo
Copy link
Contributor

zonyitoo commented Feb 28, 2021

This is vulnerable because somebody can "fake" the response stream / packets from a known shadowsocks server.

  • If the attacker doesn't know the correct password, then he can only replay some known respond streams or packets.
  • If the attacker knows the correct password, then he doesn't need to do this.

So the key is to prevent replay on the client side.

Here are some of my stupid guess about the applications of this vulnerability:

  • Most users uses shadowsocks for accessing HTTP services, so if he is accessing a HTTP (without the S) service, then the attacker can respond to him a "fake" website. If possible.
  • The attacker just want to destroy your shadowsocks connection, and he just makes some random respond to you.

But these vulnerability doesn't seem to affect personal and private users. :P

@RPRX
Copy link
Author

RPRX commented Feb 28, 2021

@zonyitoo 想象一下引发 TLS 错误从而根据报错长度来判断是代理。以及,SS AEAD 作为一个加密传输层,本应保证回应可靠。

@Mygod
Copy link
Contributor

Mygod commented Feb 28, 2021

What happened to random IV?

@bdbai
Copy link

bdbai commented Feb 28, 2021

@Mygod Client IV, even random, does not reflect in a server response by any means. While Server IV may be random because it is out of control. Therefore, an arbitrary Shadowsocks stream, either uploading or downloading, could be reused by a MITM as a response stream to another request as long as both of them involve the same Shadowsocks service.

@zonyitoo
Copy link
Contributor

zonyitoo commented Feb 28, 2021

What happened to random IV?

This issue indicates that the respond stream / packet could be faked.

Consider a senario, a client makes 2 independent TCP connections to a server, then a attackers sliently swaps these 2 connections' respond packets to the client. The client won't notice but only find errors from the applications, because the shadowsocks' client can always decrypt the two respond streams successfully.

Further application still unknown, but it should be a problem because sslocal doesn't know it is being attacked.

@Mygod
Copy link
Contributor

Mygod commented Feb 28, 2021

Random IV should prevent cross session reordering. You should somehow ensure the IV is random.

As for reordering within the same TCP stream, I don't remember how it is implemented but we could presumably authenticate the previous tag as well, similar to Merkle-Damgard.

@zonyitoo
Copy link
Contributor

Random IV should prevent cross session reordering. You should somehow ensure the IV is random.

The swapping happens before the client receives the first respond packet from the server. So it is unrelated that whether the IV is random. A correct implementation of server returns a random IV for every independent connection, but the situation described by this issue can still happen.

@Mygod
Copy link
Contributor

Mygod commented Feb 28, 2021

Sure. I guess in this case, you could resolve it by having server's real IV to be random xor client IV for TCP. I don't think this is an issue for UDP as UDP is designed to be unreliable.

Anyway this is a very active adversary, which is not very realistic in most cases. Although we could implement these mitigations (xored IVs and Merkle-Damgard authentication) in the future, I do not see the urgency at the moment.

@zonyitoo
Copy link
Contributor

zonyitoo commented Feb 28, 2021

you could resolve it by having server's real IV to be random xor client IV for TCP.

And then client xor it back? Hmm, that is a rather good idea.

But the attacker can still have your client's IV, so he can also make it happens.

@Mygod
Copy link
Contributor

Mygod commented Feb 28, 2021

Yeah never mind 😜. We should maybe authenticate client's IV as additional data instead.

P.S. Also now that I think about it the IV autoincrements so nothing needs to be done there. Oops.

@viRikaRe
Copy link

I just wonder what kind of MITM attacker will ever use this vulnerability.

  • When ISP/GFW doesn't suspect you, it shall not mess-up the connection. Otherwise it degrades the service and may impact other software.
  • When ISP/GFW is suspecting you, does this method reveal a distinctive pattern to confirm the suspicion?
  • When ISP/GFW confirms you are using proxy, why not just cut off your network?
  • If other MITM attacker wants to destroy your network, why not just drop the packets?

Anyway, a fix would be appreciated, but I don't see much priority.

@RPRX
Copy link
Author

RPRX commented Feb 28, 2021

@viRikaRe

如果你正在使用 SS 浏览 HTTPS 网页,那么理论上,对调两条 TCP 返回的数据会导致服务端 TLS alert + 断连,这一行为是固定的。

没错,实际上不光是 AEAD,Shadowsocks 协议的 ciphers 都可以被这样操作,所以理论上是一个通用的识别 SS 的方式。

甚至可以根据 alert 长度推测出你大概在用什么 cipher

@zonyitoo
Copy link
Contributor

zonyitoo commented Feb 28, 2021

TBH, I believe that we have already come to an agreement that the vulnerability this issue described do exists. So all of subsequence discussion should be based on this agreement.

It doesn't mean it is not vulnerable while the application of it is still unknown. It may not be fixed quickly without changing the design of shadowsocks' protocol IF we found an effective POC.

@madeye
Copy link
Contributor

madeye commented Mar 1, 2021

To avoid the short-term replay attacks to the clients, we'd better keep the bloom-filter persistent on the disk.

@zonyitoo Can you try implement this in shaowsocks-rust first? The implementation should be straightforward, just make sure we can restore the previous bloom-filter after restarting the client.

@zonyitoo
Copy link
Contributor

zonyitoo commented Mar 1, 2021

Can you try implement this in shaowsocks-rust first?

Ok. I will give a try. But the current implementation about "Ping Pong Bloom Filter" can only filter some of the recent IVs / Salts. If we decide to persist this filter, should we use another data structure?

On the other hand, persisting the filter couldn't resolve the issue about stream swapping.

@madeye
Copy link
Contributor

madeye commented Mar 1, 2021

For a client, ping-pong bloom filter should be good enough. Although we call it short-term replay avoidance, it usually takes weeks to flush the filter for a client.

Right, swapping cannot be avoided by bloom-filter. As discussed above, swapping avoidance should be low priority issue.

@Mygod
Copy link
Contributor

Mygod commented Mar 2, 2021

@madeye I don't think a bloom filter is useful. The attacker could replay some server messages sent to other clients.

@madeye
Copy link
Contributor

madeye commented Mar 2, 2021

@Mygod You're assuming different clients share the same key?

@Mygod
Copy link
Contributor

Mygod commented Mar 2, 2021

Isn't that what shadowsocks supposed to do?

@madeye
Copy link
Contributor

madeye commented Mar 2, 2021

Okay, you mean the clients connected to the same server port. Yes, you're right.

@database64128
Copy link
Contributor

So, in response to #183 (comment), instead of continuing the conversation and encouraging new ideas, the organization changed its description in 7fbcd0d to drop mentions of "secure" and "protect". What a shame.

@madeye
Copy link
Contributor

madeye commented Mar 9, 2021

Any new idea is welcome. In fact we encourage anyone to fork our code and build their own protocols.

In the past few years, I have seen many interesting innovations like v2ray, trojan and XTLS. They work pretty well and that's why we even have a plugin called v2ray...

I see many good ideas in this issue. Even shadowsocks may not adopt them (what a shame), but forking is still possible. ShadowsocksR and Outline are two good examples.

BTW, I dropped the "secure" and "protect" from the project's description, as they are misleading now. I agree that shadowsocks is just an access tool, and should not be used as security layer.

@zonyitoo
Copy link
Contributor

zonyitoo commented Mar 9, 2021

shadowsocks is a fast tunnel proxy that helps you bypass firewalls.

I think the "original" description of shadowsocks have defined its design goal.

@madeye
Copy link
Contributor

madeye commented Mar 9, 2021

shadowsocks is a fast tunnel proxy that helps you bypass firewalls.

I almost forgot our original design goal...

Let me put it back.

@QChWnd
Copy link

QChWnd commented Mar 11, 2021

Recently I find both Shadowsocks and Trojan are detected by gfw, even though I restrict access ip and use strongest encryption. So I think gfw could have ability to inject data into ip package?

I try many methods such as change iv_len, change encryption method, rebuild oldest shadowsocks-python with aead encryption, but no success.

So If you want more security, I recommend that use TLS/SSL tunnel to proxy socks5(shadowsocks over ssltunnel). Because TLS is used worldwide and be turn out safe, you could use authority on both server and client side.

Many people have discovered the same thing, especially during the Spring Festival this year.
But this seems to have nothing to do with replay. GFW is reluctant to use this method for a long time. It may be that such a method is not accurate enough or the performance is too high.
But I think it may be more sensible to forge the characteristics of TLS than to use TLS directly. Shadowsocks has performance advantages over TLS-based proxy servers.

@QChWnd
Copy link

QChWnd commented Mar 11, 2021

如果改變Server回包格式、在Client添加相關機制、針對同連接包的IV添加關於密鑰的限制(只是舉例),「雜交」「自交」都能很輕易地解決。
特別的:保留向下支援(特別是不區分C&S發包結構)的前提下做修補是很危險的。以我的能力也沒有辦法想到完美的方案。我個人認為放棄向下支援影響不大,因為在今天使用不帶TLS的Shadowsocks裸翻在非專業場合通常被認為是一種很有問題的行為(雖然事實並非總是如此)。

@QChWnd
Copy link

QChWnd commented Mar 11, 2021

如果改變Server回包格式、在Client添加相關機制、針對同連接包的IV添加關於密鑰的限制(只是舉例),「雜交」「自交」都能很輕易地解決。
特別的:保留向下支援(特別是不區分C&S發包結構)的前提下做修補是很危險的。

我觉得对称加密已经走到头了,无论是aes/chacha20/aead加密算法,除了xor还是xor。为什么不试试非对称加密呢,或许有人正在设计轻量级的非对称加密算法。
Shadowsocks 协议或许真的到了需要彻底改写的时候了。如果单纯改写中间的传输过程,换成非对称加密,那shadowsocks 协议也就不复存在了,完全可以用socks5+tls实现。所以这是两难命题吗?

不對稱加密並不能取代對稱加密,只能用於增強對稱加密的安全能力。TLS也不得不使用AES-GCM等AEAD加密演算法。同時,不對稱加密也並不意味如TLS一樣工作。
TLS的CH、SH導致無法實現0-RTT,而CH、SH後Payload也大都使用臨時密鑰AEAD對稱加密。代理協定相比普通的HTTPS訪問,可以預先協商太多,因此甚至或許可以實現邊通信邊鑑權等方案,真正杜絕重放,自交,雜交等問題。
況且對稱加密除了流加密,也有塊加密。塊加密有P盒原理。定長的東西使用塊加密而非流加密在某些場合也是可取的,舉一個不恰當的例子:如果某很有問題的思路希望以連續的IV表示同一連接以防止自交雜交,直接傳送IV顯然是不可取的,但將IV與某些與密鑰相關的東西做塊加密後發送就能杜絕這個問題。(當然實際工程中,這種方案是非常不理想的)
順帶一提,TLS也有它不善於做的東西,如流式壓縮以及抗包長等弱特徵。

@Howard0o0
Copy link

@viRikaRe

如果你正在使用 SS 浏览 HTTPS 网页,那么理论上,对调两条 TCP 返回的数据会导致服务端 TLS alert + 断连,这一行为是固定的。

没错,实际上不光是 AEAD,Shadowsocks 协议的 ciphers 都可以被这样操作,所以理论上是一个通用的识别 SS 的方式。

甚至可以根据 alert 长度推测出你大概在用什么 cipher

每一条消息对应的nonce都不一样,移花接木后,ssserver不是会解密失败吗?

@DuckSoft
Copy link

@Howard0o0 Nonce 是 counting nonce... 建议再看看 spec...

@Howard0o0
Copy link

@DuckSoft
噢噢,会错意了
那对调后,salt不是不一样吗,每个加密chunk的(salt,nonce)组合是唯一的吧

@OG-C
Copy link

OG-C commented Jun 3, 2021

感谢大佬

@z991238
Copy link

z991238 commented Aug 11, 2021

@fortuna

I see Shadowsocks as an access tool, not a security tool.

这是有一定道理的,但其实我一直想说... According to

A secure socks5 proxy, designed to protect your Internet traffic.

@uvu

SSR 的 auth_chain_*有此类问题么

@QChWnd
Copy link

QChWnd commented Aug 20, 2021

@fortuna

I see Shadowsocks as an access tool, not a security tool.

这是有一定道理的,但其实我一直想说... According to

A secure socks5 proxy, designed to protect your Internet traffic.

@uvu

SSR 的 auth_chain_*有此类问题么

https://github.com/shadowsocksrr/shadowsocksr/blob/manyuser/shadowsocks/obfsplugin/auth_chain.py#L538
https://github.com/shadowsocksrr/shadowsocksr/blob/manyuser/shadowsocks/obfsplugin/auth_chain.py#L553
沒有

@qeatzy
Copy link

qeatzy commented May 19, 2022

Jm2c, why not implement with long running tunnels multiplexing to different connections, N connections local <-> M tunnels <-> N mirror connections remote. In this fashion, you can use any method to authenticate/obfuscate/fake-characteristics, you can combine ecdh with pretending tls, thus no replay attack can ever happen, support one port multiple users naturally, also saved one RTT, that's a lot, if you open tens of browsers tabs at same time (eg, search google with vim-like extensions), not to mentions the timeout due to censorship will greatly decrease since it will be forced to track & analyze already established connection instead of the setup phase. I don't understand, why so serious about the original protocol and stuck with it? For what? Clowwindy might agree with me, or maybe?

@Fangliding
Copy link

挖个坟
令人欣慰的是 ss2022已经解决这个问题力

@rurirei
Copy link

rurirei commented May 10, 2023

a bit of off-topic.

https://xtls.github.io/development/protocols/vless.html#request-response
我一直觉得“响应认证”不是必要的,ALPHA 时为了提升生成随机数的性能,还用 math/rand 替换 crypto/rand,而现在都不需要了。
“协议版本”不仅能起到“响应认证”的作用,还赋予了 VLESS 无痛升级协议结构的能力,带来无限的可能性。 “协议版本”在测试版本中均为 0,正式版本中为 1,以后若有不兼容的协议结构变更则应升级版本。
I have always felt that "response authentication" is not necessary. In order to improve the performance of generating random numbers during ALPHA, math/rand was used to replace crypto/rand, but now it is not needed.
"Protocol version" can not only play the role of "response authentication", but also endows VLESS with the ability to upgrade the protocol structure painlessly, bringing unlimited possibilities. The "protocol version" is 0 in the test version and 1 in the official version. If there is an incompatible protocol structure change in the future, the version should be upgraded.

在不需要密码的情况下,可以随意对客户端接收的数据进行移花接木或重放等操作,使得被代理程序收到的数据没有任何可靠性。
比如将 A、B 两条 TCP 连接返回的数据对调,Shadowsocks AEAD 客户端是无法发现异常的,只会成功解密并传给被代理程序。
In the case that no password is required, operations such as embedding or replaying the data received by the client can be performed at will, so that the data received by the proxy program has no reliability.
For example, if the data returned by the two TCP connections of A and B are swapped, the Shadowsocks AEAD client will not be able to find the exception, and will only successfully decrypt it and pass it to the proxy program.

@RPRX how protocol version provides a response authentication as that's a const of 0 or 1, what i'm missing? then do you have a good idea to implements a response authentication for protocols without tls-security some like vmess or shadowsocks? thanks.

@rurirei
Copy link

rurirei commented May 11, 2023

either shadowsocks-2022 implements the response authentication with the request-salt attached.

https://shadowsocks.org/doc/sip022.html#_3-1-3-header Request salt in response header: This maps a response stream to a request stream. The client MUST check this field in response header against the request salt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests