WordPress is highly performant. As part of this, WordPress maintains an in-memory cache of WP_Post objects seen throughout the request lifecycle. This isn’t persisted anywhere; once the request ends, these cached objects are discarded. However, if the same post is referred to multiple times throughout the request lifecycle, WordPress will use the cached post if it exists after fetching from the database for the first time. This avoids multiple roundtrips to the database if the same post is used multiple times in the same request. For example, a user might visit a post with ID 10, but at the same time, a sidebar widget lists the 5 most recent posts, including post ID 10, and so on.
Since we have a SQLi in the posts endpoint, we can use a UNION-based injection to ‘fake’ the posts that come back. Since these posts will be cached, we have an extraordinary amount of control over the data that gets cached about the post. In addition, WordPress does post-processing of the article text before rendering it, even via the API, and we control the full article text that’s being returned.
Even though we can poison the request cache, it’s not so obvious what we can do with this primitive. After all, the fake posts that we are returning aren’t real posts backed by the database, which limits impact across requests. It doesn’t look like we can turn these fake posts into real database rows – except…