Project Introduction: SOAR Workflow: Detecting and Isolated Malware-Infected Endpoints
This project outlines the implementation of an automated malware detection and response workflow leveraging Elastic Stack, Slack, and Cloudflare Tunnels. The workflow is designed to detect malicious file installations, generate alerts, notify security teams via Slack, and provide an automated response mechanism to isolate compromised endpoints using a Slack slash command.
The system functions as follows:
Detection: When a user machine attempts to install a file with a known malware signature, an alert is triggered.
Alerting: A Slack message is automatically generated with critical details, such as the hostname, endpoint ID, and file information.
Response: The recipient of the Slack message can execute the
/isolate
command, which will initiate an endpoint isolation process through Kibana.
When a malicious file is detected, Elastic Agent (running with the Elastic Defend integration) enforces the detection rule and logs the event in Kibana. Kibana then triggers a Slack Connector, notifying the security team. If isolation is required, an analyst can execute the /isolate
command, which sends an HTTP POST request to a PythonAnywhere-hosted Flask web app. This app parses the request and forwards it through a Cloudflare Tunnel to the Kibana API, which isolates the affected machine based on the Host ID provided in the Slack message.
A logical flow of this process is illustrated below:
Agent Setup & Malware Detection
II. Alerting through Kibana and Slack
From our rule settings, we'll utilize our Slack connector created in our Bruteforce SSH Workflow project to create a new alert to send to Slack automatically when the alert is generated in Kibana
III. Domain & Tunnel Configration
Next, we need to ensure that traffic coming from Slack to tonesw.pythonanywhere.com
is securely forwarded to our internally hosted Kibana server. This requires configuring a Cloudflare Tunnel, which will act as a secure bridge between external traffic and our internal environment.
To set this up, navigate to your Cloudflare account dashboard, then:
Access the Zero Trust Overview page.
Select "Tunnels" under the Network settings in the left pane.
Create a new tunnel, which will securely route requests from PythonAnywhere to the Kibana API inside your lab environment.
Compliance Considerations:
ISO 27001 (Network Security): Cloudflare Tunnel encrypts all traffic between external clients (Slack, PythonAnywhere) and Kibana, ensuring no plaintext data is exposed during transit.
NIST 800-27 (Zero Trust Architecture): Cloudflare Tunnel eliminates the need for opening direct inbound ports, reducing attack surfaces. NIST 800-27 which advises hiding internal resources from direct exposure and using brokered, authenticated access for external services.
NIST 800-53 (SC-12, SC-13, SC-31) – Enforces encryption for system communications.
On the next menu, we'll select the Cloudflared offering
Next, we need to name our Cloudflare Tunnel and generate a unique authentication token. This token will be used to install and authenticate the Cloudflared service on our Kibana machine, allowing it to securely communicate with Cloudflare.
Next we we will install the cloudflared service on the Kibana machine, using our (now destroyed at time of writing) token.
After installation, we will update the Cloudflared service by pulling the latest version from GitHub, ensuring we are using the most up-to-date and secure version of the software.
And we can see that our tunnel is connected and working!
IV. PythonAnywhere App Deployment
Next, we will navigate to PythonAnywhere, which will be used to deploy our parsing script and handle incoming Slack isolation requests. This setup will allow us to process commands from Slack and securely forward them to Kibana via Cloudflare Tunnel.
To begin, we'll create a PythonAnywhere account, and link our smmodded.net domain to it, ensuring that Slack requests can be properly routed.
This setup will provide a stable external endpoint for receiving Slack commands while keeping our Kibana instance internal and protected.
Once the app is created, we'll need to create the /isolate command. The Request URL we will supply will act as a web address where Slack sends a HTTP POST request when an event, slash command, or interactive action occurs.
In this case, Slack will send data to the Request URL so that our PythonAnywhere service can process the request and return a response.
We'll set the Request URL to:
https://smmodded.net/slack/isolate
The smmodded.net will refer to our CNAME entry, but the /slack/isolate will be the directory where our webapp will reference for Slack requests as shown when we begin creating it
After completing, we'll install the app into Demo Org
def verify_slack_signature():
"'"Verify the Slack request signature to ensure authenticity."""
slack_signature = request.headers.get("X-Slack-Signature", " ")
slack_timestamp = request.headers.get("X-Slack-Request-Timestamp" , "")
# Reject requests older than 10 minutes to prevent replay attacks
if abs(time.time() - int(slack_timestamp)) > 60 * 10:
abort (400, "Request timestamp is too old")
# Build the basestring and calculate the signature
request_body = request.get_data().decode("utf-8")
basestring = f"VO:{slack_timestamp}:{request_body}".encode("utf-8")
computed_signature = (
"vO="
+ hmac.new(
SLACK_SIGNING_SECRET.encode("utf-8"), basestring, hashlib. sha256,
). hexdigest()
)
V. Slack App Configuration
Finally, in order for Slack to send requests to https://smmodded.net, the website must have a valid certificate. For this, we'll use CloudFlare's Edge Certificate service through their Advanced Certificate Manager
First, we'll navigate to Edge Certificates and select Order Advanced Certificate
From the next page, we'll select certificates from Google Trust Services and specify that we'll need a certificate for all hostnames records using smmodded.net. We'll leave the default expiry of 3 months
Finally, we'll turn on Total TLS to ensure any proxied connections also have a certificate issued, and enable Always Use HTTPS
And that should be it! We can now test our slash command to make sure that the output is as expected within slack based on HostID
And to finish testing, we'll just navigate back to our Response actions history within Elastic Defend in our Kibana Dashboard to find the Ubuntu-user machine has been isolated in response successfully
And as we can see it was a success! Slack was successfully able to send a POST request to our web app, which was forwarded to our Cloudflare tunnel connected back to our Kibana machine, and for the Kibana API to successfully isolate the machine using Elastic Defend