Geo-Chat, schädliche Bots und Steganographie: bereicherndes Wissen über Telegramm


Was wissen Sie über Telegramme in Telegramm? Und können Sie Steganographie in VideoNote (im Volksmund - Round-Robin) unterscheiden? Wir analysieren genau die Aufgabe NeoQUEST-2020 , die unserer Unterstützung die meisten Fragen und Ausrufe gestellt hat! Spoiler: Ja, und hier wird es auch ein bisschen Krypto geben :)

In der NeoQUEST-2020- Legende finden wir auf Instagram einen Link zum Profil eines fahrenden Roboters. Nichts Außergewöhnliches, oder? Also haben wir uns auch dafür entschieden, aber wir müssen die Aufgabe noch lösen, also betrachten wir alle Bilder im Profil sorgfältig und suchen nach zumindest einigen Hinweisen. Eine kleine Meditation über ein wunderschönes Bild des Baikalsees, und wir kommen zu der Erkenntnis, dass der Hinweis im letzten Beitrag ist:


Dank des Bildes verstehen wir, dass wir Baikal (Shaman Rock) und Telegramm irgendwie verbinden müssen ("Du kannst meinem beitreten ..." - erinnert es nichts daran?). Zuerst haben wir beschlossen, den Teilnehmern keinen direkten Hinweis auf Geo-Chat zu geben (und genau das ist es!). Viele von ihnen haben die Aufgabe erfolgreich mit einem Emulator oder einem mobilen Gerät bewältigt, mit dem sie den Geostandort ändern können. Shamanim Wir setzen die Koordinaten (53.20074, 107.349426) (Sie können mit dem Auge) im Bereich des Shamanka-Felsens und bereiten uns auf das Schwierigste vor - das Warten. Das Telegramm arbeitet seltsamerweise mit der Geopositionierung und ruft relevante Kontakte und Chats für eine Stunde auf. Für unseren Fleiß und unsere Geduld werden wir voll belohnt - der gewünschte Chat wird im Bereich Kontakte angezeigt -> Personen in der Nähe finden -> Gruppen in der Nähe.


Voila, wir sind im Geschäft!


Der Bot stellt uns vor eine Aufgabe in Form einer Datei some.bytes mit nicht identifizierten Inhalten, in der wir die Zeilen "Decrypt me" und "Apocalypse Spares Nobody" lesen können.

Wir verstehen die erste Zeile ohne Probleme, aber was bedeutet die zweite? Hier teilten sich die Teilnehmer in zwei Lager auf: Einige schrieben uns per Post, weil sie in eine Sackgasse gerieten, während andere den Satz "Apocalypse Spares Nobody" sorgfältig betrachteten und dies erkannten ? Recht! Gutes altes ASN.1- Format ( hier haben wir bereits darüber geschrieben, wie man es analysiert).


Lass es uns richtig machen. Im Inneren befinden sich 2 Strukturen. In einem finden wir eine Reihe von Bytes mit der Bezeichnung "Decrypt me", von denen wir annehmen, dass dies Chiffretext ist. In der zweiten Struktur sehen wir zwei Zahlen. Es ist unwahrscheinlich, dass dies ein Schlüssel ist, den der Teilnehmer großzügig zusammen mit dem Chiffretext präsentiert, was höchstwahrscheinlich bedeutet. Wir haben es mit einem öffentlichen Schlüssel zu tun. Alle gesammelten Informationen führen uns zu der offensichtlichen Schlussfolgerung - warum nicht RSA ausprobieren ?

Wir haben also ein Modul und einen offenen Indikator, der übrigens ziemlich groß ist. Nach einer krampfhaften Untersuchung der RSA für einige Überlegungen kommen wir zu dem Schluss, dass der geschlossene Indikator klein ist, was bedeutet das? Bingo! Wir können definitiv Bösewichte spielen und Wieners Angriff nutzen .

Wir dachten , es durch selbst für diejenigen , die nicht wie Kryptographie tun - Sie eine fertige Version des Angriffs verwenden könnten zum Beispiel dieses .

Dann erhalten wir den Wert einer geschlossenen Figur d=40553818206320299896275948250950248823966726834704013657854904761789429403771und entschlüsseln den Chiffretext : key=nq2020faAeFeGUCBjYf7UDrH9FapFCdFPa4u;pass=passCxws3jzYhp0HD5Fy84.

Wir erhalten den Schlüssel "nq2020faAeFeGUCBjYf7UDrH9FapFCdFPa4u" für den ersten Teil des Jobs und das Passwort "passCxws3jzYhp0HD5Fy84", das Sie benötigen, um den Bot-Vertreter zu füttern. Es befindet sich unter den Chat-Teilnehmern unter dem Namen @neoquestbot.

Da wir nach Erhalt des ersten Schlüssels auf der positiven Welle sind, stellen wir nicht sofort fest, dass der Bot in der Kommunikation wählerisch ist, und sagen ständig, dass er den Gesprächspartner nicht sieht:


Aber der Bot empfängt gerne VideoNote-Rundennachrichten und beantwortet sie sogar ... in derselben Rundenform:


Es scheint, dass Video und Ton gleich sind, aber dies ist nur auf den ersten Blick. Was ist, wenn unser Bot uns geheime Zeichen gibt? Um dies herauszufinden, speichern wir das Originalvideo und vergleichen es mit der Antwort des Bots. Dafür und für die nächsten Schritte ist das FFmpeg- Paket großartig für uns . Mal sehen, was da ist:



Format aac -> flac, Frequenz 44100 Hz -> 98000 Hz. Sie fanden heraus, dass wir weiter mit Audio arbeiten.

Mit einer geschickten Bewegung der Hände ziehen wir es aus dem Video heraus:


Das gleiche kann mit unserer ursprünglichen Nachricht gemacht werden, damit wir sie später vergleichen können. Öffnen Sie aus Gründen der Übersichtlichkeit beide Spuren in Audacity .


Der Amplitudensprung in der Audioantwort des Bots fällt sofort auf (besonders seltsam, wenn wir überhaupt geschwiegen haben). Bei näherer Betrachtung stellen wir die klaren Grenzen der Intervalle während des Wechsels der "Wellenstille" fest:


Wir empfehlen, alle Dinge beiseite zu legen und ein wenig zu zählen. Wir analysieren nach Fragmenten:

0 - 0,005 - Stille
0,005 - 0,01 - Welle
0, 01 - 0,0225 - Stille
0,025 - 0,04 - Welle
0,04 - 0,045 - Stille

Das kleinste Intervall beträgt 0,005 und alle anderen Intervalle sind Vielfache von 0,005.
Wir nehmen eine Welle von 0,005 für 1 und Stille für 0 an. Wir erhalten nichts weiter als einen Binärcode!
Wir erinnern uns, dass sich die Frequenz geändert hat, und versuchen, das Spektrum-Diagramm (Analyse -> Spektrum-Diagramm) zu betrachten:


Wir sehen, dass das stärkste Signal bei einer Frequenz von ~ 44100 Hz liegt, was Ultraschall ist.
Dann sollten Sie nur mit hohen Frequenzen arbeiten.

Tatsächlich überlagert der Bot sein Signal dem Originalaudio im hörbaren Spektrum. Und diejenigen Teilnehmer, die im Originalvideo Ton hatten, bemerkten dies in Audacity.

Wir schneiden die hohen Frequenzen mit einem Hochpassfilter entweder in Audacity oder im selben ffmpeg ab:


Wir haben also eine 16-Bit-Mono-WAV-Datei. Es besteht aus einem Header, einem unkomprimierten Audiostream und Metadaten. Der Audiostream selbst ist in Frames unterteilt (und Frames können mehrere Samples speichern, dies ist jedoch eine völlig andere Geschichte), in unserem Fall jeweils 16 Bit (dies wird durch die Buchstaben pcm_s16 in den Screenshots angezeigt). Frames sind Bitfolgen, die die Amplitude der Welle gleichzeitig für einen oder mehrere Kanäle (in unserem Fall für einen) beschreiben. Die Abtastfrequenz des Audiostreams beträgt 98000 Bilder (dh 98000 Bilder pro Sekunde), 490 Bilder pro 0,005 Sekunden Intervall.

Daher arbeiten wir weiter nach einem einfachen Algorithmus: Wir lesen jeweils 490 Frames, bestimmen, ob es sich um eine Welle oder eine Stille handelt, und setzen abhängig davon das Bit auf 0 oder 1. Wir

verwenden Python und ein PaketWelle zum Parsen von WAV-Dateien.
Wenn beim Öffnen der Datei der Fehler "wave.Error: unknown format: 65534" auftritt, ersetzen Sie "wFormatTag" im Header von "FE FF" auf "01 00":

fh = open(input_file, "r+b")
fh.seek(20)
fh.write(b'\x01\x00')
fh.close()

Öffnen Sie also die Datei, verarbeiten Sie 490 Frames und berechnen Sie den Durchschnittswert:

file = wave.open(input_file,"r")
    for i in range (1, int(file.getnframes()/490)+1):
        frames = file.readframes(490)
        bit = 0
        sum = 0
        for k in range(0, 246):
            frame_bytes = frames[k*2:k*2+2]
            sum += int.from_bytes(frame_bytes, "big")
        if sum/490 > 16000:
            bit = 1
        bits.append(bit)

Es ist möglich, dass dort, wo Stille herrschen sollte (vergleiche mit dem Bild in Audacity), Rauschen verbleibt. Daher setzen wir den Schwellenwert (sei 16000), bei dessen Überschreitung wir das Signal als 1 betrachten.

Dann gruppieren wir die Bits in Bytes:

bytes = []    
for i in range (1, int(len(bits)/8)+1):
        b1 = bits[i*8-8]
        b2 = bits[i*8-7]
        b3 = bits[i*8-6]
        b4 = bits[i*8-5]
        b5 = bits[i*8-4]
        b6 = bits[i*8-3]
        b7 = bits[i*8-2]
        b8 = bits[i*8-1]
        byte = (b1 << 7) | (b2 << 6) | (b3 << 5) | (b4 << 4) | (b5 << 3) | (b6 << 2) | (b7 << 1) | b8
        bytes.append(byte.to_bytes(1, byteorder='big')) 

Wenn alles richtig gemacht wurde, ist das Ergebnis die Zeichenfolge "Givemethepassword". Da der Bot mithilfe der Steganografie in Kreisen kommuniziert, ist es logisch, ein Passwort dafür (und wir haben es zusammen mit dem Schlüssel als Ergebnis der Entschlüsselung erhalten) im selben Format einzugeben.

Erstellen Sie zunächst eine Audiospur mit einem Kennwort. Dazu verwenden wir die Daten, die während der Analyse der Nachricht vom Bot erhalten wurden: Abtastfrequenz 98000 Hz; die Dauer des Signals, das jedes Bit beschreibt, beträgt 5 ms; Die Signalfrequenz entspricht dem Bitwert „1“ - wie aus den Diagrammen hervorgeht, 44100 Hz.

Jetzt müssen wir Stille "erzeugen". Wir tun dies, indem wir neutralisieren:

sample_rate = 98000.0
def generate_silence(duration_milliseconds=5):
    fragment = []
    num_samples = duration_milliseconds * (sample_rate / 1000.0)
    for x in range(int(num_samples)): 
        fragment.append(0.0)
    return fragment

Um Schall zu erzeugen, verwenden wir eine Sinuswelle (Informationen finden Sie hier ):

def generate_sinewave(
        freq=41000.0, 
        duration_milliseconds=5, 
        volume=0.5):
    fragment = []
    amplitude = volume * 32767.0
    num_samples = duration_milliseconds * (sample_rate / 1000.0)
    for x in range(int(num_samples)):
        fragment.append(amplitude * math.sin(2 * math.pi * freq * ( x / sample_rate )))
    return fragment

Jetzt ist die Sache klein: Es bleibt, das Passwort in Bits und dann in Sound umzuwandeln.

Hinweis: Der Bot verwendet die ursprüngliche Eingangsvideospur, um seine Nachricht zu überlagern, wie bereits erwähnt. Daher müssen Sie nach dem Kennwort einige Null-Bytes hinzufügen, um den gesamten Schlüssel aus dem Bot herauszuschütteln und nicht nur seinen Anfang (die Schlüssellänge betrug 36 Byte).

Klangerzeugung
    audio = []
    f = open(input_file, 'rb')
    for character in f.read():
        a = character
        b8 = a & 0b00000001 
        b7 = (a & 0b00000010) >> 1 
        b6 = (a & 0b00000100) >> 2
        b5 = (a & 0b00001000) >> 3
        b4 = (a & 0b00010000) >> 4
        b3 = (a & 0b00100000) >> 5
        b2 = (a & 0b01000000) >> 6
        b1 = (a & 0b10000000) >> 7
        if b1 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b2 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b3 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b4 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b5 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b6 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b7 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b8 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()


Jetzt erstellen wir die fertige Wave-Datei:

wav_file=wave.open(file_name,"w")
    nchannels = 1
    sampwidth = 2
    nframes = len(audio)
    comptype = "NONE"
    compname = "not compressed"
    wav_file.setparams((nchannels, sampwidth, sample_rate, nframes, comptype, compname))
    for sample in audio:
        wav_file.writeframes(struct.pack('h', int(sample)))
    wav_file.close()

Wir speichern unseren Track zum Beispiel in pass.wav. Unterwegs prüfen wir mit unserem Stego-Decoder, ob das Passwort erkannt wird. Wenn alles in Ordnung ist, erhalten wir ein neues Video mit einem Passwort aus dem Originalvideo my_video.mp4, das die Audiospur ersetzt:


Jetzt müssen wir VideoNote daraus machen. Sie können versuchen, nach funktionierenden zu suchen (einige der Teilnehmer haben beispielsweise @TelescopyBot gefunden), oder Sie können Ihren Bot mit TelegramAPI schreiben.


Wie auch immer, freuen wir uns auf unseren Bot:


Wir bekommen eine neue Runde und Glückwünsche (wir hätten so einen Job gemacht!), Dekodierten das Audio gemäß dem bereits ausgearbeiteten Szenario und bekamen den Schlüssel: „nq2020SyOMK7SnnJP1sNlvbTs8zt35vUrrsD“

In der Tat wird Steganographie ohne Grund als einer der schwierigsten Bereiche der Cybersicherheit angesehen - versuchen Sie, all dies hier zu erraten! Aber die NeoQUEST-Teilnehmer zeigten bei dieser Aufgabe große Geschicklichkeit und Sinn für Humor. Deshalb sprechen wir unsere aufrichtige (von den Bot-Glückwünschen) aufrichtige Bewunderung an!

All Articles