Trello Workspace Self-Join Bypass via Slack Integration

DP
Deev Pal
Mar 17, 2025 16 min read
This vulnerability was reported to Atlassian via Bugcrowd, accepted as P3, fixed, and rewarded $1,200. Disclosure: Bugcrowd CrowdStream.

TL;DR

Trello Premium workspaces can be linked to Slack workspaces. When the Trello workspace owner sets the join method to "Invite Only," Slack members should only be able to join the Trello workspace when explicitly invited by an admin. This restriction can be completely bypassed — any Slack workspace member can self-join the linked Trello workspace by clicking a "Join" button in the Slack Trello integration, or by replaying the underlying joinTeam API request via Burp Suite.

The authorization check for the "Invite Only" setting was not enforced on the Slack integration's joinTeam callback. The Slack-side API trusted the action without verifying Trello-side permissions.

Background — Slack + Trello

Before diving into the vulnerability, let's make sure everyone's on the same page about what these tools are and how they connect.

Slack is a messaging platform for teams — think of it like a work-specific group chat. Companies create a "Slack workspace" where employees communicate in channels, send direct messages, and integrate with other tools.

Trello is a project management tool by Atlassian. Teams organize work using boards, lists, and cards — like a digital whiteboard with sticky notes. Companies create a "Trello workspace" to group their project boards together.

Many companies use both tools, so Atlassian built an official Slack-Trello integration that lets you do things like create Trello cards from Slack, get notifications in Slack when cards move, and manage your Trello boards without leaving Slack. To enable this, a workspace admin "links" their Slack workspace to their Trello workspace.

The key concept: When two platforms are linked together, security settings from one platform need to be respected by the other. If Trello says "only invited people can join our workspace," then Slack shouldn't offer a "Join" button that bypasses this restriction. This kind of cross-application trust boundary is where security bugs love to hide.

Context — Slack-Trello Integration

When a Trello workspace admin links their workspace to Slack, the settings page confirms the connection and shows how Slack members can join the Trello workspace:

Trello workspace settings showing Slack workspaces linking. The techycodec workspace is linked to techycodec.slack.com, with text saying Slack workspace members must be invited manually to this Trello Workspace
Slack-Trello link configuration: The Trello workspace "techycodec" is linked to the Slack workspace at techycodec.slack.com. Notice the text: "Slack workspace members must be invited manually to this Trello Workspace." This is the security contract we're about to break.

The integration offers two modes for how Slack members can join the linked Trello workspace:

Modal showing Who can join this Trello Workspace with two options: Self-join and Invite only, with Invite only selected
The two join modes: "Self-join" allows any Slack member to automatically join the Trello workspace. "Invite only" (currently selected, shown with the checkmark) means Slack members must be explicitly invited by a Trello admin. This is the restrictive setting the admin has chosen.
Why would an admin choose "Invite Only"? Imagine a company where 200 people are in the Slack workspace, but only 30 people should have access to the Trello workspace (maybe it contains sensitive project information, client data, or strategic plans). The admin links Slack for convenience but sets "Invite Only" so only specific people get Trello access. If anyone in Slack can bypass this and join anyway, the admin's access control is meaningless.

The Permission Model

With "Invite Only" selected, the settings page clearly confirms the restriction is active:

Trello settings showing the workspace is linked and Slack workspace members must be invited manually
Restriction confirmed: The word "invited" (highlighted in red) confirms that the "Invite Only" mode is active. Slack members should not be able to join without an explicit invitation from a Trello admin.
Full Trello workspace settings panel showing Slack workspaces linking section with invited text highlighted in red box
Full settings view: The complete Slack workspaces linking section. The restriction text "Slack workspace members must be invited manually to this Trello Workspace" is highlighted, confirming that this is a deliberate access control choice by the admin.

The security contract is explicit: Slack members who are not members of the Trello workspace should only be able to join when a Trello admin manually invites them. There should be no self-service join mechanism.

This creates a cross-application trust boundary. The restriction is defined in Trello (the "Invite Only" setting), but it must be enforced by both Trello and the Slack integration. If either side fails to check the setting, the restriction can be bypassed.

Attack Surface Analysis

The attack surface here is unique because it spans two separate applications — Slack and Trello — that communicate through an integration layer. This creates three places where the "Invite Only" check could (and should) be enforced:

  1. Trello's web interface — When someone tries to join via Trello directly. This was checked.
  2. Trello's API — When a direct API request attempts to add a member. This was checked.
  3. The Slack integration's callback — When the Slack Trello bot processes a "Join" action from a Slack user. This was NOT checked.
What's a "callback"? When you click a button in Slack (like the "Join" button from the Trello bot), Slack doesn't join you to Trello directly. Instead, it sends a message to Trello's servers saying "this user wants to join." That message is called a "callback" — it's Slack calling back to Trello to process the action. The vulnerability is that when Trello receives this callback, it doesn't check whether the "Invite Only" setting should prevent the join.

This pattern — where the main application enforces a restriction but a third-party integration bypass it — is extremely common. Developers focus on securing the primary UI and API, but forget that integrations create alternative entry points that need the same security checks.

Exploitation — Two Attack Paths

Prerequisites

  • Attacker: A member of the Slack workspace (e.g., any employee in the company's Slack)
  • Attacker is NOT: A member of the Trello workspace (they haven't been invited)
  • Target: The linked Trello workspace with "Invite Only" restriction enabled

Path 1: The Easy Way — Just Click "Join"

This is the simplest attack — the attacker doesn't even need any security tools. They just use Slack normally.

Step 1: The attacker opens their Slack workspace and navigates to the Apps section in the sidebar. They find the Trello integration, which is installed workspace-wide:

Slack sidebar showing the techycodec workspace with channels, direct messages, and the Trello app listed under Apps
Attacker's Slack view: The attacker is a member of the "techycodec" Slack workspace. Under "Apps" in the sidebar, the Trello integration is visible. The attacker clicks on the Trello app to open it.

Step 2: The Trello bot detects that the attacker's linked Trello account is not a member of the connected Trello workspace. It helpfully offers a "Join" button:

Trello bot message in Slack saying Thanks it looks like you're not yet a member of some of the linked Trello workspaces. I can add you automatically with a Join button for ssrftestworkspace001. Below that it says Congrats you are now a member of ssrftestworkspace001
The bypass in one click: The Trello Slack bot sends a message: "Thanks! It looks like you're not yet a member of some of the linked Trello workspaces. I can add you automatically:" followed by the workspace name and a "Join" button. After clicking, the bot confirms: "Congrats, you are now a member of ssrftestworkspace001." The "Invite Only" restriction was completely ignored.
The attacker is now a full member of the Trello workspace — with access to all boards, cards, and conversations. The Trello admin never invited them. The "Invite Only" setting was completely bypassed by a single click inside Slack.
Why is this so concerning? The attacker doesn't need any hacking tools. They don't need to modify any requests. They don't need any technical knowledge. They just open Slack, click on the Trello app, and click "Join." This makes the attack accessible to literally anyone in the Slack workspace — not just security researchers.

Path 2: The API Way — Replaying the Request

For a more targeted approach (or to automate the attack), the attacker can intercept and replay the API request that the "Join" button sends. Using Burp Suite as a proxy, the attacker captures the exact request:

Burp Suite showing a POST request to the Slack API chat.attachmentAction endpoint with the joinTeam callback and the server response
The API request behind the "Join" button: Burp Suite shows the POST request sent to Slack's API when the Join button is clicked. The request body contains a joinTeam callback action with the target Trello workspace ID (idTrelloTeam). The response confirms the action succeeded.

The Exploit Request — Dissected

Let's break down what happens when the "Join" button is clicked. The request goes to Slack's API, not Trello's — which is part of why the Trello-side restriction isn't enforced:

HTTP Request — Join via Slack Integration
POST /api/chat.attachmentAction HTTP/2
Host: techycodec.slack.com
Cookie: <ATTACKER-SLACK-COOKIES>
Content-Type: application/x-www-form-urlencoded

payload={
  "actions": [{
    "name": "joinTeam",
    "value": "{\"idTrelloTeam\":\"TARGET_WORKSPACE_ID\"}"
  }],
  "callback_id": "trello-join-workspace",
  ...
}

Here's what each piece means:

Field What it does Why it matters
POST /api/chat.attachmentAction Slack API endpoint for processing button clicks in bot messages The request goes to Slack's servers, not Trello's directly
Host: techycodec.slack.com The Slack workspace's API hostname Confirms this is a Slack-side action, processed by Slack's infrastructure
name: "joinTeam" The action to perform — join a Trello team/workspace This is the core action. Slack forwards this to Trello's integration handler
idTrelloTeam The Trello workspace ID to join Identifies which Trello workspace the user wants to join
Cookie Attacker's Slack session cookies The attacker uses their own Slack session — no victim credentials needed
The trust boundary problem: When the "Join" button is clicked, the request flows through multiple systems: Browser → Slack API → Trello Integration Handler → Trello. At each handoff, someone needs to check: "Is this user allowed to join?" The Trello integration handler received the joinTeam callback from Slack and processed it without checking the "Invite Only" setting. It trusted that if Slack sent the request, it must be allowed — but Slack had no knowledge of Trello's "Invite Only" restriction.

Full Attack Chain

Attack Flow
1. Admin links Slack workspace to Trello workspace
   └── Sets join method to "Invite Only"
   └── Trello settings confirm: "members must be invited manually"

2. Attacker is a member of the Slack workspace
   └── NOT a member of the Trello workspace
   └── Has a Trello account linked to their Slack profile

3. Attacker opens Trello app in Slack sidebar
   └── Trello bot detects: "you're not a member of linked workspaces"
   └── Bot offers a "Join" button — despite "Invite Only" being set

4. Attacker clicks "Join"
   └── Slack sends: POST /api/chat.attachmentAction
   └── Action: joinTeam with target Trello workspace ID
   └── Trello integration handler processes the join
   └── "Invite Only" check is NOT performed

5. Result: Attacker is now a full workspace member
   └── Access to all workspace boards
   └── Can view, create, and modify cards
   └── Admin receives no alert about the unauthorized join
   └── "Invite Only" setting appears unchanged — admin doesn't know it failed

Root Cause Analysis

The vulnerability exists because the "Invite Only" restriction was enforced by Trello's core application but not by the Slack integration's callback handler. These are two separate code paths maintained by the same team, but the authorization check was only implemented in one of them.

An analogy: Imagine a building with a strict front door policy — you need a badge to enter. But there's a side door connected to the building next door (Slack). When someone from the adjacent building walks through the connecting door, the security guard at the front desk doesn't check them because they came from inside the connected building. The assumption is: "if they got past the other building's security, they must be authorized." But the other building has completely different access rules — it lets anyone in.

The authorization logic was approximately:

Pseudocode — Authorization Check
// Trello Web UI / Direct API — join workspace
function joinWorkspace(user, workspaceId) {
    const workspace = getWorkspace(workspaceId);

    if (workspace.slackJoinMethod === "invite_only") {
        if (!user.hasBeenInvited(workspaceId)) {
            return 403; // Blocked ✓
        }
    }

    workspace.addMember(user);
    return 200;
}

// Slack Integration — joinTeam callback
function handleSlackCallback(action) {
    if (action.name === "joinTeam") {
        const user = getTrelloUserFromSlack(action.slackUser);
        const workspaceId = action.value.idTrelloTeam;
        const workspace = getWorkspace(workspaceId);

        // ✗ Missing: check workspace.slackJoinMethod
        // ✗ Missing: check if user has been invited

        workspace.addMember(user); // Directly adds the user
        return 200; // "Congrats, you are now a member!"
    }
}

The first function (used by Trello's web interface and direct API) correctly checks the "Invite Only" setting. The second function (used by the Slack integration) skips the check entirely. This is a classic missing authorization at the integration boundary — the security check exists in the main application but was not replicated in the integration handler.

Impact

CVSS 3.0: AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N — 6.5 (Medium)

  • Confidentiality: Indirectly high — once the attacker joins, they can access all boards and cards in the workspace, potentially including sensitive business information, client data, and strategic plans
  • Integrity: High — the workspace membership boundary is violated; unauthorized users gain full member privileges including the ability to modify cards, lists, and boards
  • Availability: None directly

Real-world scenarios:

  • Contractor overreach: A company adds contractors to their Slack for communication but restricts Trello access to full-time employees only. Any contractor can bypass this and access all project boards.
  • Departmental isolation: A large organization uses one Slack workspace but separate Trello workspaces per department (Engineering, Finance, Legal). An engineer could self-join the Finance Trello workspace and access financial data.
  • Former access: An employee who was removed from the Trello workspace (but remains in Slack) can re-join at any time without the admin knowing.
  • Silent access: The admin receives no notification that someone self-joined despite the "Invite Only" setting. The breach could go undetected indefinitely.
The core security contract violation: the "Invite Only" setting promises that only explicitly invited users can join the Trello workspace. This promise is broken by the Slack integration, which offers a self-service "Join" button regardless of the setting.

Remediation

Atlassian resolved this by adding the "Invite Only" authorization check to the Slack integration's joinTeam callback handler. The fix ensures that when a Slack user clicks "Join," the integration checks the workspace's join method setting before processing the action:

  1. If the workspace is set to "Self-Join" — allow the join (existing behavior)
  2. If the workspace is set to "Invite Only" — reject the join and inform the user they need an admin invitation (new check)

Defensive pattern: When building integrations between applications, every action that crosses the application boundary must carry the full authorization context of the original application. The integration should never assume that because the action came from a trusted partner (Slack), it's automatically authorized. Each action must be independently validated against the target application's (Trello's) access control rules.

The lesson for developers: When you add a new integration or third-party connection to your application, treat it as a brand new entry point that needs all the same security checks as your main UI. Go through every action the integration can trigger and ask: "Does this action go through the same authorization checks as if the user did it directly through our main interface?" If the answer is no, you have a vulnerability.

Timeline

  • 2025-03-17 — Vulnerability discovered and reported via Bugcrowd
  • 2025-03-17 — Triaged as P3 (Broken Access Control > Privilege Escalation)
  • 2025-08-25 — Fix deployed, vulnerability resolved
  • 2025-08-25 — $1,200 bounty awarded
  • 2025-08-25 — Disclosure approved

Takeaways for Hunters

  1. Integrations are goldmines. When two applications are connected, security checks often exist in one but not the other. The integration boundary — where App A talks to App B — is where authorization checks are most likely to be missing. Always test what happens when actions cross that boundary.
  2. Click every button. This vulnerability was exploitable by literally clicking a "Join" button in Slack. No Burp Suite, no API manipulation, no technical skill required. Sometimes the bug is just… there, in the UI, waiting to be clicked. Don't overthink it — try the obvious thing first.
  3. Test with the restrictive setting enabled. Many hunters test features with default (permissive) settings. The bugs hide in the restrictive settings. Toggle every permission to its most restrictive option, then test whether the restriction is actually enforced across all entry points.
  4. Trust boundaries are vulnerability boundaries. Every time data or actions flow between two systems (Slack → Trello, OAuth → App, Webhook → Handler), there's a trust boundary. At each boundary, ask: "Does the receiving system independently verify authorization, or does it trust the sending system?" Blind trust across boundaries is a bug.
  5. Check for silent failures. In this bug, the admin received no alert that someone self-joined despite "Invite Only." Even if a restriction fails, does the admin know it failed? Silent restriction failures compound the impact because the breach goes undetected.