The Wayback Machine - https://web.archive.org/web/20160829052958/https://github.com/breakwa11/shadowsocks-rss/issues/38
Closed

ShadowSocks协议的弱点分析和改进 #38

@breakwa11
breakwa11 opened this issue
about 1 year ago
research

本文内容比较偏向于技术,可能有点难看懂 :(

原ShadowSocks协议的TCP连接部分,使用非常简单的协议,加密前是这样子的:
地址类型(1byte)|地址(不定长)|端口(2byte)
其中,地址类型有三个可能取值:1,3,4,分别对应IPv4,Remote DNS,IPv6
地址的长度就按这个地址类型取值来决定,后面这些不详细介绍了,不是关键的东西。

而加密的时候,会根据不同的加密算法,在前面添加一些随机字节,再进行加密,以使得即使发送相同的数据,加密的结果也不会相同。但是,同一个加密算法下,前面添加的随机字节的长度是固定的,而最常用的加密算法只有rc4-md5和aes系列(aes-128-cf8和aes-256-cfb等等),而非常的不巧,这些加密算法在使用的时候前面均添加16字节,于是前文所说的表示地址类型的那个字节的位置是完全固定的(在第17字节上)。

服务端为了判断数据是否有效,判断的依据就是表示地址类型的那个字节,看它是不是在那三个可能取值,如果不是,立即断开连接,如果是,就尝试解析后面的地址和端口进行连接。

基本的原理介绍完了,以下介绍如何进行主动探测。
因为SS判断数据的合法性,仅仅依据表示地址类型的那个字节,与此同时,根据信息论,对加密后的数据解密的时候,同一个位置上的数据如果穷举全部256个编码,那么能正好一一对应解密后的全部256个原码,这与密码长度还是用什么加密算法完全无关。那么,可以通过暴力尝试全部256种可能,找出服务器是否有三种情况下没有立即关闭连接,从而成功判断出这个端口开放的是SS服务。成功判断出这是SS服务器为了什么呢?聪明的你应该明白,在这之后你的IP或者你的服务器IP其中一方会落入黑名单。
PS.因为chacha20填充的长度是8字节,和其它加密用的长度并不一样,于是你会发现用chacha20的往往比其它加密算法稳定,只是因为这个长度较少被用于探测。

关键的问题就是,只需要穷举256种可能,成本太低了,太不安全了。也许clowwindy也意识到这个问题,他在issue中提出到考虑让服务端发现连接的数据不正确的时候,把数据重定向到一个正常的http网站,让其行为看起来比较一致从而不好分辨。但是这个修改还没有完成,为什么clowwindy会被请喝茶?因为如果不阻止,那么加入这个特性后会让ss封锁起来困难很多,所以请喝茶,阻止更新,然后才方便对其实施大规模封杀(毕竟按照新的特性重新写探测代码很麻烦不是)。在我知道clowwindy被请喝茶那天,我已经明白根本不是因为ss不好封杀,而是阻止更新然后去封杀,这个弱点已经被掌握并正在被运用,大规模封杀那天会到来得很快。

但是,那个改进依然不安全,还是只要穷举256种可能,看一看是不是有253种情况是http/https协议就行了,所以我没有考虑这么做(不过我想得还是粗糙,这点其实是猜的,有误请指出)。如果是多用户服务器,就更简单了,直接端口扫描,看看开放的接口是不是大部分都是http(甚至全都指向同一地方)就行了,总之这种做法特征明显。

我感觉上,这个如果不从协议上去解决,还是很难避免被封杀,于是在原来的SS协议头,添加了一个包装,在这里复制一下这个包装的结构说明:
标志版本号(1byte)|首包总长度(2byte)|随机填充长度(1byte)|随机填充数据|原ss首数据包|CRC32(4byte)
起关键作用的,就是末尾的CRC32,前面的数据包不管你如何去猜测,有了CRC32作为整个数据包的校验,穷举成本提升到至少256的5次方,即需要尝试10^12次,或1T次,产生的数据量将达到几十T,这样子去暴力破解还不如直接DDOS的成本低。另外,为啥CRC32放最末尾?一是为了方便校验,二是根据加密的特性,前面的数据只要改了一个,就会导致后面的数据完全不一样,这样只要前面的数据一动,后面所有的值就都错了,让CRC32校验充分发挥作用。
另外还有一个关键的地方就是随机填充不定长度的数据。现在其实除了新的Windows C#版客户端,其它平台基本没有把握手包和第一个数据包连起来发送,这样会导致SS客户端发送的第一个数据包的长度,在很多情况下是固定的,从而成为被检测的依据。就算实现了连同第一个数据包一起发送,但某些协议的连接并不会立即产生第一个数据包,而是先等待服务端的回应(比如 SSH),于是依然存在定长的问题(会精确等于16+1+4+2=23字节,只要统计发现你的IP经常产生23字节的TCP首包便可封杀)。为方便其它平台以最少的代码实现,直接在协议头部加入这个随机填充长度,以弥补ss在加密时总是产生定长数据包的问题。

这个解决方案能极大增加主动探测的难度以及首包长度检测的难度(非首包长度目前没有混淆,尽管还是可以通过非首包长度进行猜测,但毕竟需要的计算资源较大,这样调整后的SS还是能存活较长的时间的),但不想对SS协议做太大的改动从而造成各平台实现的修改困难(在Python或C#上增加支持这个协议才10行代码不到),SS之所以能实现如此多的平台,正是因为其协议简单,容易实现,所以我决定使用比较简单的协议来解决以上问题。

如果以上协议你还发现存在弱点,请留言告诉我,大家一起讨论——by Breakwa11

然而你的加密写的极差无比,人品也是

@Xuchenhao 不要人身攻击!!

@Xuchenhao 然而你看也看不懂,写也不会写,小朋友。

不会写的小朋友,喝喝茶

果然触碰到一些人的利益了。

在敏感时期连一个有技术的妹子都敢于挑大梁,勇气何等珍贵!那些海量的共匪狗,五毛狗利益被威胁,所以跳出来乱咬,滚去给你刁包主子跪舔吧

为什么你不能先Google一下ecb和cbc呢?
https://en.m.wikipedia.org/wiki/Block_cipher_mode_of_operation

@renyidong 因为我不知道,现在粗略看了一下,就是描述对数据流的加密所使用的加密方式,不知道是否是我原文哪里说错了?请指教

@breakwa11 虽然我觉得对一个合格的程序员来说,这点英文水平至少应该有,可再怎么说你也可以点一下左侧的中文。
https://zh.m.wikipedia.org/wiki/块密码的工作模式
中文wiki虽然内容比英文少很多,不过也很足够说明问题了。

感谢你(再)发明了轮子,成功的解决了一个不存在的漏洞。
既然你要接手这个项目,为什么你连ae256-cfb中的cfb是什么意思都懒得Google一下?

@renyidong 这个问题其实对于流加密来说(不管你如何处理)均有此问题,和cfb还是ofb无关,具体是实现上的问题

检测到这种协议就直接丢包。。。。是一个可行的方案

我还没有严格验证过,昨天写得很匆忙,今晚验证一下

@breakwa11 哎呦,脸打的这么响,都肿了,还不承认啊。我就问了,你这样歧视古今中外的密码学家的智商,也不怕折寿?

1976年,IBM发明了密码分组链接(CBC,Cipher-block chaining)模式。在CBC模式中,每个平文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有平文块。同时,为了保证每条消息的唯一性,在第一个块中需要使用初始化向量。

就这一段,cbc是为了解决什么问题,初始向量什么作用,说的清清楚楚。流式加密只有ecb才有这个问题。因为ecb块与块之间独立加密。

而非常的不巧,这些加密算法在使用的时候前面均添加16字节

呵呵,这不是不巧,而是完全正确的用法。16byte=256bit,就是一个块长度的初始向量。

我看此issue极其宝贵,可使广大程序员免受拔牙之苦。

如果你还要接着嘴硬:talk is cheap,show me the code。Wireshark抓包记录秀出来。

以ctrl+s

cbc根本没有解决我在文章中所说的问题,文章中本来就考虑到了。所以其实我还是不知道你想说啥,能再说清楚一些么

@breakwa11 你就接着满地打滚装傻好了。有高中水平的数学,认真看完以上内容,早就懂怎么回事了。该说的都说了。再往下,纯粹就是智力测验了。

@renyidong
剛剛看了一下https://en.m.wikipedia.org/wiki/Initialization_vector
我對密碼學並不了解。要看一看了

@renyidong
理論上拔牙之苦我以前也有經歷過,哈哈,真是慘痛的教訓,只怪以前無知

目前并没有发现你的说明与本文有些什么关系,文中本来就考虑到后面加密数据依赖上文的情况,但对于此攻击没有任何防御能力,如果没有有力的说明,那么我会完全无视你说的内容。至于说到严格证明,文章由于写得匆忙,还没有加上具体的加解密和攻击示例,会在晚上慢慢补上

@breakwa11
shadowsocks源碼我沒有具體看過,要去扒一下才能驗證了.

说了吧,从这往下都是智力测验的内容。不是我当别人弱智。只要智商正常的人,看完中文维基多少该懂。反之,modus tollens。
不懂没关系,可以学。人傻没关系,可以多努力。然而你叫不醒装睡的人。
@breakwa11 again, talk is cheap, show me the code. 我除了开头结尾各两句情绪化语言,中间全是干货。

不妨最后再给你个机会,我楼上所有发言,有个问题完全没提及,然而却能使你提出的攻击从理论上可行。
当然,即使你能指出来,也只能证明你认真读了维基,而且是在本issue开始之后。因为你先前提出的解决方法,是完全错误的。此外,即使该攻击理论上可行,实际上也是没有意义的。原因现在先保留。无论你是否能说对,我都会公布问题所在,以及解决办法,供真心想改进ss的人参考。

@renyidong 讨论问题就说问题,说什么打脸,打滚的有个卵用。别人一妹子程序媛也是边做变学习,懂得多的多指教,懂的少的虚心点。套用温影帝常掰的:知无不言,言无不尽。
再套个你的,talk is cheap, show us the code,你倒是写个参考实现出来阿,you can you up, no can no bb. github上牛人一抓一大堆,装啥聊斋,一天天的闹心

我发现我每个帖子都有一个特别高姿态的人,还每次都不一样的id,为啥之前的人不跟过来呢,好奇怪

围观撕逼

@breakwa11 因为我们都知道您的水军很厉害,也明白了一个道理,永远不要试图叫醒装睡的人

ilc22xe5vcm7 kqjv 9iv4

有一些头像都没有的

@aOJzQT 唉呦,水军来啦?
我说了,无论对错,我都会将我发现的问题和解决办法一并奉上。code当然有,但我也不想有人马后炮出来说:“其实早就知道了”云云。台面上摆明白,大家看清楚。

懂得少的是该虚心点,然而某人虚心了么?
懂得多的是该多指教,我先贴链接,又指出读哪一段,最后直接节选关键,这不叫指教什么叫指教?我耐不耐心,明眼人一看就懂,你搅什么浑水?

GitHub上牛人多,水军更多。

@breakwa11 什么时候正面回答了我的问题,再来叫我。除此之外,一律无视。

I think I am not a communist.And I am the admin of long-live-shadowsocks.So donnot act like a snob.You are a dog but I am notPick up your 'friends'.
I just told a truth.Your code is fool! :D

前来膜拜SSR

@XhstormR no ad hominem. This would be violation of GitHub ToS. I know that she knows nothing about cryptography, and so does everyone who is conscious. But direct ad hominem is another story.

@renyidong 你既然想解决问题就别和大家打嘴仗,撕逼,一个男的跟女的撕什么,还是在github上,还来劲了。你管我水不水军,这是重点?关键是你的建议和code,你可是夸了口了,别到时候操到一半痿了。

就算 @breakwa11 技术欠缺,代码“烂”,起码别人在尝试,你几个大神倒是fork过去提pull request阿,要不干脆直接参与不就好了。可别来什么怕被查水表、我很忙,不肖云云的托词。

我本不想参与"撕逼",这里给所有人致歉,都是共匪沦陷区的墙友,何苦相互为难。

跟贱人瞎掰,你们没毛病吧

@renyidong 这和性别无关把...

@renyidong
抱歉刚刚at错了。刚刚扫了一下,你说的确实没有错。

@polong 每天上班在那坐一天,多没意思,看这个 repo 放松心情啊

@breakwa11
老实说作为一个严谨的程序员,你不能说这句“不过我想得还是粗糙,这点其实是猜的,有误请指出”,或者诸如“据可靠消息”之类的。望注意

骂人的小学生连高中都没上就辍学了,还是好好上学吧,连高中都读不上,骂人可是强项。

@renyidong 也是醉了,你要真想SS,就贡献代码;而不是在这里玩嘴皮子,勾心斗角,或者装个只会talk的大神;我就纳闷了,到底是谁是talk is cheap, show me the code,难道不是你吗?要我说,你还是去当狗仔队记者比较好,不要在这里浪费大家时间。

你忘了开Github这个网站的目的了?就是为开源代码,集众人力量,改进代码,然后Push;而不是让你看到代码漏洞,就在那里嘚瑟;国外开源库都要有你这样的人,开源社区早见鬼了。

看到这些冷嘲热讽的人,真令人恶心。

笑看装x还不贡献代码,却满脸牛x样子的“文学家”。

話說那種新註冊然後自己的帳戶裡根本沒有項目或者只是fork了SS的項目的...簡直和在FB上面成天罵人的無臉帳號一個樣啊(

建议大家停止争吵,虽然争吵可以暴露出一些问题,但相比由此带来的种种不好的影响而言,此举仍然是得不偿失。虽然大家意见不合,但这并不是太大的问题。就像clowwindy说的那样:“The license problem is rather a small problem, compared to other problems we face now.”

相比我们现在所面临的问题,这些小问题算得了什么?我们安安静静的在这讨论技术不好吗?为什么非要扯那些有的没的?为什么非要针锋相对?就算是有人的言辞和态度让人感到不适,也没必要这样斤斤计较吧?冷静下来想一想,这样真的对吗?事实就是你认为的那样吗?
希望大家不要一开始就抱着鄙夷或怀疑的态度来对待每个人,这样很容易造成误解。

@falseen 自己去 clowwindy 的 twitter 看看,到底说的是什么

为什么不能安安静静讨论技术?因为 repo 主根本没有技术,屡次空穴来风的故意说原版 SS 有缺陷,有 XX 漏洞,比如这个 issue 就是,然而他改写协议缺陷更多,漏洞更多,还不能说,一说他的话不对,一说他写的代码不好,里面就会有好几个,甚至几十个小号来骂你。这是不能讨论技术的最主要的原因,大家应该也看到了,某人小号回复的时候换错了号,然后又自己删了。

为什么要扯那些别的?因为 repo 主一直在不停地扯别的,他要写协议就写吧,还要来说 SS 原版的协议不安全,有问题,renyidong 过来指出了 repo 主文章中的说错的地方,然而 repo 主根本不懂是什么意思,假装看懂了一样的,回复了一堆牛马不相及的东西,最后发现有问题,明白了一点的时候,又开始故意装傻。

为什么要针锋相对?为什么要计较?上面两段已经说了,repo 主不懂装懂,写一些有问题的东西骗小白,这就算了,竟然还开小号或者叫水军来骂人,请问你是来写翻墙的还是来建立帝国的?

“事实就是你认为的那样吗?”你难道没有看到吗?

是我们真的不支持 SS 吗?我们难道真的会去跪舔土工吗?历史上跪舔土工的,最后哪个人不是死在了土工手里?

非常同意 @TearDownGFW 的观点。
我们是想让不明“真相”的多数人看到:Repo主并没有成熟的技术、不懂装懂、装疯卖傻、用这种软件的意义来博取人们的同情,造成误解,导致出现这么多的撕逼。
@breakwa11 你开了马甲小号来给自己洗地,随后删除,你这样做是把我们当傻子看么?

我们的目的都一样,是为了更好的网络环境,希望大家意识到了这点后,冷静思考。

@wszf5560
别喷了, http://we.poppur.com/thread-1446352-1-1.html 先给自己洗洗地吧

@TearDownGFW
哪来这么多小号,你看我是小号吗?

到底是谁在扯些别的,不是你吗?工信部的朋友,还说是挂代理钓鱼,你真的没事干嘛吗?

你说repo主不懂装懂,那你倒是给个说法啊?就扯小号水军什么的,你以为这里是微博啊,哪来这么多小号,5毛你给?

你不待见这个repo或者repo主,就请你unwatch吧,别来浪费口舌贬低了。Github是来写代码的,其他的事跑GOOGLE Group去,真是污染眼球。

@shuangzhijinghua 没有恶意,我只是随手百度了一下看到的。大家都要冷静一些嘛。

@TearDownGFW @YouRepelMe 这是给你洗地的小号吗?

@YouRepelMe
YouRepelMe
Joined on 29 Aug 2015

只是随手点进了你的个人资料而已。

这里奇葩人真多,包括那个许宸皓,就是本贴第一回复,http://tieba.baidu.com/p/3718260428
好笑死了,看戏都可乐。

@YouRepelMe 别人卖个东西出争执有什么好洗地的,倒是你,小鲜肉,今天刚注册的新货,穿太多马甲小心把憋着。

@wszf5560 @TearDownGFW
你们说的很有道理,问题确实是出在了“某些人”的身上。他们就像旧时的“红卫兵”,如果没有他们,争吵是很难发生的。但从另一个角度考虑,他们的行为也是可以理解的,只是走错了地方。
技术上的争吵是很正常的,没有争吵就没有进步。但上升到人身攻击,性质就变了。真希望那些不懂github是什么地方的人转移阵地,不要污染了这里的环境。

@wszf5560 这样说确实没错,这个时候不管是谁站出来都是值得鼓励的。如果站在这个角度来看TearDownGFW和其他几个人的观点的话,那他们的行为确实是值得批判一番的。我之前就是站在这个角度来看的,但通过TearDownGFW后面的回复我才发现事情没有我想的那么简单,总结起来就是:“冰冻三尺非一日之寒”。矛盾是逐渐累积的,想要化解自然也需要一段时日。

能像你这样理性的指出错误的人太少了,如果人人都像你这样那该多好。

另外我不站在任何一方,我只站在ss的一方。而且我也不认为breakwall的行为全是对的,但既然clowwindy都公开支持她了,我也就试着去接受她一下,看看情况如何。以上。

少拿小号来洗地,拿真名上Github Fork Shadowsocks,我不是你们想惹就惹得起的。
作者你还真不要脸,当心精分啊。

@Xuchenhao 得了吧,你都敢拿真名去做缺德事了,还怕拿真名来这骂人嘛,你连脸都不要了你还有什么可怕的,你没听过一句话:人至贱则无敌。不是怕惹得起你的问题,而是惹有毛线用,你别太看得起自己。
另外,你难不成觉得我是作者的小号,如果不是,那还好,如果是,除了你的思想也是奇葩之外,你实在太看得起我了,我没作者的好技术,我更没作者的好脾气。此刻,我是多么希望我有作者的技术,我也能为这场斗争出点真正的力,而不是只是来这里费舌。

@Xuchenhao @polong @wszf5560
以上@到和没@到的各位,这样的争吵是没有任何意义的,因为大家所站的角度和所掌握的信息不一样,所以争来争去也争不出什么结果来。这就像古代打仗,利用的就是信息的不对称。虽然这里可能并没有谁利用谁,但矛盾确实是由信息的不对称而逐渐升级的。

如果各位一定要为了一个破事争个你死我活、争到天荒地老,那我希望你们能站在ss的角度去争吵,而不是站在某个人的角度。这样即便是争得面红耳赤、争到头破血流那也是有意义的!!

因为早就从讨论degrade到人身攻击了

grassmudhorse

都散了吧,真撕个没完啦,来一发

@falseen 哎,本来我的想法和你一样,但是实在看不过去,作者曾经因为违反开源协议被骂也就罢了,这回都开源了,按正常是人的思维,你若有能耐,作者的思路有什么问题,大可认真指出,可是它们是不是真有能耐不知道,舌头跟舔过屎似的,把嘴弄臭倒挺有能耐。
我只是祝愿它们是五毛吧,至少五毛它们为主而活为主说话,至少它们嘴还臭得还有原因,如果它们不是五毛,它们才真正的可悲。
好了算了,我也不扯皮了。

小白悄悄路过,纯支持。感谢有你。

测试了以下,FORCE_NEW_PROTOCOL=True 没有路由器和手机端 的支持?

没看懂上面 @renyidong 说的问题重点是在哪里, @breakwa11 说的是墙可以主动检测,使用的方法是利用服务器对于"地址类型"这个字节不同值的反应,你说的似乎不是在反驳 @breakwa11 的观点?

不过对于 @breakwa11 的说法我也有疑问,是不是把"看它是不是在那三个可能取值,如果不是,立即断开连接"改成不断开连接就行了?因为主动检测没办法知道密码,所以这时候即使发出请求,也是不会得到服务端的回应的,是不?

我这也是小号,有点疑问的是,在这里不用小号的应该都是对自己信心满满不怕土共的,是不?

@bigoktesk cbc、cfb前面的字节会影响后面的结果,所以她说的256->256的对应关系不成立。也就是说地址类型位明文确实只有256个取值,密文根本不是

有话好好说

@bigoktesk 其实你说的办法是可选的一种解决办法。不reset而是先返回一些垃圾数据。

一句吐槽变成撕逼就算了
撕逼还能扯到政治 ,还能搞批斗,也是醉了。repo主急需提高粉丝的质量啊

@renyidong
bigoktesk说的方法正是clowwindy离开之前准备着手去做的,具体你翻下原项目的issue。而且breakwall也提到了这个事。

我觉得你的说法有一定的道理,至于她为什么不承认,我也有点想不通,且看她如何反驳吧。但不管她会作何反应,我都希望你能把你的想法说出来。毕竟这里并不止她一个人,至少我看了你发的东西之后是获益良多的。

"于是前文所说的表示地址类型的那个字节的位置是完全固定的", 其实作者被@renyidong提醒的地方是在这里。

具体的话,看完维基自行做个实验就明白了。(aes-256-cfb,然后尝试穷举某个byte,看看解密那边什么反应)

嗯...其实就是说需要实践检验一下字节的位置是不是完全固定的......
如果不是就说明并不存在这个漏洞....

只说技术,如果有之前讨论到但眼花没看到的内容,请补充

@renyidong 在加密这一块上,的确是你的理解有问题,协议中的这个弱点是真实存在的
你提到了块密码的工作模式,那应该是对密码学有一定理解的,那你应该知道AES不管工作在什么模式下,密文和明文的位置都是一一对应的,修改某个位置的密文,即对应修改了某个位置的明文(根据加密模式的不同,可能影响到后面其他密文块的解密,也可能影响不到,但在这里这个性质并不重要),如果预先知道了明文的模式,虽然无法解密还原出内容,但可以修改密文中的特定字节,起到修改解密后的明文的效果。在这里解密后的明文是多少只有天知道,但一共只有一个字节,所以穷举一下就行了,一共256种可能,这就是 @breakwa11 的意思。

Talk is cheap, show me the code.

举个例子,现在有一个协议包,共7个字节

0x01, 0x08, 0x08, 0x08, 0x08, 0x00, 0x50

对照socks5协议,很明显这是一个IPv4包(第一个字节是0x01),目的地是8.8.8.880端口

被shadowsocks加密了以后(密码abc,加密方式aes-256-cfb),数据包就变成了这样

0xbb, 0x59, 0x1c, 0x4a, 0xb9, 0x0a, 0x91, 0xdc, 0x07, 0xef, 0x72, 0x05, 0x90, 0x42, 0xca, 0x0d, 0x4c, 0x3b, 0x87, 0x8e, 0xca, 0xab, 0x32

前16个字节,从0xbb0x0d,都是iv,根据issue中提到的弱点和之前的总结,只需要修改0x4c,即真正密文中的第一个字节,就可要起到修改明文中的第一个字节的效果。

那就把0x4c修改成0x4d吧,解密以后的结果是

0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x50

的确只有第一个字节被改掉了,根据breakwa11的理论,不难推出其他情况,其中合法的是

0x4e => 0x03 (Domain Name)
0x49 => 0x04 (IPv6)

其他的转换情况也一并附在这里,可以数一下,解密后的结果是不存在重复的,可以说是一一对应的

0x00 => 0x4d, 0x01 => 0x4c, 0x02 => 0x4f, 0x03 => 0x4e, 0x04 => 0x49, 0x05 => 0x48
0x06 => 0x4b, 0x07 => 0x4a, 0x08 => 0x45, 0x09 => 0x44, 0x0a => 0x47, 0x0b => 0x46
0x0c => 0x41, 0x0d => 0x40, 0x0e => 0x43, 0x0f => 0x42, 0x10 => 0x5d, 0x11 => 0x5c
0x12 => 0x5f, 0x13 => 0x5e, 0x14 => 0x59, 0x15 => 0x58, 0x16 => 0x5b, 0x17 => 0x5a
0x18 => 0x55, 0x19 => 0x54, 0x1a => 0x57, 0x1b => 0x56, 0x1c => 0x51, 0x1d => 0x50
0x1e => 0x53, 0x1f => 0x52, 0x20 => 0x6d, 0x21 => 0x6c, 0x22 => 0x6f, 0x23 => 0x6e
0x24 => 0x69, 0x25 => 0x68, 0x26 => 0x6b, 0x27 => 0x6a, 0x28 => 0x65, 0x29 => 0x64
0x2a => 0x67, 0x2b => 0x66, 0x2c => 0x61, 0x2d => 0x60, 0x2e => 0x63, 0x2f => 0x62
0x30 => 0x7d, 0x31 => 0x7c, 0x32 => 0x7f, 0x33 => 0x7e, 0x34 => 0x79, 0x35 => 0x78
0x36 => 0x7b, 0x37 => 0x7a, 0x38 => 0x75, 0x39 => 0x74, 0x3a => 0x77, 0x3b => 0x76
0x3c => 0x71, 0x3d => 0x70, 0x3e => 0x73, 0x3f => 0x72, 0x40 => 0x0d, 0x41 => 0x0c
0x42 => 0x0f, 0x43 => 0x0e, 0x44 => 0x09, 0x45 => 0x08, 0x46 => 0x0b, 0x47 => 0x0a
0x48 => 0x05, 0x4a => 0x07, 0x4b => 0x06, 0x4d => 0x00, 0x4f => 0x02, 0x50 => 0x1d
0x51 => 0x1c, 0x52 => 0x1f, 0x53 => 0x1e, 0x54 => 0x19, 0x55 => 0x18, 0x56 => 0x1b
0x57 => 0x1a, 0x58 => 0x15, 0x59 => 0x14, 0x5a => 0x17, 0x5b => 0x16, 0x5c => 0x11
0x5d => 0x10, 0x5e => 0x13, 0x5f => 0x12, 0x60 => 0x2d, 0x61 => 0x2c, 0x62 => 0x2f
0x63 => 0x2e, 0x64 => 0x29, 0x65 => 0x28, 0x66 => 0x2b, 0x67 => 0x2a, 0x68 => 0x25
0x69 => 0x24, 0x6a => 0x27, 0x6b => 0x26, 0x6c => 0x21, 0x6d => 0x20, 0x6e => 0x23
0x6f => 0x22, 0x70 => 0x3d, 0x71 => 0x3c, 0x72 => 0x3f, 0x73 => 0x3e, 0x74 => 0x39
0x75 => 0x38, 0x76 => 0x3b, 0x77 => 0x3a, 0x78 => 0x35, 0x79 => 0x34, 0x7a => 0x37
0x7b => 0x36, 0x7c => 0x31, 0x7d => 0x30, 0x7e => 0x33, 0x7f => 0x32, 0x80 => 0xcd
0x81 => 0xcc, 0x82 => 0xcf, 0x83 => 0xce, 0x84 => 0xc9, 0x85 => 0xc8, 0x86 => 0xcb
0x87 => 0xca, 0x88 => 0xc5, 0x89 => 0xc4, 0x8a => 0xc7, 0x8b => 0xc6, 0x8c => 0xc1
0x8d => 0xc0, 0x8e => 0xc3, 0x8f => 0xc2, 0x90 => 0xdd, 0x91 => 0xdc, 0x92 => 0xdf
0x93 => 0xde, 0x94 => 0xd9, 0x95 => 0xd8, 0x96 => 0xdb, 0x97 => 0xda, 0x98 => 0xd5
0x99 => 0xd4, 0x9a => 0xd7, 0x9b => 0xd6, 0x9c => 0xd1, 0x9d => 0xd0, 0x9e => 0xd3
0x9f => 0xd2, 0xa0 => 0xed, 0xa1 => 0xec, 0xa2 => 0xef, 0xa3 => 0xee, 0xa4 => 0xe9
0xa5 => 0xe8, 0xa6 => 0xeb, 0xa7 => 0xea, 0xa8 => 0xe5, 0xa9 => 0xe4, 0xaa => 0xe7
0xab => 0xe6, 0xac => 0xe1, 0xad => 0xe0, 0xae => 0xe3, 0xaf => 0xe2, 0xb0 => 0xfd
0xb1 => 0xfc, 0xb2 => 0xff, 0xb3 => 0xfe, 0xb4 => 0xf9, 0xb5 => 0xf8, 0xb6 => 0xfb
0xb7 => 0xfa, 0xb8 => 0xf5, 0xb9 => 0xf4, 0xba => 0xf7, 0xbb => 0xf6, 0xbc => 0xf1
0xbd => 0xf0, 0xbe => 0xf3, 0xbf => 0xf2, 0xc0 => 0x8d, 0xc1 => 0x8c, 0xc2 => 0x8f
0xc3 => 0x8e, 0xc4 => 0x89, 0xc5 => 0x88, 0xc6 => 0x8b, 0xc7 => 0x8a, 0xc8 => 0x85
0xc9 => 0x84, 0xca => 0x87, 0xcb => 0x86, 0xcc => 0x81, 0xcd => 0x80, 0xce => 0x83
0xcf => 0x82, 0xd0 => 0x9d, 0xd1 => 0x9c, 0xd2 => 0x9f, 0xd3 => 0x9e, 0xd4 => 0x99
0xd5 => 0x98, 0xd6 => 0x9b, 0xd7 => 0x9a, 0xd8 => 0x95, 0xd9 => 0x94, 0xda => 0x97
0xdb => 0x96, 0xdc => 0x91, 0xdd => 0x90, 0xde => 0x93, 0xdf => 0x92, 0xe0 => 0xad
0xe1 => 0xac, 0xe2 => 0xaf, 0xe3 => 0xae, 0xe4 => 0xa9, 0xe5 => 0xa8, 0xe6 => 0xab
0xe7 => 0xaa, 0xe8 => 0xa5, 0xe9 => 0xa4, 0xea => 0xa7, 0xeb => 0xa6, 0xec => 0xa1
0xed => 0xa0, 0xee => 0xa3, 0xef => 0xa2, 0xf0 => 0xbd, 0xf1 => 0xbc, 0xf2 => 0xbf
0xf3 => 0xbe, 0xf4 => 0xb9, 0xf5 => 0xb8, 0xf6 => 0xbb, 0xf7 => 0xba, 0xf8 => 0xb5
0xf9 => 0xb4, 0xfa => 0xb7, 0xfb => 0xb6, 0xfc => 0xb1, 0xfd => 0xb0, 0xfe => 0xb3
0xff => 0xb2

@phpbestlang 看来我对AES的工作方式理解确实不对。我之前的理解是AES加密是以块为单位,是块到块的映射关系。因为有ShiftRows和MixColumns两步,所以修改一个bit的明文就会导致超过一个bit的密文改变,反之亦然。我会再做验证。

不过其实这依然不属于协议的问题,而是实现中没有考虑恶意客户端重用IV导致的。所以除了验证失败时返回随机内容,还有两点可做:
1. 对最近的IV进行统计,如果发现IV频繁重用,则说明该客户端是恶意的。
2. 对验证失败的次数进行统计
两者都可以用一个IP为key的dict实现,开销不会太大。我想到的只有这么多,欢迎交流。

@phpbestlang 我之前的想法是,要穷举明文的1个byte,需要穷举整个密文块,现在看来并不是这样。

小白路过…围观撕逼…

@phpbestlang 我发现我也是没有考虑到这点。

博主加油啊,一定要隐藏好自己,千万别再被喝茶

继续路过,建议楼主不理会那些人身攻击的、冷嘲热讽的帖子。时间很宝贵的,不要回复没有意义的人,那些人如果真的想帮助你,可以明确指出不足,甚至提供改进代码,当然,他们绝不可能那么做的,因为他们生活在阴暗里,你越理他们,他们越来劲的,而你的时间却被浪费掉了。

加油。

@falseen 原项目的issue都看不到了吧?

@phpbestlang 我不懂算法,但是我刚刚跑了 SS 的代码测试了,不要嘴上说,一定要实践

我觉得你可能没有注意到的是,SS 在每条连接的加密和解密过程中都是使用的同一个 encryptor 或者 decryptor 实例,并不是每次都初始化新的。

我们循环 0,255 之间数的 binary,前 16 位是 iv ,所以替换第 31-32 位的 hex 数据进行解密

首先是每次都初始化不同的解密器

Starting test ...
METHOD:aes-256-cfb
Plan Data: 5468697320697320746573742064617461
Real 31-32 bytes: 01
Tesing: 00
4281ed35cde1bbee006a1f57edfb04d761
Tesing: 01
5468697320697320746573742064617461
Tesing: 02
70a5d23fd751871b72a9e50b4be274f961
.......
Tesing: fd
9441b08a9a8c950a4696a57be5abf47461
Tesing: fe
55d7d9375a1cb0b3c2f811055290811361

我们可以看到,除了没有修改过的 “01” 位,只要修改了整个加密包中的任何一个字节为任何数据,解密出来的东西全部都是乱码,与原文没有任何关系,所以你的问题不成立

我们再按照实际的 SS 代码试一下

Starting test ...
METHOD:aes-256-cfb
Plan Data: 5468697320697320746573742064617461
Real 31-32 bytes: 04
Tesing: 00
5d5bf38b32acae600cf488274fc2892961 // 第一次尝试,不是 04 ,解密失败
Tesing: 01
ERROR: Decrypt failed
Tesing: 02
ERROR: Decrypt failed 

实际的 SS 代码,如果实行你所说的 IV 重放攻击,会直接对后续的一切数据包解密失败,只要第一次不正确,就算真正的提交了正确的猜测,也会解密失败。

不要搞理论,实践出真知。

所有代码均来自被删前大概 10 天的 git 备份

如果哪里有问题,请指出

所以说这个问题是存在还是不存在?😵

@pluto6496 我相信你看了上面所有的讨论,以及 breakwa11 回复的话,应该会明白

这个 issue,算是智商测验吧。

@pluto6496
breakwa11之前固然有很多地方在吹牛,对,我指的就是多路TCP这种不靠谱的“黑科技”,但在这个问题上,我认为这个弱点是真实存在的

@TearDownGFW
我也是跑shadowsocks代码测试的,并且贴出了所有的参数和测试结果,都是可以复现的
你可以把那段加密后的数据以密码abc,加密方式aes-256-cfb做解密,看看是不是我给的明文
再换一下第17个字节,看看是不是就变动了一个字节

您给出的结果,呃,说实话我没看明白,这不是握手包,也没有其他信息,可以给出详细的测试代码吗

另外,根据你说的

我觉得你可能没有注意到的是,SS 在每条连接的加密和解密过程中都是使用的同一个 encryptor 或者 decryptor 实例,并不是每次都初始化新的。

这句话,我觉得你可能没能理解这个弱点是如何利用的

搬运过来一个关于CFB工作模式的说明参见http://www.cnblogs.com/happyhippy/archive/2006/12/23/601353.html
优点:
1.隐藏了明文模式;
2.分组密码转化为流模式;
3.可以及时加密传送小于分组的数据;
缺点:
1.不利于并行计算;
2.误差传送:一个明文单元损坏影响多个单元;
3.唯一的IV;
@breakwa11 AES本质是块加密的,没有错
我也用openssl做了下测试,openssl在输入密码后,会产生随机盐值,在加密后会将这个盐值写在密文消息的头部
密码也用 abc
d是明文消息 e是加密后的消息体
$openssl aes-256-cfb -p -in d -out e
enter aes-256-cfb encryption password:
Verifying - enter aes-256-cfb encryption password:
salt=C71FC8D631013D49
key=EE5BA56E4C74F4108320E558A8FEB1BA652334D37940AC028938A047A9B971CE
iv =96EAB019C7B859D9AFD7D7D46AD4BB19
加密后:
$ hexdump -C e
00000000 53 61 6c 74 65 64 5f 5f c7 1f c8 d6 31 01 3d 49 |Salted__....1.=I|
00000010 b7 21 d9 5a 21 a5 63 |.!.Z!.c|
00000017
修改第17字节为任意值:
$ hexdump -C e1
00000000 53 61 6c 74 65 64 5f 5f c7 1f c8 d6 31 01 3d 49 |Salted__....1.=I|
00000010 30 21 d9 5a 21 a5 63 |0!.Z!.c|
00000017
解密修改后的密文
$ openssl aes-256-cfb -d -p -in e1 -out d2
enter aes-256-cfb decryption password:
salt=C71FC8D631013D49
key=EE5BA56E4C74F4108320E558A8FEB1BA652334D37940AC028938A047A9B971CE
iv =96EAB019C7B859D9AFD7D7D46AD4BB19
查看解密后的结果
$ hexdump -C d2
00000000 86 08 08 08 08 00 50 |......P|
00000007
的确发现第一字节值改变了,而后续值仍对的。
但是如果将cfb 8bit模式更改为cfb 1bit模式,就会影响了。
$ openssl aes-256-cfb1 -p -in d -out f
enter aes-256-cfb1 encryption password:
Verifying - enter aes-256-cfb1 encryption password:
salt=66321315CAAE5D96
key=690B35D3A8FBD10BDDFC3F62E248194FDD11A83E0DFCA6BDA2CD89AE5B975BE4
iv =68FC77DD023AEEBAFF5E5A13E7404B22
$ hexdump -C f
00000000 53 61 6c 74 65 64 5f 5f 66 32 13 15 ca ae 5d 96 |Salted__f2....].|
00000010 c7 7d a7 f8 75 15 fc |.}..u..|
00000017

将密文f 修改
$ hexdump -C f1
00000000 53 61 6c 74 65 64 5f 5f 66 32 13 15 ca ae 5d 96 |Salted__f2....].|
00000010 30 7d a7 f8 75 15 fc |0}..u..|
00000017
解密
$ openssl aes-256-cfb1 -d -p -in f1 -out df
enter aes-256-cfb1 decryption password:
salt=66321315CAAE5D96
key=690B35D3A8FBD10BDDFC3F62E248194FDD11A83E0DFCA6BDA2CD89AE5B975BE4
iv =68FC77DD023AEEBAFF5E5A13E7404B22

