OpenSSH XMSS Key 解析整数溢出漏洞(CVE-2019-16905)分析
2019-10-27 11:26:15 Author: www.4hou.com(查看原文) 阅读量:108 收藏

导语:从实际情况来看,许多托管服务允许用户上传用户自己的一对私钥/公钥进行身份验证。然后可以在创建新的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, &amp;index)) != 0 ||
         (r = sshbuf_get_u32(encoded, &amp;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, &amp;dp)) != 0 ||
         (r = cipher_init(&amp;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 漏洞修复

该漏洞已在OpenSSH版本8.1中修复。


文章来源: https://www.4hou.com/vulnerable/21124.html
如有侵权请联系:admin#unsafe.sh