# Unauthenticated Spree Commerce users can access all guest addresses

Spree guest checkout accepts plain bill_address_id/ship_address_id without ownership checks, letting any guest bind and read other guests' addresses by enumerating sequential IDs.

- Payout: Research
- Severity: high
- Program: Spree Commerce
- Found by: p-
- Disclosed: 2026-02-05
- CVE: CVE-2026-25758
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-87fh-rc96-6fr6
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-87fh-rc96-6fr6
- Tags: area:web domain:web impact:data-leak impact:pii-leak kind:research platform:ghsa sev:high tech:ruby technique:source-review vuln:access-control vuln:business-logic vuln:idor

**Target:** Checkout address update endpoint (/checkout/:token/update/address) in spree_api
**Root cause:** validate_address_ownership only checks nested *_attributes[id] params; plain bill_address_id/ship_address_id are permitted and applied directly. The address_book setters only compare address.user_id == order.user_id, which is nil == nil for guest orders, so the check passes.
**Steps:**
1. Create a guest order and note a victim address ID from spree_addresses (e.g. 3).
2. As attacker guest, add a product, go to checkout, fill an address, capture order token, CSRF token and session cookies.
3. Send `POST /checkout/${ATTACKER_ORDER_TOKEN}/update/address` with `_method=patch`, `authenticity_token`, and `order[ship_address_id]=${VICTIM_ADDRESS_ID}`.
4. Visit `/checkout/${ATTACKER_ORDER_TOKEN}/delivery` to view the victim's full address details.
5. Enumerate sequential address IDs to harvest all guest addresses.
**How found:** Code review of the checkout update path: noticed nested address attributes were validated by validate_address_ownership but plain *_address_id params were permitted and applied via update_from_params, then traced the setters to the nil==nil guest comparison.
**Exploitation:** Attacker with any guest cart submits a checkout address update setting order[ship_address_id] to a victim's sequential address ID; the order now references the victim's address and the delivery step renders their name, address and phone.
**Escalation:** Sequential address IDs allow enumeration to mass-harvest all guest PII.
**Insight:** When a framework validates one parameter shape (nested *_attributes[id]) but also permits a flat *_id equivalent, the flat path often skips the ownership check; and equality checks like address.user_id == order.user_id silently pass for guest records where both are nil.

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

**curl request binding victim's guest address to attacker order**

```
curl -i -X POST \
  -H "x-csrf-token: ${CSRF_TOKEN}" \
  -H "Content-Type: application/x-www-form-urlencoded;charset=UTF-8" \
  -b "${SESSION_COOKIE}" \
  --data-urlencode "_method=patch" \
  --data-urlencode "authenticity_token=${CSRF_TOKEN}" \
  --data-urlencode "order[ship_address_id]=${VICTIM_ADDRESS_ID}" \
  "http://localhost:3000/checkout/${ATTACKER_ORDER_TOKEN}/update/address"
```

## Write-ups of the same bug (oldest first)
- 2026-02-05: [Unauthenticated Spree Commerce users can access all guest addresses](http://bugbountylibrary.com/r/ghsa%3AGHSA-87fh-rc96-6fr6) — p- (this one)
- 2026-03-06: [How to scan for vulnerabilities with GitHub Security Lab’s open source AI-powered framework](http://bugbountylibrary.com/r/blog%3Agithub-blog-security-how-to-scan-for-vulnerabilities-with-github-security-labs-open-source-ai-powered-framework) — Man Yue Mo