结果:
$ hexdump -C df
00000000 c5 ba 45 07 7f 98 48 |..E...H|
00000007

至于说ss本身是怎么加密传输的,那你们接着争吵吧,我不关心,--我对客户端证书式认证传输感兴趣--。

我的搬瓦工上的ss的log:

2015-08-29 16:23:47 ERROR    can not parse header when handling connection from 198.35.46.6:34252
2015-08-29 16:23:47 WARNING  unsupported addrtype 222, maybe wrong password
2015-08-29 16:23:47 ERROR    can not parse header when handling connection from 198.35.46.6:34253
2015-08-29 17:23:47 WARNING  unsupported addrtype 44, maybe wrong password
2015-08-29 17:23:47 ERROR    can not parse header when handling connection from 198.35.46.6:55072
2015-08-29 17:23:47 WARNING  unsupported addrtype 222, maybe wrong password
2015-08-29 17:23:47 ERROR    can not parse header when handling connection from 198.35.46.6:55073
2015-08-29 18:23:48 WARNING  unsupported addrtype 44, maybe wrong password
2015-08-29 18:23:48 ERROR    can not parse header when handling connection from 198.35.46.6:46499
2015-08-29 18:23:48 WARNING  unsupported addrtype 222, maybe wrong password
2015-08-29 18:23:48 ERROR    can not parse header when handling connection from 198.35.46.6:46500
2015-08-29 19:23:49 WARNING  unsupported addrtype 44, maybe wrong password
2015-08-29 19:23:49 ERROR    can not parse header when handling connection from 198.35.46.6:43293
2015-08-29 19:23:49 WARNING  unsupported addrtype 222, maybe wrong password
2015-08-29 19:23:49 ERROR    can not parse header when handling connection from 198.35.46.6:43294
2015-08-29 20:23:48 WARNING  unsupported addrtype 44, maybe wrong password
2015-08-29 20:23:48 ERROR    can not parse header when handling connection from 198.35.46.6:59003
2015-08-29 20:23:48 WARNING  unsupported addrtype 222, maybe wrong password
2015-08-29 20:23:48 ERROR    can not parse header when handling connection from 198.35.46.6:59004
2015-08-29 21:23:48 WARNING  unsupported addrtype 44, maybe wrong password
2015-08-29 21:23:48 ERROR    can not parse header when handling connection from 198.35.46.6:57282
2015-08-29 21:23:48 WARNING  unsupported addrtype 222, maybe wrong password
2015-08-29 21:23:48 ERROR    can not parse header when handling connection from 198.35.46.6:57283
2015-08-29 22:23:49 WARNING  unsupported addrtype 44, maybe wrong password
2015-08-29 22:23:49 ERROR    can not parse header when handling connection from 198.35.46.6:32812
2015-08-29 22:23:49 WARNING  unsupported addrtype 222, maybe wrong password
2015-08-29 22:23:49 ERROR    can not parse header when handling connection from 198.35.46.6:32813
2015-08-29 23:23:49 WARNING  unsupported addrtype 44, maybe wrong password
2015-08-29 23:23:49 ERROR    can not parse header when handling connection from 198.35.46.6:47019
2015-08-29 23:23:49 WARNING  unsupported addrtype 222, maybe wrong password
2015-08-29 23:23:49 ERROR    can not parse header when handling connection from 198.35.46.6:47020
2015-08-30 00:23:50 WARNING  unsupported addrtype 44, maybe wrong password
2015-08-30 00:23:50 ERROR    can not parse header when handling connection from 198.35.46.6:57978
2015-08-30 00:23:50 WARNING  unsupported addrtype 222, maybe wrong password

