Self-hosted service
RedSiege/C2concealer avatar
RedSiege/C2concealer

C2concealer: randomising Cobalt Strike malleable profiles for red team use

C2concealer is a command line tool that generates randomized C2 malleable profiles for use in Cobalt Strike.

1,127 stars171 forksPythonGPL-3.0

At a glance

What is it?
C2concealer is a GPL-3.0 licensed command line tool from RedSiege that generates randomised Cobalt Strike malleable C2 profiles, one Python module per profile block, with the output linted by c2lint and a data directory the operator is expected to rewrite. The README is unusually candid that vendors can write signatures for what it produces and that several profile options are left out on purpose.
Who is it for?
Adopt C2concealer if you run authorised red team engagements with Cobalt Strike and need a starting malleable profile per target rather than a copy of the same one across every campaign, since the tool exists to randomise the indicators that make traffic recognisable.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The job: a different profile for every engagement

C2concealer is a command line tool that generates randomized C2 malleable profiles for use in Cobalt Strike.

The problem it solves is a specific operational one on the red team side. A malleable C2 profile is a configuration language that describes how the beacon's HTTP traffic looks, and every operator who has used one has the same habit of starting from a copy. Two engagements with the same profile produce the same indicators, and an indicator is exactly what a defender writes a detection for. A tool that randomises the values turns a static artefact into a per-engagement one.

That framing matters for reading the rest of the documentation, because the README is unusually direct about the limits of what the tool does. It states that this is an open source tool and that vendors can easily write signatures for the profiles it generates, and it insists that you dig into the data used to populate the profiles. The project's own summary of its value is that the skeleton code to automatically generate the profiles has been built, and that it is up to you to think through what values make sense for each attribute for your campaigns and update the data sources.

That is an unusual amount of candour for a tool in this category, and it is the most useful sentence in the README for an evaluator. C2concealer is a generator of starting points with a linter, not a finished evasive configuration, and the difference determines how much work follows.

The intended user is a red team operator with a Cobalt Strike licence and the legal authority to run a campaign against a client who has asked for it. The primary language is Python, the licence is GPL-3.0 with the file at the repository root, the default branch is `main`, the last push was on 2026-04-13, and the repository has no GitHub releases, so the tool is used from a clone.

How the generation pipeline is put together

The How it works section describes the design in enough detail to reconstruct it, which is unusual and useful.

The first step was research. The authors pored over the Cobalt Strike documentation and defined ranges of values that would make sense for each profile attribute. Sometimes the data is as simple as a random integer within some range and other times they need to pick a random value from a Python dictionary. Either way, the tool creation started with defining the data that would make a valid profile.

The second step is the structure. Each malleable profile section, or block, is a separate `.py` file containing the logic to draw random appropriate values for each attribute and then output a formatted string for that profile block. The blocks are then concatenated, a few quick consistency checks are run, and the profile is passed through the Cobalt Strike linter, c2lint. The result is a profile that should work for your engagements, and the README recommends always testing the profile, including process injection and spawning, prior to running a campaign.

The reading guide is given too, and it is a good one for a project of this shape. If you are looking into the code, start with `/C2concealer/__main__.py` and `/C2concealer/profile.py`, read the comments, and then look at the individual profile block generators in `/C2concealer/components`.

The repository layout matches. The `C2concealer/` package holds the entry point, the profile assembly, the `components/` directory of block generators, and the `data/` directory of the value sources. The top level also has `setup.py`, a `Dockerfile`, a `DOCKER.md` and an `install.sh`.

The `setup.py` is minimal: a console script entry point named `C2concealer` pointing at `C2concealer.__main__:main`, a `python_requires` of greater than 3.5.2, and version 1.0.

Installing from source with a virtual environment

Installation is a clone, a virtual environment and an editable install, and the README gives the commands exactly.

bash
git clone https://github.com/RedSiege/C2concealer.git
cd C2concealer
python3 -m venv .venv
source .venv/bin/activate
pip3 install -e .

The editable install is the right choice here, and it is not an arbitrary one. The whole point of the tool is that you edit the data files and the block generators, and an editable install means your edits are live without a reinstall.

A Docker route exists as well, with the instructions in the `DOCKER.md` file in the repository. The `Dockerfile` shows what it does, and the choice of base image is the interesting part. It is built `FROM kalilinux/kali-rolling`, the image copies the repository into a `C2concealer` directory, runs `./install.sh` to set up the virtual environment and the package, and the entrypoint is the tool inside that environment:

dockerfile
FROM kalilinux/kali-rolling
RUN mkdir C2concealer
COPY ./ /C2concealer
WORKDIR /C2concealer
RUN ./install.sh
ENTRYPOINT [ "/C2concealer/.venv/bin/C2concealer" ]

Two practical notes follow from the base image. The container has the Cobalt Strike tooling environment that the tool expects to find, or at least the shape of one, since c2lint is part of Cobalt Strike and the tool searches for it on the system. And a rolling Kali base is not a pinned build, so two runs of the image can differ, which matters if you are recording what produced a given profile.

