OpenSSH XMSS Key 解析整数溢出漏洞(CVE-2019-16905)分析 - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

OpenSSH XMSS Key 解析整数溢出漏洞(CVE-2019-16905)分析

h1apwn 漏洞 2019-10-27 10:26:15
597391
收藏

导语:从实际情况来看,许多托管服务允许用户上传用户自己的一对私钥/公钥进行身份验证。然后可以在创建新的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主机密钥,则在尝试连接到该服务器时它将崩溃:

 root@ubuntu:~/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: ssh-xmss@openssh.com 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: ssh-xmss@openssh.com [preauth]
 debug1: SSH2_MSG_KEXINIT sent [preauth]
 debug1: SSH2_MSG_KEXINIT received [preauth]
 debug1: kex: algorithm: curve25519-sha256 [preauth]
 debug1: kex: host key algorithm: ssh-xmss@openssh.com [preauth]
 debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none [preauth]
 debug1: kex: server->client cipher: chacha20-poly1305@openssh.com 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”客户端将崩溃:

 pi3@ubuntu:~/orig/openssh-8.0p1$ ./ssh -i ~/.ssh/id_xmss 127.0.0.1 -p 65535 -oHostKeyAlgorithms="ssh-xmss@openssh.com" -oPubkeyAcceptedKeyTypes="ssh-xmss@openssh.com" -l test
 verify:: idx = 20
 Segmentation fault
 pi3@ubuntu:~/orig/openssh-8.0p1$

如果尝试将此密钥添加到ssh-agent,它也会崩溃:

 pi3@ubuntu:~/orig/openssh-8.0p1$ rm -rf ~/.ssh/*
 pi3@ubuntu:~/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;
 pi3@ubuntu:~/orig/openssh-8.0p1$ SSH_AUTH_SOCK=/tmp/ssh-VEgZcFmzWZ2o/agent.8927; export SSH_AUTH_SOCK;
 pi3@ubuntu:~/orig/openssh-8.0p1$ SSH_AGENT_PID=8928; export SSH_AGENT_PID;
 pi3@ubuntu:~/orig/openssh-8.0p1$ ./ssh-add -M 2
 pi3@ubuntu:~/orig/openssh-8.0p1$ cp ~/p_key_bug/* ~/.ssh/
 pi3@ubuntu:~/orig/openssh-8.0p1$ ./ssh-add -M 2
 Segmentation fault
 pi3@ubuntu:~/orig/openssh-8.0p1$

从实际情况来看,许多托管服务允许用户上传用户自己的一对私钥/公钥进行身份验证。然后可以在创建新的VM时自动使用它。他们需要解析它,以便攻击者可以在分析过程中上传恶意密钥并触发漏洞。示例包括由Google Cloud提供的Azure Key Vault,Amazon Key Management Service(KMS)或Cloud Key Management Service。任何密钥管理服务都可能是攻击媒介。

0x02 漏洞修复

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

  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务