Terrapin Attack 学习

Terrapin Attack 漏洞是由这边的几个安全研究员提出的漏洞模型,这个漏洞有一阵子特别火,所以我特别想好好的研究一下,然而这个漏洞(的背景)相当的复杂,得从一些密码学相关的前置知识慢慢学习,这边先简单的把基础知识给过一遍

SSH 前置知识学习

整个加密中扯到了相当多的密码学知识,这边从一开始的部分开始讲起

密码学前置知识

加密算法本身在网上有非常多的优秀资料。这边仅对部分信息进行展开叙述。
以AES为例,AES的加密算法至今为止还是非常稳定的,然而这类算法称作块加密算法(与之相对的还有流加密算法),其作用的对象仅针对128bit|16bytes 192bit|24bytes 256bit|32bytes这三种长度的数据进行加密。而现实中待加密的数据总是非常的长,这就需要对原数据进行一定的处理。这种不同的处理方式通常称作块加密模式(Block cipher mode),为了方便,在本文称作加密模式

加密模式

在讨论加密模式的时候,我们通常有以下约定俗称的称呼:

  • 块(Block):表示当前加密的最小单位
  • IV(Initialization Vector):初始化向量,通常与一个Block长度相等。有时候也用于指代由密文生成的参与运算的向量
  • Padding:结尾的填充字符串
  • Nonce:随机数。这个词在讨论加解密的时候经常使用

Padding

当我们尝试对一个比较长的数据进行加密的时候,需要将数据按照Block大小切分。通常来说,切分的大小都是4字节对齐,然而我们输入的字符根本不能保证长度一定是4的倍数,这个时候程序往往会给数据的末尾增加一些字符用作Padding,例如:

1
ThisIsAPassword\0x01

其中,这里的\x01正好组成了字符串结尾的最后一个字符,并且这个字符数字是1,表示当前的字符的padding长度为1。这样当解密完成后,程序就会根据padding抛弃最后的字符,得到完整的字符串,上述例子中即为ThisIsAPassword。当然,这也只是一种约定,作为开发者也可以通过约定,告诉用户你的输入必须要满足为XX的倍数,否则就出错,我们提到的这种padding方式可以在RFC2040这里看到。

ECB(Electronic codebook)

根据前文可知,当遇到多余的字符时,可以使用padding来描述(或者直接要求用户输入为Block对齐)。那现在我们就需要一个方式来进行处理不同的Block,最简单的思路就是直接切分。例如下面的字符串:

1
FirstPartAAAAAAASecondPartBBBBBB

加密的时候,简单的分解成

1
FirstPartAAAAAAA SecondPartBBBBBB

这样进行加密即可。这种加密方式叫做电子密码本加密模式ECB(Electronic Codebook)。加密方式如下图:

加密的时候,使用同一个密钥key,将切割后的字符串进行加密。这种加密方法还有几个比较有趣的好处:

  • 由于加解密同时发生,所以可以异步发生
  • 加解密的时候可以从任意位置开始

其实在现实场景中,除了加密的可靠性,效率也是非常值得考虑的一点。甚至在某些场景下,牺牲一定程度的安全性也是可以被允许的,为了安全而抛弃效率其实是一种过于理想化的状态。

CBC(Chain Block Cipher)

然而,其实有心人稍加考虑就会发现ECB有许多问题:

  • 当攻击者知道被加密的数据大致范围的时候,会很容的爆破
  • 加密数据混淆程度不高

实际上,wiki中有给出ECB加密算法的一个极端例子:

可以看到,被ECB加密的图片能看到大致的形状。现实中,如果我们企图猜测某个用户输入的密钥,并且知道一个大致的输入范围的话,爆破起来将会非常快,此时的ECB模式就会失去应有的作用。
在这个背景之下,就提出了一种叫做链式块加密CBC的加密模式。这种模式的加密流程如下:

此时能看到其格式大致如下:

Ci=Enc(PiCi1)C_i = Enc(P_i{\oplus} C_{i-1})

其中当i=0的时候,有以下特殊情况

C0=Enc(P0IV)C_0 = Enc(P_0{\oplus} IV)

IV为需要用户指定的一个输入长度的字符。
这种加密方式与ECB相比,有以下几个好处:

  • 第i个密文会受到前面i-1个所有密文的影响,这就导致特征变得不明显
  • 即使密钥和明文一定,只要IV发生变化,密文也会发生变化
  • 解密的时候,由于只依赖密文,所以解密步骤依然可以异步指型

此时,解密的时候则需要将上述的流程进行相反的处理:

Pi=Dec(Ci)Ci1P_i = Dec(C_i){\oplus} C_{i-1}

这种方式为一种比较主流的加密方式。然而其还是有一点缺点:

  • 当解密的时候,IV出错只会影响第一个数据块(因为解密的时候,异或步骤依赖的是密文而非明文)
  • 由于加密的时候依赖上一个状态,此时加密为非并行

PCBC(Propagating Chain Block Cipher)

上文提到,CBC有一个还算致命的问题,就是IV即使出错了,也只影响一个数据块。所以提出了一种新的加密方式:PCBC,这种方式的加密如图:

可以注意到,此时多了一个异或的操作。也就是说,加密算法改成了:

Ci=Enc(PiCi1Pi1),P0C0=IVC_i = Enc(P_i{\oplus} C_{i-1}{\oplus}P_{i-1}),P_0{\oplus}C_0=IV

这样一来,只要IV出错,错误就可以传播给每一个密文。然而这样一改,又引入了新的问题:

  • 由于强依赖上一个状态,此时的加解密都无法异步
  • 相邻的密文块如果发生颠倒,此时的解密逻辑不受到影响

论证:
假设此时存在三个block,分别为0,1,2.此时已知