The other dependency to notice is c2lint itself, and the README treats it as an assumed presence rather than something the tool installs. The console output shows the search: Searching for the c2lint tool on your system (part of Cobalt Strike). Might take 10-20 seconds.

The command line and the interactive SSL prompt

The tool takes two flags, and the README's usage block gives the whole interface.

bash
Usage:
	$ C2concealer --hostname google.com --variant 3

Flags:
	
	(optional)
	--hostname 
		The hostname used in HTTP client and server side settings. Default is None.
	--variant 
		An integer defining the number of HTTP client/server variants to generate. 
		Recommend between 1 and 5. Max 10.

The hostname is the value that ends up in the client and server side settings of the generated profile, and leaving it unset produces a profile with no hostname in those places, which is not usually what an engagement wants. The variant count is the number of HTTP client and server variants to generate, and the recommendation is between one and five with a hard maximum of ten.

The variants feature is worth understanding, because the README gives the reason. HTTP Variants allow you to select different IOCs for http traffic on different beacons, and the recommendation is a value of at least one. In other words, a single engagement can present a different set of indicators on each beacon, which is the same argument as the randomisation itself applied within a session rather than across sessions.

The run is interactive, and the first thing it asks is about TLS. The console output shows the sequence: the search for c2lint, then a prompt reading Choose an SSL option, with four choices.

bash
1. Self-signed SSL cert (just input a few details)
2. LetsEncrypt SSL cert (requies a temporary A record for the relevant domain to be pointed to this machine)
3. Existing keystore
4. No SSL

The choice matters for an engagement, and the README's advice is explicit. Always use an SSL certificate, preferably a cert from LetsEncrypt or similar. The LetsEncrypt option requires a temporary A record for the relevant domain pointed at the machine, which is an operational task the operator has to arrange before the tool can use it.

The container image, and the `install.sh` script it runs, exist to make this repeatable, and the flag defaults and the interactive prompt are the entire surface you interact with before the profile appears.

Customising the data is the actual work

The section on customising the tool opens with a warning that tells you how to read the rest of the documentation. This is an open source tool, vendors can easily write signatures for the profiles it generates, and the recommendation is to dig into the data used to populate them.

The data lives in `/C2concealer/data/`, and the README lists what each file controls.

`dns.py` customises the dns subdomains. `file_type_prepend.py` customises how http-get-server responses look, described as the C2 control instructions. `params.py` holds two dictionaries containing common parameter names and a generic wordlist. `post_ex.py` is the spawn_to process list, and it is marked as definitely change this one. `reg_headers.py` holds typical HTTP headers such as user-agent and server. `smb.py` holds SMB pipe names for use when comms go over SMB, and `ssh.py` holds SSH pipe and banner names when using SSH beacons. `stage.py` holds data for changing indicators related to the stager, and `urls.py` holds filetypes and URL path components used for building URIs all across the tool, and it is the second file marked definitely change this one.

`transform.py` is the one the README says you do not need to change, since it holds payload data transformations.

Beyond the data files, the README points at the block generators themselves, giving a concrete example: in `/C2concealer/components/stageblock.py` you can change the range from which the PE image size value is drawn from, near lines 73 to 74, and it asks you to look through all the different files in the components directory.

The last paragraph is the clearest statement of the project's intent. The way to view the tool is that the skeleton code to automatically generate the profiles has been built, and now it is up to you to think through what values make sense for each attribute for your campaigns and update the data sources.

That is a fair division of labour, and it is also a warning. A profile generated entirely from the shipped dictionaries and ranges is the profile most likely to match a signature, because everyone else's generated profile uses the same dictionary.

What the tool deliberately does not set

The further customising section is a set of tables listing the profile options that are missing or set statically, and it is the most technically informative part of the README.

The `http-config` block is missing `set block_useragents`, which blocks non-beacon user agent patterns, and `set allow_useragents`, an allowlist of user agent patterns in Cobalt Strike 4.3 and later.

The `stage` block is where the omissions are heaviest. `set allocator` is hardcoded to `VirtualAlloc` with `HeapAlloc` and `MapViewOfFile` randomization missing. The MZ header spoofing options `set magic_mz_x86` and `magic_mz_x64`, the PE marker spoofing `set magic_pe`, and the compiler metadata spoofing `set rich_header` are all absent, as are `set module_x86` and `module_x64` for module stomping, `set copy_pe_header`, `set data_store_size`, `set eaf_bypass`, and `set syscall_method` with its None, Direct and Indirect options from Cobalt Strike 4.8. The reflective DLL loader options `set rdll_loader` with PrependLoader and StompLoader, `rdll_use_syscalls`, `rdll_use_driploading` and `rdll_dripload_delay` are listed too. The `transform-obfuscate` block is absent entirely, with the `base64`, `lznt1`, `rc4` and `xor` options missing, and in the `transform-x86` and `transform-x64` blocks only `prepend` and `strrep` are used.

