shadowsocks-org icon indicating copy to clipboard operation
shadowsocks-org copied to clipboard

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

Open RPRX opened this issue 4 years ago • 108 comments

这个问题是昨天发现的,本来我只是想先给 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(实际上它有其它问题,但不是这种严重漏洞),最后感谢某个开发者群成员参与讨论与验证。


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

RPRX avatar Feb 28 '21 03:02 RPRX

开发者群成员进行了 本地 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. 输入任意内容,<Enter> 即可发现内容被 echo 返回

参考复现结果 (仅参考)

TCP

image

UDP

image

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

ghost avatar Feb 28 '21 04:02 ghost

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 avatar Feb 28 '21 04:02 zonyitoo

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

RPRX avatar Feb 28 '21 04:02 RPRX

What happened to random IV?

Mygod avatar Feb 28 '21 05:02 Mygod

@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.

bdbai avatar Feb 28 '21 06:02 bdbai

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.

zonyitoo avatar Feb 28 '21 06:02 zonyitoo

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.~~

Mygod avatar Feb 28 '21 06:02 Mygod

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.

zonyitoo avatar Feb 28 '21 06:02 zonyitoo

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.

Mygod avatar Feb 28 '21 06:02 Mygod

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.

zonyitoo avatar Feb 28 '21 06:02 zonyitoo

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.

Mygod avatar Feb 28 '21 06:02 Mygod

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 avatar Feb 28 '21 11:02 viRikaRe

@viRikaRe

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

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

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

RPRX avatar Feb 28 '21 11:02 RPRX

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.

zonyitoo avatar Feb 28 '21 14:02 zonyitoo

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.

madeye avatar Mar 01 '21 06:03 madeye

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.

zonyitoo avatar Mar 01 '21 06:03 zonyitoo

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 avatar Mar 01 '21 07:03 madeye

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

Mygod avatar Mar 02 '21 01:03 Mygod

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

madeye avatar Mar 02 '21 01:03 madeye

Isn't that what shadowsocks supposed to do?

Mygod avatar Mar 02 '21 01:03 Mygod

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

madeye avatar Mar 02 '21 01:03 madeye

I think we can draft a protocol upgrade for this, like what we did before in SIP004 and SIP007.

Several guidelines:

  1. Minimize the protocol change and packet overhead.
  2. Solve the replay issue totally in this upgrade.
  3. Like SIP004, introduce this upgrade as new ciphers, and keep backward compatibility with old ciphers.

@database64128 @zonyitoo @Mygod What do you think?

madeye avatar Mar 02 '21 02:03 madeye

@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.

DuckSoft avatar Mar 02 '21 02:03 DuckSoft

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 avatar Mar 02 '21 03:03 zonyitoo

@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 avatar Mar 02 '21 03:03 madeye

@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 avatar Mar 02 '21 04:03 Mygod

@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 avatar Mar 02 '21 05:03 madeye

@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?

Mygod avatar Mar 02 '21 05:03 Mygod

I propose allowing replay because why not.

@Mygod https://gfw.report/blog/gfw_shadowsocks/

database64128 avatar Mar 02 '21 05:03 database64128

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...

madeye avatar Mar 02 '21 05:03 madeye