导语:从实际情况来看,许多托管服务允许用户上传用户自己的一对私钥/公钥进行身份验证。然后可以在创建新的VM时自动使用它。他们需要解析它,以便攻击者可以在分析过程中上传恶意密钥并触发漏洞。
0x01 漏洞分析
OpenSSH是Internet用户所依赖的SSH连接工具的免费版本。telnet,rlogin和ftp的用户可能不知道他们的密码是未经加密地通过Internet传输的,但实际上确实是这样。 OpenSSH会对所有流量(包括密码)进行加密,以有效消除窃听,连接劫持和其他攻击。此外,OpenSSH提供安全隧道功能和多种身份验证方法,并支持所有SSH协议版本。
OpenSSH支持多种签名算法(用于身份验证密钥),根据其利用的数学原理,它们可以分为两组:
· DSA和RSA,依靠分解两个大质数的乘积的实际困难
· ECDSA和Ed25519,依赖于椭圆曲线离散对数问题
椭圆曲线密码术(ECC)算法是公钥密码系统的最新补充。它们的主要优点之一是能够以较小的密钥提供相同级别的安全性,从而减少了计算密集型操作(即,更快的密钥创建,加密和解密),并减少了存储和传输需求。
OpenSSH 7.7添加了对PQC(后量子密码)XMSS密钥(扩展的基于哈希的签名)的实验性支持,可以在在后量子领域中使用。目前,默认情况下未编译代码。
XMSS
扩展的Merkle签名方案(XMSS)是最新的基于状态的哈希签名方案。它具有此类方案中最小的签名,并带有多种变体,可以解决密钥生成速度慢的问题。 此外,可以证明XMSS是安全的,仅对基础哈希函数进行了假设分析。为了XMSS的安全性,不用使密码哈希函数具有抗冲突性。
与传统的签名方案相比,XMSS中使用的签名方案是有状态的,这意味着密钥随时间而变化。如果两次使用密钥状态,则不会保留任何加密安全性保证。结果使得在新消息上伪造签名变得可行。
漏洞细节
在解析XMSS私钥的过程中,发现了导致内存破坏的Integer Overflow漏洞。此过程需要考虑以前保存的“状态”(如果有)。负责处理XMSS保存的“状态”的函数会由于整数溢出漏洞而导致内存破坏:
int sshkey_xmss_decrypt_state(const struct sshkey *k, struct sshbuf *encoded, struct sshbuf **retp) { ... struct sshbuf *copy = NULL, *decrypted = NULL; ... size_t keylen, ivlen, authlen, aadlen; u_int blocksize, encrypted_len, index; ... blocksize = cipher_blocksize(cipher); keylen = cipher_keylen(cipher); ivlen = cipher_ivlen(cipher); authlen = cipher_authlen(cipher); ... if ((copy = sshbuf_fromb(encoded)) == NULL || (decrypted = sshbuf_new()) == NULL || (iv = malloc(ivlen)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } ... if (sshbuf_len(encoded) < sizeof(XMSS_MAGIC) || ... if ((r = sshbuf_consume(encoded, sizeof(XMSS_MAGIC))) != 0 || (r = sshbuf_get_u32(encoded, &index)) != 0 || (r = sshbuf_get_u32(encoded, &encrypted_len)) != 0) goto out; ... /* check size of encrypted key blob */ if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* check that an appropriate amount of auth data is present */ [1] if (sshbuf_len(encoded) < encrypted_len + authlen) { r = SSH_ERR_INVALID_FORMAT; goto out; } aadlen = sshbuf_len(copy) - sshbuf_len(encoded); ... /* decrypt private state of key */ [2] if ((r = sshbuf_reserve(decrypted, aadlen + encrypted_len, &dp)) != 0 || (r = cipher_init(&ciphercontext, cipher, key, keylen, iv, ivlen, 0)) != 0 || [3] (r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(copy), encrypted_len, aadlen, authlen)) != 0) goto out; /* there should be no trailing data */ if ((r = sshbuf_consume(encoded, encrypted_len + authlen)) != 0) goto out; if (sshbuf_len(encoded) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* remove AAD */ if ((r = sshbuf_consume(decrypted, aadlen)) != 0) goto out; ... }
如果攻击者生成的状态“ aadlen” +“ encrypted_len”大于INT_MAX,则可以成功通过验证。 另外,如果'authlen'+'encrypted_len'也大于INT_MAX,则整数溢出导致分配的缓冲区小于所需的缓冲区。
溢出可能发生在调用cipher_crypt()处。该函数的操作如下:
· 将“ aadlen”字节(不带加密/解密)从“ src”复制到“ dest”。
· 这些字节被视为已认证加密模式的其他已认证数据。
· 从“ src”到“ dest”的偏移量“ aadlen”对“ len”字节进行加密/解密。
· 使用偏移量为“ len” +“ aadlen”的“ authlen”字节作为身份验证标签。
· 该标签写在加密上,解密后验证。
由于cipher_crypt()函数的性质,在崩溃前可能会溢出许多有用的数据,因为“复制”不是一次完成,而是在加密操作期间逐块进行。
攻击媒介
任何可以解析私有XMSS密钥的OpenSSH函数都容易受到攻击。例如,如果将“ sshd”守护进程配置为使用格式错误的XMSS主机密钥,则在尝试连接到该服务器时它将崩溃:
[email protected]:~/orig/openssh-8.0p1# gdb -q /root/orig/openssh-8.0p1/sshd Reading symbols from /root/orig/openssh-8.0p1/sshd...done. (gdb) r -d Starting program: /root/orig/openssh-8.0p1/sshd -d debug1: sshd version OpenSSH_8.0, OpenSSL 1.0.2g 1 Mar 2016 debug1: private host key #0: [email protected] SHA256:vVBn0NvOCLKdVFT3CtEFxNHiEgJ1xXBhHdr/YXq5tGc debug1: rexec_argv[0]='/root/orig/openssh-8.0p1/sshd' debug1: rexec_argv[1]='-d' debug1: Set /proc/self/oom_score_adj from 0 to -1000 debug1: Bind to port 65535 on 0.0.0.0. Server listening on 0.0.0.0 port 65535. debug1: Bind to port 65535 on ::. Server listening on :: port 65535. <Someone connects> debug1: Server will not fork when running in debugging mode. debug1: rexec start in 5 out 5 newsock 5 pipe -1 sock 8 process 8844 is executing new program: /root/orig/openssh-8.0p1/sshd debug1: inetd sockets after dupping: 3, 3 Connection from 127.0.0.1 port 39378 on 127.0.0.1 port 65535 debug1: Local version string SSH-2.0-OpenSSH_8.0 debug1: Remote protocol version 2.0, remote software version OpenSSH_8.0 debug1: match: OpenSSH_8.0 pat OpenSSH* compat 0x04000000 debug1: permanently_set_uid: 108/65534 [preauth] debug1: list_hostkey_types: [email protected] [preauth] debug1: SSH2_MSG_KEXINIT sent [preauth] debug1: SSH2_MSG_KEXINIT received [preauth] debug1: kex: algorithm: curve25519-sha256 [preauth] debug1: kex: host key algorithm: [email protected] [preauth] debug1: kex: client->server cipher: [email protected] MAC: <implicit> compression: none [preauth] debug1: kex: server->client cipher: [email protected] MAC: <implicit> compression: none [preauth] debug1: expecting SSH2_MSG_KEX_ECDH_INIT [preauth] Program received signal SIGSEGV, Segmentation fault. 0xb7e40723 in ?? () from /lib/i386-linux-gnu/libcrypto.so.1.0.0 (gdb) bt #0 0xb7e40723 in ?? () from /lib/i386-linux-gnu/libcrypto.so.1.0.0 #1 0x0e66e054 in ?? () #2 0xa2a4b21b in ?? () ... #195 0xca2d8e00 in ?? () #196 0xa442f816 in ?? () #197 0x0000d868 in ?? () #198 0x00000000 in ?? () (gdb) i r eax 0xba 186 ecx 0xb0afbbd0 -1330660400 edx 0x4fa0c440 1335936064 ebx 0xb0ae4770 -1330755728 esp 0xbfffebcc 0xbfffebcc ebp 0x4f0b80 0x4f0b80 esi 0x4f1e46 5185094 edi 0x4f0e96 5181078 eip 0xb7e40723 0xb7(gdb) e40723 eflags 0x210282 [ SF IF RF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb) x/i $eip => 0xb7e40723: movups -0x10(%edx,%ecx,1),%xmm0 (gdb)
如果将“ authorized_key”配置为使用XMSS公钥并保留私钥以能够连接到服务器,则“ ssh”客户端将崩溃:
[email protected]:~/orig/openssh-8.0p1$ ./ssh -i ~/.ssh/id_xmss 127.0.0.1 -p 65535 -oHostKeyAlgorithms="[email protected]" -oPubkeyAcceptedKeyTypes="[email protected]" -l test verify:: idx = 20 Segmentation fault [email protected]:~/orig/openssh-8.0p1$
如果尝试将此密钥添加到ssh-agent,它也会崩溃:
[email protected]:~/orig/openssh-8.0p1$ rm -rf ~/.ssh/* [email protected]:~/orig/openssh-8.0p1$ ./ssh-agent SSH_AUTH_SOCK=/tmp/ssh-VEgZcFmzWZ2o/agent.8927; export SSH_AUTH_SOCK; SSH_AGENT_PID=8928; export SSH_AGENT_PID; echo Agent pid 8928; [email protected]:~/orig/openssh-8.0p1$ SSH_AUTH_SOCK=/tmp/ssh-VEgZcFmzWZ2o/agent.8927; export SSH_AUTH_SOCK; [email protected]:~/orig/openssh-8.0p1$ SSH_AGENT_PID=8928; export SSH_AGENT_PID; [email protected]:~/orig/openssh-8.0p1$ ./ssh-add -M 2 [email protected]:~/orig/openssh-8.0p1$ cp ~/p_key_bug/* ~/.ssh/ [email protected]:~/orig/openssh-8.0p1$ ./ssh-add -M 2 Segmentation fault [email protected]:~/orig/openssh-8.0p1$
从实际情况来看,许多托管服务允许用户上传用户自己的一对私钥/公钥进行身份验证。然后可以在创建新的VM时自动使用它。他们需要解析它,以便攻击者可以在分析过程中上传恶意密钥并触发漏洞。示例包括由Google Cloud提供的Azure Key Vault,Amazon Key Management Service(KMS)或Cloud Key Management Service。任何密钥管理服务都可能是攻击媒介。
0x02 漏洞修复