跳到主要内容

SSH安全现状离谱:一个基础协议如何坑遍全球服务器(完整文稿)

· 阅读需 6 分钟
Low Level

本文是视频《SSH安全现状离谱:一个基础协议如何坑遍全球服务器》(作者:Low Level)的完整文稿(全文整理版),与精读笔记配套阅读;▶ 中配视频。

Bug 简介​

这个bug我真不敢相信它存在。本来要拍九点八分那个CVE的视频讲Linux的TCP IP栈,结果推特上正好刷到了。我还以为十月过了呢我以为网络安全月已经过去,可以不用管这些CVE了。但这个太疯狂了,是SSH本身的漏洞好消息是不是所有SSH都中招,只有几个发行版。但SSH到处在用,不太妙这期讲的是别人的研究,不是我发现的。发现者是厄兹贡·科尔泰昆,韦克菲尔德的研究员去支持他。我用他的仓库讲bug,大家也自己看看他们写了个很棒的TLDR,我直接用它讲。孩子楼上太吵了,我尽量忍住别爆粗口这个新闻标题说得很对,组合漏洞。

密码输超二百五十六个字符,就能进Alpine Linux等发行版OpenSSH和musl libc紧急修复,数百万系统马上补。二零一三我头回去黑帽大会上了个不咋地的入侵课,教我们在二零零八年的Solaris机器上做缓冲区溢出登录提示符输二百九十四个A,就能拿到shell。那可是二零零八、零九年的机器都二零二六年了,这问题还在。我得说清楚这个bug有趣的是,虽然要往缓冲区塞很多字符,但它不是缓冲区溢出这问题很有意思,安全边界和函数之间的约定,彼此越离越远时间一长越来越乱,大家忘了这两个函数干嘛的,可它们又互相依赖。

这个后面细讲。感觉每天都有新零日漏洞冒出来,网络被攻击只是时间问题。

赞助商插播:Flare​

先说重点。你用SSH登录系统时,其实就是在比对密码。输的是密码,跟公钥认证无关。系统会拿密码跑各种函数,查etc shadow文件。我的没有,因为跑在Docker镜像里。注意看,有些值没有哈希。比如你看,这是root,这个星号很重要,表示账号没密码。这是1980年Bill Joy定的,针对daemon账号,意思是daemon没密码。星号不是哈希,不会发生哈希碰撞,谁也没法登录。好。这些加密背后,其实是调用库里的crypt函数。crypt是musl libc里的函数。musl libc是libc的一种实现。我们熟悉的GNU libc很常见。这是另一种libc实现,轻量、快、简单、免费,力求正确。

行吧,大多时候对。2012年8月有个问题。原本musl的crypt函数负责跑加密算法,验证密码哈希。

技术背景​

比如这里有美元符号6,它表示用SHA512算法,这是SHA512密码哈希,这是盐值。格式是算法、盐、哈希。盐是哈希前拼接的值,基础加密知识。很长时间里这个crypt函数,如果加密失败,数据太多或太少,很长时间它都返回null。很多程序其实不检查函数返回值。所以2012年他们决定不再返回null,改成返回一个字符。返回的是什么字符?不是别的,正是那个星号。对,又是星号,它正好和一九八零年伯克利的值对上了。没什么大不了,反正不会比较这两个值,哈。它永远不会和etc shadow的值比,一边是星号,一边是报错,没事。

长期以来这都不是问题。直到二零一六年,有人发现musl libc的加密存在计时攻击。比如,我想知道这台机器上有哪些用户名,怎么办?比如我用SSH试着登录一个不存在的用户,比如Bob。输密码时它马上就失败了,秒退。这就是我在做计时攻击。后台没跑加密,我用时间差就能枚举出有哪些账号。比如登录Alice。不用知道密码,对比Alice和Bob的耗时。Bob快,说明他不存在。Alice慢,是因为跑了加密。为了修复,二零一六年七月,对没密码或不存在的账号,他们会临时借用别人的哈希。

他们临时借用别的账号的哈希和盐,让你在那个盐上跑加密,其实跟这个账号无关。比如Bob在这台机器上不存在。加密后端就会借用Alice的盐,拿你输的密码在她的盐上做一次假账号的校验。

2016 年的时序攻击修复​

这是为了消除加密库里的计时攻击。比如我想以root用户登录。root用户没有密码。系统会借另一个用户的加密算法,放我登录。它会用SHA 512,借他们的哈希,对我输入的内容跑一遍SHA 512。看着挺好,能出啥问题?看musl libc里SHA 512 crypt的实现。注意,如果i大于key max,就返回零。回想一下前面说的,想想哪里会出问题。i大于key max就返回零,而key max是二百五十六。

如果SHA 512。crypt返回零而crypt函数出错时返回的正是星号。如果root的哈希是星号,最后strcmp一比,算出来的是星号,shadow里存的也是星号,两个一样,密码校验就过了。那么问题来了:谁会中这个招?首先,这是musl libc实现上的失误。常见的glibc不受影响。你看不同的sshd构建和配置就知道了。受影响的只有musl libc构建的OpenSSH,而且不带PAM模块。

核心逻辑失效​

PAM即可插拔认证模块。没shadow文件、哈希放etc passwd也中招。要中招得满足几个前提。第一,得开启密码登录。注意,这个默认是开的。另外sshd不能启用PAM模块。有的sshd默认开着PAM,这种情况就不会中招。但麻烦的是,Alpine的Docker镜像就是这个配置。它开着密码登录,不用PAM,默认就中招。剩下的条件是:有个账号,在etc shadow里密码是星号;没shadow文件就放etc passwd。还得有个真实密码的账号,用SHA 512,就是默认的$6。

受影响的系统及影响​

满足这些条件,输入一堆a,大概二百五十七到一千零二十四个就行。这样你就直接拿到shell了。更麻烦的是,Alpine的主用户也中招,它还是doas用户,等于sudo、等于root,很不妙。如果你在Debian、Ubuntu、Fedora上,走PAM或OpenBSD的都安全。其实就是这几种配置。虽然可能覆盖互联网很大一块,但范围不算广。这个漏洞有意思在于,它不是内存破坏,说是逻辑bug吧,也不完全是。不是忘了查字符串长度n或n加一的问题。这是两个函数,各有各的接口约定,时间一长,就漂移得越来越远。这边用个星号,想着星号绝不会变成密码哈希。

然后这边就想,好,让Crypt返回个不是空的值。啊,什么值其实都无所谓?星号,行。然后最后,好,就让任何不存在的用户,或者没密码的用户,去跑加密算法,超过一定长度它就会失败。失败会级联成一个星号,然后就成了其他用户的哈希。真是疯了。我真正想问的是,修完CVE后它就有洞。叹号会弄坏PAM,Alpine改成星号,二一年起就坏了。

这漏洞一六年就有,现在二六年,整整十年。他们理由是没人用三百位密码。你想啊,只要拿个有漏洞的版本,登录一个密码或哈希是星号的账号。

结论与反思​

再等三百天就行。这可是九二年的黑客水平,疯了。你会问,Rust能修好吗?其实不能。也许类型、枚举,或Result T、Option T这些错误检查能防住,但论释放后使用那类内存安全,这漏洞跟它无关,挺有意思。就到这儿。讲太快抱歉,家里有孩子,我得压着声音赶紧录完。喜欢这类视频就点个赞,点订阅,下期见。