Skip to content

🐛 [Bounty] - Mass PII and Voter De-Anonymisation Leak: Emails, DRep Vote Choices, and Voting Power Exposed #4170

Description

@Hornan7

Context

received through the security mail

be.pdf.gov.tools (Intersect Budget Discussion / Proposal Platform) — Strapi Public Role Has
find/findOne Enabled on 21 of 31 Collections; 2,832 Individual Votes, Real Email Addresses, and
Company Affiliations Exposed; GDPR Art. 33 / 34 Notifiable

Image Image Image

Steps to reproduce

Critical Privacy Impacts

  1. Voter De-Anonymisation at Web3 Scale
    The bd-poll-votes collection ships 2,832 individual vote rows, each containing the voter's drep_id (28-byte
    blake2b-224 hash = on-chain Cardano DRep credential), the specific poll, the YES/NO vote choice, and the
    delegated ADA voting power. The first page alone exposes 23 distinct DRep IDs with ~3 billion ADA YES voting
    power and ~8.9 billion ADA NO voting power. Extrapolated to all 2,832 rows, this is the entire off-chain advisory vote
    history of the platform, fully linked to on-chain DRep identities:
$ curl -sS "https://be.pdf.gov.tools/api/bd-poll-votes?pagination%5BpageSize%5D=3"
| jq '.meta, .data[].attributes'
{
"pagination": {"page":1,"pageSize":3,"pageCount":944,"total":2832}
}
{
"user_id": "1274",
"bd_poll_id": "50",
"vote_result": true,
"drep_id": "957ff6477708440672253092e9682ad0c90c512d8cc0218729f922c3",
"drep_voting_power":"210201714730",
"createdAt": "2025-04-04T21:28:31.361Z"
}
  1. Direct PII Exposure — Real Email Addresses
    The bd-proposal-ownerships collection contains a social_handles free-text field that routinely contains real email
    addresses. Confirmed examples include an Intersect employee email (lorenzo.bruno+44@intersectmbo.org),
    external contributor emails, and company/group affiliations (Cardano Foundation, Anastasia Labs, Aira Co. Ltd.,

Cryptofluency LLC, DripDropz LLC, ELK gmbh, Bondex Ecosystem Ltd, etc.):

$ curl -sS "https://be.pdf.gov.tools/api/bd-proposal-ownerships?pagination%5BpageSize%5D=100" \
| jq -r '.data[].attributes | [.social_handles, .company_name, .submited_on_behalf] | @tsv' \
| grep -E '@[A-Za-z0-9.-]+\.' | head
lorenzo.bruno+44@intersectmbo.org Individual
engie@matou.nz Individual
lorenzo@email.com Individual
... (more)
  1. Cross-Collection Re-Identification Join Graph
    The platform was never designed to publish the join between its tables, but the unauthenticated access makes the full
    graph available:
Image
  1. Vote-Buying / Coercion Attack Surface
    Once a vote-to-voter mapping is public, attackers can construct verifiable vote-buying markets ('pay me X ADA and I
    will vote YES on poll P — verifiable here') because anyone can independently confirm the published bd-poll-votes
    after the vote. This is the canonical Web3 governance attack the Cardano CIP-1694 designers explicitly designed to
    prevent.
    Confirmed Collection Counts
collection HTTP total
auth-challenges 200 1,733
bd-poll-votes 200 2,832
bds 200 715
bd-proposal-details 200 835
bd-proposal-ownerships 200 768
proposals 200 (147 KB first page)

# Write paths correctly locked:
$ curl -sS -X POST "https://be.pdf.gov.tools/api/bd-poll-votes" \
-H "Content-Type: application/json" -d "{}"
{"data":null,"error":{"status":403,"name":"ForbiddenError","message":"Forbidden"}}

Actual behavior

Impact
• GDPR Art. 33 / 34 notification required. Real, identifiable email addresses of individuals are publicly
accessible without authentication. The 72-hour supervisory-authority notification window begins on awareness.
Individual notification to affected data subjects is also likely required.
• Retroactive Web3 governance vote de-anonymisation. 2,832 rows mapping DRep on-chain identities to
specific YES/NO votes on specific polls cannot be undone. Even after the collections are locked, any party who
scraped the data before the fix holds a permanent vote-to-voter map for the platform's entire advisory-poll
history.

• Vote-buying and coercion. The voter+vote linkage enables verifiable vote-buying markets and coercion
attacks against DReps whose votes are now a matter of public record, contrary to the privacy model off-chain
advisory polls were designed to provide.
• Cross-collection re-identification. Joining the publicly accessible tables produces a complete
de-anonymisation chain: email address « wallet credential « DRep identity « vote history « proposal
authorship.
• Pseudo-anonymous draft leak. bds and bd-proposal-details expose drafts that were never intended for
public release, including test/sandbox content and unfinished proposal narratives. Authors who used the platform
as a draft tool see their unfinished work published.
• Systemic misconfiguration pattern. The same Strapi instance (be.pdf.gov.tools) shares UUID
e4220d9f-5b33-4174-bca2-17dd38e96044 with api.members.intersectmbo.org, confirmed via /admin/init. The
misconfiguration is likely cloned to the sister instance and should be audited there as well.

Expected behavior

Remediation
• Immediate (P0): In Strapi admin ® Settings ® Roles ® Public: revoke find and findOne on every
collection containing PII or governance-vote data. At minimum immediately revoke: auth-challenges,
bd-poll-votes, bd-proposal-details, bd-proposal-ownerships, bds, proposal-contents,
polls, bd-polls, proposals. Move them behind the Authenticated role.
• Immediate (P0 — GDPR): Treat this as a personal data breach under GDPR. File a supervisory-authority
notification within 72 hours of awareness. Assess whether affected individuals (email-address holders,
contributing organisations) must also be notified individually under Art. 34.
• Short-Term: Review field-level Strapi permissions. Even after collections are restricted to authenticated users,
sensitive fields (drep_id, drep_voting_power, social_handles, user_id, user_govtool_username)
should only be returned via dedicated controller methods that aggregate or anonymise appropriately.
• Short-Term: Audit the sister Strapi instance at api.members.intersectmbo.org (shares the same admin
UUID). Apply the same role-permission audit there.
• Short-Term: Treat the 2,832 existing vote rows as permanently compromised in privacy terms. Re-running
future polls with a fixed configuration does not undo past leaks. Publish a public disclosure statement so DReps
can recalibrate their prior privacy expectation.
• Long-Term: Add an automated test to CI: on every deploy, send an unauthenticated GET to every Strapi
collection and fail the build if any non-allowlisted collection returns HTTP 200 with data.

Metadata

Metadata

Assignees

Type

No type

Projects

Status
No status
Status
No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions