الدردشة الجغرافية ، الروبوتات الضارة وإخفاء المعلومات: إثراء المعرفة حول Telegram


ماذا تعرف عن البرقيات في برقية؟ وهل يمكنك التمييز بين إخفاء المعلومات في VideoNote (شعبي - مستدير - روبن)؟ نحن نحلل المهمة NeoQUEST-2020 ، التي تسببت في معظم الأسئلة والتعجب لدعمنا ! المفسد: نعم ، وسيكون هناك أيضًا القليل من التشفير هنا :)

في أسطورة NeoQUEST-2020 ، نجد رابطًا إلى الملف الشخصي لروبوت متنقل على Instagram. لا شيء خارج عن المألوف ، أليس كذلك؟ لذلك قررنا ذلك أيضًا ، ولكننا ما زلنا بحاجة إلى حل المشكلة ، لذلك نحن نفكر بعناية في جميع الصور في الملف الشخصي ونبحث عن بعض القرائن على الأقل. القليل من التأمل في صورة جميلة لبحيرة بايكال ، ونصل إلى إدراك أن الدليل موجود في آخر مشاركة:


بفضل الصورة ، نتفهم أننا بحاجة إلى ربط بايكال (شامان روك) وتليجرام بطريقة ما ("يمكنك الانضمام إلى ..." - ألا تذكر أي شيء؟). في البداية ، قررنا عدم إعطاء المشاركين تلميحًا مباشرًا للدردشة الجغرافية (وهذا كل شيء!) ، وقد تعامل العديد منهم بنجاح مع المهمة باستخدام محاكي أو جهاز محمول مع القدرة على تغيير الموقع الجغرافي. Shamanim قمنا بتعيين الإحداثيات (53.20074 ، 107.349426) (يمكنك بالعين) في منطقة صخرة شامانكا والاستعداد لأصعب شيء - الانتظار. يعمل Telegram بشكل غريب مع تحديد الموقع الجغرافي ويسحب جهات الاتصال والدردشات ذات الصلة لمدة ساعة. بالنسبة لجهدنا وصبرنا ، فإننا نكافأ تمامًا - تظهر الدردشة المطلوبة في قسم جهات الاتصال -> البحث عن الأشخاص القريبين -> المجموعات القريبة.


فويلا ، نحن في العمل!


يقابلنا البوت بمهمة في شكل ملف some.bytes بمحتويات غير محددة ، حيث يمكننا قراءة السطور "فك تشفير لي" و "نهاية العالم قطع الغيار لا أحد".

نحن نفهم السطر الأول دون أي مشاكل ، ولكن ماذا يعني الثاني؟ .. هنا ، انقسم المشاركون إلى معسكرين: كتب إلينا البعض عن طريق البريد ، لأنهم وصلوا إلى طريق مسدود ، بينما نظر البعض الآخر بعناية في عبارة "نهاية العالم قطع الغيار لا أحد" ووجدوا أن ؟؟؟ حق! تنسيق ASN.1 قديم جيد ( كتبنا هنا بالفعل حول كيفية تحليله).


دعنا نحصل على حق. في الداخل هيكلين. في واحدة ، نجد مجموعة من وحدات البايت المميزة بعلامة "فك تشفير لي" ، والتي نفترض منها أن هذا نص مشفر. في الهيكل الثاني ، نرى رقمين. من غير المحتمل أن يكون هذا هو المفتاح الذي قدمه المشاركون بسخاء مع نص مشفر ، وهو ما يعني على الأرجح. نحن نتعامل مع مفتاح عام. جميع المعلومات التي تم جمعها تقودنا إلى استنتاج واضح - لماذا لا تجرب RSA ؟

لذلك ، لدينا وحدة نمطية ومؤشر مفتوح ، بالمناسبة ، كبير جدًا. بعد دراسة متشنجة لـ RSA لبعض الفكر ، نستنتج أن المؤشر المغلق صغير ، مما يعني ماذا؟ البنغو! يمكننا بالتأكيد لعب الأشرار واستخدام هجوم Wiener .

لقد فكرنا في ذلك حتى بالنسبة لأولئك الذين لا يحبون التشفير - يمكنك استخدام نسخة جاهزة من الهجوم ، على سبيل المثال ، هذا .

ثم نحصل على قيمة الرقم مغلق d=40553818206320299896275948250950248823966726834704013657854904761789429403771وفك تشفير النص المشفر: key=nq2020faAeFeGUCBjYf7UDrH9FapFCdFPa4u;pass=passCxws3jzYhp0HD5Fy84.

نحصل على المفتاح "nq2020faAeFeGUCBjYf7UDrH9FapFCdFPa4u" للجزء الأول من المهمة وكلمة المرور "passCxws3jzYhp0HD5Fy84" ، التي تحتاجها لإطعام ممثل برنامج الروبوت. يمكن العثور عليه بين المشاركين في الدردشة تحت اسمneoquestbot.

كوننا على الموجة الإيجابية من تلقي المفتاح الأول ، فإننا لا ندرك على الفور أن البوت صعب الإرضاء في التواصل ويقول دائمًا أنه لا يرى المحاور:


لكن البوت يتلقى بسعادة رسائل جولة VideoNote وحتى يجيبها ... في نفس شكل الجولة:


يبدو أن الفيديو والصوت متشابهان ، ولكن هذا فقط للوهلة الأولى. ماذا لو أعطانا البوت بعض العلامات السرية؟ لمعرفة ذلك ، سنقوم بحفظ ومقارنة الفيديو الأصلي مع استجابة الروبوت. لهذا وللخطوات التالية ، فإن حزمة FFmpeg رائعة بالنسبة لنا . لذا ، دعنا نرى ما هو موجود:



تنسيق aac -> flac ، تردد 44100 هرتز -> 98000 هرتز. اكتشفوا أننا نواصل العمل مع الصوت.

بحركة بارعة للأيدي ، نخرجه من الفيديو:


يمكن فعل الشيء نفسه مع رسالتنا الأصلية ، حتى نتمكن من مقارنتها لاحقًا. للتوضيح ، افتح كلا المسارين في Audacity .


القفزة في السعة في الاستجابة الصوتية للبوت تجذب انتباهك على الفور (غريب بشكل خاص إذا كنا صامتين على الإطلاق). عند الفحص الدقيق ، نلاحظ الحدود الواضحة للفترات أثناء تناوب "صمت الموجة":


نقترح وضع كل الأشياء جانبا والعد قليلا. نقوم بتحليل الشظايا:

0 - 0.005 - الصمت
0.005 - 0.01 - الموجة
0 ، 01 - 0.0225 - الصمت
0.025 - 0.04 - الموجة
0.04 - 0.045 - الصمت

أصغر فاصل 0.005 ، وجميع الآخرين الفترات هي مضاعفات 0.005.
نأخذ وجود موجة 0.005 لـ 1 ، والصمت عند 0. لا نحصل على أكثر من رمز ثنائي!
نذكر أن التردد قد تغير ، ونحاول إلقاء نظرة على الرسم البياني للطيف (التحليل -> الرسم البياني للطيف):


نرى أن أقوى إشارة على تردد ~ 44100 هرتز ، وهو الموجات فوق الصوتية.
لذا ، يجب أن تعمل فقط بترددات عالية.

في الواقع ، يضاعف البوت إشارته على الصوت الأصلي في الطيف المسموع. وقد لاحظ هؤلاء المشاركون الذين ظهروا في الفيديو الأصلي ذلك في الجرأة.

نقطع الترددات العالية باستخدام مرشح تمرير عالي إما في Audacity ، أو في نفس ffmpeg:


لذلك ، لدينا ملف WAV أحادي 16 بت. يتكون من رأس ، دفق صوتي غير مضغوط ، وبيانات تعريف. ينقسم دفق الصوت نفسه إلى إطارات (ويمكن للإطارات تخزين عدة عينات ، ولكن هذه قصة مختلفة تمامًا) ، في حالتنا 16 بت لكل منها (يشار إلى ذلك بالحروف pcm_s16 في لقطات الشاشة). الإطارات هي تسلسلات من البتات التي تصف اتساع الموجة في وقت واحد لقناة أو أكثر (في حالتنا ، لقناة واحدة). تردد أخذ العينات لدفق الصوت هو 98000 إطارًا (أي 98000 إطارًا لكل ثانية واحدة) ، 490 إطارًا لكل 0.005 ثانية.

لذلك ، نعمل أيضًا وفقًا لخوارزمية بسيطة: نقرأ 490 إطارًا ، ونحدد ما إذا كانت موجة أم صمتًا ، واعتمادًا على ذلك ، قم بتعيين البت إلى 0 أو 1.

سنستخدم python وحزمةموجة لتحليل ملفات WAV.
إذا حدث الخطأ "wave.Error: unknown format: 65534" عند فتح الملف ، فاستبدل "wFormatTag" في الرأس من "FE FF" إلى "01 00":

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

لذا ، افتح الملف ، وقم بمعالجة 490 إطارًا واحسب متوسط ​​القيمة:

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)

من الممكن أنه عندما يكون هناك صمت (مقارنة بالصورة في الجرأة) ، فقد تبقى الضوضاء. لذلك ، قمنا بتعيين العتبة (فليكن 16000) ، وعند تجاوزها نعتبر الإشارة هي 1.

ثم نجمع البتات في بايت:

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

إذا كان كل شيء يتم بشكل صحيح ، فإن النتيجة هي سلسلة "Givemethepassword". نظرًا لأن البوت يتواصل في دوائر باستخدام طريقة إخفاء المعلومات ، فسيكون من المنطقي أن ننقل كلمة مرور له (وقد استلمناها مع المفتاح نتيجة فك التشفير) بنفس التنسيق.

للبدء ، أنشئ مسارًا صوتيًا بكلمة مرور. للقيام بذلك ، نستخدم البيانات التي تم الحصول عليها أثناء تحليل الرسالة من البوت: تردد أخذ العينات 98000 هرتز ؛ مدة الإشارة التي تصف كل بتة 5 ms ؛ تردد الإشارة المقابل لقيمة البتات "1" - كما رأينا من الرسوم البيانية ، 44100 هرتز.

الآن نحن بحاجة إلى "توليد" الصمت. نقوم بذلك عن طريق تحييد:

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

لتوليد الصوت ، سنستخدم موجة جيبية (يمكن قراءة المعلومات هنا ):

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

الآن الشيء صغير: يبقى تحويل كلمة المرور إلى أجزاء ، ثم إلى صوت.

ملاحظة: يستخدم الروبوت مسار الفيديو الأصلي المدخل لتراكب رسالته ، كما ذكرنا سابقًا. لذلك ، تحتاج إلى إضافة عدد قليل من وحدات البايت صفر بعد كلمة المرور من أجل التخلص من المفتاح بالكامل من البوت ، وليس فقط بدايته (كان طول المفتاح 36 بايت).

توليد الصوت
    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()


الآن سنقوم بإنشاء ملف الموجة النهائي:

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

نقوم بحفظ مسارنا ، على سبيل المثال ، في pass.wav. على طول الطريق ، نتحقق من وحدة فك ترميز stego الخاصة بنا لمعرفة ما إذا كان يتم التعرف على كلمة المرور. إذا كان كل شيء على ما يرام ، فإننا نحصل على فيديو جديد بكلمة مرور من الفيديو الأصلي my_video.mp4 ، ليحل محل المسار الصوتي:


الآن نحن بحاجة إلى جعل VideoNote خارج هذا. يمكنك محاولة البحث عن العاملين (على سبيل المثال ، وجد بعض المشاركينTelescopyBot) ، أو يمكنك كتابة برنامج الروبوت الخاص بك باستخدام TelegramAPI.


على أي حال ، إلى الأمام لدينا الروبوت:


لقد حصلنا على جولة جديدة وتهنئة (كنا سنقوم بمثل هذا العمل!) ، وقمنا بفك تشفير الصوت وفقًا للسيناريو الذي تم وضعه

بالفعل ، وحصلنا على المفتاح: "nq2020SyOMK7SnnJP1sNlvbTs8zt35vUrrsD" في الواقع ، يعتبر إخفاء المعلومات أحد أصعب مجالات الأمن السيبراني دون أي سبب هنا - حاول تخمين ذلك! لكن المشاركين في NeoQUEST أظهروا براعة ممتازة وإحساسًا بالفكاهة خلال هذه المهمة ، لذلك نحن نتحدث عن إعجابنا الصادق (من تهنئة البوت)!

All Articles