The `process-inject` block has the same shape. The allocator is hardcoded to `VirtualAllocEx` with `NtMapViewOfSection` randomization missing, `use_driploading` and `dripload_delay` are absent, only `prepend` is used in the transform blocks, and four execute techniques are not in the execute block: `NtQueueApcThread`, `NtQueueApcThread-s`, `ObfSetThreadContext` and `SetThreadContext`. Execute function spoofing with the `module!function+0x##` syntax is not supported.

The `post-ex` block is missing `set pipename` for post-ex jobs, and the `dns-beacon` block is missing `set ns_response`, which handles `drop`, `idle` and `zero` NS behaviour and defaults to `drop` if it is not set, and `set dns_stager_prepend` for prepending data to staged payloads.

The README also notes that the Malleable Command and Control documentation has many options the tool does not set, and it encourages consulting it and modifying the profile. In other words, the output is a subset of the language, and the gap between the subset and the full language is where the operator's work lives.

Where C2concealer is the wrong tool

There are three clear cases where this tool is the wrong answer.

The first is a Cobalt Strike that is not installed. The tool does not generate a beacon or a listener, it generates a profile file, and the c2lint step means the workflow assumes the Cobalt Strike installation is present. There is no path from this tool to a working command and control without that prerequisite, and the Docker image does not include Cobalt Strike itself.

The second is an engagement where the profile has to be indistinguishable from a real application. A randomised profile drawn from a shipped dictionary of parameter names, headers, URIs and file types is reproducible in structure, and the README says plainly that vendors can write signatures for the profiles it generates. A defender watching for a set of URIs built from a generic wordlist will see a family of profiles, not just one.

The third is the assumption that a generated profile is ready. The README's own wording is that the output is a profile that should work for your engagements, and it recommends testing the profile, including process injection and spawning, prior to running a campaign. The stage and process-inject blocks that a beacon depends on are the ones with the most static values, so the part of the profile that matters most operationally is the part the tool varies least.

There is also a governance point that belongs in the evaluation rather than the review, and it is the one that decides whether the tool should be in the repository at all. A profile generator is a tool for authorised testing, and its use is governed by the engagement rules, the client's written authorisation and the legal framework of the jurisdiction. The GPL-3.0 licence covers the code and says nothing about the activity, and the tool's existence in a public repository is not an endorsement of any particular use.

The alternative approach, for comparison, is to write the profile by hand for each target, which is what the README recommends in substance, and to use a generator only as a source of candidate values to adapt. The difference in approach is that the generator gives you randomised material quickly, and the hand written profile gives you a profile you can defend to a client as matching their environment, which for a mature red team operation is usually the more valuable artefact.

Editorial conclusion

Adopt C2concealer if you run authorised red team engagements with Cobalt Strike and need a starting malleable profile per target rather than a copy of the same one across every campaign, since the tool exists to randomise the indicators that make traffic recognisable. Do not adopt it if you have no Cobalt Strike installation, since the tool depends on the c2lint binary that ships with it, and do not treat its output as production ready, because the README says plainly that a profile that should work for your engagements still needs testing. Verify three things first: that c2lint is present, since the tool searches for it and the search alone takes ten to twenty seconds, that you have rewritten the data files the README marks as definitely change, especially `post_ex.py` and `urls.py`, and that your own detection engineering can see the output, because randomised data drawn from a shared dictionary is the part of the profile most likely to match an existing signature.

Frequently asked questions

What does C2concealer do?

It is a command line tool that generates randomized C2 malleable profiles for use in Cobalt Strike. Each profile block is a separate Python module that draws random values for its attributes, the blocks are concatenated, consistency checks run, and the result is passed through the c2lint linter that ships with Cobalt Strike.

How do I install C2concealer?

Clone the repository, create a virtual environment with `python3 -m venv .venv`, activate it with `source .venv/bin/activate`, and install with `pip3 install -e .`. A Docker route is documented in DOCKER.md, and the Dockerfile builds from kalilinux/kali-rolling and runs install.sh before the tool.

What do the --hostname and --variant flags do in C2concealer?

The hostname is used in the HTTP client and server side settings and defaults to None, and the variant is the number of HTTP client and server variants to generate, with a recommendation of between one and five and a maximum of ten. Different variants let different beacons present different indicators.

Which data files should I change before using a generated profile?

The README marks post_ex.py, the spawn_to process list, and urls.py, the filetypes and URL path components, as ones to definitely change, and lists dns.py, file_type_prepend.py, params.py, reg_headers.py, smb.py, ssh.py and stage.py as further customisation points. Block generators in the components directory can be adjusted too, for example the PE image size range in stageblock.py.

Is a C2concealer generated profile ready to use in a campaign?

Not on its own. The README says the output is a profile that should work for your engagements and recommends testing the profile, including process injection and spawning, before running a campaign. It also notes that the stage and process-inject blocks set several options statically and leave others out entirely, and that vendors can write signatures for the profiles the tool generates.

Official sources

  1. Issues
  2. License: GPL-3.0
  3. README
  4. RedSiege/C2concealer on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/redsiege-c2concealer.svg)](https://hysenlabs.com/projects/redsiege-c2concealer)

Was this useful?