[Esapi-dev] HMAC validation bypass in ESAPI Symetric Encryption

Kevin W. Wall kevin.w.wall at gmail.com
Mon Dec 2 18:02:32 UTC 2013


Short answer (for now): this & similar attacks are being addressed in 2.1.1
(not yet complete) to address CVE-2013-5960. Release 2.1.0 *only* addressed
CVE-2013-5679.

-kevin
Sent from my Droid; please excuse typos.
On Dec 2, 2013 10:56 AM, "Renaud Dubourguais" <
renaud.dubourguais at synacktiv.com> wrote:

> Hi guys,
>
> After some investigations, we found a new way to bypass the HMAC
> validation in the ESAPI Symmetric Encryption.
>
> Problem description:
> ====================
>
> The security fix for the CVE-2013-5679 vulnerability ("MAC Bypass in
> ESAPI Symmetric Encryption") does not prevent a malicious user to bypass
> the HMAC validation on a serialized cipher text.
>
> A serialized cipher text generated by ESAPI has the following structure:
>
> <kdfInfo/kdfPrf:int> : identification of the key derivation function
> <timestamp:long> : time when the cipher text is generated
> <cipherlen:short> : size of the "cipher" string
> <cipher:str> : algorithm for encryption and decryption
> <keysize:short> : size of the HMAC validation key (in bits)
> <blocksize:short> : size of the encryption blocks
> <ivlen:short> : size of the IV (in bytes)
> <iv:str> : Initialisation Vector
> <ciphertextlen:int> : size of the cipher text in bytes
> <ciphertext:str> : encrypted value
> <maclen:short> : size of the HMAC in bytes
> <mac:str> : HMAC of the IV and the cipher text
>
> The "keysize" field, under the control of a malicious user, is used to
> compute the HMAC encryption key. The ESAPI library checks for a minimal
> key size of 56 bytes using an assertion. However, if the application is
> not started with the "-ea" java option (which is usually not the case in
> production environments), assertions are turned off. By setting the
> "keysize" field in a serialized CipherText to a value between 1 and 8
> (bits), ESAPI will compute the HMAC using a 1 byte key, which can be
> trivially brute-forced to produce a valid HMAC.
>
> From the code:
> ==============
>
> If "keysize" is set to [1-8], the CryptoHelper.computeDerivedKey()
> method used to compute the HMAC encryption key will return an "authKey"
> of 1 byte. The following extract from the file
> org/owasp/esapi/crypto/CryptoHelper.java shows were the
> computeDerivedKey() is called:
>
> public static boolean isCipherTextMACvalid(SecretKey sk, CipherText ct)
> {
>    if ( CryptoHelper.isMACRequired( ct ) ) {
>             try {
>                 SecretKey authKey = CryptoHelper.computeDerivedKey(sk,
> ct.getKeySize(), "authenticity");
>                 // VULN: If ct.getKeySize() is 1, authKey.length will be
> 1 byte
>                 boolean validMAC = ct.validateMAC( authKey );
>                 return validMAC;
>             } catch (Exception ex) {
>                 logger.warning(Logger.SECURITY_FAILURE, "Unable to
> validate MAC for ciphertext " + ct, ex);
>                 return false;
>             }
>         }
>         return true;
>     }
>
> The computeDerivedKey() method is found in the file
> org/owasp/esapi/crypto/KeyDerivationFunction.java:
>
> public SecretKey computeDerivedKey(SecretKey keyDerivationKey, int
> keySize, String purpose)
>                         throws NoSuchAlgorithmException,
> InvalidKeyException, EncryptionException
>      {
> [...]
>                 assert keySize >= 56 : "Key has size of " + keySize + ",
> which is less than minimum of 56-bits.";
>                 // VULN: assert not triggered in production
> [...]
>                 keySize = calcKeySize( keySize ); // if keySize is 1,
> calcKeySize() returns 8 bits
> [...]
>                 do {
>                         mac.update( ByteConversionUtil.fromInt( ctr++ ) );
>                         mac.update(label);
>                         mac.update((byte) '\0');
>                         mac.update(context);
>                         tmpKey = mac.doFinal(ByteConversionUtil.fromInt(
> keySize ) );
>
>                         if ( tmpKey.length >= keySize ) {
>                                 len = keySize;
>                         } else {
>                                 len = Math.min(tmpKey.length, keySize -
> totalCopied);
>                         }
>                         System.arraycopy(tmpKey, 0, derivedKey, destPos,
> len);
>                         label = tmpKey;
>                         totalCopied += tmpKey.length;
>                         destPos += len;
>                 } while( totalCopied < keySize );
> [...]
>                 return tmpkey; // VULN: tmpkey will only contains 1 byte
>      }
>
> If CryptoHelper.computeDerivedKey() returns an "authKey" containing only
> 1 byte, ct.validateMac(authKey) will compute a HMAC using a 1-byte key.
> It will then compare the calculated HMAC with the HMAC in the CipherText
> sent by the attacker. The validateMAC() method is in the file
> org/owasp/esapi/crypto/CipherText.java :
>
> public boolean validateMAC(SecretKey authKey) {
>             boolean requiresMAC =
> ESAPI.securityConfiguration().useMACforCipherText();
>
>             if (  requiresMAC && macComputed() ) {
>                 byte[] mac = computeMAC(authKey); // VULN:
> authKey.length is 1 byte
>                 assert mac.length == separate_mac_.length : "MACs are of
> different lengths. Should both be the same.";
>                 return CryptoHelper.arrayCompare(mac, separate_mac_);
> [...]
>
> It means that a malicious user can set the "keysize" field to a value
> between 1 and 8 in the CipherText structure, recompute all possible HMAC
> in the CipherText using a 1-byte key, until CipherText.computeMAC(key)
> matches the forged HMAC. Brute forcing 1 byte takes only 256 iterations
> in the worst case. When the right value is found,
> CipherText.validateMAC(key) will return "true" and the HMAC check will
> be bypassed.
>
> Impacts:
> ========
>
> Once the MAC is bypassed, the attacker will be in a good position to
> attack the encryption layer, by using for example padding oracle
> attacks. Other cryptographic attacks are also possible depending on the
> encryption algorithm and mode used.
>
> Affected versions:
> ==================
>
> 2.1.0
> 2.1.1-SNAPSHOT (from svn)
>
> Proof of concept:
> =================
>
> A sample application using the ESAPI library and a proof of concept of
> the attack can be found here:
> http://www.synacktiv.fr/ressources/owasp_esapi_hmac_bypass_poc.tgz.
>
> To decrypt a token, use:
> $ java EsapiVictim d <token>
>
> To generate a token, use:
> $ java EsapiVictim g key=value
>
> To use the exploit trying to bypass the HMAC validation of
> EsapiVictim.java, the java classpath should be adjusted following your
> system, then do:
>
> $ python esapi_brute.py <token>
>
> If esapi_brute.py bypasses the MAC validation, a padding exception will
> be thrown by the EsapiVictim application:
> "Caused by: javax.crypto.BadPaddingException: Given final block not
> properly padded"
>
> Remediation:
> ===========
>
> Enforce a minimal size for the HMAC key at runtime (and not using
> "assert" which is usually turned off). Other vulnerabilities may lurk in
> the dark, as the serialized CipherText exposes sensitive parameters used
> for the decryption and HMAC validation. Do we really need the "keysize"
> field in the CipherText structure?
>
> --
> Renaud Dubourguais
> Security Expert - Synacktiv
>
> _______________________________________________
> Esapi-dev mailing list
> Esapi-dev at lists.owasp.org
> https://lists.owasp.org/mailman/listinfo/esapi-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.owasp.org/pipermail/esapi-dev/attachments/20131202/54c2bb04/attachment.html>


More information about the Esapi-dev mailing list