A meeting-room window with a view into the office next door
OpenProject's GET /api/v3/meetings/:meeting_id/agenda_items/:agenda_item_id endpoint forgets to re-check project membership on the *linked* work package. Any authenticated user who can see a meeting can ask for an agenda item and get back fields from a work package that belongs to a project they have no rights to view. It's a classic broken-object-level-authorization (BOLA / IDOR) bug — the controller trusted the meeting's ACL and never re-authorized the join. Affected: all OpenProject versions prior to 17.4.1; the meeting agenda feature lands in modern 13.x+ branches, so anyone on a recent self-hosted release is in scope.
Vendor's MEDIUM (4.3) is fair-to-slightly-generous. The CVSS vector AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N reads correctly: network-reachable, low complexity, low-privilege required, partial confidentiality only. No integrity loss, no availability loss, no privilege escalation, no scope change. For a defender running 10,000 hosts this is a data-confidentiality hygiene issue, not a fire.
4 steps from start to impact.
Obtain an authenticated OpenProject account
- Network reachability to the OpenProject web UI/API
- Valid user credentials or an API token
- Most enterprise OpenProject deployments are internal-only behind SSO/VPN
- Self-registration is usually disabled in corporate tenants
Enumerate meeting and agenda item IDs
/api/v3/meetings for meetings they are invited to, then iterates numeric agenda_item_id values. IDs are sequential integers, so an ffuf/Burp Intruder sweep across small ranges is enough.- Membership in at least one meeting
- Knowledge of API path (public docs)
- Rate-limited reverse proxies blunt brute enumeration
- Meeting agendas are usually scoped to small project teams
Pull linked private work-package data
- Agenda item references a work package in a private project
- Vulnerable version <17.4.1
- Only agenda items that actually link cross-project work packages disclose anything
- Attacker only learns what was linked — not full project contents
nuclei template can flag it once writtenAggregate disclosed fields into intel
- Sustained access over time
- No lateral movement, no creds harvested, no shell
/api/v3/meetings/*/agenda_items/* route at the reverse proxy for non-essential users — An nginx/Apache rule scoped to user-group headers (if you front OpenProject with SSO that injects them) blocks the single vulnerable endpoint while keeping the rest of the API live.agenda_items/:id from a single account. A simple rate-anomaly rule will catch enumeration attempts.- WAF signature rules — there is no malicious payload to match; the request is a normal authenticated GET. Generic OWASP CRS rules will not catch it.
- MFA — the attacker is already authenticated as a legitimate user; a second factor at login does nothing.
- Network segmentation alone — most exploiters of this bug will be on-prem authenticated users, not external scanners. Putting OpenProject behind a VPN doesn't help against insiders or compromised employee accounts.
- EDR on the OpenProject host — there is no malicious process; the application itself serves the over-privileged response.
The supporting signals.
| In-the-wild exploitation | None observed. No CISA KEV, no public exploit campaigns, no GreyNoise tag as of 2026-06-30. |
|---|---|
| Public PoC | No standalone PoC published. Bug reproduction is trivial from the GHSA description — single curl against the documented endpoint. |
| EPSS | 0.00214 (≈0.2%) — bottom-quartile likelihood of exploitation in the next 30 days. |
| KEV status | Not listed. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N — network, low complexity, low-privilege required, partial confidentiality only. |
| Affected versions | All OpenProject releases prior to 17.4.1 that ship the Meetings module with agenda-item linking. |
| Fixed version | OpenProject 17.4.1 (released 2026-06-08). 17.5.0 also fixed. |
| Exposure data | Shodan/Censys shows roughly 6–9k internet-reachable OpenProject instances globally; the majority are corporate self-hosted behind SSO. |
| Disclosure date | 2026-06-26, GHSA-g387-6rm2-xw88. |
| Reporter | Researcher via YesWeHack OpenProject Bug Bounty program, sponsored by the European Commission. |
Sources.
Why this verdict
- Authentication required —
PR:Lin the vector is correct; an anonymous internet scan cannot trigger this. Drops the unauthenticated-RCE class of pressure off the verdict. - Confidentiality-only impact —
I:N/A:N/S:U. No code execution, no privilege escalation, no tenant boundary break, no host takeover. Worst case is leaked work-package subjects and custom fields. - Narrow data surface — only work packages actually *linked* into agenda items are exposed; the attacker does not get arbitrary read across all projects.
- EPSS 0.00214 and not in KEV — exploitation likelihood is in the bottom quartile and there is no observed in-the-wild activity.
- Role multiplier — LOB collaboration tier: Even in its most sensitive realistic deployment (PMO/engineering tracking confidential roadmap), the chain ends at *data disclosure of linked work packages*. It does not pivot to fleet compromise, identity takeover, or supply-chain impact. OpenProject is not in the high-value-role catalog, so the CRITICAL/HIGH floor does not apply.
Why not higher?
MEDIUM would require either a meaningfully larger blast radius (cross-tenant read of *all* projects, not just linked work packages) or active exploitation evidence. Neither is present — the bug is a narrow joined-object authorization gap, EPSS is ~0.2%, KEV is silent, and no public PoC campaigns exist.
Why not lower?
IGNORE is wrong because regulated workloads (GDPR personal data, M&A codenames, customer support tickets containing PII) routinely live in OpenProject work-package descriptions, and disclosure of those fields to any authenticated user is a real compliance exposure. The bug deserves a tracked patch on the standard hygiene window, not silent backlog.
Crowdsourced verification payload.
Run this on an auditor workstation with network reach to your OpenProject server. It checks the running version via the public /api/v3 root and decides VULNERABLE / PATCHED. Requires curl and jq. Example: ./check_op_49355.sh https://openproject.corp.example.com. No credentials needed.
#!/usr/bin/env bash
# noisgate verification — CVE-2026-49355 (OpenProject meeting agenda IDOR)
# Usage: ./check_op_49355.sh https://openproject.example.com
set -u
BASE="${1:-}"
if [[ -z "$BASE" ]]; then
echo "usage: $0 https://openproject.example.com" >&2
exit 2
fi
command -v curl >/dev/null || { echo "curl required"; exit 2; }
command -v jq >/dev/null || { echo "jq required"; exit 2; }
ROOT=$(curl -fsSL --max-time 10 "$BASE/api/v3" 2>/dev/null) || {
echo "UNKNOWN — cannot reach $BASE/api/v3"; exit 3; }
VER=$(echo "$ROOT" | jq -r '.coreVersion // empty')
if [[ -z "$VER" ]]; then
echo "UNKNOWN — coreVersion not in API root response"; exit 3
fi
# Compare against 17.4.1
FIX="17.4.1"
lowest=$(printf '%s\n%s\n' "$VER" "$FIX" | sort -V | head -n1)
if [[ "$lowest" == "$FIX" && "$VER" != "$FIX" ]]; then
echo "PATCHED ($VER >= $FIX)"
exit 0
elif [[ "$VER" == "$FIX" ]]; then
echo "PATCHED ($VER == $FIX)"
exit 0
else
echo "VULNERABLE ($VER < $FIX) — CVE-2026-49355"
exit 1
fi