是不是 @breakwa11 说的主动检测呢?

@renyidong 看到最后,你自己被打脸了。真是笑死我了~对了,以我的注册时间和发言记录,大概就是你口中的水军吧。~.~

@yifei0727
aes-256-cfb1的工作模式意味着单个block大小为1bit,而非常见的128bit,在这种情况下,单个位的误差会传播影响到后续的所有块。即使在这种情况下,由于shadowsocks只校验第一位,所以特征还是在的。

@bigoktesk 不是,原因已经在log里了,自己想一想

@TearDownGFW
我忽然明白了您的意思,但IV重放不是这么做的,每重放一次需要断开连接重新连接的

楼主 给你想个特别简单的办法 不增加额外的信息量又能达到目的。

把17字节开始的“1.2.3类型名” 设置为随机值即可… 随机值由C/S协商好

比如说 服务器和客户端之间除了协商密码类型和密码,还要协商代表“地址类型”的随机赋值。 比如我可以设置为ABC,而不是123.

具体实现起来为了简便和安全,可以这么实现:

地址类型从1字节扩展到16字节(或者更长),3个代表“地址类型”的字符串,由服务器和客户端自动从用户密码中采取,做到不重复且随机即可。

比如说要求用户密码不少于15位,服务器自动将密码前15位分成3组,每组通过HASH等手段变换为16位无规律字符,作为地址类型的标识。

