Handbook IV – RedTeam

What is Red Teaming?

Red Team Infrastructure

Red Team Do’s and Dont’s

Red Team Tradecraft and TTP Guidance | Red Team Development and Operations

DoDon’t
Log all significant eventsUse untested tools on a target system
Consult with peersUse unencrypted channels for C2
Understand tools and technology usedAttempt to exploit or attack unencrypted websites
Perform situational awarenessExecute from non-executable locations
Minimize callback (C2) volumeDownload restricted datasets
Use binaries for initial access

Resources

Atomic Red Teaming

Atomic Red Team

This site is designed to help you explore and navigate the Atomic Red Team library of tests, as they are mapped to the MITRE ATT&CK® framework and the platforms they support.

Expired Domains

Using expired domains in a red team engagement can help bypass domain reputation filter and avoid detection. Expired domains are less likely to be flagged as suspicious. A newly created domain will probably be picked up by blue team as malicous.

Expired Domains | Daily Updated Domain Lists for 579 TLDs

C2 Infrastructure

Below is a high-level diagram of a secure C2 infrastructure.

Made by https://twitter.com/malcomvetter

Traffic between Victim network and Redirector should be encrypted HTTPS traffic only. On the attack network (Simulated Adversary) only SSH tunnel should be allowed to redirector. No inbound access from the internet to attack network should be allowed. HTTPS from attacker should be allowed to test web server.

Apache as Redirector

Install apache as per your requirements.

sudo apt install apache2
sudo a2enmod ssl rewrite proxy proxy_http

# Use default SSL Config
sudo ln -s ../sites-available/default-ssl.conf ../sites-enabled/default-ssl.conf
sudo systemctl restart apache2

Generate an SSL certificate using a legitimate Certificate Authority(CA) like Let’s Encrypt, VeriSign, GlobalSign or DigiCert, etc.
Edit the config file to include the generated SSL certificate.

# Replace with generated cert
SSLCertificateFile     /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile  /etc/ssl/private/ssl-cert-snakeoil.key


root# sudo systemctl restart apache2

Enabling Apache redirector

Enable .htaccess to configure apache to proxy traffic to Cobalt Strike.
To enable .htaccess edit /etc/apache2/sites-enabled/default-ssl.conf. Underneath <VirtualHost> add a new <Directory>.

You can also add conditions and rewrite rules directly in the configuration. Rules in configuration apply to entire server or specific virtual host, while rules applied in .htaccess only apply to the directory where the file is located and the subdirectories.

<Directory /var/www/html/>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Require all granted
</Directory>

Add SSLProxyEngine on underneath SSLEngine on. Restart apache after.
In /var/www/html create a new .htaccess file. Add these lines

RewriteEngine on
RewriteRule ^.*$ https://localhost:8443%{REQUEST_URI} [P]

The [P] stands for proxy.
The RewriteRule will proxy everything to our TeamServer. For example curl https:// domain.com/test. From an opsec point of view this is not ideal and we will have to add some conditions in order to redirect unwanted traffic elsewhere. For that we will use RewriteCond.

Rewrite Conditions RewriteCond can be combined with RewriteRule. The syntax is TestString Condition [Flags]. htaccess have multiple flags that can be used.

  • [L] – Last.  Tells mod_rewrite to stop processing further rules.
  • [NE] – No Escape.  Don’t encode special characters (e.g. & and ?) to their hex values.
  • [R] – Redirect.  Send a redirect code in response.
  • [S] – Skip.  Skip the next N number of rules.
  • [T] – Type.  Sets the MIME type of the response.
  • [PT] – Pass Through. Pass rewritten URL to next handler.
  • [NC] – No Case. Not casesensitive.

mod_rewrite documentation have a full details. https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html

Apache Rules

Taken from here.

The below rules assumes the following;

  • using the Cobalt Strike webbug profile
  • data is sent via a cookie instead of in URL
  • if a file exists on the redirector (in /var/www/html) it should always be returned
  • abc, and d are files hosted on Cobalt Strike that should be accessible
  • /var/www/html/diversion is a file on the redirector that displays fake content for abc, and d if they are requested using wget or curl https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html

In order to get get exact parameter of the C2 profile, we can use Cobalt Strikes c2lint. And from there we can craft our rules based on these parameter. This way, only “legitimate” C2 traffic will reach our Teamserver from the redirector.

RewriteEngine on

# check beacon GET
RewriteCond %{REQUEST_METHOD} GET [NC]
RewriteCond %{HTTP_COOKIE} SESSIONID
RewriteCond %{REQUEST_URI} __utm.gif
RewriteCond %{QUERY_STRING} utmac=UA-2202604-2&utmcn=1&utmcs=ISO-8859-1&utmsr=1280x1024&utmsc=32-bit&utmul=en-US
RewriteRule ^.*$ https://localhost:8443%{REQUEST_URI} [P,L]

