Detecting Passwords Shared Via Slack In Real Time
- Aashiq Ramachandran
- Sep 22, 2024
- 5 min read
More often than not, an organisation has a plethora of people within the organisation attempting to log in to password-protected resources belonging to the enterprise. With remote work only increasing the use of cross-company collaboration platforms such as Slack / Microsoft Teams, the attack surface of credential leakage via messaging platforms increases multifold.
In this blog, we detail how to deploy a real-time monitoring solution via Slack and AWS Lambda, where we can detect passwords being shared over Slack in seconds.
This is achieved by implementing a Slack bot that forwards messages to an AWS Lambda function that then analyzes messages and, if a password is detected, alerts us of the leakage.
Requirements
To successfully implement this function, we require the following.
1 AWS Lambda Function
AWS’s API Gateway Endpoint Linked to the λ function
Slack Bot configured with the Slack Events API to forward messages to the Gateway endpoint
Overall Design
High-level workflow of slack-shared credential detection
Configure Lambda Function
We must first configure the AWS Lambda function to kick off the workflow. This function aims to receive messages from Slack and extract passwords/tokens via regex.
The Slack Events API connecting to an external source such as AWS Lambda requires a validation function that returns a challenge value when we link the events API.
All taken into account, this lambda function on a high level is split into two parts.
First where we return the challenge token if the request type coming from Slack is a url_verification request type
The second part is where if the message type is not url_verification, we proceed to attempt to extract passwords via regex.
Configuring the Lambda Function to handle challenge answers
The challenge section of the lambda function will look something like the one below.
lambda_function.py
def lambda_handler(event, context):
print(f"Event received - {event}")
body: str = event.get("body")
body: dict = json.loads(body)
type: str = body.get("type")
token: str = body.get("token")
if type == "url_verification" and body.get("token") == token:
challenge_answer = body.get("challenge")
return {
'statusCode': 200,
'body': challenge_answer
}
In the above code block, we first extract the slack event body from the request body and check the request type. If the request type is “url_verification”, we return the challenge token to Slack with the Status Code 200.
If the request type is not URL verification, we can proceed to pass the message to a regex extraction module and attempt to extract messages.
Configuring the Regex Extraction module to attempt to extract passwords from regex
We will create another file called regex_search.py, which will house our regex extraction code. That will look something like the one below.
regex_search.py
import re
import yaml
def read_yaml(regex_path: str):
"""
Method used to load YAML path of file containing regex's
:param regex_path: Enter the yaml path containing regex's
:return: json structure of regexes
"""
with open(regex_path, "r") as stream:
data = yaml.safe_load(stream)
return data["re_strings"]
def main(message_string):
pickled_regex = {}
base_path = "regex_checks/base.yaml"
re_strings = read_yaml(
regex_path=base_path
)
matches = {}
for dict_ in re_strings:
string: dict = dict_
key = list(string.keys())[0]
value = re.compile(pattern=string[key])
pickled_regex[key] = value
for key_type in pickled_regex:
response = pickled_regex[key_type].findall(string=message_string)
if len(response) > 0:
matches[key_type] = response
return matches
In the above code section, we import the re and yaml modules used in the remainder of the script. From there, we define a primary function, which takes a string as an input, loads a YAML file containing multiple regex patterns, and attempts to match the input string with regexes loaded from the YAML file.
The above code block returns matches if there are any matches; otherwise, it returns an empty list.
The YAML file (regex_checks/base.yaml) will look like the following.
re_strings:
- jwt_token: (?:[a-zA-Z0-9]{36}\.[a-zA-Z0-9]{74}\.[A-Za-z0-9_]{43})
We can keep adding regex patterns to the above file to match other credential exposures.
Now that we’ve configured the initial lambda function (to handle challenge tokens) and a regex module, we can link the regex_search function into our lambda to detect matches in Slack messages.
Upgrading the Lambda Function to Extract Passwords/ Tokens from Slack Messages
Next, we can return to our lambda function, validating the payload received to ensure it comes from Slack and extracting the Slack message. This extracted message is then sent over to our regex_search.py to get potential password leaks.
That code would look like the one below and be appended to the lambda_function.py file.
if token != verification_token:
return {
"statusCode": 401
}
base_event: dict = body.get("event")
message_type: str = base_event.get("type")
bot_profile = base_event.get("bot_profile")
user_id = base_event.get("user")
if user_id == credentials.bot_user_id:
return {
"statusCode": 201
}
if bot_profile is not None:
return {
"statusCode": 201
}
if message_type != "message":
return {
"statusCode": 201
}
message: str = base_event.get("text")
response = main(message_string=message)
if len(response) == 0:
return {
"statusCode": 201
}
url = “detection endpoint/ SIEM ingestion URL here”
r = requests.post(url, json=event)
return {
“statusCode”:201
}
In the above block of code, we check if the token extracted matches the verification token Slack gives us. If not, we return a 401 Status Code. After that, we extract the message from the slack event and pass it to the main method in the regex_handler.py file.
If the response we receive has a length of 0, we assume no passwords have been leaked; if the response is over 0, we assume that secrets are shared over Slack.
From there, we send this event (message containing a password) over a webhook to our detection use case.
Once we’ve detected a message with a password shared, we can write code to suit our use case to enable custom detections. We can connect the lambda back to Slack to DM the user to remove passwords shared in Slack, connect this lambda to a SOAR product such as Cyware Orchestrate to begin incident orchestration or connect it to a custom endpoint that attaches to our SIEM.
Our overall folder structure for the lambda will look something like the one below.
|
|__ lambda_function.py
|__ regex_search.py
|__ credentials.py
|__ regex_checks
|__ base.yaml
With that, we’ve configured a simple lambda function to detect credential leakage via Slack using regex.
Configure AWS API Gateway To Receive Events From Slack
To configure the API Gateway to our Lambda function, we first need to add a trigger to the lambda function.
From here, we choose API Gateway as the trigger and select Create an API (REST). We can leave the API authentication as Open for this use case and add this API.
Our Lambda function will then look like this.
Now we will receive an API endpoint such as https://unique_code.execute-api.aws_region.amazonaws.com/default/lambda-function-name
We will attach this URL in our Slack Bot in the section below.
Configure Slack Bot
First, we go to https://api.slack.com/apps/, choose create a new app and choose create from scratch in the options menu.
We give the app a name and select a Slack workspace to install it into.
We are then presented with a screen like the one below.
From here, we select Event Subscriptions to configure our lambda URL.
Once we configure the URL, it'll look something like the above.
Next, we need to subscribe to Bot Events. These events are then forwarded to our lambda function.
The scopes we need to configure in events are attached below.
We can next proceed to install the app into our workspace. With this, we’ve successfully configured a Slack bot to forward messages to our lambda function, which will, in turn, analyse the message for credential leakage.
If there is a leak, we send this alert to a webhook; otherwise, continue to monitor other messages!
We can now add this bot to channels, and if any credentials are shared over Slack, the lambda function will alert us of the same!
PS: This is meant as an overview of setting up a detection mechanism for credentials shared over Slack. One should always confine to organizational best security practices while setting up Slack Apps / AWS Resources / Forwarding Messages!
Credits
Slack
AWS Lambda
Comments