Ka Wing Ho, a Senior Consultant within Sekuro RED (our talented Offensive Security Team), shares his solution to the RCEME Preparation Challenge from the renowned Full Stack Web Attack (FSWA) Course.
As mentioned on the SourceIncite website:
Additionally, before signing up for this course students should complete the challenge to self assess if this course is right for them.
Before proceeding, I highly HIGHLY recommend you give it a try yourself before reading the spoilers below.
As someone who has tried to do source code review in the past and failed miserably, this challenge helped me to learn:
- How to use tools like
- Patch diffing (and comparing codebase differences)
- Nuances in Python3 and Java (read: pain)
- Java Deserialization primitives and their signs/tells
The challenge is not as straightforward as it might seem, as explained further in the blog!
You may ask: Is this challenge a true representation of the course?
Hard to say, because everyone will probably take different paths to solve this challenge. I had a friend who wrote their Proof-of-Concept in Java while I wrote mine in Python.
I will follow up with a future blog post talking more about my experience taking the course!
Running the Challenge
As of the time of writing, the command to start the docker container is as follows:
docker run -it --rm -p 8888:8080 registry.gitlab.com/source-incite/fswa-challenge/rceme:latest
What is Apache Shiro?
According to the source:
Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.
After browsing around looking for functionality, the target application – an Apache Shiro sample running on an Eclipse Jetty server, gives you some dummy users which you can log in with to view a dummy account page. Just that, literally nothing else!
So what next?
As a seasoned penetration tester who tries harder, my first obvious instinct was to take the path of least resistance, I mean why do the work of writing an exploit when I can just go run something off the Internet that someone else wrote? (note: don’t actually do this without checking the code)
Consulting the oracle that is the Exploit Database, I found the following:
Actually Understanding the Exploit
CVE-2016-4437, I eventually came to the conclusion that:
- Apache Shiro
1.2.4uses a default encryption key (which is actually visible above in Metasploit)
- The rememberMe cookie is unsafely deserialized in the backend, which leads to RCE
In this case, the rememberMe cookie is issued on successful login and is sent in subsequent requests to the application.
On a slightly lower-level, this is how Apache Shiro processes the rememberMe cookie:
For issuing the cookie to the user:
- An object is serialized by the application
- The object is encrypted using the encryption key and stored in a cookie
- The cookie is base64-encoded
- The cookie is issued/passed to the user
For reading the cookie passed by the user:
- User passes the cookie to the application
- The cookie is base64-decoded
- The cookie is then decrypted using the encryption key to retrieve the object
- The object is deserialized by the application
- Generate a malicious Java object to be deserialized (with
- Encrypt the
ysoserialpayload with the encryption key
- Base64-encode the encrypted payload
- Send it off to Apache Shiro within the rememberMe cookie
- Apache Shiro does the reverse of everything above
ysoserialpayload is eventually deserialized leading to RCE.
The main takeaway for the purposes of this blog though is that serialised objects can be recognised by the signature
What Do WebLogic, WebSphere, JBoss, Jenkins, OpenNMS, and Your Application Have in Common? This Vulnerability.
By @breenmachine What? The most underrated, underhyped vulnerability of 2015 has recently come to my attention, and I'm about to bring it to yours. No one gave it a fancy name, there were no press releases, nobody called Mandiant to come put out the fires. In fact, even though proof of concept code was released...
ac ed 00 05bytes, or by the
rO0string after passing through base64-encoding.
However, in the current case the base64-decoded rememberMe cookie value still needs to be decrypted with the secret key to work!
most definitely not
ac ed 00 05
Following from that, I scoured the Internet and happened upon this blog which had a nifty script to decode the rememberMe cookie and view its contents:
# pip install pycrypto import sys import base64 from Crypto.Cipher import AES def decode_rememberme_file(filename): with open(filename, 'rb') as fpr: key = "kPH+bIxk5D2deZiIxcaaaA==" mode = AES.MODE_CBC IV = b' ' * 16 encryptor = AES.new(base64.b64decode(key), mode, IV=IV) remember_bin = encryptor.decrypt(fpr.read()) return remember_bin if __name__ == '__main__': with open("/tmp/decrypt.bin", 'wb+') as fpw: fpw.write(decode_rememberme_file(sys.argv))
no sign of
ac ed 00 05
Repeating this with the sample value in the blog, the serialized object can be seen:
Looks like there’s still some work to be done! There was definitely something different about the Apache Shiro running in the docker container…
Reading the source
At that point, it was then time for me to review the source to find out:
- Was the cipher key the same in this case?
- What version of Apache Shiro was currently running in the challenge docker container?
I first rummaged around the docker container and found the main Web Archive (WAR) file:
I then copied it out of the container, unpacked the contents and inspected them in Visual Studio Code.
docker cp busy_wilson:/var/lib/jetty/webapps/root.war . && unzip root.war
shiro.iniconfiguration file, there was positive confirmation that the default encryption key was still in use! There was also reference to a
rememberMeManagerclass which I then further explored…
libfolder and referenced in the
pom.xmlfile, so I continued my search there:
shiro-core used as dependencies within the running Apache Shiro Sample
pom.xml file also provided the “version” of Apache Shiro core in use:
However, for the uninitiated, a Java Archive (JAR) file contains
.class files which aren’t exactly the main
.java source but can be decompiled using certain tools.
.classfiles, but entire
.warfiles which was perfect for the current use case!
.war, then individual
.jarfiles, and finally the CookieRememberMeManager.class file
At this point in time I had made several key observations:
- The docker container’s version of Apache Shiro was
- The latest public version of Apache Shiro on github was
1.9.1(as of time of writing)
- All publicly known exploits and PoCs target
The encryption key was the same but the codebases were different, perhaps something different was being done in the backend? Therefore it was worth taking a look at how each version was processing the encryption/decryption, as I was still stuck at that stage from before!
- Top-side is VS Code showing Apache Shiro
1.2.4code, which can be found on GitHub
- Bottom-side is the JD-GUI of Apache Shiro
2.0.0running in the challenge container
shiro-corerepositories, as these were the parent classes inherited by
CookieRememberMeManager.javaas shown above.
decryptfunctions and constructors operated similarly although with minor differences, I then began mucking around looking at other functions such as
getCipherService(), eventually leading me to
- In Apache Shiro
1.2.4, the encryption/decryption is done in AES CBC mode.
- In Apache Shiro
2.0.0, the encryption/decryption is done in AES GCM mode.
yoserial tool is essentially a toolkit to aid in the generation of payloads utilizing gadget chains in well-known dependencies. However, in order to use the tool, one must identify what dependencies are in use by the target application.
The official SourceIncite solution post already mentions utilizing Common Collections, but I confirmed this by inspecting the dependencies as well:
The list of dependencies in
ysoserial supports payloads that map to many common libraries, two of which are related to the
commons-collections-3.2.2.jar as shown above:
CommonsCollections1 payloads are suitable types for this target
java -jar ysoserial-all.jar CommonsBeanutils1 "touch /tmp/pwned" | base64 -w 0 rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2 ... ...
Scripting the Exploit
pycryptodomelibrary which supported the GCM mode (native Python crypto did not)
wingz@ubuntu:/tmp/demo/$ pip install pycryptodomex Collecting pycryptodomex Downloading pycryptodomex-3.15.0-cp35-abi3-manylinux2010_x86_64.whl (2.3 MB) |████████████████████████████████| 2.3 MB 3.8 MB/s Installing collected packages: pycryptodomex Successfully installed pycryptodomex-3.15.0
After yet another few frustrating nights of fruitless results, I finally figured out pycryptodome’s AES GCM and got the decryption process working, with help from this StackOverflow post:
Unlike traditional CBC or EBC, GCM uses a
tag feature in the first 12 and last 16 bytes of the encrypted string.
I modified the previous decryptor script to use AES GCM and it worked! The cookie was properly decrypted:
# for GCM encrytion in newer versions of shiro # pip install pycryptodomex import sys import base64 #from Crypto.Cipher import AES from Cryptodome.Cipher import AES def decode_rememberme_file(filename): with open(filename, 'rb') as f: key = "kPH+bIxk5D2deZiIxcaaaA==" data = f.read() nonce, tag = data[:16], data[-16:] cipher = AES.new(base64.b64decode(key), AES.MODE_GCM, nonce) # mac_len=16 remember_bin = cipher.decrypt_and_verify(data[16:-16], tag) return remember_bin if __name__ == '__main__': with open("/tmp/cookie", 'wb+') as f: f.write(decode_rememberme_file(sys.argv))
ysoserialpayload generation and payload delivery to the target application, and can be found here: https://gist.github.com/wingzRED/bf0f91d675a10893fb11032e52818ce4
The deserialization vulnerability arose from the
org.apache.shiro.lang.codec.io.DefaultSerializer class, where a
readObject call can be found in the
Ka Wing Ho
Ka Wing is a seasoned Senior Consultant within Sekuro RED, our talented Offensive Security Team. He specialises in attack surface reconnaissance, phishing simulation assessments, and web application penetration testing. At Sekuro, Ka Wing delivers projects and presents outcomes and findings to key stakeholders, ranging from C-suite executives and application owners to end developers. Some of Ka Wing's certifications include OSCP, CRTO and CRT.