# check beacon POST
RewriteCond %{REQUEST_METHOD} POST [NC]
RewriteCond %{REQUEST_URI} ___utm.gif
RewriteCond %{QUERY_STRING} utmac=UA-220(.*)-2&utmcn=1&utmcs=ISO-8859-1&utmsr=1280x1024&utmsc=32-bit&utmul=en-US
RewriteRule ^.*$ https://localhost:8443%{REQUEST_URI} [P,L]

# if a,b,c,d and using wget or curl, change file to diversion
RewriteCond %{HTTP_USER_AGENT} curl|wget [NC]
RewriteRule ^a|b|c|d$ diversion [PT]

# if file exists on redirector, show that file
RewriteCond /var/www/html/%{REQUEST_URI} -f
RewriteRule ^.*$ %{REQUEST_FILENAME} [L]

# if a,b,c,d and NOT using wget or curl, redirect to CS web server
RewriteCond %{REQUEST_METHOD} GET [NC]
RewriteCond %{REQUEST_URI} a|b|c|d
RewriteRule ^.*$ https://localhost:8443%{REQUEST_URI} [P,L]
  1. The first rule checks if a GET request is made with a specific cookie and URL pattern (__utm.gif). If these conditions are met, the request is forwarded to https://localhost:8443 with the same URI.
  2. The second rule does something similar for POST requests, but it checks for a different URL pattern (___utm.gif). If matched, the request is also forwarded to https://localhost:8443.
  3. The third rule checks if the User-Agent is curl or wget and if the request URI matches a, b, c, or d. If so, it changes the URI to diversion and passes it to other rules.
  4. The fourth rule checks if the requested file exists on the server. If it does, the server serves that file directly.
  5. The fifth rule handles GET requests for a, b, c, or d when the User-Agent is not curl or wget. These requests are redirected to https://localhost:8443.

NGINX as Redirector

Nginx can also be used as a redirector, same as apache.

Below is a configuration file similar to one used above in apache.

server {
    listen 443 ssl;
    server_name yourdomain.com;

    ssl_certificate     /etc/ssl/certs/your-certificate.pem;
    ssl_certificate_key /etc/ssl/private/your-private-key.pem;

    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    # Check for beacon GET request with specific cookie and query string
    location / {
        if ($request_method = GET) {
            if ($http_cookie ~* "SESSIONID") {
                if ($request_uri ~* "__utm.gif") {
                    if ($query_string ~* "utmac=UA-2202604-2&utmcn=1") {
                        proxy_pass https://localhost:8443;
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        proxy_set_header X-Forwarded-Proto $scheme;
                        break;
                    }
                }
            }
        }

        # Check for beacon POST request with specific URI and query string
        if ($request_method = POST) {
            if ($request_uri ~* "___utm.gif") {
                if ($query_string ~* "utmac=UA-220(.*)-2&utmcn=1") {
                    proxy_pass https://localhost:8443;
                    proxy_set_header Host $host;
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_set_header X-Forwarded-Proto $scheme;
                    break;
                }
            }
        }

        # If wget or curl is used and request URI is a, b, c, or d, serve diversion
        if ($http_user_agent ~* "curl|wget") {
            if ($request_uri ~* "(a|b|c|d)") {
                rewrite ^ /diversion break;
            }
        }

        # Serve file if it exists on the redirector
        try_files $uri $uri/ =404;

        # Redirect a, b, c, d to C2 server if User-Agent is not curl or wget
        if ($request_method = GET) {
            if ($request_uri ~* "(a|b|c|d)") {
                proxy_pass https://localhost:8443;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                break;
            }
        }
    }
}

Generate an SSL certificate using the above mentioned legitimate Certificate Authority(CA) and change line 5 and 6 to match the generated certificate. Restart nginx after systemctl restart nginx and verify that the config is correct using nginx -t.

Socat as redirector

You can use socat to spin up a redirector using one command.

sudo socat TCP4-LISTEN:443,fork TCP:localhost:8443

This works fine until you introduce proxies, SSL certificates and other more advanced techniques.

Dealing with proxies

Many organizations have proxies that will filter out traffic they deem malicous or undesirable. Organizations can also use DNS to block traffic from malicous websites, such as Cisco Umbrella.
In order to circumvent these defences we need a valid SSL certificate and a C2 domain that is categorized.

In order to get a domain categorized we need to host content on the site that fits the categorization we are going for. One example is how Stuxnet used two domains used as soccer fans site to phone home.

Setting up SSH Tunnel

Establish a reverse SSH tunnel from TeamServer to Redirector.

ssh -N -R 8443:localhost:443 attacker@10.10.0.100
  • -N stops the session from dropping in to a shell.
  • -R is remote-port:host:host-port.  This will bind port 8443 on the target (Redirector 1) and any traffic hitting that port will be redirected to 127.0.0.1:443 on the team server.

