Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date: Mon, 19 Sep 2016 15:20:02 -0400
From: Mike Santillana <michael.santillana@...ork.com>
To: oss-security@...ts.openwall.com
Cc: "'Apple' via" <infosec@...ork.com>
Subject: CVE Request - Ruby OpenSSL Library - IV Reuse in GCM Mode

Product: Ruby's OpenSSL Library
Version: Tested on 2.3.1 (latest)
Bug: IV Reuse
Impact: Depends on the usage of the library

Hello,

An IV reuse bug was discovered in Ruby's OpenSSL library when using
aes-gcm. When encrypting data with aes-*-gcm, if the IV is set before
setting the key, the cipher will default to using a static IV. This creates
a static nonce and since aes-gcm is a stream cipher, this can lead to known
cryptographic issues.

The documentation does not appear to specify the order of operations when
setting the key and IV [1]. As an example, see the following insecure code
snippet below:

Vulnerable Code:

def encrypt(plaintext)
    cipher = OpenSSL::Cipher.new('aes-256-gcm')
    iv = cipher.random_iv # Notice here the IV is set before the key
    cipher.key = '11111111111111111111111111111111'
    cipher.auth_data = ""
    ciphertext = cipher.update(plaintext) + cipher.final
    tag = cipher.auth_tag

    puts "[+] Encrypting: #{plaintext}"
    puts "[+] CipherMessage (IV | Tag | Ciphertext): #{bin2hex(iv)} |
#{bin2hex(tag)} | #{bin2hex(ciphertext)}"
end

A developer that uses the code above may incorrectly assume that their code
is secure from the pitfalls associated with IV reuse in aes-*-gcm, since
the ‘cipher.random_iv’ method is used. According to the documentation, this
should generate a random IV each time the encryption method is called.

When the code above is run with the same key and same plaintext message,
the following results are obtained:

Output:
# Run 1
./gcm_encrypt.rb 'This is some secret message.'
[+] Encrypting: This is some secret message.
[+] CipherMessage (IV | Tag | Ciphertext): e32594080cca2b37f7d7e968 |
8c676db7551cf046266252ee776ecaa9 | 81092d16b62902d9985656253891dc
800a5bb48fb1c4ad0b7bdf6054

# Run 2
./gcm_encrypt.rb 'This is some secret message.'
[+] Encrypting: This is some secret message.
[+] CipherMessage (IV | Tag | Ciphertext): 431d70714f5e5f876d1c7830 |
8c676db7551cf046266252ee776ecaa9 | 81092d16b62902d9985656253891dc
800a5bb48fb1c4ad0b7bdf6054

Notice that in the output above a unique IV is returned for both runs, but
with the same ciphertext. This proves that even though the random_iv method
is called, the code is defaulting to a static IV. If an attacker can
retrieve multiple ciphertext messages, it is possible to decrypt the
ciphertexts by applying the same attack one would use in a two-time pad
(XOR ciphertexts and crib drag).

Next review the following code snippet and output, which depicts a secure
implementation of the code:

Valid Code:

def encrypt(plaintext)
    cipher = OpenSSL::Cipher.new('aes-256-gcm')
    cipher.key = '11111111111111111111111111111111'
    iv = cipher.random_iv # Notice here the IV is set after the key
    cipher.auth_data = ""
    ciphertext = cipher.update(plaintext) + cipher.final
    tag = cipher.auth_tag

    puts "[+] Encrypting: #{plaintext}"
    puts "[+] CipherMessage (IV | Tag | Ciphertext): #{bin2hex(iv)} |
#{bin2hex(tag)} | #{bin2hex(ciphertext)}"
end

Output:
# Run 1
./gcm_encrypt.rb 'This is some secret message.'
[+] Encrypting: This is some secret message.
[+] CipherMessage (IV | Tag | Ciphertext): 8beb4aa05533e90f4f4eddd3 |
ea1b015958a9b8bd2aafa61887309caf | 19574a9c9869b92140a57a5fd43a14
9a5eaa7e5beefdff5d56cc4136

# Run 2
./gcm_encrypt.rb 'This is some secret message.'
[+] Encrypting: This is some secret message.
[+] CipherMessage (IV | Tag | Ciphertext): 87361b3f1e32291602ac7b40 |
bce7093daa10cc9d2fad0f2b91e077f2 | 47f9a5ba55631204233ace70f169e6
65846e877dca11a6e13a659540

Notice that this time both the IV and ciphertexts are both different for
the same plaintext. This is the intended result a developer would expect to
happen when using this library.

It should be noted that when I went to Ruby's github page to report this
bug, I noticed a developer also independently encountered this weird
phenomenon [2]. Since it has already been brought up to the Ruby team, I
have not created a new ticket.

References:
 [1]
https://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html#class-OpenSSL::Cipher-label-Authenticated+Encryption+and+Associated+Data+-28AEAD-29
 [2] https://github.com/ruby/openssl/issues/49

I'd like to to request a CVE ID for this issue.

Thanks

*WeWork | Mike Santillana*
Security Engineer
845-709-5655
www.wework.com

Create Your Life's Work

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.