这就使得地址类型也变为“PSK”协商了。

@phpbestlang
按这样下去,数据包的crc32值是可以有的

@QLrGWTXz
密码15位......晕

@QLrGWTXz
把密码hash掉再取3组又如何?而不是限制长度。

我就贴个链接,什么也不说,不算“参与维护”吧。
https://github.com/shadowsocks/shadowsocks/wiki/Ban-Brute-Force-Crackers

@phpbestlang 又看了下,这种模式AES算法加密的不是明文数据,而是用于计算保护数据的密钥!
就是说利用算法、 IV、密钥产生一个用于与明文数据XOR计算的“密钥”,然后XOR后就是密文!这也就解释了为什么密文结果和明文结果一样长,而不是16字节块大小整数倍了。
那么接下来我看了了下这里 https://github.com/openssl/openssl/blob/6218a1f57e7e25a6b9a798f00cf5f0e56a02ff31/crypto/modes/cfb128.c 最后两个函数

发现,如果是8bit模式,按字节模式加密,1bit模式,按位加密,也就是说同样一段数据,是原来的8次调用,性能影响太大!
只是为什么1bit模式下会影响后续字节,我还得继续看

@yifei0727
其实是用key加密iv而已再对明文做xor而已
正常情况block大小是128bit,8和1刻意缩小的,的确会影响性能
影响后续字节的原因是因为模式的不同,OFB就不会有这个问题,具体的好好想一下