Other tools to use is, but not limited to;

  • Sshuttle sshuttle -r username@address subnet
  • Autossh autossh -M 0 -N -f -R 8443:localhost:443 attacker@10.10.0.100
    • -M 0: Disables the monitoring port
    • -N: Prevents executing remote commands; just sets up the tunnel.
    • -f: tells autossh to run in the background.
    • -R 8443:localhost:443: Specifies the remote port forwarding, binding port 8443 on the remote server (Redirector 1) to localhost:443 on the local machine (TeamServer).

Verify that the tunnel is setup correct.

sudo ss -ltnp

You can also curl from Redirector to verify that you hit Cobalt Strike.

curl -v https://localhost:8443/r1

Beacon Certificate

Cobalt strikes beacon will use its own self-signed certificate by default. Since our Teamserver is not be accessible from the internet, we cannot use a public domain. We will generate our own self-signed certificate instead.

openssl req -x509 -nodes -newkey rsa:2048 -keyout localhost.key -out localhost.crt -sha256 -days 365 -subj '/CN=localhost'

openssl pkcs12 -inkey localhost.key -in localhost.crt -export -out localhost.pfx

keytool -importkeystore -srckeystore localhost.pfx -srcstoretype pkcs12 -destkeystore localhost.store

Place the .store file in the Cobalt Strike directory and in the malleable profile add these lines.

https-certificate {
     set keystore "localhost.store";
     set password "pass123";
}

Now we can launch the teamserver with the updated profile.

 sudo ./teamserver 10.10.5.50 Passw0rd! c2-profiles/normal/webbug.profile

To verify the certificate on the listener we can use curl.

curl -v -k https://10.10.5.50

Beacon Staging

Avoid using staged payloads as this is considred bad opsec. The reason for this is that it increases network exposure and web logs can reveal the stager. Since staging is unauthenticated it can be made from anyone.

Let’s try this out ourself. First we generate a listener and a stager payload and detonate it on a victim machine. We are using wireshark to look at the traffic between the attacker and the victim.

Those 4 random characters determins if the payload is x86 or x64 bit but doing some mathematical equations.

  • Converts each character into its integer representation.
  • Calculates the sum of those integers.
  • Divides that sum by 256 and checks the remainder.
    • If the remainder equals 92, it’s an x86 request.
    • If the remainder equals 93, it’s an x64 request.

We can now request the shellcode using curl.

And now we can use a Beacon Parser like the one made by Sentinel One and extract all the information about the beacon.

python3 parse_beacon_config.py /mnt/c/Payloads/shellcode.bin

BeaconType                       - HTTPS
Port                             - 443
SleepTime                        - 60000
MaxGetSize                       - 1048616
Jitter                           - 0
MaxDNS                           - Not Found
PublicKey_MD5                    - a3b7d2c9e5f4a1bc8d6e29f3c1b4a7e2
C2Server                         - 10.2.99.1,/__utm.gif
UserAgent                        - Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; WOW64; Trident/5.0; msn OptimizedIE8;ENUS)
HttpPostUri                      - /___utm.gif

Payload Guardrails

Guardrails prevents payload to fully execute outside of predefined scope. If an AV sandbox try to execute your payload, it will fail and thus prevent it from analyzing the behaviour of your payload. This has also legal concerns as executing payload outside of agreed upon scope is a very big no-no.

The available options are:

  • IP Address
    • Internal IP address(es)
    • Supports wildcards on the rightmost segments, e.g. 10.10.120.* or 10.10.*.*
  • User Name
    • Case sensitive username
    • Supports wildcards on the left or right, e.g. svc_* or *adm
  • Server Name
    • Case sensitive computer name
    • Supports wildcard on the left or right
  • Domain
    • Case sensitive domain name
    • Supports wildcard on the left or right, e.g. acme.* or *.acme.corp

External C2

External C2 allows third-party programs to act as communication channel between Cobalt Strike and its beacon.
More information can be read here.

Basically what this means that the operator can use whatever protocol to communicate between the teamserver and the beacon as long as egress traffic is allowed. That can be for example using Discord, Office 365, Google drive, Dropbox. The only condition is that the two components can transfer data between each other.

Using External C2 can be stealthy as you design your own communication channels.

This image from @riccymaster illustrate it very nicely.

Exploring Attacker Command & Control: C3 (Custom Command and Control) and Cobalt Strike External C2 | by Ricci Tua Gultom | Medium

Redirectors can be layerd on top for added OPSEC. As a matter of fact, it is recommended!

To use External C2, we first need to setup a listener.

This will bind it to port 2222.

On this website, Cobalt Strike team have made a External C2 example using named pipe. The code provided creates a named pipe that handles communication.

After running the External C2 payload on a victim machine you can see that the payload sends the configuration options such as the architecture, the pipename that will be used and the block size.

And we can interact with the beacon using the CS client.