Customizing C2Concealer - Part 1

About a year ago, we publicly released our C2 malleable profile generator for Cobalt Strike, C2Concealer. You can read the initial blog post here.

In the GitHub Readme page and the initial blog post, we included some information on how to modify C2Concealer to change the default values used to build the malleable profiles. However, while teaching our Intrusion Operations course recently, several students asked for a blog post detailing how to customize the tool. So, here it is.

In Part I (this post), we'll discuss customizing the data lists.

In Part II, we'll look at additional places within C2Concealer to customize.

/C2Concealer/data/dns.py

Description: These are the subdomains that are used for DNS-based beacon activities. There are two python lists in this file, "subdomains" and "normal_subdomains".

  • "subdomains" is used to provide a value for DNS TXT-based payload staging. In the existing list, we used classic Name Server style values, like "ns01" and "ns02".
  • "normal_subdomains" is used to provide a value for DNS beacon communications and consists of a random list of tech words like "test" and "online" and "remote".

Ideas for New Values: You could change these to any common-looking subdomain. The easiest change is to grab a chunk of subdomains from a massive DNS wordlist like from SecLists and just throw some of them into those two python dictionaries.

/C2Concealer/data/file_type_prepend.py

Description: When you issue a command from Cobalt Strike, an HTTP-based beacon will receive an HTTP response with the tasking information. We get HTTP responses all the time. When you visited this blog post, your browser issued an HTTP GET request for the HTML for this page and our server replied back with the HTML (and CSS and JS). Cobalt Strike would include the beacon tasking orders in the HTTP Response pages. To blend into normal HTTP response traffic, we captured some common JavaScript, CSS and HTML server response pages. This file "file_type_prepend.py" contains three Python lists "js", "css" and "html". Each list contains multiple string elements that are basically just a string of the first part of a common JS, CSS or HTML response page. For example, the first js list element is the start of the jQuery version 3.4.1 code. We just proxied a page that required jQuery and grabbed the first ten lines of text and copied it into this Python list.

Ideas for New Values: Open up Burp. Proxy some traffic from commonly visited sites that would fit your target environment. For example, the target company's home page or Google or CNN. Review some of the HTML, CSS and JS resources that are loaded. Grab the first ten lines and swap them in for the existing values in the Python lists.