@renyidong
AES只是保证重复的明文不会被加密为同样的密文,但位置还是一样的
用dict过滤对抗重放攻击并不现实,Eve完全可以等到几天以后再做这些事,而此时你的dict不是被清空,就是已经撑爆了内存

@clowwindy
Take care

@clowwindy 但是这样也会有误伤的吧?注意隐藏自己.....

@falseen
不会有误伤,除非下次IP被分配到和之前攻击者一样的IP,但这概率并不高

@phpbestlang 我的意思是如果不小心输错了密码…这种情况应该很常见。想要避免误伤的话就只能修改客户端了(如果密码错误就停止发包,等待用户重新输入密码)。

@aOJzQT
aOJzQT
commented

运行下面的脚本,封掉试图暴力破密码的共匪骇客的IP
来一发,@clowwindy 老大 可以删楼了

Shadowsocks 2.6.2+ output the IPs that try to brute force crack your password.

You can use utils/autoban.py to ban them.

python autoban.py < /var/log/shadowsocks.log

Use -c to specify with how many failure times it should be considered as an attack. Default is 3.

To continue watching for the log file:

nohup tail -F /var/log/shadowsocks.log | python autoban.py >log 2>log &

Use with caution. Avoid to ban yourself.
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (c) 2015 clowwindy
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import absolute_import, division, print_function, \
    with_statement

