shadowsocks-org
shadowsocks-org copied to clipboard
Shadowsocks AEAD 加密方式设计存在严重漏洞,无法保证通信内容的可靠性
这个问题是昨天发现的,本来我只是想先给 Xray-core 的 Shadowsocks 明文结构加个时间戳以彻底解决对服务端的重放问题。
顺便研究了一下 SS AEAD 的安全性,我脑洞比较大,考虑得比较多,比如是否可以造成 对客户端的攻击,简述如下:
- 对于 TCP,Shadowsocks AEAD 往返是完全一样的加密结构,HKDF 的
info也相同,且响应的加密完全独立于请求 - 对于 UDP,同样存在上面的问题(甚至可以与 TCP 杂交),更糟糕的是,UDP 往返连明文结构都是完全一样的
以上“特性”的利用方式太多了,真的很难排列组合完,还是主要说对客户端的攻击:
在不需要密码的情况下,可以随意对客户端接收的数据进行移花接木或重放等操作,使得被代理程序收到的数据没有任何可靠性。
比如将 A、B 两条 TCP 连接返回的数据对调,Shadowsocks AEAD 客户端是无法发现异常的,只会成功解密并传给被代理程序。
一些 SS 实现是全局共用 IV 过滤器,只能在单一客户端且不重启(which is impossible)的前提下防御重放,防不了移花接木。
Shadowsocks 流加密也存在此问题,有兴趣的可以研究一下 SSR 的 auth_chain_* 系列是否也存在此问题。
建议先用 VMess AEAD(实际上它有其它问题,但不是这种严重漏洞),最后感谢某个开发者群成员参与讨论与验证。
需要说明,这里主要是描述存在的漏洞,而对漏洞的利用有很多方式,比如 移花接木、重放、自交、杂交 等,欢迎补充测试结果。
开发者群成员进行了 本地 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 方式,其他攻击方式后续发布)
- 搭建本地 echo 环境
socat UDP4-LISTEN:2000,fork EXEC:'cat'和socat TCP4-LISTEN:2000,fork EXEC:'cat' - 启动任意上述受影响的实现
- 使用 proxychains 或 cgproxy (因为 proxychains 不能处理 UDP 流量) 代理
nc进行连接proxychains nc 任意IP地址 任意端口号cgproxy nc -u 任意IP地址 任意端口号 - 输入任意内容,<Enter> 即可发现内容被 echo 返回
参考复现结果 (仅参考)
TCP

UDP

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

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
@zonyitoo 想象一下引发 TLS 错误从而根据报错长度来判断是代理。以及,SS AEAD 作为一个加密传输层,本应保证回应可靠。
What happened to random IV?
@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.
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.
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.~~
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.
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.
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.
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.
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.
@viRikaRe
如果你正在使用 SS 浏览 HTTPS 网页,那么理论上,对调两条 TCP 返回的数据会导致服务端 TLS alert + 断连,这一行为是固定的。
没错,实际上不光是 AEAD,Shadowsocks 协议的 ciphers 都可以被这样操作,所以理论上是一个通用的识别 SS 的方式。
~~甚至可以根据 alert 长度推测出你大概在用什么 cipher~~
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.
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.
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.
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.
@madeye I don't think a bloom filter is useful. The attacker could replay some server messages sent to other clients.
@Mygod You're assuming different clients share the same key?
Isn't that what shadowsocks supposed to do?
Okay, you mean the clients connected to the same server port. Yes, you're right.
I think we can draft a protocol upgrade for this, like what we did before in SIP004 and SIP007.
Several guidelines:
- Minimize the protocol change and packet overhead.
- Solve the replay issue totally in this upgrade.
- Like SIP004, introduce this upgrade as new ciphers, and keep backward compatibility with old ciphers.
@database64128 @zonyitoo @Mygod What do you think?
@madeye We are already working on it. (with @database64128 @zonyitoo et al) A draft version would be soon available as long as it's somewhat complete.
But that proposal is still based on the current protocol. Should we move forward to shadowsocks v2 ? Making patches into the current protocol still cannot resolve #184 .
@zonyitoo I think a protocol upgrade should solve the replay issue entirely. Something like session ID can be introduced.
Moving to TLS based SOCKS6 protocol can solve everything, but that's not what we expected for shadowsocks.
@madeye I propose allowing replay because why not. For this issue in particular, I do not see the urgency but you could presumably consider the fix here: https://github.com/shadowsocks/shadowsocks-org/issues/183#issuecomment-787404696
Basically you are going to authenticate the client TCP header (before decryption) or client IV/nonce as your additional authentication data (AAD). This way it shall prevent connection mangling.
The bottom line is that I suggest do nothing.
@Mygod Yes, I think your solution is straightforward for TCP. But for UDP, we have multiple and out-of-order IV/nonce from client, which makes the implementation complicated.
I agree that the issue described here is kind of low priority, and I don't think we should change anything immediately. In the long term, we should keep the current AEAD ciphers as they actually works pretty well.
However, if we can have some SIP to solve the replay issue once and for all, we'd better adopt it. In the past few years, I have received many reports and PoCs for attacking shadowsocks protocol based on replay. Given we already finished the refactoring of shadowsocks-rust, I think it's time to move on and do some protocol upgrading.
BTW, what about dropping all the ciphers and recommend users to use plain shadowsocks protocol over TLSv1.3 plugins. Then I don't need to write so many words here to response a protocol design flaw. 😅
@madeye As I mentioned UDP is designed to be unreliable and stateless so that is an issue that the UDP client should handle instead. Protecting it against TCP is sufficient imo.
As for replay, I still think it is best to ignore the issue for the vanilla protocol. (If you do not like them, the way I see it you need to do timing/statefulness/handshakes. TLS handshake is the best option here.) After all, what is the point of solving this replay issue once and for all, if you could just use plugins like TLS instead?
I propose allowing replay because why not.
@Mygod https://gfw.report/blog/gfw_shadowsocks/
IMO, adding something like session ID should be acceptable, as the overhead is negligible.
Let's see what's the proposal from @DuckSoft and others. Hope it can make most of people happy here, then they can stop spamming my inbox...