A couple important notes:

  • Don't grab too much text from the proxied traffic. Try to match the length we already have in there.
  • Your string of HTML, CSS or JS needs to be enclosed by double quotes ("). Escape all single quotes (\'). Change all double quotes inside the string to single quotes and then escape them (\'). This has to do with how Cobalt Strike will process these values.

/C2Concealer/data/params.py

Description: This file has two Python lists "common_params" and "words".

  • "common_params" contains a bunch of ... common parameters that you'd find in HTTP interactions (whether in a GET request or POST request).
  • "words" is literally just a wordlist that is used to provide cookie names and values in the HTTP-based Staged payload response.

Ideas for New Values: This SecLists file is a good start for some common parameter values. The wordlist can be built using anything you'd like, common words in your language of choice, randomly generated tech-y words, or run CeWL against a few websites.

Pro Tips:

  1. You can change the corresponding values for the parameters randomly grabbed from the "common_params" list. For example, in the file /C2Concealer/components/getclient.py on line 78, you'll see two hardcoded values "true" and "false". These could literally be any value. Don't make them too large, but consider adding "null" or "zero" or "3485".
  2. You can change how the values drawn from the "words" dictionary are encoded to provide cookie names and values in lines 47-54 in the file /C2Concealer/components/stagerserver.py.

/C2Concealer/data/post_ex.py

Description: This is a small Python list containing a list of processes which we will use in fork+run operations. Meaning, we'll spawn these processes, conduct our post-exploitation jobs in them and then tear them down.

The code driving the usage of this list, looks like this.

spawn_processes = ['runonce.exe','svchost.exe','regsvr32.exe','WUAUCLT.exe']

process = str(random.choice(spawn_processes))
spawnto_x86 = "%windir%\\\\syswow64\\\\" + process
spawnto_x64 = "%windir%\\\\sysnative\\\\" + process

Ideas for New Values: You can add a whole bunch of different processes in here, but a few things to keep in mind:

  1. The process must be located on all versions of Windows that you'll be operating on.
  2. As the code is written, a process by the same name must exist in both the syswow64 and system32 directories.
  3. Some processes are not good for these post-exploitation jobs. The best way to test this is to manually insert a process name into a C2 profile, spin up a teamserver, get a beacon and then execute some long-running post-exploitation jobs. I know it's a pain to do all that work, but trust us, we've struggled for hours trying to understand why our post-exploitation jobs weren't working, when everything else was, only to realize we chose a bad process for fork+run processes.
  4. As an example, check out mstsc.exe and logman.exe.

/C2Concealer/data/reg_headers.py

Description: There are multiple python lists in this file, but they're all straightforward and related to HTTP request or response headers.

  • "server" - common HTTP response header values for the "server" header
  • "cookie_prefixes" - common cookie names
  • "cookie_suffixes" - common cookie suffixes related to the security and scope of the cookies
  • "user_agent" - common client user-agent values
  • "accept" - file types accepted by client browser when sending an HTTP request
  • "accept_stager" - files types accepted by the client browser when staging over HTTP
  • "accept_encoding" - encoding formats accepted by the client's browser in HTTP requests
  • "accept_language" - languages accepted by the client's browser in HTTP requests
  • "content_encoding" - in HTTP responses the encoding of the content

All of these values are just window dressing, meaning we're just trying to blend into normal HTTP traffic for our beacon communications. None of them actually impact anything in our operations. As a result, we can change them to any acceptable value.

Ideas for New Values: First, here is a good page on Wikipedia detailing a bunch of different header values. Second, here are some resources for each list:

/C2Concealer/data/smb.py

Description: This file contains one Python dictionary consisting of SMB pipenames, which are used for SMB Beacon's peer-to-peer communication. You'll notice each dictionary element is a string with a word and underscore and two hashtag signs. Ex: word_##. When Cobalt Strike grabs these values, each # is replaced with a random hex value.

Ideas for New Values: We'd suggest coming up with some tech sounding words and then just creating a small list, like "sorting_##", "binomial_##", "rev_##". You could also list all named pipes currently in use on your system with the following PowerShell command and attempt to use those in your list. Just remember to add in "_##" after whatever word is included.

[System.IO.Directory]::GetFiles("\\.\\pipe\\")

/C2Concealer/data/stage.py

Description: There are two Python lists in here related to staging.

  • The list "transform_names" is a list of tech-sounding strings that are used to replace the string "ReflectiveLoader" in the beacon DLL.
  • The list "binary_types" is a list of dictionaries. Each dictionary entry contains two key/value pairs. The "name" key corresponds to a file type extension (ex jpg). The "content_type" key corresponds to the file type's mime type (ex image/jpeg). The "name" value is used to append to the stager's URL. The mime type value is used in the Content-Type server response header for the staged DLL beacon. In case that was confusing, let's walk through an example. If we select the "jpg" entry, then the stager response Content-Type will be "image/jpeg" and the URL the client reaches out to for the beacon DLL will be /whatever/something.jpg. The values in this list are ¬†file types that wouldn't look too odd having binary or base64-encoded data embedded.

Ideas for New Values: Get creative and come up with some additional strings for replacing "ReflectiveLoader" in the beacon DLL. Keep the length to the same length as "ReflectiveLoader"...don't go over 16 characters. If you wan to add to the "binary_types" list, also edit the consistencyCheck function in the profile.py file, and include an extra elif statement(s) to append the relevant file type to the stager URL.

/C2Concealer/data/transform.py

Description: A Python list of data transformation functions. This is largely set by Cobalt Strike, so unless they update the documentation with additional functions, you can skip this.

Ideas for New Values: n/a

/C2Concealer/data/urls.py

Description: This Python file contains 3 lists of

  • "file_types" is a list of file types that get appended to URLs for various HTTP-based communications.
  • "urls" is a list of random URL subpaths that could be included in various components of HTTP-based stagers and beacon communications.
  • "stager_urls" is a list of ¬†URL subpaths that are only used with stagers. This list was built so it'd seem like a natural subpath to server the beacon DLL file (which contains encoded binary data being sent in it). As an example, the value "image_directory" is in here, so if we serve the beacon DLL as a .jpg file, it'd make sense.

Ideas for New Values:

  • "file_types" could be expanded to include all types of files, but they're only used in plaintext communications, so avoid images, audio, etc. Stick with things like "html", "php", "asp", etc.
  • "urls" should be a massive list. Like 100 lines long. Just go grab a whole bunch of different commonly seen subpaths within a URL. This SecLists file is a good starting point (don't use any values that end in a file extension).
  • For "stager_urls", we'd recommend looking at that SecLists file recommended right above and look for any good file paths that might be related to files that could contain binary data. For example, "static" or "media". Also, you can combine multiple subpaths into one string, like "static/media". That will eventually turn into: http://domain.com/static/media/randomURL_from_urls_list

That's all for this post. In Part II (coming soon), we'll look at customizing values in the functions that build the malleable profiles. This first post is the easiest and quickest impact. The second post will require more patience to customize, as well as more consideration for appropriate values, but both types of customization are what we use internally on our private version.

If you have any feedback, we'd love to hear it! As always, you can reach us via Twitter, LinkedIn, and through our website.