C2=Enc(P2C1P1)C1=Enc(P2C0P0)C_2 = Enc(P_2{\oplus}C_{1}{\oplus}P_1) \\ C_1= Enc(P_2{\oplus}C_{0}{\oplus}P_0)

P2=Dec(C2)C1P1=Dec(C2)C1Dec(C1)C0IVP_2 = Dec(C_2){\oplus}C_1{\oplus}P_1 = Dec(C_2){\oplus}C_1{\oplus}Dec(C_1){\oplus}C_0{\oplus}IV

此时,C1C_1C0C_0顺序调换,并不影响最终答案。

这两条缺点非常明显。所以这个算法并没有特别流行

Cipher feedback (CFB)

这个算法不同于前面两个,并非是明文处理后参与AES这种加密算法,而是IV会参与到加密算法中,加密过程如图:

此时的加解密流程可以参考这个:

其与CBC模式很类似,也是加密的时候无法并行化,但是解密可以。

Output feedback (OFB)

加密算法类似CFB,区别在于IV使用的为加密后的临时状态,而非加密完成后的C:

此时的加解密流程可以参考这个:

同样由于加密过程依赖上下文,所以不能进行并行加密。

CTR(Counter) mode

可以看到,前文提过的加密算法(除了ECB)都有一个比较麻烦的问题:不能并行化。曾经和做密码学的朋友聊过,密码学算法除了加解密的安全性外,其实效率也是非常重要的一环。如果效率不行的情况下,某些所谓的安全信道将会花费大量的时间,这就会导致很多加密算法无法投入到实际生产中。于是这里提出了一种既能够保证安全性,又能并行加密的算法。
这种算法的模式如下:

其中,Counter会组成一个IV,这个IV的大小为每一个Plaintext Block 的大小。每一个Plaintext Block都有一个单独的IV。
首先这里生成一个随机数Nonce,并且使用任意一种可逆算法F,将随机数和counter组合,形成IV,形如

IVi=F(Nonce,Crti)IV_i = F(Nonce, Crt_i)

之后,将这个单独的IV与Key放入对应的块加密算法中,最后用这个加密的到的数值和明文异或。

Ci=PiEnc(Key,IVi)C_i = P_i {\oplus} Enc(Key, IV_i)

由于其孤立性的特点,CTR mode的加密算法允许算法进行并行化执行。

MAC 与 AEAD

随着时代的发展,安全攻防也在升级。很多攻击者不但直接对加密算法本身下手,有时候也改而转向对整个通信过程下手,考虑通过截断,丢弃,甚至篡改数据包,起到对数据进行劫持攻击的效果。这种时候,就对加密算法提出了新的需求,即是需要能够保证当前的加密后的数据不被篡改,于是提出了MAC和AEAD的概念。

MAC(Message Authentication Codes)为一种用于保证通信信道完整性的代码。这种能够保证通信过程不被监听的通信方式叫做Authenticated Encryption认证加密。认证加密能够保证通信的机密性真实性
通俗来说,就是保证以下两点:

  1. 不用密钥解开就不知道通信的内容,又叫做隐私性
  2. 这个加密只有通信双方能做到,第三者无法伪造同样的内容

为了能够保证上述两点,这种加密过程中会带有一个用于表示认证的标识authentication tag(AD)。这种数据保证其数据的完整性,同时不被加密,例如网络请求头中存放请求的目标地址。尽管所有的路由都能获得这个地址,但是中途的请求路由却没有一个能获取对应的key。这种带有请求就叫做 authenticated encryption with associated data(AEAD)

AE 加密流程

这种AE(Authentication Encryption)加密流程如下

输入参数:

  • 明文
  • 密钥
  • 可选的头部,例如附加认证信息或者关联数据

输出参数:

  • 密文
  • 认证标记

解密流程如下

输入参数:

  • 密文
  • 密钥
  • 可选头部
  • 认证标记

输出参数:

  • 明文
  • 如果tag无法认证密文或者附加头部,则抛出错误

常见的AEAD加密模式

规范化后的AEAD模式分为这几种

Encrypt-then-MAC (EtM) 加密后添加MAC

这种加密方式是唯一完全符合AE安全标准的。注意这种加密中,两个key一定要是分离的,否则依然会带来认证绕过的风险

Encrypt-and-MAC (E&M) 同时获得密文与MAC

遇上一个的区别是,此时MAC基于明文形成,并且只存在一个密钥,这种方式在某些特定的加密模式下会导致某种变种的Padding Oracle攻击,详见Plaintext Recovery Attacks Against SSH
概括来说,就是早年的SSH在做出错检查的时候,如果MAC值不对的时候,会发送一个区别其他消息的错误信息。

MAC-then-Encrypt (MtE) 先获取MAC,然后获得明文

首先基于明文生成MAC,然后将明文和MAC一同加密,获得密文。虽然看起来和上面的加密手法是一致的,所以同样也有Padding Oracle的问题,其出现在历史上著名的针对TLS的攻击Lucky_Thirteen_attack

在过去,ssh会使用CBC模式对包含MAC的数据进行加密,叫做Encrypt-and-MAC,就是在加密内容的最后增添MAC值,这样就能够在,这就非常容易导致著名的Padding Oracle攻击,从而在加密状态下,泄露位于数据末端的MAC码,实现对请求过程的篡改。

Galois/Counter Mode (GCM)

这个算法将CTR的算法和认证算法结合。明文加密得到密文之后,再使用类似CBC的模式,生成对应的AuthTag。这里可以认为使用了EtA

总结

这篇文章大致上把密码学的一些基本概念过了一遍,之后在分析SSH漏洞的时候,会反复提及。

参考链接

https://datatracker.ietf.org/doc/html/rfc2040
https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation