Géo-chat, robots nuisibles et stéganographie: enrichir les connaissances sur Telegram


Que savez-vous des télégrammes en télégramme? Et pouvez-vous distinguer la stéganographie dans VideoNote (populairement - round-robin)? Nous analysons la tâche même NeoQUEST-2020 , qui a suscité le plus de questions et d'exclamations à notre support! Spoiler: oui, et il y aura aussi un peu de crypto :)

Dans la légende NeoQUEST-2020 , on retrouve un lien vers le profil d'un robot voyageur sur Instagram. Rien d'extraordinaire, non? Nous avons donc décidé de le faire aussi, mais nous devons encore résoudre le problème, nous examinons donc attentivement toutes les images du profil et recherchons au moins quelques indices. Une petite méditation sur une belle photo du lac Baïkal, et on en vient à réaliser que l'indice est dans le dernier post:


Grâce à l'image, nous comprenons que nous devons en quelque sorte connecter le Baïkal (Shaman Rock) et le télégramme ("U peut rejoindre mon ..." - cela ne me rappelle-t-il rien?). Au début, nous avons décidé de ne pas donner aux participants un indice direct de géo-chat (ce qui est exactement ça!), Et beaucoup d'entre eux ont réussi à faire face à la tâche en utilisant un émulateur ou un appareil mobile avec la possibilité de changer la géolocalisation. Shamanim Nous avons défini les coordonnées (53.20074, 107.349426) (vous pouvez à l'œil nu) dans la zone du rocher de Shamanka et préparons la chose la plus difficile - l'attente. Telegram fonctionne étrangement avec le géo-positionnement et récupère les contacts et les chats pertinents pendant une heure. Pour notre diligence et notre patience, nous sommes pleinement récompensés - le chat souhaité apparaît dans la section Contacts -> Trouver des personnes à proximité -> Groupes à proximité.


Voila, nous sommes en affaires!


Le bot nous rencontre une tâche sous la forme d'un fichier some.bytes avec un contenu non identifié, dans lequel nous pouvons lire les lignes "Decrypt me" et "Apocalypse Spares Nobody".

Nous comprenons la première ligne sans aucun problème, mais que signifie la seconde? .. Ici, les participants se sont divisés en deux camps: certains nous ont écrit par courrier, car ils se sont retrouvés dans une impasse, tandis que d'autres ont soigneusement examiné la phrase "Apocalypse Spares Nobody" et ont discerné cela ? Droite! Bon vieux format ASN.1 ( ici, nous avons déjà écrit sur la façon de l'analyser).


Faisons les choses correctement. A l'intérieur se trouvent 2 structures. Dans l'un, nous trouvons un ensemble d'octets marqué "Decrypt me", à partir duquel nous supposons qu'il s'agit d'un texte chiffré. Dans la deuxième structure, nous voyons deux nombres. Il est peu probable qu'il s'agisse d'une clé généreusement présentée par le participant avec un texte chiffré, ce qui signifie très probablement. Nous avons affaire à une clé publique. Toutes les informations recueillies nous conduisent à la conclusion évidente - pourquoi ne pas essayer RSA ?

Nous avons donc un module et un indicateur ouvert, qui, soit dit en passant, est assez grand. Après une étude convulsive du RSA pour une réflexion, nous concluons que l'indicateur fermé est petit, ce qui signifie quoi? Bingo! Nous pouvons certainement jouer les méchants et utiliser l'attaque de Wiener .

Nous l'avons pensé même pour ceux qui n'aiment pas la cryptographie - vous pouvez utiliser une version prête à l'emploi de l'attaque, par exemple, ceci .

Ensuite , nous obtenons la valeur d'une figure fermée d=40553818206320299896275948250950248823966726834704013657854904761789429403771et Décrypter cryptogramme: key=nq2020faAeFeGUCBjYf7UDrH9FapFCdFPa4u;pass=passCxws3jzYhp0HD5Fy84.

Nous obtenons la clé "nq2020faAeFeGUCBjYf7UDrH9FapFCdFPa4u" dans la première partie du travail et le mot de passe "passCxws3jzYhp0HD5Fy84", dont vous avez besoin pour nourrir le représentant du bot. Il peut être trouvé parmi les participants au chat sous le nom @neoquestbot.

Étant sur la vague du positif de la réception de la première clé, nous ne réalisons pas immédiatement que le bot est pointilleux en communication et dit tout le temps qu'il ne voit pas l'interlocuteur:


Mais le bot reçoit avec joie les messages ronds VideoNote et y répond même ... sous la même forme ronde:


Il semble que la vidéo et le son soient les mêmes, mais ce n'est qu'à première vue. Et si notre bot nous donne des signes secrets? Pour le savoir, nous allons enregistrer et comparer la vidéo originale avec la réponse du bot. Pour cela et pour les prochaines étapes, le package FFmpeg est idéal pour nous . Voyons donc ce qu'il y a:



Format aac -> flac, fréquence 44100 Hz -> 98000 Hz. Ils ont découvert, nous continuons à travailler davantage avec l'audio.

Avec un mouvement habile des mains, nous le retirons de la vidéo:


La même chose peut être faite avec notre message d'origine, afin que nous puissions les comparer plus tard. Pour plus de clarté, ouvrez les deux pistes dans Audacity .


Le saut d'amplitude dans la réponse audio du bot attire immédiatement votre attention (particulièrement étrange si nous étions silencieux). En y regardant de plus près, on note les limites claires des intervalles lors de l'alternance du "silence d'onde":


Nous suggérons de mettre toutes choses de côté et de compter un peu. Nous analysons par fragments:

0 - 0,005 - silence
0,005 - 0,01 - vague
0, 01 - 0,0225 - silence
0,025 - 0,04 - vague
0,04 - 0,045 - silence

Le plus petit intervalle est 0,005, et tous les autres les intervalles sont des multiples de 0,005.
On prend la présence d'une onde de 0,005 pour 1, et du silence pour 0. On n'obtient rien de plus qu'un code binaire!
Nous rappelons que la fréquence a changé et essayons de regarder le graphique du spectre (Analyse -> Graphique du spectre):


Nous voyons que le signal le plus puissant est à une fréquence de ~ 44100 Hz, ce qui est des ultrasons.
Donc, vous ne devriez travailler qu'avec des fréquences élevées.

En fait, le bot superpose son signal sur l'audio d'origine dans le spectre audible. Et les participants qui avaient du son dans la vidéo originale l'ont remarqué dans Audacity.

Nous coupons les hautes fréquences avec un filtre passe-haut soit dans Audacity, soit dans le même ffmpeg:


Nous avons donc un fichier wav mono 16 bits. Il se compose d'un en-tête, d'un flux audio non compressé et de métadonnées. Le flux audio lui-même est divisé en images (et les images peuvent stocker plusieurs échantillons, mais c'est une histoire complètement différente), dans notre cas, 16 bits chacune (cela est indiqué par les lettres pcm_s16 dans les captures d'écran). Les trames sont des séquences de bits qui décrivent l'amplitude de l'onde à la fois pour un ou plusieurs canaux (dans notre cas, pour un). La fréquence d'échantillonnage du flux audio est de 98 000 images (c'est-à-dire 98 000 images par seconde), 490 images par intervalle de 0,005 seconde.

Par conséquent, nous travaillons plus loin selon un algorithme simple: nous lisons 490 trames, déterminons s'il s'agit d'une onde ou d'un silence, et, en fonction de cela, mettons le bit à 0 ou 1. Nous

utiliserons python et un paquetwave pour analyser les fichiers wav.
Si l'erreur «wave.Error: unknown format: 65534» se produit lors de l'ouverture du fichier, remplacez «wFormatTag» dans l'en-tête de «FE FF» à «01 00»:

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

Alors, ouvrez le fichier, traitez 490 images et calculez la valeur moyenne:

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)

Il est possible que là où il devrait y avoir du silence (comparer avec l'image dans Audacity), le bruit peut rester. Par conséquent, nous fixons le seuil (que ce soit 16000), en dépassant ce que nous considérons comme étant le signal 1.

Ensuite, nous groupons les bits en octets:

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')) 

Si tout est fait correctement, le résultat est la chaîne «Givemethepassword». Étant donné que le bot communique en cercles à l'aide de la stéganographie, il sera logique de lui glisser un mot de passe (et nous l'avons reçu avec la clé à la suite du déchiffrement) dans le même format.

Pour commencer, composez une piste audio avec un mot de passe. Pour ce faire, nous utilisons les données obtenues lors de l'analyse du message du bot: fréquence d'échantillonnage 98000 Hz; la durée du signal décrivant chaque bit est de 5 ms; la fréquence du signal correspondant à la valeur de bit "1" - comme nous l'avons vu sur les graphiques, 44100 Hz.

Nous devons maintenant "générer" le silence. Nous le faisons en neutralisant:

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

Pour générer du son, nous utiliserons une onde sinusoïdale (les informations peuvent être lues ici ):

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

Maintenant, la chose est petite: il reste à convertir le mot de passe en bits, puis en son.

Remarque: Le bot utilise la piste vidéo d'entrée d'origine pour superposer son message, comme mentionné précédemment. Par conséquent, vous devez ajouter quelques zéro octets après le mot de passe afin d'effacer la clé entière du bot, et pas seulement son début (la longueur de la clé était de 36 octets).

Génération sonore
    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()


Nous allons maintenant créer le fichier wave fini:

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()

Nous sauvegardons notre trace, par exemple, dans pass.wav. En cours de route, nous vérifions avec notre décodeur stego si le mot de passe est reconnu. Si tout va bien, nous obtenons une nouvelle vidéo avec un mot de passe de la vidéo d'origine my_video.mp4, en remplaçant la piste audio:


Maintenant, nous devons en faire VideoNote. Vous pouvez essayer de chercher ceux qui fonctionnent (certains des participants, par exemple, ont trouvé @TelescopyBot), ou vous pouvez écrire votre bot en utilisant TelegramAPI.


Quoi qu'il en soit, transmettez à notre robot:


Nous obtenons un nouveau tour et félicitations (nous aurions fait un tel travail!), Décodé l'audio selon le scénario déjà élaboré et obtenu la clé: "nq2020SyOMK7SnnJP1sNlvbTs8zt35vUrrsD"

En effet, la stéganographie est considérée comme l'un des domaines les plus difficiles de la cybersécurité pour aucune raison - essayez de deviner tous ces domaines! Mais les participants de NeoQUEST ont montré une grande dextérité et un sens de l'humour durant cette tâche, nous leur adressons donc nos sincères félicitations du bot)!

All Articles