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

Renaud Dubourguais renaud.dubourguais at synacktiv.com
Mon Dec 2 20:12:57 UTC 2013


Oh damn, I didn't see the r1908 which replaces asserts with "if"
statements... It seems to correctly fix the issue ;)

On 12/02/2013 07:02 PM, Kevin W. Wall wrote:
> 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
>>
> 


-- 
Renaud Dubourguais
Security Expert - Synacktiv
14 rue de Mademoiselle 75015 Paris
Tel +33 6 98 41 62 38
Fax +33 9 56 54 31 28
http://www.synacktiv.com/


More information about the Esapi-dev mailing list