import os
import sys
import argparse

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='See README')
    parser.add_argument('-c', '--count', default=3, type=int,
                        help='with how many failure times it should be '
                             'considered as an attack')
    config = parser.parse_args()
    ips = {}
    banned = set()
    for line in sys.stdin:
        if 'can not parse header when' in line:
            ip = line.split()[-1].split(':')[0]
            if ip not in ips:
                ips[ip] = 1
                print(ip)
                sys.stdout.flush()
            else:
                ips[ip] += 1
            if ip not in banned and ips[ip] >= config.count:
                banned.add(ip)
                cmd = 'iptables -A INPUT -s %s -j DROP' % ip
                print(cmd, file=sys.stderr)
                sys.stderr.flush()
                os.system(cmd)

autoban.py

73 items not shown.

可以做一个server的预设密码,
根据密码hash一个随机值,
然后根据随机值自定义一个错误返回值。
同时也可以根据这个随机值定义协议混淆方式什么的。
简单地说,希望每个SS连接都有一个独特的连接模式,不重复就难以被侦测。

很简单的一个特征,你每次新开的TCP连接第一位是固定的,所谓的版本标志。然后第四位是随机长度,只要防火墙按照前面几个字节指定的长度计算一下,就能访问到原来的SS头,而SS头的第一位在首次连接的时候是有一定特征的,一旦这几点确认,基本就能确认这是一个SS-R连接,从而可以进行阻断。

同时我也想说一下,那16位长度的叫做IV,长度必须是固定的,也不是你想象的随机填充用来混淆,IV和密码必须同时匹配才能解密密文。而每次生成的IV都是随机的(仅在握手阶段传递),在不知道密码的情况下,密文是绝对安全的。即便回放攻击也无法得到密码,因为每次的IV就是随机串,和密码没有任何关系。

@dosgo IV是明文的,so?

@dosgo
发送IV的代码有的

原版C#客户端 IVEncryptor 抽象类127行就是添加IV到数据头部

_encryptIVSent = true;
randBytes(outbuf, ivLen);
initCipher(outbuf, true);
outlength = length + ivLen;
lock (tempbuf)
{
     cipherUpdate(true, length, buf, tempbuf);
     outlength = length + ivLen;
     Buffer.BlockCopy(tempbuf, 0, outbuf, ivLen, length);  // 这里
}

原谅我还没有Review服务器端代码

的确不能有标志版本号这个东西

标志版本号(1byte)|首包总长度(2byte)|随机填充长度(1byte)|随机填充数据|原ss首数据包|CRC32(4byte)

@breakwa11
所以应该是这样

随机填充长度(1byte)|随机填充数据|首包总长度(2byte)|随机填充长度(1byte)|随机填充数据|原ss首数据包|随机填充长度(1byte)|随机填充数据|CRC32(4byte)

我想,总长度是可以从密文被计算出来的吧..所以应该在他的前面也填充一些数据来保护一下.
并且.CRC的长度也是固定的.所以原始包数据的“第17字节”是可以从包尾反向数出来的吧..所以我们也填一点东西吧.23333丧心病狂

然后,反正不可能做到向前兼容
当有多个版本的服务器在同一个客户端上时,让用户选择哪个服务器对应哪种版本通信


或者说.想要后向兼容的话..可以直接这样就好

随机填充长度(1byte)|随机填充数据|标志版本号(1byte)|首包总长度(2byte)|原ss首数据包|随机填充长度(1byte)|随机填充数据|CRC32(4byte)

或者

随机填充长度(1byte)|随机填充数据|标志版本号(1byte)|首包总长度(2byte)|原ss首数据包|CRC32(4byte)|随机填充长度(1byte)|随机填充数据

反正填充长度的那一个byte会被加密..所以不会知道到底填充了多少东西.也就无法从两端定位中间的数据位置

没有错的话.自我感觉越来越变态了.FUFUFUFU~.

2015-9-4 11:55
刚才起床的时候想了一下
因为我们有了末尾的CRC校验
所以仅仅只是篡改密文第一个byte进行尝试永远不会成功的
但是对于AES来说,已知初始化向量和初始化向量与Key进行Encode之后的结果,能不能在足够数量的前提下获得Key?

然后另一个是从末尾逆推原始SS包第17位的问题.
虽然"地址类型(1byte)"之后会有一些"不定长"的数据..但是在实际实现当中会不会出现这一些不定长数据常常是定长的呢?

@dosgo
是的
每次加密都要有一个 IV (初始化向量)
你可以看维基的块密码的工作模式

@breakwa11
其实对于IV重放攻击,刚看到wiki有这么一段

初始化向量与密钥相比有不同的安全性需求,因此IV通常无须保密,然而在大多数情况中,不应当在使用同一密钥的情况下两次使用同一个IV。对于CBC和CFB,重用IV会导致泄露明文首个块的某些信息,亦包括两个不同消息中相同的前缀。对于OFB和CTR而言,重用IV会导致完全失去安全性。

所以我觉得一个严格保证安全性的“私有加密通信协议”的实现是应该会有对IV生成的限制的
所以对于重复的IV直接判错并不是不可思议或不可能的...(也就是说这可能是一个共有特征)
但是也应该模仿一般的私有协议的处理方式
要么直接忽略这一个重放攻击导致的“错误”(通常私有协议意义上的)
要么直接中断连接(一般的私有协议并不是不能这样实现)

这样看上去,这个办法也不错,直接丢包,这样搬砖的也不能把我们怎么样,毕竟这样是合理的。
你输错密码了,我凭什么还要handle你的request。
不知道我是否理解对了,你们是想要将搬砖的重放攻击模拟成输错密码后的反应

@MuSamDu

或者说是认为私有协议的一个端出现了错误.发出了无法理解的请求.
而且对于重放攻击就是
"你不遵循通信协定,我为什么要为你服务"

我好像没有在C#客户端的代码里面看到有发给SS服务器的"地址类型(1byte)|地址(不定长)|端口(2byte)"包呀

待我把服务器端代码git下来仔细看一下先

如果不是 get /post等http方法,一般http服务器是直接close吧,而不是返回 http xxx error。所以一个随机数据的探测却响应http 错误,不巧好说明了什么吗?

@yifei0727 我也觉得有点“此地无银三百两”。现在我们讨论的都是怎么应对墙的针对性探测,而我认为我们需要做的是怎么避免引起墙的注意,从一开始就避免进入墙的针对性扫描名单,这样就不存在针对性探测的问题了。

@breakwa11
如果墙一定要针对你进行扫描,那不管你怎么应对都是徒劳的,就像猫捉老鼠的游戏,我们要做的不是到处躲藏,而是让猫认不出(分不出)哪个是我们是老鼠。不需要考虑它认出或怀疑我们的时候该怎么应对,那样太被动,只需要考虑怎么变换伪装、扰乱它的视线让它继续认不出就行了。

最后,我认为这种轻装上阵的思路是符合ss的理念的。

To avoid wasting your time, read these issues first:
shadowsocks/shadowsocks#69
shadowsocks/shadowsocks#64

If you believe the firewall is doing CCA, what you're doing is nonsense. The protocol you designed is still only CPA secure. Unless you keep changing the protocol, it will not be any safe.

ok, I read them first

the first rule in infosec is
theres no security
😁

从 autoban 我另外想了一个比较别扭的方法,就是ss端口的iptables IP白名单
具体说起来就是用 iptables对于ss端口配置ss client IP 白名单,不在白名单内的DROP

又想到这样搞对于手机经常变ip的之类的情况要怎么方便在白名单加ip,那么可以写个web接口提供快速加白名单ip,当然这时候用https来用这个web接口比较好

开脑洞了请轻喷

@ligboy
ligboy
commented

我的服务器被reply-attack了……
光临之后,整个服务器就被墙,过很长一段时间恢复。

qq 20150906162139
qq 20150906162529

我现在的想法是让每个人的ss都具有不同的特征,因为就目前的观察来看,墙比较关心的是那些具有广泛性的、影响范围较大的私有协议。只要有这些特征,不管用哪种加密方式、也不管探测或封杀的容易程度如何,墙都会想尽一切办法把它干掉。但相反的,对于那些影响较小的私有协议,墙并不怎么关心。

@ligboy
ligboy
commented

@falseen 必须保证基础协议是统一的,不然社区就分崩离析了。

@ligboy Have you try autoban.py? I think it works.
https://plus.google.com/115672731999047849097/posts/KQjuBH6wyBJ I will post use English.

@xxnet
xxnet
commented

@xxnet 第四点是不正确的,不是首包长度固定,而是首包的IV头最常见的是16位。整个首包长度不是确定的

@ligboy
ligboy
commented

@Qixingchen 我怀疑GFW伪造了我的IP来尝试我的ss服务。因为所有的尝试的IP都是我自己的IP而且仅有一个。

@xxnet
xxnet
commented

