Exploit-DB #40961
Description: Apache mod_session_crypto - Padding Oracle
Type: webapps
Platform: multiple
Date: 2016-12-23
Author: RedTeam Pentesting GmbH

Exploit:
'''
Advisory: Padding Oracle in Apache mod_session_crypto

During a penetration test, RedTeam Pentesting discovered a Padding
Oracle vulnerability in mod_session_crypto of the Apache web server.
This vulnerability can be exploited to decrypt the session data and even
encrypt attacker-specified data.


Details
=======

Product: Apache HTTP Server mod_session_crypto
Affected Versions: 2.3 to 2.5
Fixed Versions: 2.4.25
Vulnerability Type: Padding Oracle
Security Risk: high
Vendor URL: https://httpd.apache.org/docs/trunk/mod/mod_session_crypto.html
Vendor Status: fixed version released
Advisory URL: https://www.redteam-pentesting.de/advisories/rt-sa-2016-001.txt
Advisory Status: published
CVE: CVE-2016-0736
CVE URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0736


Introduction
============

The module mod_session_crypto of the Apache HTTP Server can be used in
conjunction with the modules mod_session and mod_session_cookie to store
session data in an encrypted cookie within the users' browsers. This
avoids server-side session state so that incoming HTTP requests can be
easily distributed amongst a number of application web servers which do
not need to share session state.


More Details
============

The module mod_session_crypto uses symmetric cryptography to encrypt and
decrypt session data and uses mod_session to store the encrypted data in
a cookie (usually called "session") within the user's browser. The
decrypted session is then made available to the application in an
environment variable (in case of a CGI script) or in a custom HTTP
request header. The application can add a custom HTTP response header
(usually "X-Replace-Session") which instructs the HTTP server to replace
the session's content with the value of the header. Detailed
instructions to set up mod_session and mod_session_crypto can be found
in the documentation:
https://httpd.apache.org/docs/2.4/mod/mod_session.html#basicexamples

The module mod_session_crypto is configured to use either 3DES or AES
with various key sizes, defaulting to AES256. Encryption is handled by
the function "encrypt_string":

modules/session/mod_session_crypto.c
------------------------------------------------------------------------
/**
 * Encrypt the string given as per the current config.
 *
 * Returns APR_SUCCESS if successful.
 */
static apr_status_t encrypt_string(request_rec * r, const apr_crypto_t *f,
        session_crypto_dir_conf *dconf, const char *in, char **out)
{
[...]
    apr_crypto_key_t *key = NULL;
[...]
    const unsigned char *iv = NULL;
[...]

    /* use a uuid as a salt value, and prepend it to our result */
    apr_uuid_get(&salt);

[...]

    res = apr_crypto_passphrase(&key, &ivSize, passphrase,
            strlen(passphrase),
            (unsigned char *) (&salt), sizeof(apr_uuid_t),
            *cipher, APR_MODE_CBC, 1, 4096, f, r->pool);

[...]

    res = apr_crypto_block_encrypt_init(&block, &iv, key, &blockSize, r->pool);
[...]
    res = apr_crypto_block_encrypt(&encrypt, &encryptlen, (unsigned char *)in,
            strlen(in), block);
[...]
    res = apr_crypto_block_encrypt_finish(encrypt + encryptlen, &tlen, block);
[...]

    /* prepend the salt and the iv to the result */
    combined = apr_palloc(r->pool, ivSize + encryptlen + sizeof(apr_uuid_t));
    memcpy(combined, &salt, sizeof(apr_uuid_t));
    memcpy(combined + sizeof(apr_uuid_t), iv, ivSize);
    memcpy(combined + sizeof(apr_uuid_t) + ivSize, encrypt, encryptlen);

    /* base64 encode the result */
    base64 = apr_palloc(r->pool, apr_base64_encode_len(ivSize + encryptlen +
                    sizeof(apr_uuid_t) + 1)
            * sizeof(char));
[...]
    return res;
}
------------------------------------------------------------------------

The source code shows that an encryption key is derived from the
configured password and a randomly chosen salt by calling the function
"apr_crypto_passphrase". This function internally uses PBKDF2 to derive
the key. The data is then encrypted and the salt and IV prepended to the
encrypted data. Before returning to the caller, the result is encoded as
base64.

This procedure does not guarantee integrity of the ciphertext, so the
Apache module is unable to detect whether a session sent back to the
server has been tampered with. Depending on the application this often
means that attackers are able to exploit a Padding Oracle vulnerability.
This allows decrypting the session and encrypting arbitrary data chosen
by the attacker.


Proof of Concept
================

The vulnerability can be reproduced as follows. First, the modules
mod_session, mod_session_crypto and mod_session_cookie are enabled and
configured:

------------------------------------------------------------------------
Session On
SessionEnv On
SessionCookieName session path=/
SessionHeader X-Replace-Session
SessionCryptoPassphrase RedTeam
------------------------------------------------------------------------

In addition, CGI scripts are enabled for a folder and the following CGI
script is saved as "status.rb" and is made available to clients:

------------------------------------------------------------------------
#!/usr/bin/env ruby

require 'cgi'

cgi = CGI.new
data = CGI.parse(ENV['HTTP_SESSION'])

if data.has_key? 'username'
        puts
        puts "your username is %s" % data['username']
        exit
end

puts "X-Replace-Session: username=guest&timestamp=" + Time.now.strftime("%s")
puts
puts "not logged in"
------------------------------------------------------------------------

Once the CGI script is correctly set up, the command-line HTTP client curl
can be used to access it:

------------------------------------------------------------------------
$ curl -i http://127.0.0.1:8080/cgi-bin/status.rb
HTTP/1.1 200 OK
Date: Tue, 19 Jan 2016 13:23:19 GMT
Server: Apache/2