Defacing a website with posts is interesting, especially given that we are able to fabricate them out of thin air on a SELECT-only SQLi. But it’s not RCE. What’s the next step? Sol hones in on a special type of post called a customize_changeset.
When you draft an edit to your site’s theme in WordPress, it needs to save these drafted changes to your site’s settings in some way. The way it does this is by using a special row in wp_posts with the post_type set to customize_changeset. Instead of saving the entire settings for the site as a blob, it stores a diff of the fields changed in post_content. A sample might look like this:
{
"blogname": {
"value": "This is a test site",
"type": "option",
"user_id": 1
},
"blogdescription": {
"value": "I edited the description too",
"type": "option",
"user_id": 1
},
"header_textcolor": {
"value": "112233",
"type": "theme_mod",
"user_id": 1
}
}
If you resume editing the site theme, WordPress will temporarily apply the changeset, allowing you to continue editing where you left off. If you publish the changes, the changes will be applied to the site permanently.
Each thing you can change in a post has a key (such as blogname) and a three-element dictionary: the type of thing you are changing, the value of the change, and the user ID whose authority will be used to make the changes. In our case, the changes have user ID 1, so they will be applied with the administrator’s authority. When a changeset is applied, WordPress temporarily sets the current user using the user_id specified in the changeset:
wp_set_current_user($setting_user_id);
Thus, if a changeset is applied while we are an anonymous user, we can temporarily assume the administrator’s identity. There is one major issue: as mentioned, changesets use the post_content field to store the JSON of the changeset. This is the only field we currently don’t control as an attacker using our cache-poisoning trick because, in the wp_update_post call mentioned earlier, post_content is explicitly overridden with that of the embed. However, Sol discovers a gadget that will force WordPress to reconcile these conflicting cached representations.
=⇒ Qua 2 khái niệm là embed thì ko RCE được nhưng mà nếu cộng thêm Changeset thì cũng không RCE được 🙂. Nhưng mà giải thích chút tức là nếu chúng ta có thể đổi được post_content như sau:
{
"blogname": {
"value": "This is a test site",
"type": "option",
"user_id": 1
},
"blogdescription": {
"value": "I edited the description too",
"type": "option",
"user_id": 1
},
"header_textcolor": {
"value": "112233",
"type": "theme_mod",
"user_id": 1
}
}
Nếu mà cái publish cái thay đổi này thì nó sẽ chạy cái hàm :
wp_set_current_user($setting_user_id);
khi đó chúng ta sẽ là admin tạm thời ở vòng request đó , nhưng mà giống như lúc nãy nói nói hàm :
wp_update_post([
'ID' => 11,
'post_content' => "benign html coming from the embed",
]);
Nó set cứng luôn cho cái post_content rồi thì làm sao đổi được cái post_content. Phải nhờ vào một cái đó chính là life-cycle