@librehat 确实是不固定的,除非连接方式是ip,而大多数情况下是域名,因此长度是不固定的,只能有个最小长度和长度分布概率。

作为墙,需要先判断有可能是ss,然后在进行探测确认是ss。
如果随便一个怀疑对象都进行探测,那影响面就太大了,这不是墙希望的效果。

@ligboy Are you sure? If GFW make fake ip,how do it receive the return IP data packet?

I think GFW can send wrong data as your IP when most server turn on autoban. That means GFW use your autoban to ban yourself

@breakwa11 But why don't GFW ban your SS ip directly?

为什么都在纠结重放攻击,特征的源头不是地址类型么,处于精简考虑完全舍弃地址类型也是一个方法,毕竟ss不是标准socks5,ip也好地址也罢,都是能用字符串来访问的。

支持去除版本号。

另外,在转发的时候希望可以直接转发至本地端口,这样灵活性更高一些,端口可以简单的使用webserver进行301转向或rewrite,或者干脆用iptables进行drop。

以及,根据GFW的特性,进行一个简单的问答握手也是可靠的方法,由服务器端以密文提出随机的加减法等问题,客户端以密文和CRC进行一次回答,相当于进行了零知识证明。(好像有种CAPTCGA Completely Automated Public Turing test to tell Clients and GFW Apart的感觉

抱歉,晚上脑袋有些不清醒,忽略了仅限首包的问题。

那么,在发送首包时,将首包进行随机偏移是否可行?

偏移(1byte)|以偏移为起始点的首包实际内容(环形)

这样,因为无法确定偏移,所以复杂度大为提升。可惜缺点是并不能有效识别出错误的包。

@xxnet
xxnet
commented

@xxnet 那样的话,为何不对每个包都加入crc验证和有序性验证呢?我在使用ss浏览网页时,经常因为丢包造成图片花屏等问题,若是加入crc的话,或许可以一起制订一个更为完善的新版协议。

唔。。恩,这个漏洞肯定是存在的。。不过你的原理好像有点问题。。

我不是很理解Wa11该如何未知秘钥的前提下构造数据包使得第16字节是特定的之类的
甚至由于AES等加密算法,只构造256个包来遍历第16字节也是几乎不可能的

因为AES实际上构造了一个块与块之间的几乎随机的映射,想构造密文操纵原文是不可能的

但是,这个漏洞是存在,对于任意随机的第16字节,总有3/256的几率成功建立连接
所以根本不需要遍历第16字节,只需要构造足够的任意密文包,使得原文也是随机的,这样同样有3/256的概率建立起连接。

所以我认为Wa11的做法是,发送一定数量随机包,看成功连接几率是不是3/256就可以了

增加CRC32验证是可以的,不过也许并不需要这么多代码量

窝觉得可以把前面16个随机字节改一改,比如前8个字节是包发送时的unix时间戳,server收到了以后看看时间戳合理不合理(是不是在上下10分钟内)大概就可以了?
这样Wa11的成功几率就是两亿分之1?大概看不出来了?
而且代码量大概不超过10行?

BTW。。这样无论是用了什么加密算法,有没有cfb也好,只要是分块加密算法,就都是块与块之间的映射,上面说过的随机试探通过几率的算法都攻击有效。。

@xxnet
xxnet
commented

@xxnet 有加密嗒。。怕什么。。

嘛 既然都 CRC了就CRC好了~ 辛苦 breakwa11 啦~

还有。。“于是依然存在定长的问题(会精确等于16+1+4+2=23字节,只要统计发现你的IP经常产生23字节的TCP首包便可封杀)” 这是不成立的。。

ase-256-cfb的密文必须是一块一块的。。所以至少32bytes。。所以shadowsocks的头是32bytes的至少。。

@xqyww123 你的理解不太正确,具体直接看隔壁的主动攻击代码issue,已经完全写出了代码,并且有两种不同的实现版本,有疑问直接看代码(直接统计3/256概率法是不现实的,概率本身的误差足够让你难以判断到底是3/256还是4/256)。另外,你对cfb的理解也不对,至少32bytes这说法不成立

@breakwa11 噗 =。= 我看到了。。cfb是我弄错了。。原来原文最后是抑或上去的 = =。。。

Orz... 原来 cfb 可以被 iv 重放攻击。。恩,的确是256次。。

breakwa11 added the research label
@mrltr
mrltr
commented

纯打酱油的小白用户路过,喷子们还是积点口德吧,老话you can you up,说得天花乱坠,敢把你自己做过的项目拿出来看看?

I use wireshark analyze the shadowsocks package find nothing but encrypted data , why?

现在中国移动网络已经完全无法ss了(哭)下次换成这个部署

有问题另开issue, 请讨论主题相关内容

最近Xcode的木马炒作的沸沸扬扬,在此提醒大家,下载的exe文件要做hash比对啊。

@breakwa11 ,建议代码用C或python实现,方便跨平台,最终定义好协议后,希望整理一下协议的开发文档,方便后续维护。求iv重放测试代码的链接。

本文中的协议弱点是假设GFW已经知道加密模式后,然后发起的针对特定模式的攻击,那么,通常的网络数据流量中,他们是没办法识别出来用什么算法加密的,难道他们会尝试所有加密算法的穷举?那计算量太大了吧。

我没怎么用过ss,一直都是用花钱的vpn,但从ss被墙之后开始关注了。看到你们的讨论,我想提提我的思考和建议。为反喝茶这是小号,专门参与这个项目的讨论。
ss的关键是,如何让这个连接没特征。如何加密,加密强度都不是重点,因为gfw一定无法穷举。
不知道大家是否看过垃圾邮件的算法,有一种叫做贝叶斯算法。就是通过机器学习,来判定样本的特征。比如,它先知道某个连接是翻墙的,然后去重播这个连接的数据,样本多了之后,它就可以采集到这个连接的特征。
我现在不能确定gfw是如何重建这个tcp连接,因为重建是很耗费资源的,墙是要对付全中国的出国流量。如果它是动态的重建tcp连接,那么有一招可以简单粗暴的笨办法破掉它,让它失效,就是跟它拼消耗,耗它的资源。
如果墙要动态重建tcp连接,它必然需要保存每个连接的上下文,把每个包都保存起来。基于效率考虑,如果你下载一部电影,它不可能从头看到尾,因此,最有可能的做法,它只是保存连接最开始的几个或者几十个数据包。
如果ss最开始的数据全部发无用的不定长字节会如何?那么墙会认为这是一个正常的连接。
为了把这个伪装得更象真的,可以在本机抓包,把上一个非ss的连接内容复制过来。

如果ss总是在开始发1M字节的无用连接,那么墙想破这个,就得为每个连接耗费1M内存,就算墙有上百G内存,也吃不住这样消耗。随着带宽提高,还可以继续增加前置的字节数。
这个办法可以考虑测试下。因为不知道墙的工作方式,所以现在只能推测。

@HowAreYourMotherGfw 思路貌似可以,就是最开始的时候随机发送正常的网络连接,不过貌似对效率有损耗,如果ss都是短链接,就意味着后面所有的连接开始都要发送,降级效率。除非才有http2的策略。

关于加密,其实无需考虑太多。因为墙不可能穷举。加密无非是一种变换。比如,把所有的流量,都跟一部两边都知道的电影异或,也是一种加密算法,只要够长,它能破吗?不能。

如果避免连接有特征,数据集中在某个区域,就是特征。比如第一个字节,总是0x01到0x03之间,是一种特征。第一个包,如果总是40字节,也是一种特征。前几个包的长度,要避免固定。这里说的不是协议里定的包长度,而是指tcp包的长度。如果我们先发第一个报文,40字节,然后send,一般情况,协议栈会立即发送,这样,第一个包的ip报文长度就是固定的。但是如果我们把包放在一起,混着发,拆散了发,顺序不变,对tcp的协议还是一样的,但抓包看起来就不一样了。

@ohyeah521 这种办法,就是跟墙拼消耗,因为它的消耗更大。如果墙的工作原理是实时的重建tcp连接,那这种办法,对它来说,相当于ddos攻击,玩赖皮的。但如果这个猜错了,或者它以后会改变算法,那就再说。

@HowAreYourMotherGfw 这种方法没有什么实际的作用,最终的结果只能是加速大中华局域网的形成。

在服务端和客户端把地址位改为64字节随机整数,取余来表示地址类型。

先放本书Network Security with OpenSSL
尤其是对称加密那章,感兴趣的可以好好看看。加密的安全性上肯定不是考虑的重点,GFW很少关心具体的内容(除非某些特殊情况)。
根据Tor的经验:
https://blog.torproject.org/blog/learning-more-about-gfws-active-probing-system
https://blog.torproject.org/blog/knock-knock-knockin-bridges-doors
如果GFW能够利用某些技术(例如深包检测)发现某些疑似的流量,会再利用主动探测技术进一步确认,如果被确认,就屏蔽。
所以我认为正确的思路应该是跟ScrambleSuit 和obfs4一样,尽量减少特征,也就是进行混淆。如果被检测出特征,我认为几乎是很难防止主动探测的,毕竟你的项目是开源的,对GFW来说都是白盒(我想这也是原作者被喝茶的真正原因,就像人类对付病毒一样,会进化会变异的病毒是最可怕的,如果停止了进化,查杀就简单多了)。
目前只看过ss-libev的代码,我认为特征还是比较明显的,例如数据头部的IV,单个connection中的IV都是相同的。
我的思路是在学习了ss源码/加密通信/GFW基本原理的基础上研究ScrambleSuit 和obfs4,学习它们的混淆技术。

@fovecifer 可以做到把所有非正常请求,如主动探测,重放攻击都抓出来,然后每个服务端的行为都不一样,让其无法有效探测的。当然还需要统计对比一下,看是立即断开的,还是等待超时的,还是返回些啥的服务器哪种更容易被临时封锁,来猜测GFW期待什么样的行为

@luxin88
luxin88
commented

@bigoktesk 俺英文不好,但是你说的这个,伪造ip后如何获取到返回数据,这个对于运营商级别来说太简单了,不管是发出的还是返回的数据包,都要经过人家的设备,只要匹配一下dst和src,然后把这段数据复制下来就可以了,比较类似中间人攻击

@luxin88
luxin88
commented

@breakwa11 日志怎么看到,shadowsocks-libev 2.3.1 没有找到/var/log/shadowsocks.log文件

http://crad.ict.ac.cn/CN/abstract/abstract3031.shtml
匿名通信系统不可观测性度量方法

@luxin88
luxin88
commented

FUCK FBX
不知道是不是真对ss的。。

lixin9311 referenced this issue from another issue

Could anybody tell me where I can find Shadowssocks OS X client source code and iOS 9 client source code? I want to continue the development.

@snnn
snnn
commented

@breakwa11 说的是对的。这是cfb设计上的缺陷。

“Bit errors in the incoming cipher block (bytes in this context) will cause bit error at the same bit positions in the first plain text block。”

source: http://www.pvv.ntnu.no/~asgaut/crypto/thesis/node16.html

其实解决办法很简单,随便换个其它的模式就行了。

@snnn
snnn
commented

再有一个解决办法,就是在头部填16个字节的随机填充。没有任何作用,就是随机填一点bytes而已。

再有,@breakwa11,既然你都提到了CRC,为何不用hmac?

怎么感觉楼上像是个产品经理在跟程序员要代码。。。。人家又没有卖license给你。。。。。想稳定。。。自己学编程。。或者雇个程序员

@8884361
8884361
commented
@8884361
8884361
commented
@8884361
8884361
commented

小白路过,不明觉厉,只是觉得这本来是一件好事,有人开了头,大家各尽其才,各显神通就好, 你可以有你的风度,但这是大家的选择。
Stay hungry. Stay foolish,

@liyue80
liyue80
commented

本人大学时密码学是选修课,所以没修过,说得不对就请忽略吧。
楼主文中提到,“有三种情况下没有立即关闭连接”,某WALL如果能以此判断SS服务器的话,SS服务器就当收到错误的包时,随便回一些随机的包回去,某WALL会不会认为这是一个未知的协议呢。

A thought:
先使用Rivest发明的 all or nothing transform对原始协议数据包进行搅混
然后再对搅混的数据包进行加密,更妙的是只需加密数据包的一小部分即可,剩余的数据依赖于加密的数据部分,如果不截获全部数据就无法解密和分析
Botan里有这个算法

@cool5
cool5
commented

teredo服务可以临时性的提供给本机一个IPv6地址。虽然这个v6是包在v4内的,但是SS协议包在ipv6之后,拆包检测的特征是否会有变化呢?如果搭建一些teredo服务在SS服务端和客户端之间?

@jang-u
jang-u
commented

作者跟五毛掰的后果就是被精确定位
被强迫不更新
社会经验等于零

@cool5
cool5
commented

@jang-u 作者出什么情况了?楼上知道内情了?

@dosgo
dosgo
commented

@jang-u 可怜啊,一个妹子程序员,更新。。竟然让你们气跑了。。不过原因不止这个吧

@outgofw
outgofw
commented

你们吵得毫无道理……包括@renyidong ,一开始我以为你说的是对的,直到后来发现是ss的缺陷而非cfb 的缺陷。你关于aes的理解也毫无问题。@phpbestlang,你的测试问题在于数据不够长。建议超过32字节,再进行测试。理论上结果应该是每16字节中的第一字节出问题。

大概考虑了一下,结论应该是无法做到绝对安全,在限制添加信息较少的范围内,包括lz的协议。可以通过改变256到1字节再重放来试探偏移地址。crc可以穷举。不过这样一来,如果用crc32,每次判定的成本是256^6。另crc范围可以减少,目前我能想到的是前32字节crc,不知道有没有漏洞?

@lersh
lersh
commented

作者的ssr非常好,解决了我的燃眉之急。我不知道指责作者重新发明轮子和代码质量差的人是什么心态。反正在ss不能用后,ssr让我重新连上了fb和tw。你们不动手不肯贡献代码为什么还要来指责一个肯脚踏实地的妹子?

breakwa11 closed this issue

虽然关掉了,但表示楼主很棒。思考很深入,我们需要你。加油

@zagfai
zagfai commented • edited about 1 month ago

总算看完争论了

ss ota看上去已经解决问题

如果包的字节数非常少 是不是还是存在被穷举的风险?

@andy520 very correct

@breakwa11
Thanks.

"But why don't GFW ban your SS ip directly?" @bigoktesk
I believe that is because it can easily cause your ss server reject yourself while not harming normal internet traffic,as long as you enabled your autoban. It can do this trick on some suspectsed hosts.

@Qingluan
Qingluan
commented

@phpbestlang @renyidong 作为一个学密码的。。我得先说下, 你提到的 一个密文 对应明文 ,或者明文对应 密文是不存在的。。。除非这玩意根本就没实现AES, 或者只在古典密码里面。。。而现代密码的 不管是流密码, 或者分组加密,都有一个很重要的一步,混淆。这个特性的具体效果就是,让明密空间不再简单对应(任何对明文的一点更改,会导致整个密文改变)
https://en.wikipedia.org/wiki/Confusion_and_diffusion#Analysis_of_AES
screenshot

@breakwa11 @renyidong 但是像 @breakwa11 所说的。如果IV填充不当确实是可以探测出代理的特征的
后续我会附上测试的

@zagfai
zagfai
commented

@Qingluan 不是, 由于这个东西使用了流加密,导致了每一个字节是一个分块,没有了混淆效果。
我觉得比较靠谱的方法是直接block填充。用padding。每一次发送数据直接128bit。这样旧没问题了

@Qingluan 来来来,来写,我非常想知道你是如何理解的,反正我的主动探测代码已经从去年就写好了,到现在都还能正常使用,正确检出SS服务端

首先我看了下,加密部分的代码(只针对 aes-256 ) 这个部分直接使用openssl,
screenshot
它用了EVP_CipherInit_ex , 所以不会出现每次都重置初始化,也就是说cfb模式只有第一包最有可能被监测出来,@zagfai 这个是直接调用openssl的aes,cfb只是分组加密的方式类似流加密的 反馈寄存器的方式而已 ,它对每一个块都会padding的啊
screenshot

其次 我对ss的tcp部分的连续10000个 tcphdr->len > 0 的报文进行了随机性监测,结果如下
kde.pdf
kde -fig
前 40 个bit 的分布,能很清楚的看到,几乎能均匀地分布到0-255, 也就是说每一bit的 都是很均匀的,随机性很好。

然后对所有报文的进行 每一位进行期望计算,这个看上去简单粗暴
mean
每一位的均值,最小的也没有低于120,最高的没高过132

我反而很好奇你的探测是如何捕捉到特征数据的
@breakwa11

最后附上 kde 前40bit payload 的分布图
screenshot

@phpbestlang 对于你说的那种情况,我没遇到过,你确定用的aes-cfb-256 模式是调用了openssl的还是个人实现的流密钥 冒充aes?

其实你把前面的所有人的回复都看过了么。。。前面不是有人已经解释很清楚了么。。。我这个不学密码学的都知道你这逻辑推理哪里出了问题。。。最后这个统计更是毫无说服力。。。。

@Qingluan
Qingluan commented • edited 36 minutes ago

有问题的地方你明说出来 。
前面的评论我看了。
你们一直在说流密钥 流密钥 ,这个根本就没有用流密钥加密,aes-cfb-256的块填充没有问题。

说我有问题的,请具体指出,哪里, 证据, 数据,结果,分析,
而不是单纯的说我有问题。别乱逼逼
其次,说我统计没有说服力的,我已经写明这个统计的来源 ,统计数据,和结果,以此来证明一点,ss本身的数据在前 40 bie都很随机,没法拿到特征。并不需要说服什么 。

我在最后已经注明,你说你能检测出ss的数据,请拿出数据,和证据附上来讨论

@breakwa11

@breakwa11
breakwa11 commented • edited 34 minutes ago

我只能说你是一个好人,只想着从正常的角度去看问题,而且你没有理解cfb的具体代码实现是怎么做的,从你所说的

cfb只是分组加密的方式类似流加密的 反馈寄存器的方式而已 ,它对每一个块都会padding的啊

就知道,你其实根本不知道标准的cfb实现是怎么样的,谁告诉你会padding的?这又不是cbc
cfb根本就是流加密,cfb模式就是让块密码变成流密码,变成流密码自然就存在所有流密码相同的通病
更严重的是,你一直在说的是加密的方向怎么怎么样,但明明本issue说的是解密方向如果恶意构造数据会怎么怎么样,你完全就理解错了好了么?

Comment on issue
Sign in to comment or sign up to join this conversation on GitHub