DeKrypto – Padding Oracle attack against IBM WebSphere Commerce (CVE-2013-05230)
Background
IBM WebSphere Commerce or WebSphere Commerce Suite (WCS), developed by IBM, is a software platform framework for e-commerce and is actively being used by giant retailers.
While working on an engagement last year, I stumbled upon a crypto vulnerability in the IBM Websphere Commerce framework, originally found by VSR Security (CVE-2013-05230). Essentially the krypto parameter found in some URLs could be decrypted using a padding oracle attack. Since no public exploit has been published as time of this writing, I figured it might be an interesting exercise to write one.
IBM uses their own crypto library for Cipher Block Chaining (CBC) ciphers, but my guess is that the implementation will be approximate to the OpenJDK snippet found at: https://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/com/sun/crypto/provider/CipherCore.java
if (decrypting) { if (outputCapacity < paddedLen) { cipher.save(); } // create temporary output buffer so that only "real" // data bytes are passed to user's output buffer. byte[] outWithPadding = new byte[totalLen]; totalLen = finalNoPadding(finalBuf, finalOffset, outWithPadding, 0, totalLen); if (padding != null) { int padStart = padding.unpad(outWithPadding, 0, totalLen); if (padStart < 0) { throw new BadPaddingException("Given final block not " + "properly padded"); }
This crypto library will throw a BadPaddingException when invalid padding is encountered. It is then up to the code that is using this library to handle this exception. In this case, IBM WebSphere Commerce framework seems to leak the information about the padding byte by returning a different response when invalid padding occurs.
Detection
Once you have found a page with krypto URL parameter, differentiate between the following conditions:
- Valid ciphertext decrypted
- Valid padding – You will need this one to de-krypto 🙂
- Invalid padding error
In real world scenario, I found condition (1) and (2) are the same for most of the time. Here is how to test it:
Let us assume we have this valid krypto value:
krypto=rKPqPoqBmZV96l9Ot5GLUtgQwuEYt4oDEG[TRUNCATED]
Record the normal response – this is condition (1)
Replacing first few characters and run it again, record the response – this is condition (2):
krypto=AAAqPoqBmZV96l9Ot5GLUtgQwuEYt4oDEGrhpdNbubd[TRUNCATED]
Delete random characters and run it one final time, record the response – this is condition (3):
krypto=qPoqBmZV96l9Ot5GLUtgQwuEYt4oDEGrhpdNbubd[TRUNCATED]
You can use Burp’s Comparer tool to compare those responses. If (3) is different from (2) and (1) then it may be possible to decrypt this parameter.
Exploitation
Padding oracle attack is not specific to any cipher, rather it is a generic attack against CBC ciphers. Therefore any padding oracle implementations will work in this scenario. I have looked at several padding oracle algorithms, and Ron Bowes’ algorithm by far is the fastest one. The reason is, most padding oracle algorithms iterate over the ciphertext bytes of [Block (n-1)] randomly (bytes ranging from 0-255) to find the plaintext of [Block n]. Ron’s algorithm also iterates over the ciphertext bytes of [Block (n-1)]; but instead of doing so randomly, it loops over a defined character set first (which is composed of ASCII characters, bytes ranging from 32-126), then the rest of the possible byte values (0-31 and 127-255). This greatly reduces number of requests sent, since most meaningful string is composed of ASCII characters.
In addition, I made the following adaptations to Ron’s poracle framework in order for the exploit to work with this particular case:
Encoding/decoding
Ron’s Bowes’ script assumes the payload is in hexadecimal format. So extra encoding/decoding of the krypto parameter is required. The decoding process is as follow:
- URL decode key characters (including newline)
- Base64 decode
- ASCII encode
Encoding steps:
- ASCII decode
- Insert newline character at every 77th character (RFC 2045 or 4880)
- Base64 encode
- URL encode key characters
Format of payload
Ron’s original script is made on the assumption that the server will decrypt any two-block payload provided by the attacker. Let us assume that our valid ciphertext blocks is:
[Block 0] [Block 1] [Block 2] [Block 3]
To decrypt [Block 2], we need to send
[Block 1] [Block 2*]
and wait until the server returns padding error.
However, with IBM Web Commerce, the server does not decrypt any arbitrary two-block payload. It seems to require a valid header or expect certain values at the beginning of the block. Therefore, in order to decrypt [Block 3], we need to send:
[Block 0] [Block 1] [Block 2*] [Block 3]
This is an easy fix, I only needed to append the early blocks to the payload.
Parallelization
- I added support for multi-threading. The script uses Meh’s Ruby thread pool library to reduce the overhead of creating and destroying threads.
- I also added the ability to skip already-decrypted blocks and save temporary results to a session file. In situations where the connection ends abruptly, the script only needs to work off the remaining blocks.
Usage
Usage: Usage: Dekrypto.rb [options] Specific options: -s, --sort Sort temporary results -v, --verbose Show debug messages -t, --threads SIZE Set threadpool size -f, --file FILE Save temporary results to file -h, --help Show this message
Example
Running test server:
ruby KryptoTestServer.rb
Running Dekrypto script with verbose output, ten threads, saving temporary results to a text file
(decrypted.txt)
ruby DeKryptoDemo.rb -v -f decrypted.txt -t 10
Ouput
Temporal Result: 0, 1,1996&use 2,rName=de 3,rp%20&pa 4,ssword=t 5,his+is+a 6,+secure+ 7,password 8,&promoti 9,onCode=S 10,PRING+20 11,13 DECRYPTED: 1996&userName=derp%20&password=this+is+a+secure+password&promotionCode=SPRING+2013 DURATION: 12.66 seconds
Limitation
When testing against real applications, I notice that some blocks may not be decrypted properly. My guess is that the server may catch earlier exception in the decoding phase (at either the URL decoding step or the UTF-8 decoding step) and return valid response. Currently, I do not know how to overcome this limitation so any suggestions will be much appreciated.
The source code could be found at: https://github.com/NetSPI/Dekrypto
References
- https://en.wikipedia.org/wiki/IBM_WebSphere_Commerce
- https://www.vsecurity.com/resources/advisory/20130619-1/
- https://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/com/sun/crypto/provider/CipherCore.java#CipherCore.doFinal
- https://www.skullsecurity.org/blog/2013/padding-oracle-attacks-in-depth
- https://en.wikipedia.org/wiki/Base64
- https://github.com/iagox86/Poracle
- https://github.com/meh/ruby-thread
Explore more blog posts
CTEM Defined: The Fundamentals of Continuous Threat Exposure Management
Learn how continuous threat exposure management (CTEM) boosts cybersecurity with proactive strategies to assess, manage, and reduce risks.
Balancing Security and Usability of Large Language Models: An LLM Benchmarking Framework
Explore the integration of Large Language Models (LLMs) in critical systems and the balance between security and usability with a new LLM benchmarking framework.
From Informational to Critical: Chaining & Elevating Web Vulnerabilities
Learn about administrative access and Remote Code Execution (RCE) exploitation from a recent Web Application Pentest.