Make WordPress Core

Opened 7 weeks ago

Last modified 2 weeks ago

#65562 new defect (bug)

CSRF in wp-signup.php allows logged-in users to be forced to create another site

Reported by: yaghoot Owned by:
Priority: normal Milestone: Future Release
Component: Networks and Sites Version: trunk
Severity: normal Keywords: has-patch has-unit-tests 2nd-opinion
Cc: Focuses: multisite

Description

## Problem

On multisite installations where site registration is enabled (registration option is blog or all), a logged-in user can be tricked into creating a new site via a cross-site POST request to wp-signup.php.

The "create another site" flow (signup_another_blog()validate_another_blog_signup()wpmu_create_blog()) does not use a nonce. Unlike most state-changing admin actions in WordPress, this logged-in POST handler has no CSRF protection.

## Steps to reproduce

  1. Set up a multisite network with site registration enabled (Settings → Network Settings → Registration Settings → "User accounts may be registered" or "Both user and site accounts…").
  2. Log in as a regular user who is allowed to create sites.
  3. From an external origin, submit a POST form to https://example.com/wp-signup.php:

`html
<form action="https://example.com/wp-signup.php" method="POST">

<input type="hidden" name="stage" value="gimmeanotherblog" />
<input type="hidden" name="blogname" value="evil-site" />
<input type="hidden" name="blog_title" value="Unwanted Site" />
<input type="hidden" name="blog_public" value="1" />

</form>
<script>document.forms[0].submit();</script>
`

  1. A new site is created under the victim's account without their consent.

## Impact

  • Unwanted sites created on behalf of logged-in users
  • Potential abuse for phishing, spam, or quota exhaustion
  • Sites are created immediately via wpmu_create_blog(), not held for email activation

## Proposed fix

  1. Add wp_nonce_field( 'add-another-blog' ) to the form in signup_another_blog().
  2. Add check_admin_referer( 'add-another-blog' ) at the start of validate_another_blog_signup(), after the logged-in check and before wpmu_create_blog().

Scope is intentionally limited to the logged-in "create another site" path. Public user/site registration flows are unchanged.

Change History (4)

This ticket was mentioned in PR #12363 on WordPress/wordpress-develop by @yaghoot.


7 weeks ago
#1

  • Keywords has-patch has-unit-tests added

https://core.trac.wordpress.org/ticket/65562

Adds nonce protection to the logged-in "create another site" flow in wp-signup.php:

  • wp_nonce_field( 'add-another-blog' ) in signup_another_blog()
  • check_admin_referer( 'add-another-blog' ) in validate_another_blog_signup() before wpmu_create_blog()

Without this, a logged-in multisite user could be forced to create a site via a cross-site POST when site registration is enabled.

Adds PHPUnit tests in tests/phpunit/tests/multisite/wpSignupAnotherBlogNonce.php.

This ticket was mentioned in Slack in #core by cbravobernal. View the logs.


6 weeks ago

#3 @realloc
3 weeks ago

I can confirm the vulnerability against trunk, and the fix looks correct and well scoped: wp_nonce_field( 'add-another-blog' ) plus check_admin_referer() before any state change, matching the standard pattern.

My one concern is the tests. Most assertions match source strings or call check_admin_referer() directly, so they don't actually invoke the patched flow and would pass even if the check were removed. Behavioral tests would be stronger: post without a nonce and assert no site is created, and with a valid nonce assert it is. The blocker is that these functions live in the self-executing wp-signup.php and can't be required from PHPUnit, so this likely needs a small refactor first, probably as a separate patch.

e2e isn't a good fit here either: the e2e suite and its CI job are single site only with no multisite setup, whereas PHPUnit already runs a dedicated multisite matrix. So the ms-required PHPUnit group is the right layer once the functions are loadable.

#4 @realloc
2 weeks ago

  • Keywords 2nd-opinion added
  • Milestone Awaiting ReviewFuture Release
Note: See TracTickets for help on using tickets.