02 August 2021
If you haven't read Part I, we recommend starting there. If you're ready for further C2Concealer customization, then let's dive in.
The bulk of C2Concealer's operations are done in the subdirectory /components. Each file in that directory corresponds to a particular block within the c2 malleable profile. For example, there is a file for DNS Options configuration named /components/dnsoptions.py.
Each of these component files contains a Python class with two functions - printify and randomizer. Printify() outputs a string of all our relevant configurations, which gets appended to the final c2 malleable profile. We can skip this function. Randomizer() is where we can make additional customizations. This function generates our configurations by grabbing data from the /data directory (discussed in Part I), randomly generating values (like integers) and using hardcoded data. Since we already reviewed the /data directory customizations, we'll look at making changes to the random() functions and hardcoded values in the various component files.
This post covers half of the files in the /components directory. Since there's a lot of options to customize and this post got quite long, we'll publish a third post (Part III), which will cover the rest.
Description: This file creates our global DNS options.
Customizable Options:
ip_num = list(range(1,9)) + list(range(11,171)) + list(range(173,191)) + list(range(193,254))
##Customize any of the options below
self.dns_idle = ".".join(map(str, (random.choice(ip_num) for _ in range(4))))
self.maxdns = str(random.randint(240,255))
self.dns_sleep = str(random.randint(100,150))
self.dns_max_txt = str(252)
self.dns_ttl = str(3600)
You can customize any of the 5 DNS option values above:
Description: Builds configurations for the http-get client section of the profile.
Customizable Options:
You can get creative with this section and add in any headers that you'd like. As is, C2Concealer includes the following HTTP GET headers: 'Host', 'User-Agent', 'Connection','Accept', 'Accept_Encoding', 'Accept_Language'.
We recommend adding in new headers and values. To add in a new header, take the following steps:
def __init__(self, name, host):
##Existing headers
self.Host = host
self.Accept = None
self.Accept_Encoding = None
self.Accept_Language = None
self.Connection = 'close'
##New headers
self.Authorization = None
self.If_Match = None
self.headerList = ['Host', 'Connection','Accept', 'Accept_Encoding', 'Accept_Language', 'Authorization', 'If_Match']
3. Decide if you want the header to be there in every profile or only some of them. If you only want it in some of them, then add the header name to the self.optionalHeaders Python list and then add in logic near lines 62-68 for what value to give the header if it's chosen for inclusion in the profile. The way this works is every time a profile is generated, it will choose between 0 and 2 optional headers and add them into the profile. If the header is to be included in every profile, then define its value anywhere else in the randomizer function.
One other area to change is line 78. Since this is a HTTP GET request, we can add HTTP GET URL parameters. As is, these parameters are randomly generated from a list in a /data file and then defined as either true or false in line 78. You could make this value to anything you want. Just don't make it too long.
Description: Builds configurations for the http-get server section of the profile. This is where beacon tasking information is sent.
Customizable Options:
The main customization for this section is similar to the getclient.py header customizations. C2Concealer already adds the headers 'Status','Connection','Content_Type', and 'Server'. Consider adding another header or two. Follow steps 1-3 from the getclient.py section for adding a header (just note that there are no optional headers, so unless you add that logic into the randomizer function, you'll be adding the new headers in for all http-get server responses.)
Description: A selection of global malleable profile options.
Customizable Options:
All of these global options could be changed rather easily.
Description: Global HTTP communications options.
Customizable Options: Nothing to do here.
Description: Settings for when the beacon POSTS data in an HTTP POST request to the team server.
Customizable Options:
Follow the same guidance for getclient.py regarding adding new headers.
Additionally, beacon session id information is sent in a header, so that the teamserver knows which beacon session to attribute the POSTed information. As C2Concealer is currently written, the beacon session ID information is always sent in a cookie called __session__id. This means every HTTP POST request from a beacon to the team server, there's a header that looks like this:
Cookie: __session__id=beacon_session_id_value
It'd be better to make the cookie name associated with the beacon session id a dynamic value, so that it's different on every engagement. Also, there's no restriction that says it has to be in a cookie; consider adding this session ID to a different header. We recommend using a legitimate, often-seen header for this, like If-Match for example.
Description: Settings for post-exploitation jobs.
Customizable Options: There's really nothing to change here, unless you want to provide a value of "false" for the obfuscate, smart-inject or amsi-disable settings. By default those three values are "true".
Alright, that will do it for this post. Stay tuned for Part III, which will finalize the settings that can be changed in the /components subfolder within C2Concealer.
If you have any feedback, we'd love to hear it! As always, you can reach us via Twitter, LinkedIn, and through our website.