Analysis of CVE-2024–9161 Vulnerability

Don’t throw in the towel just yet!!!!

My story

CVE-2024-9161 is documented as a Broken Access Control (authorization) issue. During a bug-bounty test, I inadvertently found a Remote Code Execution (RCE) vector that abuses this weakness. This indicates the impact goes beyond the MITRE-described inability to only add, remove, or modify metadata and scheme fields.

Detailed Analysis of CVE-2024-9161

Why is this unauthenticated behavior happening?

Rankmath has registered two REST APIs, /updateMeta and /updateSchemas. The permission_callback points to rest-helper, so let’s dig deeper to see what’s inside

The objectType parameter, taken from user input, must be one of three values: post, term, or user. Accordingly, $method will call one of the three functions corresponding to the objectType value (get_post_permissions_check(), get_user_permissions_check(), or get_term_permissions_check()).

  • The get_post_permissions_check function will be ruled out because it verifies whether current_user_can has the edit_post capability. Since the user is not logged in, the check will obviously fail.
  • The get_user_permissions_check function performs no checks and returns immediately. It returns ‘on’, which is effectively equivalent to true — therefore the permission check is bypassed
  • The get_term_permissions_check function calls get_term(); after querying the database we observe two values: 1 and 2. With objectID == 1 → category. The function then checks whether that category value exists in the array returned by get_accessible_taxonomies(). With debugging enabled we can see the category value is present in that array, so using objectID = 1 we can bypass this function

Therefore, by abusing get_user_permissions_check and get_term_permissions_check, we can successfully bypass authentication and invoke the other functions

Remote Code Execution

After review the auth checks, we move on to the two invoked functions — update_metadata() and update_schemas() — however, I will analyze update_metadata() because it leads directly to RCE.

At (1) it retrieves the metadata value by the user-supplied ID corresponding to rank_math_delete_schema-, then calls get_metadata_by_mid() to fetch the value from the database.

At (2) it calls maybe_unserialize. If we can control this value, it will result in deserialization.

At (3) we can overwrite any metadata value we want — which gives us what we need to trigger an insecure deserialization.

To get RCE: invoke update_metadata to store a metadata entry with the malicious object, then remove that metadata

Conclusion

I also looked for Rank Math gadget chains to complete a clean chain, but—possibly due to some gaps in my knowledge—I couldn’t find a suitable gadget chain within Rank Math. Therefore, this bug could be combined with gadget chains from other plugins or with older WordPress versions (we can use phpggc to search for existing gadget chains). In short: when you find a bug, try to dig as deep as possible

Leave a Reply

Your email address will not be published. Required fields are marked *