<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>Beavers0</title><subtitle>CTF Team</subtitle><author><name>Beavers0</name></author><id>https://teletype.in/atom/beaverszero</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/beaverszero?offset=0"></link><link rel="alternate" type="text/html" href="https://teletype.in/@beaverszero?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=beaverszero"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/beaverszero?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-10-09T08:06:28.524Z</updated><entry><id>beaverszero:2N5JnnhCH9l</id><link rel="alternate" type="text/html" href="https://teletype.in/@beaverszero/2N5JnnhCH9l?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=beaverszero"></link><title>GRSU Summer Practice Misc райтапы</title><published>2026-07-04T18:12:34.801Z</published><updated>2026-07-04T18:12:34.801Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img1.teletype.in/files/0d/a7/0da7ca84-ec26-4691-9342-fd006f20b985.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img3.teletype.in/files/2f/04/2f04100a-1ccd-4c87-af5a-778b2c2c5ea0.png&quot;&gt;Задания этой категории должны были называться Stegano = стеганография, но к сожалению на сайте такой категории не было.</summary><content type="html">
  &lt;p id=&quot;fGvw&quot;&gt;Задания этой категории должны были называться Stegano = стеганография, но к сожалению на сайте такой категории не было.&lt;br /&gt;&lt;/p&gt;
  &lt;h1 id=&quot;QaJW&quot;&gt;Птичка Птичка!&lt;/h1&gt;
  &lt;p id=&quot;Wfn8&quot;&gt;&lt;/p&gt;
  &lt;figure id=&quot;zTxD&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/2f/04/2f04100a-1ccd-4c87-af5a-778b2c2c5ea0.png&quot; width=&quot;495&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;uTED&quot;&gt;Видим картинку и сразу закидываем ее в гугл поиск: &lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;i3kc&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/25/3f/253f3e70-6f6b-4c2e-8060-2bb14fb302ae.png&quot; width=&quot;563&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;COi7&quot;&gt;Мы узнали название шифра, теперь заходим на любой сайт, вводим последовательность и получаем флаг.&lt;br /&gt;&lt;br /&gt;Флаг: &lt;strong&gt;grodno{birds_on_a_wire}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;jYgF&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;H8AG&quot;&gt;Странный файл&lt;/h1&gt;
  &lt;blockquote id=&quot;QU2K&quot;&gt;У меня есть очень важный файл, но с ним что-то стало. Помогите восстановить его.&lt;/blockquote&gt;
  &lt;blockquote id=&quot;UtZt&quot;&gt;Формат флага: grodno{}&lt;/blockquote&gt;
  &lt;p id=&quot;TQhQ&quot;&gt;&lt;br /&gt;Открываем файл и понимаем, что это очень длинная бэйс строка.&lt;/p&gt;
  &lt;p id=&quot;SDuW&quot;&gt;Закидываем ее на кибер шеф и он автоматически определяет, что эта строка - это представление файла: &lt;/p&gt;
  &lt;figure id=&quot;Idfs&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/ed/5d/ed5dc493-7783-429f-93d3-bc83e9431bd6.png&quot; width=&quot;1007&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Wlt7&quot;&gt;Флаг:&lt;strong&gt; grodno{an0th3r_b@s3_t4sk}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;DXz9&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;Gexy&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;VBqM&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;4MLh&quot;&gt;Хорошая Песня&lt;/h1&gt;
  &lt;blockquote id=&quot;ETW8&quot;&gt;Согласитесь, хорошая песня. Осталось найти флаг&lt;/blockquote&gt;
  &lt;blockquote id=&quot;iXPZ&quot;&gt;Формат флага: grodno{}&lt;/blockquote&gt;
  &lt;p id=&quot;efJZ&quot;&gt;&lt;br /&gt;К задаче прикреплен mp3 файл. Задачу можно решить просто открыв любую ИИ и закинув туда файл с просьбой достать флаг, но я расскажу исходный вектор.&lt;br /&gt;В задачах на стеганографию все сводится к перебору векторов атаки от самого простого, к сложному. &lt;/p&gt;
  &lt;p id=&quot;qPmF&quot;&gt;Для каждого типа файлов они разные, но в среднем их 6-8 на каждый тип файла.&lt;br /&gt;Самое простое что можно сделать со звуком, это спрятать флаг в спектрограмму.&lt;/p&gt;
  &lt;p id=&quot;fNHH&quot;&gt;Для просмотра спектрограммы можно использовать много утилит, но самая удобная для меня - это Sonic Visualiser&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;UmXr&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/60/79/6079d878-8eb9-4811-8633-3ff092df8606.png&quot; width=&quot;1020.5&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;KzlK&quot;&gt;&lt;br /&gt;Флаг: &lt;strong&gt;grodno{j0hnny_0n1_na_d3r3v1ah}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;UIBW&quot;&gt;Но если лень учить мат часть, то всегда можно вставить это в любимый чатгпт)&lt;/p&gt;
  &lt;figure id=&quot;urkR&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/f6/73/f673cde4-d541-49e6-bbd8-3de1f9dc46ee.png&quot; width=&quot;355&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;k39S&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;k0vC&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;q4YN&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;guc4&quot;&gt;PiH&lt;/h1&gt;
  &lt;p id=&quot;RYzw&quot;&gt;&lt;br /&gt;Этот таск тоже максимально простой. Когда мы работаем с изображениями, то очень выручает сайт &lt;a href=&quot;https://www.aperisolve.com/&quot; target=&quot;_blank&quot;&gt;https://www.aperisolve.com/&lt;/a&gt;&lt;br /&gt;Он оркестрирует основные стеганографические утилиты для работы с фото, гиф и тд.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;FHNI&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/7f/cd/7fcd6dc5-7288-490b-883f-c4684d51641a.png&quot; width=&quot;1020&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;yezf&quot;&gt;Видим, что утилита StegHide нашла какойто файл &amp;quot;pih_flag.txt&amp;quot;&lt;/p&gt;
  &lt;p id=&quot;wSLE&quot;&gt;Скачиваем, открываем и получаем флаг.&lt;br /&gt;&lt;br /&gt;Флаг: &lt;strong&gt;grodno{PiH_Hru_hRu_hrU}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;OVqB&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;8CvO&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;6D9Z&quot;&gt;Страх и Ужас&lt;/h1&gt;
  &lt;blockquote id=&quot;Zowd&quot;&gt;Райтап будет позже, так как задача будет представлена на международном CTF&lt;/blockquote&gt;
  &lt;p id=&quot;i4En&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;Bvv6&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;G1fC&quot;&gt;Крутой Скейт&lt;/h1&gt;
  &lt;p id=&quot;HZ2e&quot;&gt;Потрясающая задача, которая показала мне, что такое профдеформация в CTF&lt;br /&gt;Задача взята с одного кубанского соревнования, которое мы выиграли 2 месяца назад.&lt;br /&gt;Я просидел за ней около 8 часов, разбирал каждый кадр и ничего не смог найти.&lt;br /&gt;Потом решил посмотреть само видео и понял, что вся задача сводилась к азбуке морзе в прыжках и перекатах.&lt;br /&gt;&lt;br /&gt;Как оказалось первокурсники нашей кафедры решают эту задачу в разы проще, чем опытные CTFеры. Изначально эту задачу смогли решить только 3 комнады, у нас 20.&lt;br /&gt;&lt;br /&gt;Прыжки = &amp;quot;-&amp;quot;&lt;/p&gt;
  &lt;p id=&quot;mF3D&quot;&gt;Перекаты = &amp;quot;.&amp;quot;&lt;br /&gt;&lt;br /&gt;Флаг: &lt;strong&gt;grodno{GROB}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;GXCx&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;80eO&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;oOqQ&quot;&gt;&lt;br /&gt;АХАХАХАХАХАХАХАХАХАХАХАХАХАХАХ&lt;/h2&gt;
  &lt;blockquote id=&quot;8vl4&quot;&gt;&lt;br /&gt;⠁⠀⠀⠈⠀⠀⠀⠁⠀⠀⠈⠀⠀⠀⠁⠀⠀⠈⠀⠀⠀⠁⠀⠀⠈⠀⠀⠀⠁⠀⠀⠈⠀⠀⠀⠁⠀⠀⠈⠀⠀⠀⠁⠀⠀⠈⠀⠀⠀⠁&lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀&lt;br /&gt;⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠲⠲⠲⠲⠲⠲⠢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣌⣷⡤⠀⠀⠀⠀⠀⠀⣱⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠅⠀⠘⡆⠀⠀⠀⠀⡔⠏⠛⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡇⠀⠀⠀⠰⡀⠀⠀⢰⠃⠀⠀⠈⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⢠⠶⣀⠀⠀⠀⠀⠀⠂⠀⠀⠀⡠⢎⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠂⠀⠀⠀⠀⠀⠀⠀⠐⠈⠀⠀⠀⠀⠀⠀⡰⢱⠉⣾⠘⢄⠀⠀⠀⠀⠀⠀⠀⢜⢧⡁⢫⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠠  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⢃⢇⢰⢽⡇⢨⠀⠀⠀⠀⠀⠀⠸⢸⣽⠦⣤⢇⠀⠀⠀⠀⠀⠀⠀⠀⠐⠈⠀⠀⠀⠁⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠘⠈⠀⠉⠘⢔⡄⠀⠀⠀⠀⠀⡑⡨⠋⢀⠟⠎⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⢋⢀⠀⢠⠲⣠⠁⠆⠀⠀⠀⠀⠀⢊⠃⢀⠺⠈⠀⢨⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⢰⠈⠁⠀⠀⠀⠀⠃⠊⡂⠀⠀⠀⠀⠍⢠⠃⠈⠀⣠⠊⢳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⡴⠔⠋⠂⠀⡜⠀⠀⠀⠀⢠⠀⠈⠀⡠⡰⠁⠆⠈⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠎⠀⠀⠁⠀⠀⢠⠎⠀⠀⠀⠀⠀⠨⣄⢄⠀⠀⠀⠀⠀⠀⢐⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠄⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⢘⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢐⠐⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢐⠠⠈⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡂⠀⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡂⠄⠡⠁⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠨⠠⠁⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⢀⠀⡜⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⡈⢇⠄⠏⠁⠀⠀⠀⢰⠀⠀⠀⠀⠀⠀⠀⠀⠰⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⠀⠀⡐⠀⠀⠀⠀⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡂⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠁⠀⠀⠀⠀⠀⠀⠸⠀⠀⠀⠀⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠄⠀⠀⠀⠀⠀⠀⠀⠰⠀⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⠀⢀⠇⠀⠀⠀⠀⠀⠀⠀⡆⡰⣽⣉⢀⡀⢀⠀⠀⠀⠀⠀⡀⠐⠀⡔⠖⢅⠀⠀⠀⠀⠀⠀⠀⠀⢃⠀⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⡀⠀⠀⠀⠀⠀⡜⠀⠀⠀⠀⠀⠀⠀⢰⠠⡋⣾⣿⣯⡘⠤⡠⠀⠠⠀⢠⡀⣦⣷⣾⡦⠫⡀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠀⡜⠀⠀⠀⠀⠀⠀⠀⠀⡆⠈⢸⢼⣿⣿⣿⡧⢌⠙⠉⠋⠃⡡⣾⣿⣿⡇⡄⡆⠀⠀⠀⠀⠀⠀⠀⠀⢨⠀⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⠀⠰⠀⠀⠀⠀⠀⠀⠀⠀⠀⠂⡄⠸⢼⣿⣿⣿⠡⢈⠱⢲⠒⠎⠠⣻⣿⣿⡇⡃⢘⠀⠀⠀⠀⠀⠀⠀⠀⠀⠇⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠄⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠊⢣⢺⣹⣿⣿⣿⠈⠠⠐⢨⠐⢈⠐⢼⣿⣿⣷⠀⠨⠀⠀⠀⠀⠀⠀⠀⠀⠀⢡⠀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠀⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢨⠸⣼⣿⣿⡇⠠⠡⠈⡎⢐⠠⠈⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡀⠀⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⢐⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠐⢽⣿⣿⠇⠁⠌⠨⢂⠐⠠⠁⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢅⠀⠀⠀⠀⠀  &lt;br /&gt;⠂⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡨⠸⣿⣿⠅⠨⢈⢸⠀⠌⠄⠡⣿⣿⣿⡇⠀⡜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡄⠀⠀⠀⠀  &lt;br /&gt;⠀⠀⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⡇⠀⣿⣿⠀⠡⠐⡄⢂⠡⠈⠄⣿⣿⡿⠀⢠⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠰⠀⠀⠀⠀  &lt;br /&gt;⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⢠⠸⣟⠀⠅⠂⡃⠄⠂⡁⢂⢻⣿⠇⡀⡎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀⠀  &lt;br /&gt;⠁⠌⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⢨⠈⣇⠂⡁⢂⠐⡈⠄⠂⠄⢽⡏⢠⢦⠂⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢂⠀⠀⠀  &lt;br /&gt;⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢘⠀⡣⠂⡐⠐⡐⠠⠈⠄⡁⡺⠂⡜⡇⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀  &lt;br /&gt;⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⡆⡢⠁⠄⠡⠐⢈⠨⠐⡀⠎⢰⡑⡅⢰⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡆⠀⠀  &lt;br /&gt;⢔⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⡌⡎⠄⠡⢈⢐⢀⠂⡁⠄⣣⠗⠈⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢆⠀⠀  &lt;br /&gt;⡌⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢑⢨⠠⠁⡂⠄⢂⠐⡀⠂⣇⠔⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢘⠀⠀  &lt;br /&gt;⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡂⡁⠄⠌⡀⠂⠄⢱⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡆⠀  &lt;br /&gt;⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢱⠀⡂⠡⠀⠅⢡⠆⠀⢀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢡⠀  &lt;br /&gt;⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠪⡀⠀⠀⠀⢣⠐⠈⠄⣡⠃⠀⠀⡃⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢨⠀  &lt;br /&gt;⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⢢⠐⡄⠀⠀⠀⠀⠑⠧⡜⠀⠀⠀⠀⠓⠎⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡆  &lt;br /&gt;⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣃⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡣  &lt;br /&gt;⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢢  &lt;br /&gt;⢱⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸  &lt;br /&gt;⡐⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸  &lt;/blockquote&gt;
  &lt;p id=&quot;VlQU&quot;&gt;&lt;br /&gt;Потрясающая, но вполне обычная и простая как оказалось задача. Думал, что она продержится дольше.&lt;br /&gt;&lt;br /&gt;Файл задачи имеет немного нестандартный формат - .avif&lt;br /&gt;Поэтому идем читать что в нем такого интересного.&lt;/p&gt;
  &lt;p id=&quot;OHY2&quot;&gt;Отчасти формат очень похож на png и даже есть утилита, которая может декодировать изображение avif в png&lt;br /&gt;&lt;br /&gt;На самом изображении видны какие то помехи на белом фоне, после перевода в png. По этим помехам либо вы, либо ваша ИИшка могли с легкостью определить что есть изменения в перменной Y, которая отвечает за яркость.&lt;/p&gt;
  &lt;p id=&quot;4TtE&quot;&gt;Построим спектр яркостной плоскости:&lt;/p&gt;
  &lt;pre id=&quot;2bvs&quot;&gt;import numpy as np 
from PIL import Image  
img = np.array(Image.open(&amp;quot;decoded.png&amp;quot;).convert(&amp;quot;RGB&amp;quot;), dtype=np.float32) 
y = 0.299 * img[:, :, 0] + 0.587 * img[:, :, 1] + 0.114 * img[:, :, 2] 
spec = np.fft.fftshift(np.fft.fft2(y)) 
mag = np.log1p(np.abs(spec)) 
mag = (mag - mag.min()) / (mag.max() - mag.min()) 
Image.fromarray((mag * 255).astype(&amp;quot;uint8&amp;quot;)).save(&amp;quot;fft.png&amp;quot;)&lt;/pre&gt;
  &lt;p id=&quot;zQRc&quot;&gt;Открываем получившееся изображение и получаем фото флага:&lt;/p&gt;
  &lt;p id=&quot;ArXJ&quot;&gt;&lt;/p&gt;
  &lt;figure id=&quot;H9L3&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/02/4c/024c4ca6-f018-4121-9c2d-e2a590cd47a7.png&quot; width=&quot;793&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;82bj&quot;&gt;Флаг: &lt;strong&gt;grodno{Gruzdn13_Gruzd1_NCF_4b0b4}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;bnFj&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;9iaz&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;0jzu&quot;&gt;Кубаноид и Кубаноид Revenge&lt;/h1&gt;
  &lt;blockquote id=&quot;FrhJ&quot;&gt;Данные задачи создавал не я, так что не могу полностью описать оригинальный способ решения. Насколько я знаю, все сводилось к поиску прогруженных чанков в мире с помощью специализированных утилит и сайтов&lt;/blockquote&gt;
  &lt;p id=&quot;SMea&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;e6tw&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;kn5b&quot;&gt;Девочка С Бесплотностью&lt;/h1&gt;
  &lt;p id=&quot;S2Kc&quot;&gt;Задача состоит из 4х минутного видео с геймплеем доты.&lt;/p&gt;
  &lt;p id=&quot;Xnpz&quot;&gt;Было довольно много вопросов и претензий по поводу этой задачи в форме обратной связи. &lt;/p&gt;
  &lt;p id=&quot;Cq5a&quot;&gt;Пропишу сразу, любая задача подразумевает, что ее сможет решить ЛЮБОЙ человек, независимо от любых факторов, кроме его способностей в CTF.&lt;br /&gt;НЕ НУЖНО УМЕТЬ ИГРАТЬ В ДОТУ ЧТОБЫ ЕЕ РЕШИТЬ.&lt;/p&gt;
  &lt;p id=&quot;qUyC&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;8oS5&quot;&gt;Теперь к самому решению. Идея для задачи появилась после решения задачи &amp;quot;Крутой Секйт&amp;quot; и суть решения точно такая же.&lt;/p&gt;
  &lt;p id=&quot;Dezc&quot;&gt;Персонаж Муэрта, которая представлена в задаче имеет возможность переключения своей 3 способности (иконка способности горит зеленым, когда она включена и не горит, когда выключена).&lt;/p&gt;
  &lt;p id=&quot;o7en&quot;&gt;Внимательно посмотрев видео, можно заметить, что на моменте 3.18 я начинаю отбивать морзянку третей способностью. Выделив этот фрагмент и сопоставив с азбукой получаем флаг.&lt;br /&gt;&lt;br /&gt;Флаг: &lt;strong&gt;grodno{1and3}&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>beaverszero:ZPxT7J2j10V</id><link rel="alternate" type="text/html" href="https://teletype.in/@beaverszero/ZPxT7J2j10V?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=beaverszero"></link><title>GRSU Summer Practice Crypto райтапы</title><published>2026-07-04T13:07:20.938Z</published><updated>2026-07-04T13:07:20.938Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img2.teletype.in/files/19/82/1982a6d1-6af7-4357-851f-fd5daada310c.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img4.teletype.in/files/7d/a9/7da9ce59-b922-4550-8157-d0faafeb4072.png&quot;&gt;RSA это асимметричный шифр, на который есть множество атак. Например Boneh-Durfee attack.</summary><content type="html">
  &lt;h2 id=&quot;FsJD&quot;&gt;&lt;br /&gt;Не Придумал Название&lt;/h2&gt;
  &lt;blockquote id=&quot;jOTP&quot;&gt;RSA это асимметричный шифр, на который есть множество атак. Например Boneh-Durfee attack.&lt;/blockquote&gt;
  &lt;blockquote id=&quot;jQhR&quot;&gt;Формат флага: **grodno{}**&lt;/blockquote&gt;
  &lt;p id=&quot;NdCe&quot;&gt;Не вижу смысла объяснять теорию про рса и описывать вектор атаки и тд. и тп.&lt;br /&gt;Все равно вам это не интересно и не нужно. Никогда не поверю, что ее  решали руками.&lt;br /&gt;Вся криптография решалась через ИИшку:&lt;br /&gt;&lt;br /&gt;Кидаем файлы задачи, кидаем название и описание и ждем флаг:&lt;br /&gt;&lt;strong&gt;grodno{l4tt1c3s_wh1sp3r_wh3n_d_1s_t00_sm0l}&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;ncGS&quot;&gt;Пример рабочего эксплойта:&lt;br /&gt;&lt;/p&gt;
  &lt;pre id=&quot;icoY&quot;&gt;from math import isqrt
from pathlib import Path
import re&lt;/pre&gt;
  &lt;pre id=&quot;u0kx&quot;&gt;
DEFAULT_PATH = Path(&amp;quot;/tmp/chal1/ciphertext.txt&amp;quot;)&lt;/pre&gt;
  &lt;pre id=&quot;dwcO&quot;&gt;
def continued_fraction(numerator: int, denominator: int):
    while denominator:
        q = numerator // denominator
        yield q
        numerator, denominator = denominator, numerator - q * denominator&lt;/pre&gt;
  &lt;pre id=&quot;Fcx0&quot;&gt;
def convergents(sequence):
    n0, d0 = 0, 1
    n1, d1 = 1, 0
    for a in sequence:
        n2 = a * n1 + n0
        d2 = a * d1 + d0
        yield n2, d2
        n0, d0, n1, d1 = n1, d1, n2, d2&lt;/pre&gt;
  &lt;pre id=&quot;cnBZ&quot;&gt;
def parse_values(text: str) -&amp;gt; tuple[int, int, int]:
    values = {}
    for name in (&amp;quot;n&amp;quot;, &amp;quot;e&amp;quot;, &amp;quot;c&amp;quot;):
        match = re.search(rf&amp;quot;{name}\s*=\s*(\d+)&amp;quot;, text)
        if not match:
            raise ValueError(f&amp;quot;missing {name}&amp;quot;)
        values[name] = int(match.group(1))
    return values[&amp;quot;n&amp;quot;], values[&amp;quot;e&amp;quot;], values[&amp;quot;c&amp;quot;]&lt;/pre&gt;
  &lt;pre id=&quot;aBx4&quot;&gt;
def int_to_bytes(value: int) -&amp;gt; bytes:
    hex_value = f&amp;quot;{value:x}&amp;quot;
    if len(hex_value) % 2:
        hex_value = &amp;quot;0&amp;quot; + hex_value
    return bytes.fromhex(hex_value)&lt;/pre&gt;
  &lt;pre id=&quot;rtkG&quot;&gt;
def wiener_attack(n: int, e: int) -&amp;gt; int | None:
    for k, d in convergents(continued_fraction(e, n)):
        if k == 0 or (e * d - 1) % k:
            continue&lt;/pre&gt;
  &lt;pre id=&quot;PopF&quot;&gt;        phi = (e * d - 1) // k
        s = n - phi + 1
        discriminant = s * s - 4 * n
        if discriminant &amp;lt; 0:
            continue&lt;/pre&gt;
  &lt;pre id=&quot;FRg6&quot;&gt;        t = isqrt(discriminant)
        if t * t != discriminant or (s + t) % 2:
            continue&lt;/pre&gt;
  &lt;pre id=&quot;R3wp&quot;&gt;        p = (s + t) // 2
        q = (s - t) // 2
        if p * q == n:
            return d
    return None&lt;/pre&gt;
  &lt;pre id=&quot;4Y39&quot;&gt;
def main():
    text = DEFAULT_PATH.read_text(encoding=&amp;quot;utf-8&amp;quot;)
    n, e, c = parse_values(text)
    d = wiener_attack(n, e)
    if d is None:
        raise SystemExit(&amp;quot;Wiener attack failed&amp;quot;)&lt;/pre&gt;
  &lt;pre id=&quot;NstM&quot;&gt;    message = pow(c, d, n)
    print(int_to_bytes(message).decode())&lt;/pre&gt;
  &lt;pre id=&quot;o0vv&quot;&gt;
if __name__ == &amp;quot;__main__&amp;quot;:
    main()
&lt;/pre&gt;
  &lt;p id=&quot;723r&quot;&gt;&lt;br /&gt;Флаг: &lt;strong&gt;grodno{l4tt1c3s_wh1sp3r_wh3n_d_1s_t00_sm0l}&lt;/strong&gt;&lt;/p&gt;
  &lt;h2 id=&quot;MZ5I&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Я Твой ROT...&lt;/h2&gt;
  &lt;blockquote id=&quot;cJMc&quot;&gt;Мой китайский друг отправил мне это, но я не понял что он хотел сказать.  &lt;br /&gt;&lt;code&gt;籰类籸籭籷籸粄籟籨籛簹籝籁簹簹簹籨籶籷簼籨籗簹籰籲粆&lt;/code&gt;&lt;/blockquote&gt;
  &lt;p id=&quot;Nmpb&quot;&gt;&lt;br /&gt;Можно использовать простой гпт, а можно воспользоваться специальным сайтом для шифров - &lt;strong&gt;CyberChef&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;figure id=&quot;zD0h&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/7d/a9/7da9ce59-b922-4550-8157-d0faafeb4072.png&quot; width=&quot;657.5&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;sm4D&quot;&gt;Использовался шифр ROT8000, который сдвигает каждый символ на 8000 по юникод таблице.&lt;br /&gt;&lt;br /&gt;Флаг: &lt;strong&gt;grodno{V_R0T8000_mn3_N0gi}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;vfTk&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;NpP2&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;2aK5&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;wcbU&quot;&gt;База 64&lt;/h2&gt;
  &lt;blockquote id=&quot;3rEY&quot;&gt;Даже описание придумывать не хочется, все есть в названии.&lt;/blockquote&gt;
  &lt;blockquote id=&quot;I8JW&quot;&gt;Формат флага: grodno{}&lt;/blockquote&gt;
  &lt;p id=&quot;td56&quot;&gt;&lt;br /&gt;Как и сказано в описании, все есть в названии.&lt;br /&gt;Шифр называется Base64&lt;br /&gt;Снова воспользуемся кибер шефом:&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;NaCg&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/88/a9/88a95b31-21c6-4c92-8f09-68be6cabbf67.png&quot; width=&quot;611&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;ekVG&quot;&gt;Проще конечно же было использовать ГПТ, но и так пойдет.&lt;br /&gt;&lt;br /&gt;Флаг: &lt;strong&gt;grodno{barabarabarabereberebere}&lt;/strong&gt;&lt;/p&gt;
  &lt;h2 id=&quot;q9Ot&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Бу! Испугался? Не бойся&lt;/h2&gt;
  &lt;blockquote id=&quot;xPlh&quot;&gt;Оказывается криптографию можно засунуть даже в ехе.&lt;br /&gt;Сможете ли вы достать флаг из этого точно не вирусного файла?)&lt;/blockquote&gt;
  &lt;blockquote id=&quot;LCqH&quot;&gt;Формат флага: **grodno{}**&lt;/blockquote&gt;
  &lt;p id=&quot;3CFr&quot;&gt;&lt;br /&gt;Запустив файл у себя на устройстве, появляется безобидный скринлокер, который закрывается через 40 секунд и не несет никакой полезной нагрузки.&lt;br /&gt;Значит нам нужен не сам файл, а то что лежит внутри него.&lt;br /&gt;&lt;br /&gt;Ультимативный способ решения почти любой крипто задачи:&lt;br /&gt;ЧатГПТ, помоги, у меня есть такая-то задача, такое-то описание и такие-то файлы, достань флаг -&amp;gt; уходим пить чай -&amp;gt; приходим и получаем флаг.&lt;br /&gt;&lt;br /&gt;Флаг: &lt;strong&gt;grodno{I_f33l_y0ur_f3@r_bug@g@5h3nka}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;fzHE&quot;&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;NLIl&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;rqMT&quot;&gt;Qkd Entropy&lt;/h1&gt;
  &lt;blockquote id=&quot;xvaq&quot;&gt;Лаборатория прислала дамп своего &lt;strong&gt;“квантово-устойчивого”&lt;/strong&gt; канала распределения ключа. Они уверяют, что это честный &lt;strong&gt;BB84&lt;/strong&gt;, а синхронизационный маяк никак не влияет на безопасность. &lt;/blockquote&gt;
  &lt;blockquote id=&quot;p0YX&quot;&gt;Формат флага: grodno{}&lt;/blockquote&gt;
  &lt;p id=&quot;VMkA&quot;&gt;&lt;br /&gt;Интересная задача, которая реализует псевдо-квантовый алгоритм шифрования.&lt;br /&gt;Метод действия точно такой-же как и раньше - ЧатГПТ.&lt;br /&gt;&lt;br /&gt;Флаг: &lt;strong&gt;grodno{7h3r3_1s_n0_sp00n_0nly_b4d_b34c0n5}&lt;/strong&gt;&lt;/p&gt;
  &lt;pre id=&quot;w20i&quot;&gt;Пример рабочего солвера: 

#!/usr/bin/env python3&lt;/pre&gt;
  &lt;pre id=&quot;eAWJ&quot;&gt;from __future__ import annotations&lt;/pre&gt;
  &lt;pre id=&quot;gXJ3&quot;&gt;import hashlib
import json
from pathlib import Path&lt;/pre&gt;
  &lt;pre id=&quot;TKz0&quot;&gt;from z3 import BitVec, BitVecVal, LShR, Solver&lt;/pre&gt;
  &lt;pre id=&quot;G7pQ&quot;&gt;MASK = (1 &amp;lt;&amp;lt; 64) - 1&lt;/pre&gt;
  &lt;pre id=&quot;0gEb&quot;&gt;
def xs128p_step_z3(s0, s1):
    x = s0
    y = s1
    new_s0 = y
    x = x ^ ((x &amp;lt;&amp;lt; 23) &amp;amp; BitVecVal(MASK, 64))
    x = x ^ LShR(x, 17)
    x = x ^ y
    x = x ^ LShR(y, 26)
    new_s1 = x
    out = (new_s0 + new_s1) &amp;amp; BitVecVal(MASK, 64)
    return new_s0, new_s1, out&lt;/pre&gt;
  &lt;pre id=&quot;ndaH&quot;&gt;
class XorShift128Plus:
    def __init__(self, s0: int, s1: int) -&amp;gt; None:
        self.s0 = s0 &amp;amp; MASK
        self.s1 = s1 &amp;amp; MASK&lt;/pre&gt;
  &lt;pre id=&quot;fR7P&quot;&gt;    def next_u64(self) -&amp;gt; int:
        x = self.s0
        y = self.s1
        self.s0 = y
        x ^= (x &amp;lt;&amp;lt; 23) &amp;amp; MASK
        x ^= x &amp;gt;&amp;gt; 17
        x ^= y
        x ^= y &amp;gt;&amp;gt; 26
        self.s1 = x &amp;amp; MASK
        return (self.s0 + self.s1) &amp;amp; MASK&lt;/pre&gt;
  &lt;pre id=&quot;fcB1&quot;&gt;
def take_bits(rng: XorShift128Plus, count: int) -&amp;gt; list[int]:
    bits: list[int] = []
    while len(bits) &amp;lt; count:
        word = rng.next_u64()
        for i in range(64):
            bits.append((word &amp;gt;&amp;gt; i) &amp;amp; 1)
            if len(bits) == count:
                break
    return bits&lt;/pre&gt;
  &lt;pre id=&quot;dIHA&quot;&gt;
def bits_to_bytes(bits: list[int]) -&amp;gt; bytes:
    out = bytearray()
    for i in range(0, len(bits), 8):
        value = 0
        for shift, bit in enumerate(bits[i : i + 8]):
            value |= (bit &amp;amp; 1) &amp;lt;&amp;lt; shift
        out.append(value)
    return bytes(out)&lt;/pre&gt;
  &lt;pre id=&quot;SdZX&quot;&gt;
def recover_seed(beacon_words: list[int]) -&amp;gt; tuple[int, int]:
    s0 = BitVec(&amp;quot;s0&amp;quot;, 64)
    s1 = BitVec(&amp;quot;s1&amp;quot;, 64)
    solver = Solver()
    cur0, cur1 = s0, s1
    for observed in beacon_words:
        cur0, cur1, out = xs128p_step_z3(cur0, cur1)
        solver.add(out == BitVecVal(observed, 64))
    assert solver.check().r == 1
    model = solver.model()
    return model[s0].as_long(), model[s1].as_long()&lt;/pre&gt;
  &lt;pre id=&quot;zUjH&quot;&gt;
def main() -&amp;gt; None:
    root = Path(__file__).resolve().parents[1]
    capture = json.loads((root / &amp;quot;public&amp;quot; / &amp;quot;capture.json&amp;quot;).read_text())&lt;/pre&gt;
  &lt;pre id=&quot;Wjzv&quot;&gt;    beacon_words = [int(word, 16) for word in capture[&amp;quot;beacon_words&amp;quot;]]
    seed_s0, seed_s1 = recover_seed(beacon_words)
    rng = XorShift128Plus(seed_s0, seed_s1)
    for _ in beacon_words:
        rng.next_u64()&lt;/pre&gt;
  &lt;pre id=&quot;kKjH&quot;&gt;    pulse_count = capture[&amp;quot;pulse_count&amp;quot;]
    alice_bits = take_bits(rng, pulse_count)
    alice_bases = take_bits(rng, pulse_count)
    bob_bases = take_bits(rng, pulse_count)&lt;/pre&gt;
  &lt;pre id=&quot;dEll&quot;&gt;    sift_positions = [i for i, (a, b) in enumerate(zip(alice_bases, bob_bases)) if a == b]
    raw_key = [alice_bits[i] for i in sift_positions]&lt;/pre&gt;
  &lt;pre id=&quot;h669&quot;&gt;    sample_positions = capture[&amp;quot;sample_positions&amp;quot;]
    sample_bits = [int(bit) for bit in capture[&amp;quot;sample_bits&amp;quot;]]
    for pos, bit in zip(sample_positions, sample_bits):
        assert raw_key[pos] == bit&lt;/pre&gt;
  &lt;pre id=&quot;AE69&quot;&gt;    sample_set = set(sample_positions)
    final_key_bits = [bit for i, bit in enumerate(raw_key) if i not in sample_set]&lt;/pre&gt;
  &lt;pre id=&quot;4whW&quot;&gt;    nonce = bytes.fromhex(capture[&amp;quot;nonce&amp;quot;])
    ciphertext = bytes.fromhex(capture[&amp;quot;ciphertext&amp;quot;])
    keystream = hashlib.shake_256(nonce + bits_to_bytes(final_key_bits)).digest(len(ciphertext))
    plaintext = bytes(a ^ b for a, b in zip(ciphertext, keystream))
    print(plaintext.decode())&lt;/pre&gt;
  &lt;pre id=&quot;79bQ&quot;&gt;
if __name__ == &amp;quot;__main__&amp;quot;:
    main()
&lt;/pre&gt;
  &lt;pre id=&quot;YelC&quot;&gt;&lt;/pre&gt;
  &lt;p id=&quot;CkSr&quot;&gt;Задача мимикрирует под BB84, но ломается не на “квантовой” части, а на плохой инженерии. &lt;/p&gt;
  &lt;p id=&quot;JNkE&quot;&gt;&lt;br /&gt;Уязвимость: один и тот же XorShift128Plus используется и для публичного синхромаяка.&lt;/p&gt;
  &lt;p id=&quot;siWn&quot;&gt;&lt;br /&gt;Первые 8 слов маяка публикуются в capture.json как beacon_words. Этого хватает, чтобы восстановить внутреннее состояние PRNG через SMT&lt;br /&gt;(z3) и затем предсказать весь дальнейший поток. &lt;/p&gt;
  &lt;p id=&quot;MbwG&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;7Gm6&quot;&gt;Флаг: &lt;strong&gt;grodno{7h3r3_1s_n0_sp00n_0nly_b4d_b34c0n5} &lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>beaverszero:84CAvCMJ75l</id><link rel="alternate" type="text/html" href="https://teletype.in/@beaverszero/84CAvCMJ75l?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=beaverszero"></link><title>GRSU Summer Practice OSINT райтапы</title><published>2026-07-04T12:32:18.219Z</published><updated>2026-07-04T12:33:29.806Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img2.teletype.in/files/19/87/198745de-5115-4a72-a336-e09db7fd51af.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/84/dd/84ddfc63-8ceb-419e-9448-574886488149.png&quot;&gt;Летали мы как-то в феврале на какое-то соревнование в ЕКБ, совсем забыл что это было, помогите мне вспомнить ивент в рамках которого проходило соревнование.</summary><content type="html">
  &lt;h3 id=&quot;5Etg&quot;&gt;Начнем с райтапов на осинт. Для решения задач использовал только гугл и бесплатный гпт, чтобы показать, что каждый мог их решить.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/h3&gt;
  &lt;h2 id=&quot;DdBr&quot;&gt;Обычный февраль&lt;/h2&gt;
  &lt;blockquote id=&quot;a0LU&quot;&gt;Летали мы как-то в феврале на какое-то соревнование в ЕКБ, совсем забыл что это было, помогите мне вспомнить ивент в рамках которого проходило соревнование.&lt;/blockquote&gt;
  &lt;blockquote id=&quot;X3ok&quot;&gt;Формат ответа: grodno{Официальное_Полное_Название_Мероприятия}&lt;/blockquote&gt;
  &lt;p id=&quot;kyQX&quot;&gt;&lt;br /&gt;Одно из самых простых заданий, которое решается одним запросом в гугл: &lt;/p&gt;
  &lt;figure id=&quot;bW5k&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/84/dd/84ddfc63-8ceb-419e-9448-574886488149.png&quot; width=&quot;1094&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;7DL9&quot;&gt;Мы нашли название самого соревнования, теперь надо найти событие в рамках которого проходил сам чемпионат.&lt;br /&gt;&lt;br /&gt;Проматываем ленту в низ и первым результатом поиска видим сайт: &lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;p7Xv&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/35/7d/357d44d6-c54d-4b52-adfa-9e5eb35e868c.png&quot; width=&quot;1137&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;i7mA&quot;&gt;Вот и флаг: &lt;strong&gt;grodno{Уральский_Форум_Кибербезопасность_В_Финансах}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;qifa&quot;&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;5IZZ&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;3IQi&quot;&gt;Необычный Февраль&lt;/h2&gt;
  &lt;blockquote id=&quot;bT77&quot;&gt;Раз уж вы смогли найти соревнование на котором мы были, для вас должно быть просто найти фамилию и имя парня, который решил прилечь на меня)&lt;/blockquote&gt;
  &lt;blockquote id=&quot;uYk8&quot;&gt;Формат ответа: &lt;strong&gt;grodno{Фамилия_Имя}&lt;/strong&gt;&lt;/blockquote&gt;
  &lt;p id=&quot;adMy&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;eHLE&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;yyKM&quot;&gt;&lt;br /&gt;Не менее простая задача. Из прошлой задачи мы нашли сайт форума, теперь надо зайти на него и осмотреться: &lt;/p&gt;
  &lt;figure id=&quot;6RMu&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/c0/e2/c0e28cb4-7656-4472-9a21-ca40e1e18a0e.png&quot; width=&quot;2332&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;y7HH&quot;&gt;Видим пункт &amp;quot;Молодежная программа&amp;quot;, скорее всего нам нужна именно она.&lt;/p&gt;
  &lt;p id=&quot;b8Tt&quot;&gt;&lt;/p&gt;
  &lt;figure id=&quot;OV01&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/e5/83/e5830041-66e8-43a8-921a-c0bb51ca4259.png&quot; width=&quot;1452&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Ds0x&quot;&gt;Опа, какой то буклет, переходим на него и начинаем листать.&lt;br /&gt;В буклете собрана информация про все команды, в списке команд полуфиналистов находим нужного нам человека:&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;HJZV&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/f2/39/f2394f20-606e-4c15-817c-0e697061e378.png&quot; width=&quot;1299&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;zho7&quot;&gt;Скриншот и телетайп съедают качество, но на самом сайте отчетливо видно имя, написанное на бейдже - Дольский Степан, следовательно флаг:&lt;/p&gt;
  &lt;p id=&quot;1e9l&quot;&gt;&lt;br /&gt;&lt;strong&gt;grodno{Дольский_Степан}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;kXz6&quot;&gt;&lt;br /&gt;Видел в фидбэк форме комментарии по поводу: как мы должны искать какого-то нн, которого знают только админы?&lt;br /&gt;&lt;br /&gt;Захотел оставить свой комментарий)&lt;br /&gt;&lt;br /&gt;В этом суть заданий категории OSINT и про саму категорию я расписывал в закрепах группы. В этом и смысл задачи - найти человека, которого вы не знаете. На задаче 37 решений и на поиск самого Степы ушло 3 запроса в гугл и 10 минут времени, когда мы тестировали эту задачу, следовательно проблема не в задаче, а в чем то другом)&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;7yJS&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;EJl0&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;cCg4&quot;&gt;Гроб Гроб Кладбище&lt;/h2&gt;
  &lt;blockquote id=&quot;9LfW&quot;&gt;Обычный понедельник, гуляю я по кладбищу и вижу интересный дом, но сможете ли вы найти полное имя, записанное на этом памятнике?&lt;/blockquote&gt;
  &lt;blockquote id=&quot;T4DT&quot;&gt;Формат ответа: grodno{Имя_Фамилия1-Фамилия2}&lt;/blockquote&gt;
  &lt;figure id=&quot;50Pt&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/82/c1/82c1c554-d23e-47f4-b0d3-eb7c9ee6871c.jpeg&quot; width=&quot;476.5&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;qdgj&quot;&gt;Заходим в гугл фото и выбираем эту фотографию, видим, что она взята из какого фейсбук аккаунта и сразу рядом с ним находится фото с другой стороны памяника с нужным нам форматом:&lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;JdTx&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/f2/f8/f2f80d69-1d3a-4d4a-b80e-23a884348140.png&quot; width=&quot;790&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure id=&quot;XPqD&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/bb/5a/bb5a4641-1698-4eb0-9c1a-db033dc55b34.png&quot; width=&quot;744&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;KnPP&quot;&gt;Вот и флаг, вводим его согласно формату и все.&lt;/p&gt;
  &lt;p id=&quot;YcoR&quot;&gt;&lt;br /&gt;&lt;strong&gt;grodno{Marcellina_Jakubowskich-Protasowicz}&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;1zjV&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;Hiyo&quot;&gt;Минское Кафе&lt;/h1&gt;
  &lt;blockquote id=&quot;kAVI&quot;&gt;Каждое соревнование от БГУИР начинается с любимой общаги.&lt;br /&gt;Сидим мы голодные на 10 этаже и вспоминаем, что напротив неё есть шаурмечная с необычным названием, которое не внушает доверия.  &lt;br /&gt;Решили почитать отзывы и поняли, что лучше быть голодным, но здоровым.&lt;br /&gt;В отзыве было написано что-то про &amp;quot;конвульсии&amp;quot; и какие то &amp;quot;набеги на туалет&amp;quot;.  &lt;br /&gt;Найдите ник человека, который оставил отзыв, заставивший нас передумать.&lt;/blockquote&gt;
  &lt;blockquote id=&quot;a87V&quot;&gt;Фортам ответа: grodno{Ник_Человека}&lt;br /&gt;&lt;/blockquote&gt;
  &lt;p id=&quot;9e8h&quot;&gt;&lt;/p&gt;
  &lt;figure id=&quot;p9nH&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/27/f2/27f2d9a6-5bfb-45c7-8542-f1c6186573fe.jpeg&quot; width=&quot;640&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;yeoX&quot;&gt;Опять используем поиск по фото, находим что это 1 общежитие БГУИР. Находим его на карте:&lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;RLyt&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/f3/fb/f3fb612a-330f-49ab-8907-a55f98eeebb2.png&quot; width=&quot;603&quot; /&gt;
  &lt;/figure&gt;
  &lt;blockquote id=&quot;LVi2&quot;&gt;напротив неё есть шаурмечная с необычным названием, которое не внушает доверия.&lt;/blockquote&gt;
  &lt;p id=&quot;r7jd&quot;&gt;Как говорит нам описание. Из необычных названий тут есть &amp;quot;Эль Буритто&amp;quot; со средним отзывом 3.7&lt;br /&gt;Переходим в отзывы и включаем поиск по странице, вводим слово &amp;quot;набеги&amp;quot; из описания и находим нужный отзыв:&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;xb2t&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/97/79/97793e0c-1312-4707-822c-7c76ef5005e1.png&quot; width=&quot;585&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;aFUd&quot;&gt;Флаг: &lt;strong&gt;grodno{Dasha_Grob}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;QfNF&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;wnSV&quot;&gt;Финал&lt;/h2&gt;
  &lt;blockquote id=&quot;zfmK&quot;&gt;Недавно у нас прошел финал собственного CTF, на который к сожалению не смогла приехать команда, занявшая 1 место на квалификации. Найдите название этой команды.&lt;/blockquote&gt;
  &lt;blockquote id=&quot;wDcv&quot;&gt;Формат ответа: grodno{Название_Команды}&lt;/blockquote&gt;
  &lt;p id=&quot;EX9r&quot;&gt;&lt;br /&gt;Заходим на ТГ канал ФАМИ и находим нужный нам пост: &lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;6nkd&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/76/72/76725b49-fa2d-4c07-9067-fe717b939d23.png&quot; width=&quot;691&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;a4r9&quot;&gt;Видим название Grodno CTF 2026 и вводим его в гугл: &lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;lMWP&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/1e/37/1e37e5b8-5668-43df-8af0-78a59bc4f313.png&quot; width=&quot;726&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;1jOe&quot;&gt;1 сайт это сайт на котором проходит практика, так что он нам не подходит, а второй сайт очень интересный, на сайте пишут, что топ 15 команд примут участие в финале в Гродно, следовательно отборочный этап назывался &lt;strong&gt;New Year CTF 2026&lt;/strong&gt;.&lt;/p&gt;
  &lt;p id=&quot;MJKi&quot;&gt;В райтапах я использую то, что могли использовать и знать все участники, так что вместо использования сайта CTFtime используем простой гпт:&lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;b09L&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/9f/a3/9fa3a3db-a023-41ad-8e4c-bed907b180bb.png&quot; width=&quot;624&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;l0xc&quot;&gt;Флаг:&lt;strong&gt; grodno{Pudge_Fun_Club}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;957P&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;6tpw&quot;&gt;Разминка&lt;/h2&gt;
  &lt;blockquote id=&quot;OF2H&quot;&gt;Начнем с простого. У нашего университета есть официальная команда по кибербезопасности, найдите ее название и ник, под которым ее действующий капитан выступает на CTFах.&lt;/blockquote&gt;
  &lt;blockquote id=&quot;gV2D&quot;&gt;Формат ответа: grodno{Название_Ник}&lt;/blockquote&gt;
  &lt;p id=&quot;Bxq4&quot;&gt;&lt;br /&gt;Прямо на сайте написано: by Beavers0, вот и название команды. Осталось найти ник, под которым выступает ее капитан: &lt;br /&gt;Найти можно 2 способами: &lt;br /&gt;- Зайти на сайт CTFtime и попробовать всех участников, или просто спросить у гпт: &lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;1Isi&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/d8/4f/d84f0b34-fc3f-4abc-8fdc-966fe7daf78f.png&quot; width=&quot;572.5&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;C6Aw&quot;&gt;Флаг: &lt;strong&gt;grodno{Beavers0_Kata}&lt;/strong&gt;&lt;/p&gt;
  &lt;h2 id=&quot;C1bx&quot;&gt;&lt;br /&gt;&lt;br /&gt;Ура, работа!&lt;/h2&gt;
  &lt;blockquote id=&quot;zHzO&quot;&gt;По полученным сверхсекретным данным, в ближайшее время в Гродно будет открыт офис крупной белорусской конторы по защите информации. Найдите адрес где он будет располагаться.&lt;/blockquote&gt;
  &lt;blockquote id=&quot;jmXF&quot;&gt;Формат ответа: grodno{Улица_Пушкина_10}&lt;/blockquote&gt;
  &lt;p id=&quot;e3NB&quot;&gt;Сделаем максимально просто:&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;6uoy&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/ec/34/ec3493a2-fc1c-419d-95ab-5bbbedd574b8.png&quot; width=&quot;614&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;7ReR&quot;&gt;Флаг:&lt;strong&gt; grodno{Улица_Дзержинского_40}&lt;/strong&gt;&lt;/p&gt;
  &lt;h2 id=&quot;TUat&quot;&gt;&lt;br /&gt;&lt;br /&gt;Сильнейший Бобёр&lt;/h2&gt;
  &lt;blockquote id=&quot;4QeW&quot;&gt;Райтап будет позже, так как эта задача пойдет на международное соревнование.&lt;/blockquote&gt;
  &lt;p id=&quot;cDy2&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;mUMM&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;eZ1S&quot;&gt;&lt;br /&gt;Миш, мне 我他妈无所谓, я так чувствую&lt;/h2&gt;
  &lt;blockquote id=&quot;2pCJ&quot;&gt;Олег Тиньков, 300 метров от вас.   &lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;Формат флага: **grodno{координаты округленные до 4 знаков после запятой}**&lt;br /&gt;Пример: **grodno{1.2345, 1.2345}**&lt;/blockquote&gt;
  &lt;p id=&quot;jTs4&quot;&gt;&lt;br /&gt;Гугл фото дает нам некоторые подсказки: &lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;IIRg&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/65/37/6537b0d3-061e-4e68-9524-f2c14e513231.png&quot; width=&quot;539&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;gFkk&quot;&gt;Переходим на тикток, который прикреплен в ответе ИИ и можем увидеть точку на карте, где сделано фото. Изза качества видео нельзя точно сразу сказать место, но можно найти конкретную дорогу.&lt;br /&gt;&lt;br /&gt;На фото Олег проезжает пешеходный переход, так что просто включаем режим &amp;#x27;Спутник&amp;#x27; на гугл картах и ищем пешеходные переходы. На 3 пешеходном наконец то находим Олега и смотрим координаты.&lt;br /&gt;&lt;br /&gt;Флаг: &lt;strong&gt;grodno{44.4784,9.1002}&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>beaverszero:NYCTFCrypto2026</id><link rel="alternate" type="text/html" href="https://teletype.in/@beaverszero/NYCTFCrypto2026?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=beaverszero"></link><title>Crypto Writeups</title><published>2026-01-16T23:35:17.586Z</published><updated>2026-01-16T23:35:17.586Z</updated><summary type="html">Write-ups for Crypto tasks by Kata. English is not my main language, so I might have problems with explanations.</summary><content type="html">
  &lt;h2 id=&quot;NWZW&quot;&gt;Preview&lt;/h2&gt;
  &lt;p id=&quot;zyB9&quot;&gt;Write-ups for Crypto tasks by Kata. English is not my main language, so I might have problems with explanations.&lt;/p&gt;
  &lt;p id=&quot;vmkE&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;XaCx&quot;&gt;babyCrypto&lt;/h1&gt;
  &lt;pre id=&quot;oFGN&quot;&gt;We have many messages with short MAC-signatures.&lt;/pre&gt;
  &lt;pre id=&quot;m3uE&quot;&gt;Recover the salt and find the flag.&lt;/pre&gt;
  &lt;p id=&quot;FNDk&quot;&gt;&lt;/p&gt;
  &lt;pre id=&quot;bD5c&quot;&gt;&amp;quot;flag_enc&amp;quot;: &amp;quot;641ca46700a47839aa6f1aae7131bb761a947a01be7131b86202bf5c0fbc621794540fa7770bb97e&amp;quot;,
  &amp;quot;params&amp;quot;: {
    &amp;quot;hmac&amp;quot;: &amp;quot;HMAC-SHA256&amp;quot;,
    &amp;quot;trunc_bytes&amp;quot;: 4,
    &amp;quot;salt_len_bytes&amp;quot;: 3,
    &amp;quot;note&amp;quot;: &amp;quot;salt is secret and same for all messages; flag_enc is XOR(flag, salt repeated)&amp;quot;
  }
&lt;/pre&gt;
  &lt;p id=&quot;9Fdn&quot;&gt;Here is the most important part of the file.&lt;/p&gt;
  &lt;p id=&quot;ESTg&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;0Mjw&quot;&gt;Salt is only 3 bytes (2²⁴ possibilities) and it’s reused everywhere. Truncating HMAC to 4 bytes gives us a cheap verification oracle we can brute-force the 3-byte salt, checking that the truncated HMACs for &lt;strong&gt;all&lt;/strong&gt; known messages match the provided MACs. Once salt is known, XORing &lt;code&gt;flag_enc&lt;/code&gt; with repeated salt yields the flag.&lt;/p&gt;
  &lt;p id=&quot;6tp9&quot;&gt;Salt length = 3 bytes -&amp;gt; at most 16777216 candidates. That’s easily brute-forceable.&lt;/p&gt;
  &lt;p id=&quot;7Pu7&quot;&gt;So the attack is iterate over all 2²⁴ salts, compute truncated HMAC for a small set of known messages quickly to filter, then confirm on the rest. When the salt matches every pair, XOR-decrypt &lt;code&gt;flag_enc&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;1uob&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;A6vz&quot;&gt;Decoder:&lt;/p&gt;
  &lt;pre id=&quot;wSLN&quot;&gt;import json, hmac, hashlib
from binascii import unhexlify, hexlify

data = {
  &amp;quot;dataset&amp;quot;: [
    {&amp;quot;message&amp;quot;:&amp;quot;note_0:1d7b4557164378a4&amp;quot;,&amp;quot;mac&amp;quot;:&amp;quot;ceaedcf7&amp;quot;},...  ],
  &amp;quot;flag_enc&amp;quot;:&amp;quot;641ca46700a47839aa6f1aae7131bb761a947a01be7131b86202bf5c0fbc621794540fa7770bb97e&amp;quot;
}

pairs = [(d[&amp;#x27;message&amp;#x27;], d[&amp;#x27;mac&amp;#x27;]) for d in data[&amp;#x27;dataset&amp;#x27;]]
flag_enc = data[&amp;#x27;flag_enc&amp;#x27;]

def truncated_hmac_hex(salt_bytes, message):
    return hmac.new(salt_bytes, message.encode(), hashlib.sha256).digest()[:4].hex()

filter_pairs = pairs[:6]
full_pairs = pairs

found_salt = None
print(&amp;quot;Starting brute force over 2^24 salts (3 bytes). This will take some time but is feasible.&amp;quot;)
for k in range(0, 1&amp;lt;&amp;lt;24):
    salt = k.to_bytes(3, &amp;#x27;big&amp;#x27;)
    good = True
    for m, mac in filter_pairs:
        if truncated_hmac_hex(salt, m) != mac:
            good = False
            break
    if not good:
        continue
    # full verification
    for m, mac in full_pairs:
        if truncated_hmac_hex(salt, m) != mac:
            good = False
            break
    if good:
        found_salt = salt
        break

if found_salt is None:
    print(&amp;quot;No salt found with key=salt (big-endian). Try other interpretations (little-endian) or alternate HMAC usage.&amp;quot;)
else:
    print(&amp;quot;Found salt (hex):&amp;quot;, found_salt.hex())
    # decode flag_enc by repeating salt
    enc = unhexlify(flag_enc)
    dec = bytes([enc[i] ^ found_salt[i % len(found_salt)] for i in range(len(enc))])
    try:
        print(&amp;quot;Recovered flag:&amp;quot;, dec.decode())
    except UnicodeDecodeError:
        print(&amp;quot;Recovered bytes (hex):&amp;quot;, dec.hex())
&lt;/pre&gt;
  &lt;p id=&quot;b1EH&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;wPrA&quot;&gt;Flag: &lt;strong&gt;grodno{Walter_put_your_salt_away_Walter}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;kv8P&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;IqVf&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;Zog9&quot;&gt;CryBaby&lt;/h1&gt;
  &lt;blockquote id=&quot;H0CS&quot;&gt;The only thing I want to do is cry.&lt;br /&gt;Flag format: grodnogrodno{}&lt;/blockquote&gt;
  &lt;p id=&quot;RhiT&quot;&gt;&lt;/p&gt;
  &lt;pre id=&quot;cR7I&quot;&gt;import random
import binascii
import base64
 def xor_bytes(a: bytes, b: bytes) -&amp;gt; bytes:
length = min(len(a), len(b))
return bytes([a[i] ^ b[i] for i in range(length)])
 def encrypt_round(s: str) -&amp;gt; str:
b = s.encode(&amp;#x27;utf-8&amp;#x27;)
if random.randrange(2) == 0:
return base64.encodebytes(b).decode(&amp;#x27;ascii&amp;#x27;)
else:
return binascii.hexlify(b).decode(&amp;#x27;ascii&amp;#x27;)
 def main():
flag = b&amp;quot;REDACTED&amp;quot;
assert len(flag) == 28
  l = flag[:14]
r = flag[14:]
xored = xor_bytes(l, r) 
c = binascii.hexlify(xored).decode(&amp;#x27;ascii&amp;#x27;)
rounds = random.randint(1, 20)
  for _ in range(rounds):
c = encrypt_round(c)
  with open(&amp;#x27;chall.txt&amp;#x27;, &amp;#x27;w&amp;#x27;, encoding=&amp;#x27;utf-8&amp;#x27;) as f:
f.write(c)
 if __name__ == &amp;quot;__main__&amp;quot;:
main()&lt;/pre&gt;
  &lt;p id=&quot;7U2I&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;6Puo&quot;&gt;&lt;strong&gt;What the challenge does&lt;/strong&gt;&lt;/p&gt;
  &lt;ol id=&quot;adTP&quot;&gt;
    &lt;li id=&quot;sQMR&quot;&gt;&lt;code&gt;flag&lt;/code&gt; is 28 bytes long and split into two halves &lt;code&gt;L&lt;/code&gt; and &lt;code&gt;R&lt;/code&gt; of 14 bytes each.&lt;/li&gt;
    &lt;li id=&quot;yJxT&quot;&gt;They compute &lt;code&gt;xored = L XOR R&lt;/code&gt; (14 bytes).&lt;/li&gt;
    &lt;li id=&quot;0KK8&quot;&gt;&lt;code&gt;xored&lt;/code&gt; is hexlified once (so we have an ascii hex string).&lt;/li&gt;
    &lt;li id=&quot;K2k0&quot;&gt;Then the program applies a &lt;em&gt;random&lt;/em&gt; number (1..20) of rounds; each round chooses randomly between:&lt;/li&gt;
    &lt;ul id=&quot;c6SG&quot;&gt;
      &lt;li id=&quot;8m0U&quot;&gt;&lt;code&gt;base64.encodebytes(...)&lt;/code&gt; (note: this inserts newline bytes), or&lt;/li&gt;
      &lt;li id=&quot;3cYn&quot;&gt;&lt;code&gt;binascii.hexlify(...)&lt;/code&gt; (pure hex)&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li id=&quot;GXWF&quot;&gt;Final output is written to &lt;code&gt;chall.txt&lt;/code&gt;.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;x5ih&quot;&gt;So the final file is just a chain of alternating hex/base64 encodings of the original 14-byte XOR buffer.&lt;/p&gt;
  &lt;p id=&quot;hTdI&quot;&gt;&lt;strong&gt;Attack:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;1F2E&quot;&gt;Reversing this is straightforward: repeatedly try to decode the blob as hex &lt;strong&gt;and&lt;/strong&gt; as base64. Because each round deterministically maps bytes→ascii→bytes, we can do a branching search (BFS/DFS) over decode choices until we reach a 14-byte result. Once we have the 14 bytes &lt;code&gt;xored&lt;/code&gt;, we reconstruct &lt;code&gt;L&lt;/code&gt; and &lt;code&gt;R&lt;/code&gt; from the known flag format:&lt;/p&gt;
  &lt;ul id=&quot;iLhT&quot;&gt;
    &lt;li id=&quot;PhUs&quot;&gt;We know &lt;code&gt;L&lt;/code&gt; begins with &lt;code&gt;b&amp;quot;grodnogrodno{&amp;quot;&lt;/code&gt; (13 bytes).&lt;/li&gt;
    &lt;li id=&quot;SNwn&quot;&gt;We know the last byte of &lt;code&gt;R&lt;/code&gt; is &lt;code&gt;b&amp;#x27;}&amp;#x27;&lt;/code&gt;.&lt;br /&gt; Given &lt;code&gt;L[i]&lt;/code&gt; (for i=0..12) and &lt;code&gt;xored[i]&lt;/code&gt;, compute &lt;code&gt;R[i] = L[i] ^ xored[i]&lt;/code&gt;. Set &lt;code&gt;R[13] = ord(&amp;#x27;}&amp;#x27;)&lt;/code&gt; and compute &lt;code&gt;L[13] = xored[13] ^ R[13]&lt;/code&gt;. That gives full &lt;code&gt;L&lt;/code&gt; and &lt;code&gt;R&lt;/code&gt;. The original flag is &lt;code&gt;L || R&lt;/code&gt;.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;7KGJ&quot;&gt;There is no further brute force: once &lt;code&gt;xored&lt;/code&gt; is recovered the flag is unique.&lt;/p&gt;
  &lt;p id=&quot;qc1f&quot;&gt;&lt;strong&gt;&lt;br /&gt;Solver:&lt;/strong&gt;&lt;/p&gt;
  &lt;pre id=&quot;p09k&quot;&gt;import sys
import binascii
import base64
from collections import deque

def try_hex(b: bytes):
    s = b.replace(b&amp;#x27;\n&amp;#x27;, b&amp;#x27;&amp;#x27;).replace(b&amp;#x27;\r&amp;#x27;, b&amp;#x27;&amp;#x27;).replace(b&amp;#x27; &amp;#x27;, b&amp;#x27;&amp;#x27;)
    try:
        return binascii.unhexlify(s)
    except (binascii.Error, ValueError):
        return None

def try_b64(b: bytes):
    try:
        return base64.b64decode(b, validate=False)
    except (binascii.Error, ValueError):
        return None

def reconstruct_flag_from_xored(xored: bytes, prefix: bytes = b&amp;quot;grodnogrodno{&amp;quot;, suffix_last: int = ord(&amp;#x27;}&amp;#x27;)):
    if len(xored) != 14:
        raise ValueError(&amp;quot;xored must be 14 bytes&amp;quot;)

    L = [None] * 14
    R = [None] * 14

    for i in range(min(len(prefix), 14)):
        L[i] = prefix[i]

    for i in range(min(len(prefix), 14)):
        R[i] = L[i] ^ xored[i]

    R[13] = suffix_last
    L[13] = xored[13] ^ R[13]

    L_bytes = bytes([x if x is not None else 0 for x in L])
    R_bytes = bytes([x if x is not None else 0 for x in R])
    return L_bytes + R_bytes, L_bytes, R_bytes

def printable(b: bytes):
    try:
        s = b.decode(&amp;#x27;utf-8&amp;#x27;)
        return all(32 &amp;lt;= ord(c) &amp;lt; 127 for c in s)
    except:
        return False

def find_xored_candidates(start_bytes: bytes, max_nodes=2000000):
    seen = set()
    q = deque()
    q.append(start_bytes)
    seen.add(start_bytes)
    found = set()
    nodes = 0

    while q:
        nodes += 1
        if nodes &amp;gt; max_nodes:
            print(f&amp;quot;[!] hit max node limit ({max_nodes}), aborting search.&amp;quot;)
            break

        cur = q.popleft()

        if len(cur) == 14:
            found.add(cur)
            continue

        h = try_hex(cur)
        if h is not None and h not in seen:
            seen.add(h)
            q.append(h)

        b = try_b64(cur)
        if b is not None and b not in seen:
            seen.add(b)
            q.append(b)

    return found

def main():
    path = sys.argv[1] if len(sys.argv) &amp;gt; 1 else &amp;quot;chall.txt&amp;quot;
    raw = open(path, &amp;quot;rb&amp;quot;).read().strip(b&amp;quot;\n\r&amp;quot;)

    print(f&amp;quot;Loaded {path} ({len(raw)} bytes). Starting BFS decode...&amp;quot;)
    candidates = find_xored_candidates(raw)

    if not candidates:
        print(&amp;quot;No 14-byte candidates found. Try increasing max_nodes or check input.&amp;quot;)
        return

    print(f&amp;quot;Found {len(candidates)} candidate(s) of length 14.&amp;quot;)
    for i, x in enumerate(candidates, 1):
        print(f&amp;quot;\nCandidate #{i}: xored = {binascii.hexlify(x).decode()}&amp;quot;)
        flag, L, R = reconstruct_flag_from_xored(x, prefix=b&amp;quot;grodnogrodno{&amp;quot;, suffix_last=ord(&amp;#x27;}&amp;#x27;))
        print(&amp;quot;Left  (14):&amp;quot;, L)
        print(&amp;quot;Right (14):&amp;quot;, R)
        try:
            print(&amp;quot;Flag bytes:&amp;quot;, flag)
            print(&amp;quot;Flag string:&amp;quot;, flag.decode(&amp;#x27;utf-8&amp;#x27;))
        except Exception:
            print(&amp;quot;Flag (hex):&amp;quot;, binascii.hexlify(flag).decode())

if __name__ == &amp;quot;__main__&amp;quot;:
    main()&lt;/pre&gt;
  &lt;p id=&quot;0T5H&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;JFTF&quot;&gt;Flag:&lt;strong&gt; grodnogrodno{new_year_xor26}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;Gx9n&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;cgSe&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;uann&quot;&gt;ChristmasRSA&lt;/h1&gt;
  &lt;blockquote id=&quot;1Rk7&quot;&gt;How can you celebrate the New Year without RSA?&lt;/blockquote&gt;
  &lt;p id=&quot;PeO5&quot;&gt;&lt;/p&gt;
  &lt;pre id=&quot;iC3v&quot;&gt;from Crypto.Util.number import getPrime, bytes_to_long
from typing import Tuple
 E = 4
 def choose_prime_with_quadratic_residue(message_int: int, bits: int = 512) -&amp;gt; Tuple[int, int]:
attempts = 0
while True:
attempts += 1
p = getPrime(bits)
val = pow(message_int, E, p)
if pow(val, (p - 1) // 2, p) == 1:
return p, attempts
 def main() -&amp;gt; None:
FLAG = b&amp;quot;REDACTED&amp;quot;
m_int = bytes_to_long(FLAG)
  prime_n, tries = choose_prime_with_quadratic_residue(m_int, bits=512)
ciphertext = pow(m_int, E, prime_n)
  print(&amp;quot;n =&amp;quot;, prime_n)
print(&amp;quot;c =&amp;quot;, ciphertext)
 if __name__ == &amp;quot;__main__&amp;quot;:
main()&lt;/pre&gt;
  &lt;p id=&quot;zikV&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;GgGL&quot;&gt;Task work modulo prime &lt;code&gt;p&lt;/code&gt;. If &lt;code&gt;c = m^E mod p&lt;/code&gt; with &lt;code&gt;E = 2^k&lt;/code&gt;, then &lt;code&gt;m&lt;/code&gt; is an E-th root of &lt;code&gt;c&lt;/code&gt;. To find all E-th roots we do:&lt;/p&gt;
  &lt;ul id=&quot;lCp4&quot;&gt;
    &lt;li id=&quot;Vnff&quot;&gt;If &lt;code&gt;E = 1&lt;/code&gt; answer is &lt;code&gt;m = c&lt;/code&gt;.&lt;/li&gt;
    &lt;li id=&quot;itNq&quot;&gt;If &lt;code&gt;E = 2^k&lt;/code&gt; and &lt;code&gt;k &amp;gt; 0&lt;/code&gt;, then compute modular square roots of &lt;code&gt;c&lt;/code&gt;. For each square root &lt;code&gt;r&lt;/code&gt; we recursively compute all &lt;code&gt;2^{k-1}&lt;/code&gt; roots of &lt;code&gt;r&lt;/code&gt; for exponent &lt;code&gt;2^{k-1}&lt;/code&gt;. We obtain up to &lt;code&gt;2^k&lt;/code&gt; candidates. Tonelli–Shanks solves modular sqrt &lt;code&gt;r^2 ≡ x (mod p)&lt;/code&gt; for prime &lt;code&gt;p&lt;/code&gt; efficiently (fast for 512-bit primes). Then test the candidates for the flag pattern.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;zFt2&quot;&gt;&lt;strong&gt;Attack:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;2kcs&quot;&gt;Parse &lt;code&gt;n&lt;/code&gt;, &lt;code&gt;c&lt;/code&gt;, and &lt;code&gt;E&lt;/code&gt; (verify &lt;code&gt;E&lt;/code&gt; is a power of two; if not — beware).&lt;/p&gt;
  &lt;p id=&quot;slTa&quot;&gt;Check &lt;code&gt;c&lt;/code&gt; is a quadratic residue modulo &lt;code&gt;n&lt;/code&gt; before taking sqrt (Tonelli–Shanks requires that).&lt;/p&gt;
  &lt;p id=&quot;a7RR&quot;&gt;Recursively compute all &lt;code&gt;E&lt;/code&gt;-th roots of &lt;code&gt;c&lt;/code&gt; using Tonelli–Shanks for each square-root step.&lt;/p&gt;
  &lt;p id=&quot;yRxM&quot;&gt;For each root &lt;code&gt;r&lt;/code&gt;, convert to bytes (&lt;code&gt;long_to_bytes&lt;/code&gt;) and test for the flag format &lt;code&gt;grodnogrodno{...}&lt;/code&gt; (or any other validation you know).&lt;/p&gt;
  &lt;p id=&quot;fDDy&quot;&gt;One of candidates should decode to the flag.&lt;/p&gt;
  &lt;p id=&quot;Mym4&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;MGAe&quot;&gt;&lt;strong&gt;Solver:&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;pre id=&quot;mStk&quot;&gt;from Crypto.Util.number import long_to_bytes
from typing import List, Optional
 MOD = 0x00
CIPHER = 0x00
E = 5 
 def legendre_symbol(a: int, p: int) -&amp;gt; int:
return pow(a % p, (p - 1) // 2, p)
 def sqrt_mod_prime(n: int, p: int) -&amp;gt; Optional[int]:
n %= p
if n == 0:
return 0
if legendre_symbol(n, p) != 1:
return None
  if p % 4 == 3:
r = pow(n, (p + 1) // 4, p)
return r
  q = p - 1
s = 0
while q % 2 == 0:
q //= 2
s += 1
  z = 2
while legendre_symbol(z, p) != p - 1:
z += 1
  c = pow(z, q, p)
r = pow(n, (q + 1) // 2, p)
t = pow(n, q, p)
m = s
  while t % p != 1:
i = 1
t2i = (t * t) % p
while t2i % p != 1:
t2i = (t2i * t2i) % p
i += 1
if i &amp;gt;= m:
raise RuntimeError(&amp;quot;Tonelli-Shanks failed to find i&amp;quot;)
b = pow(c, 1 &amp;lt;&amp;lt; (m - i - 1), p)
r = (r * b) % p
c = (b * b) % pt = (t * c) % p
m = i
return r
 def all_e_th_roots(value: int, e: int, p: int) -&amp;gt; List[int]:
if e &amp;lt; 1:
return []
  if e == 1:
return [value % p]
  if legendre_symbol(value, p) != 1:
return []
  r1 = sqrt_mod_prime(value, p)
if r1 is None:
return []
  r2 = p - r1
roots = []
roots += all_e_th_roots(r1, e // 2, p)
if r2 != r1:
roots += all_e_th_roots(r2, e // 2, p)
uniq = sorted({r % p for r in roots})
return uniq
 def try_print_candidates(roots: List[int]):
if not roots:
return
  for idx, r in enumerate(roots, 1):
b = long_to_bytes(r)
hx = r.to_bytes((r.bit_length() + 7) // 8, &amp;#x27;big&amp;#x27;).hex() or &amp;quot;00&amp;quot;
try:
s = b.decode(&amp;#x27;utf-8&amp;#x27;)
dec = f&amp;quot;utf8: {s}&amp;quot;
except Exception:
dec = &amp;quot;utf8: &amp;lt;non-utf8 bytes&amp;gt;&amp;quot;
 if __name__ == &amp;quot;__main__&amp;quot;:
ls = legendre_symbol(CIPHER, MOD)
if ls != 1:
print(&amp;quot;[!] CIPHER is not a quadratic residue modulo MOD — no solutions by this method.&amp;quot;)
else:
roots = all_e_th_roots(CIPHER, E, MOD)
try_print_candidates(roots)&lt;/pre&gt;
  &lt;p id=&quot;CsTK&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;4oY9&quot;&gt;Flag: &lt;strong&gt;grodno{ohhhh_u_solved_this}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;79hn&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;PCoh&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;PQ7U&quot;&gt;NewYearsAES&lt;/h1&gt;
  &lt;blockquote id=&quot;66RB&quot;&gt;Santa tried to upgrade the gift-protection system by creating their own “New Year’s AES” based on ternary arithmetic.&lt;br /&gt;But something went wrong — an important holiday message ended up completely encrypted.&lt;br /&gt;Can you help him decode the message?&lt;/blockquote&gt;
  &lt;p id=&quot;16WK&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;aBNM&quot;&gt;Given &lt;code&gt;all_enc&lt;/code&gt; — the list of ciphertexts for all &lt;code&gt;3^9&lt;/code&gt; plaintexts under a custom tryte-based block cipher (A3S), and &lt;code&gt;enc_flag&lt;/code&gt; = &lt;code&gt;sha512(key) XOR pad(flag)&lt;/code&gt;, recover the secret key (and therefore the flag). The author leaked a massive dataset that makes a linear / differential attack feasible: recover last round key trytes by statistics, build linear equations from the round expansion &amp;amp; checkpoints, solve the GF(3) linear system for the whole round-key schedule, reconstruct the master key and recover the flag.&lt;/p&gt;
  &lt;h2 id=&quot;DftC&quot;&gt;What I assumed / what is given&lt;/h2&gt;
  &lt;ul id=&quot;9tVX&quot;&gt;
    &lt;li id=&quot;NuGu&quot;&gt;You have &lt;code&gt;all_enc&lt;/code&gt; — encryption of every plaintext (0..3^9−1) under the unknown key (produced by &lt;code&gt;gen()&lt;/code&gt;).&lt;/li&gt;
    &lt;li id=&quot;cxZ2&quot;&gt;You have &lt;code&gt;enc_flag&lt;/code&gt; = &lt;code&gt;sha512(key) XOR pad(flag)&lt;/code&gt; and &lt;code&gt;secret.txt&lt;/code&gt; structure is &lt;code&gt;(flag, key)&lt;/code&gt;. In practice the challenge supplies &lt;code&gt;all_enc&lt;/code&gt; and &lt;code&gt;enc_flag&lt;/code&gt;.&lt;/li&gt;
    &lt;li id=&quot;k2hG&quot;&gt;The cipher operates over GF(3), trytes (3-trit groups), with &lt;code&gt;sizeT = 3&lt;/code&gt;, &lt;code&gt;sizeW = 3&lt;/code&gt;, &lt;code&gt;len = 8&lt;/code&gt; round keys, and the provided &lt;code&gt;mini.py&lt;/code&gt; contains the &lt;code&gt;a3s()&lt;/code&gt; encryptor / key schedule implementation (we used it heavily).&lt;/li&gt;
    &lt;li id=&quot;TAun&quot;&gt;We can run &lt;code&gt;a3s(pt,key)&lt;/code&gt; or symbolic variants and we can mutate functions to produce affine / linear descriptions.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;CTDa&quot;&gt;Important overall observation: most of the cipher is linear (over GF(3)) except for SBOX usages. That means we can treat the forward transform up to selected checkpoints as an affine map in &lt;code&gt;{pt, kr}&lt;/code&gt;:&lt;br /&gt; &lt;code&gt;ct = ptmat * pt + ptconst + kptmat * kr&lt;/code&gt;&lt;br /&gt; where &lt;code&gt;kr&lt;/code&gt; are flattened round-key trytes (unknown). Once we can produce &lt;code&gt;ptmat&lt;/code&gt;, &lt;code&gt;ptconst&lt;/code&gt;, &lt;code&gt;kptmat&lt;/code&gt; we can combine known plaintext/ciphertext pairs into linear equations about &lt;code&gt;kr&lt;/code&gt;. By also recovering the last round key trytes via statistical differentials we reduce the unknowns drastically and get a solvable linear system.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;3Kox&quot;&gt;High-level attack plan (summary)&lt;/h2&gt;
  &lt;ol id=&quot;pNzd&quot;&gt;
    &lt;li id=&quot;k5OU&quot;&gt;Convert &lt;code&gt;all_enc&lt;/code&gt; into the internal 3×3 tryte format and flatten to 27-trit vectors (GF(3) vectors).&lt;/li&gt;
    &lt;li id=&quot;pnVh&quot;&gt;Produce symbolic affine model of the round function up to checkpoints (two checkpoints used):&lt;br /&gt; &lt;code&gt;ct = ptmat * pt + ptconst + kptmat * kr&lt;/code&gt;.&lt;/li&gt;
    &lt;li id=&quot;WyaB&quot;&gt; Using the affine model for a later checkpoint, compute &lt;code&gt;rhs_prob = server_ctv.T - (ptmat * server_ptv + ptconst)&lt;/code&gt; and select the most frequent trit per coordinate to form an approximate RHS where &lt;code&gt;M*kr = rhs&lt;/code&gt;.&lt;/li&gt;
    &lt;li id=&quot;sB3R&quot;&gt;Assemble all linear constraints:&lt;/li&gt;
    &lt;ul id=&quot;qAzN&quot;&gt;
      &lt;li id=&quot;SvaE&quot;&gt;27 equations from known last_key positions,&lt;/li&gt;
      &lt;li id=&quot;TyJf&quot;&gt;27 from 3rd step &lt;code&gt;M*kr = rhs&lt;/code&gt;,&lt;/li&gt;
      &lt;li id=&quot;UhmD&quot;&gt;linear constraints from the key expansion that are purely linear,&lt;/li&gt;
      &lt;li id=&quot;Vvm0&quot;&gt;optional key-expansion rows that touch the S-box (we try include them, but can drop small blocks if they conflict).&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li id=&quot;hDsi&quot;&gt;Attempt to solve the GF(3) linear system. If non-unique or failures appear, try removing each small 3-row SBOX block (a handful of variants) — one variant gives a unique solution.&lt;/li&gt;
    &lt;li id=&quot;8NCH&quot;&gt;Convert solved &lt;code&gt;kr&lt;/code&gt; back to canonical key bytes, derive &lt;code&gt;sha512(key)&lt;/code&gt;, XOR with &lt;code&gt;enc_flag&lt;/code&gt; to obtain &lt;code&gt;flag&lt;/code&gt;.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;EdMe&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;Ahcv&quot;&gt;My Solver:&lt;/p&gt;
  &lt;pre id=&quot;QHjh&quot;&gt;import time
import numpy as np
t = time.time()
import os, sys

cwd = os.getcwd()
if cwd not in sys.path:
    sys.path.insert(0, cwd)

import AES as orig 
from AES import *  
from output import *   

F = GF(3) # Field we are working in

server_ct = [up(int_to_tyt(byt_to_int(ct)), W_SIZE ** 2, int_to_tyt(0)[0])[-1] for ct in all_enc]
server_ctv = [vector(F, [i for j in ct for i in j]) for ct in server_ct]
server_pt = [up(int_to_tyt(pt), W_SIZE ** 2, int_to_tyt(0)[0])[-1] for pt in range(3^9)]
server_ptv = [vector(F, [i for j in pt for i in j]) for pt in server_pt]

ptkr = PolynomialRing(F, [&amp;#x27;pt%d&amp;#x27;%i for i in range(9*3)] + [&amp;#x27;kr%d&amp;#x27;%i for i in range(9*3*8)]).gens()

pt_v = ptkr[:9*3]
pt = tuple(tuple(pt_v[i*3:i*3+3]) for i in range(9))
kr_v = ptkr[9*3:]
kr = tuple(tuple(tuple(kr_v[i*9*3:i*9*3+9*3][j*3:j*3+3]) for j in range(9)) for i in range(8))

xor = lambda a,b: a+b
uxor = lambda a,b: a-b
t_xor = lambda a,b: tuple(x+y for x,y in zip(a,b))
T_xor = lambda a,b: tuple(t_xor(i,j) for i,j in zip(a,b))
t_uxor = lambda a,b: tuple(x-y for x,y in zip(a,b))
T_uxor = lambda a,b: tuple(t_uxor(i,j) for i,j in zip(a,b))

SBOX_TYT = dict((int_to_tyt(i)[0], int_to_tyt(s)[0]) for i,s in enumerate(SBOX))
ISBOX = tuple(SBOX.index(i) for i in range(27))
ISBOX_TYT = dict((int_to_tyt(i)[0], int_to_tyt(s)[0]) for i,s in enumerate(ISBOX))

def sbox_affine(i:tuple):
    return (
        (0 + i[0]*1 + i[1]*1 + i[2]*0),
        (0 + i[0]*0 + i[1]*0 + i[2]*1),
        (1 + i[0]*0 + i[1]*2 + i[2]*2)
    )

def expand(tyt):
    words   = tyt_to_wrd(tyt) 
    size    = len(words)
    rnum    = size + 3
    rcons   = rcon(rnum * 3 // size)

    for i in range(size, rnum * 3):
        k   = words[i - size]
        l   = words[i - 1]
        if i % size == 0:
            s = tuple(sbox_affine(i) for i in rot_wrd(l))
            k = T_xor(k, s)
            k = (t_xor(k[0], rcons[i // size - 1]),) + k[1:]
        else:
            k = T_xor(k, l)
        words = words + (k,)

    return up(down(words[:rnum * 3]), W_SIZE ** 2, int_to_tyt(0)[0])

def tri_mulmod(A, B, mod=POLY):
    c = [0] * (len(mod) - 1)
    for a in A[::-1]:
        c = [0] + c
        x = tuple(b * a for b in B)
        c[:len(x)] = t_xor(c, x)
        n = -c[-1]*mod[-1]
        c[:] = [x+y*n for x,y in zip(c,mod)]
        c.pop()
    return tuple(c)

def tyt_mulmod(A, B, mod=POLY2, mod2=POLY):
    fil = [(0,) * T_SIZE]
    C = fil * (len(mod) - 1)
    for a in A[::-1]:
        C = fil + C
        x = tuple(tri_mulmod(b, a, mod2) for b in B)
        C[:len(x)] = T_xor(C, x)
        num = modinv(mod[-1], mod2)
        num2 = tri_mulmod(num, C[-1], mod2)
        x = tuple(tri_mulmod(m, num2, mod2) for m in mod)
        C[:len(x)] = T_uxor(C, x)

        C.pop()
    return C

def add(a,b):
    return tuple(
        tuple(x+y for x,y in zip(i,j)) for i,j in zip(a,b)
    )

def sub(a):
    return tuple(
        sbox_affine(x) for x in a
    )

def shift(a):
    return [
        a[i] for i in SHIFT_ROWS
    ]

def mix(tyt):
    tyt = list(tyt)
    for i in range(W_SIZE):
        tyt[i::W_SIZE] = tyt_mulmod(tyt[i::W_SIZE], CONS)
    return tuple(tyt)

xkey_mat1 = []   # An array of 135 x 216 numbers
xkey_const1 = [] # A vector of 135 numbers

for i in range(6,24):
    if i%5 == 0: continue
    for j in range(9):
        r = vector([0]*3*8*9)
        r[i*9+j] = 1; r[i*9-9+j] = 2; r[i*9-9*5+j] = 2
        xkey_mat1.append(r)
        xkey_const1.append(0)

xkey_eqns = []
for i in range(5,25,5):
    rcons = ((1, 0, 0), (0, 1, 0), (0, 0, 1), (1, 0, 2))
    k  = [tuple(kr_v[(i-5)*9:(i-5)*9+9][j*3:j*3+3]) for j in range(3)]
    l  = [tuple(kr_v[(i-1)*9:(i-1)*9+9][j*3:j*3+3]) for j in range(3)]
    s = sub(rot_wrd(l))
    k0 = T_xor(k, s)
    k0 = (t_xor(k0[0], rcons[i // 5 - 1]),) + k0[1:]
    k0 = [i for j in k0 for i in j]
    k0 = [i-j for i,j in zip(k0,kr_v[i*9:i*9+9])]
    xkey_eqns.extend(k0)

xkey_mat2 = []
xkey_const2 = []
for k in xkey_eqns:
    r = vector([0]*3*8*9)
    s = 0
    for v,c in k.dict().items():
        if 1 not in v:
            s -= c
            continue
        else:
            vi = list(v).index(1)
            r[vi - 9*3] += c
    xkey_mat2.append(r)
    xkey_const2.append(s)

def forward_to_checkpoint(ctt, keys, checkpoint):
    ctt = add(ctt, keys[0])

    for r in range(1, len(keys) - 1):
        ctt = sub(ctt)
        ctt = shift(ctt)
        ctt = mix(ctt)
        ctt = add(ctt, keys[r])

    if checkpoint==0: return ctt
    ctt  = sub(ctt)
    ctt  = shift(ctt)
    if checkpoint==1: return ctt
    ctt  = add(ctt, keys[-1])

    return ctt

def gen_mat(checkpoint):
    ctt = forward_to_checkpoint(pt, kr, checkpoint)

    ptmat = []
    kptmat = []
    ptconst = []
    for w in ctt:
        for t in w:
            rpt = vector([0]*9*3)
            rkpt = vector([0]*9*3*8)
            s = 0
            for v,c in t.dict().items():
                if 1 not in v: 
                    s += c; continue
                vi = list(v).index(1)
                if vi&amp;gt;=9*3: 
                    rkpt[vi-9*3] += c
                else:    
                    rpt[vi] += c
            ptmat.append(rpt)
            ptconst.append(s)
            kptmat.append(rkpt)
    
    return matrix(F, ptmat), vector(F, ptconst), matrix(F, kptmat)

print(&amp;quot;[*] Exploiting checkpoint 1...&amp;quot;)

ptmat, _, _ = gen_mat(0)
spt = ptmat * matrix(F, server_ptv).T
spt = spt.T

dspt = []
for offset in [1,3,9,27,81,243,729,2187]:
    dspt += [(spt[i]-spt[(i+offset)%3^9], (i,(i+offset)%3^9)) for i in range(3^9)]

all_kscore = []

for cidx in range(9): # enumerate all trytes of &amp;#x60;last_key&amp;#x60;

    pidx = SHIFT_ROWS[cidx]
    kscore = [0]*27
    for dptidx in range(len(dspt)):

        dpt,(i0,i1) = dspt[dptidx]
        dct = (server_ct[i0], server_ct[i1])

        c = (dct[0][cidx], dct[1][cidx])
        p = tuple(dpt[pidx*3:pidx*3+3])

        for k in range(27): 
 
            kt = orig.int_to_tyt(k)[0]
            ci = (orig.t_uxor(c[0],kt), orig.t_uxor(c[1],kt)) 
            ci = (ISBOX_TYT[ci[0]], ISBOX_TYT[ci[1]]) 
            ci = orig.t_uxor(ci[0], ci[1])

            if ci == p: # if matches, add 1 to score
                kscore[k] += 1

        print(dptidx, end=&amp;quot;\r&amp;quot;)
        
    all_kscore.append(kscore)
    print(&amp;quot;[-]&amp;quot;, cidx, &amp;#x27;done!&amp;#x27;)

last_key = [int_to_tyt(all_kscore[i].index(max(all_kscore[i])))[0] for i in range(9)]
last_key = [i for j in last_key for i in j]

ptmat, ptconst, kptmat = gen_mat(1)

spt = ptmat * matrix(F, server_ptv).T + matrix(F, [list(ptconst)]*3^9).T
rhs_prob = matrix(F, server_ctv).T - spt

from collections import Counter
rhs = [sorted(Counter(i).items(), key=lambda x:x[1])[-1][0] for i in rhs_prob]

M = np.zeros((27,216))
for i in range(27):
    M[i][216-27+i] = 1
M = matrix(F,M) + kptmat

def build_solve_mat(xkey_mat2, xkey_const2):
    
    a3s_lhs = [[0]*216 for _ in range(27)]
    a3s_rhs = last_key.copy()
    for i in range(27):
        a3s_lhs[i][216-27+i] = 1
        
    a3s_lhs.extend(list(M))
    a3s_rhs.extend(rhs)    
    a3s_lhs.extend(list(xkey_mat1))
    a3s_rhs.extend(list(xkey_const1))
    
    a3s_lhs.extend(list(xkey_mat2))
    a3s_rhs.extend(list(xkey_const2))

    a3s_lhs = matrix(F, a3s_lhs)
    a3s_rhs = vector(F, a3s_rhs)
    return a3s_lhs, a3s_rhs


for i in range(12): # Try all 12 possible substitutions
    a = xkey_mat2.copy()
    b = xkey_const2.copy()
    a = [p for j,p in enumerate(a) if j not in range(i*3,i*3+3)]
    b = [p for j,p in enumerate(b) if j not in range(i*3,i*3+3)]
    l,r = build_solve_mat(a,b)
    try:
        key_recovered = l.solve_right(r)
        print(&amp;quot;[*] kr found! ^-^&amp;quot;)
        break
    except: # Crashes if l.solve_right has no solutions
        pass
    
assert len(l.right_kernel().basis()) == 0, &amp;quot;Not the only solution!&amp;quot;

key_recovered = [int(i) for i in key_recovered]
key_recovered = tyt_to_int(tuple(tuple(key_recovered[i*3:i*3+3]) for i in range(15)))
key_recovered = int_to_byt(key_recovered)
print(&amp;quot;[*] Original Key:&amp;quot;, key_recovered.hex())

from hashlib import sha512

hsh = sha512(key_recovered).digest()
flag = byte_xor(hsh, enc_flag)
print(flag.decode())
&lt;/pre&gt;
  &lt;p id=&quot;cDQZ&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;1xDS&quot;&gt;Flag: &lt;strong&gt;grodno{th1s_1s_my_m@gnum_0pu5_d0n1_kn0w_how_u_s0lv3d_th1s}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;0LVT&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;x6Dw&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;d732&quot;&gt;Esoteric Language?&lt;/h1&gt;
  &lt;blockquote id=&quot;U1oB&quot;&gt;Can the programming language of the 1C:Enterprise system be called an esoteric language?&lt;/blockquote&gt;
  &lt;p id=&quot;TOGO&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;O5VH&quot;&gt;The app encrypts notes with a tiny, custom stream cipher built from an LCG and byte rotations, then permutes bytes in 3-byte blocks and Base64-encodes the result. With (almost) full key material available for several records we can reproduce the PRNG keystream and fully decrypt every note. The task is: recover missing masked parts (if any), reconstruct the LCG seed, reverse block-permutation, rotate and XOR back the keystream → assemble the flag.&lt;/p&gt;
  &lt;p id=&quot;aagu&quot;&gt;The cipher is entirely deterministic and entirely keyed by three fields: &lt;code&gt;Code&lt;/code&gt;, &lt;code&gt;Ref&lt;/code&gt;, &lt;code&gt;Created&lt;/code&gt;. If you know these, you can deterministically compute the seed and therefore the keystream and decrypt the note.&lt;/p&gt;
  &lt;p id=&quot;GT95&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;BU0J&quot;&gt;Solver:&lt;/p&gt;
  &lt;pre id=&quot;lIPQ&quot;&gt;# organizer_decoder.py
import base64

RECORDS = [
    {
        &amp;quot;name&amp;quot;: &amp;quot;Record1&amp;quot;,
        &amp;quot;Code&amp;quot;: &amp;quot;ARC-01&amp;quot;,
        &amp;quot;RefFull&amp;quot;: &amp;quot;aa11bb22-33cc-44dd-55ee-66ff778899aa&amp;quot;,
        &amp;quot;CreatedFull&amp;quot;: &amp;quot;20251007122345&amp;quot;,
        &amp;quot;NoteEncrypted&amp;quot;: &amp;quot;8yR1aju+AAAX&amp;quot;
    },
    {
        &amp;quot;name&amp;quot;: &amp;quot;Record2&amp;quot;,
        &amp;quot;Code&amp;quot;: &amp;quot;ARC-02&amp;quot;,
        &amp;quot;RefFull&amp;quot;: &amp;quot;bb22cc33-44dd-55ee-66ff-778899aabbcc&amp;quot;,
        &amp;quot;CreatedFull&amp;quot;: &amp;quot;20251007121630&amp;quot;,
        &amp;quot;NoteEncrypted&amp;quot;: &amp;quot;RM5bcxPzAABi&amp;quot;
    },
    {
        &amp;quot;name&amp;quot;: &amp;quot;Record3&amp;quot;,
        &amp;quot;Code&amp;quot;: &amp;quot;ARC-03&amp;quot;,
        &amp;quot;RefFull&amp;quot;: &amp;quot;cc33dd44-55ee-66ff-7788-99aabbccdd11&amp;quot;,
        &amp;quot;CreatedFull&amp;quot;: &amp;quot;20251007103005&amp;quot;,
        &amp;quot;NoteEncrypted&amp;quot;: &amp;quot;R1YQdzaRAABI&amp;quot;
    },
    {
        &amp;quot;name&amp;quot;: &amp;quot;Record4&amp;quot;,
        &amp;quot;Code&amp;quot;: &amp;quot;ARC-04&amp;quot;,
        &amp;quot;RefFull&amp;quot;: &amp;quot;dd44ee55-66ff-7788-99aa-bbccddeeff22&amp;quot;,
        &amp;quot;CreatedFull&amp;quot;: &amp;quot;20251007104510&amp;quot;,
        &amp;quot;NoteEncrypted&amp;quot;: &amp;quot;jULmZg0V&amp;quot;
    },
    {
        &amp;quot;name&amp;quot;: &amp;quot;Record5&amp;quot;,
        &amp;quot;Code&amp;quot;: &amp;quot;ARC-05&amp;quot;,
        &amp;quot;RefFull&amp;quot;: &amp;quot;ee55ff66-7788-99aa-bbcc-ddeeff001133&amp;quot;,
        &amp;quot;CreatedFull&amp;quot;: &amp;quot;20251007143200&amp;quot;,
        &amp;quot;NoteEncrypted&amp;quot;: &amp;quot;FPZTlS0L&amp;quot;
    }
]

A = 1103515245
C = 12345
M = 2 ** 31

def ror(b, shift):
    shift &amp;amp;= 7
    return ((b &amp;amp; 0xFF) &amp;gt;&amp;gt; shift) | ((b &amp;lt;&amp;lt; (8 - shift)) &amp;amp; 0xFF)

def seed_from_key_material(code, ref, created):
    key_material = code.strip() + &amp;quot;|&amp;quot; + ref + &amp;quot;|&amp;quot; + created
    s = sum(ord(ch) for ch in key_material)
    return (s * 2654435761) % (2**31)

def reverse_block_permutation(blk_bytes):
    tmp = bytearray()
    n = len(blk_bytes)
    i = 0
    while i &amp;lt; n:
        g0 = blk_bytes[i] if i &amp;lt; n else 0
        g1 = blk_bytes[i+1] if (i+1) &amp;lt; n else 0
        g2 = blk_bytes[i+2] if (i+2) &amp;lt; n else 0
        tmp.extend(bytes([g2, g1, g0]))
        i += 3
    while tmp and tmp[-1] == 0:
        tmp.pop()
    return bytes(tmp)

def decrypt_record(record):
    code = record[&amp;quot;Code&amp;quot;]
    ref = record[&amp;quot;RefFull&amp;quot;]
    created = record[&amp;quot;CreatedFull&amp;quot;]
    note_b64 = record[&amp;quot;NoteEncrypted&amp;quot;]

    blk = base64.b64decode(note_b64)
    bytes_temp = reverse_block_permutation(blk)
    prev_plain = sum(ord(ch) for ch in ref) % 256

    seed = seed_from_key_material(code, ref, created)
    out = bytearray()
    for i, cb in enumerate(bytes_temp):
        seed = (A * seed + C) % M
        ks = (seed &amp;gt;&amp;gt; 17) &amp;amp; 0xFF
        shift = (len(code) * 3 + (prev_plain &amp;amp; 7) + i) % 8
        x = ror(cb, shift)
        pb = x ^ ks
        out.append(pb &amp;amp; 0xFF)
        prev_plain = pb &amp;amp; 0xFF

    try:
        plain = out.decode(&amp;#x27;utf-8&amp;#x27;)
    except Exception:
        plain = out.decode(&amp;#x27;utf-8&amp;#x27;, errors=&amp;#x27;replace&amp;#x27;)
    return plain

def main():
    fragments = []
    for rec in RECORDS:
        plain = decrypt_record(rec)
        print(f&amp;quot;{rec[&amp;#x27;name&amp;#x27;]}: {plain}&amp;quot;)
        fragments.append(plain)
    flag = &amp;quot;&amp;quot;.join(fragments)
    print(&amp;quot;\nAssembled flag:&amp;quot;)
    print(flag)

if __name__ == &amp;quot;__main__&amp;quot;:
    main()
&lt;/pre&gt;
  &lt;p id=&quot;1KnH&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;MJY2&quot;&gt;Flag: &lt;strong&gt;grodno{obviously_devils_language}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;aKp9&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;HQcR&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;3XW9&quot;&gt;ChristmasRing&lt;/h1&gt;
  &lt;blockquote id=&quot;kFsM&quot;&gt;Santa Claus&amp;#x27;s workshop contains a magical ring of bells — the Christmas Ring. Every time someone pulls the string, the ring emits a long set of large random numbers, which are used to wrap and encrypt gifts.&lt;br /&gt;But the elf-helper was a bit lazy and used a standard Python random number generator instead of a cryptographically secure one.&lt;/blockquote&gt;
  &lt;p id=&quot;HAEB&quot;&gt;&lt;/p&gt;
  &lt;pre id=&quot;ue4O&quot;&gt;import time
import numpy as np
import random
  def interpolate(l):
for _ in range(624):
x = random.getrandbits(32*2**4)
print(x)
mo = random.getrandbits(32*2**4)
FR.&amp;lt;x&amp;gt; = PolynomialRing( IntegerModRing(mo) )
f = prod([(random.getrandbits(32*2**4)*x-1) for _ in range(1,l)])
return f, mo
 #hmm, maybe a bit slow
def evaluate(poly, points, mo):
evaluations = []

for point in points:
evaluations.append(poly(point))

return evaluations
 if __name__ == &amp;quot;__main__&amp;quot;:
with open(&amp;quot;flag.txt&amp;quot;,&amp;quot;r&amp;quot;) as f:
flag = f.read()

size =4096
poly, mo = interpolate(size)
R = Integers(mo)
points = [R(random.getrandbits(32*2**4)) for _ in range(size)]
ans = bytearray.fromhex(hex(prod(evaluate(poly,points,mo)))[2:-10])


ciphertext = bytearray(b&amp;quot;&amp;quot;)
for i, c in enumerate(flag):
ciphertext.append(ord(c)^^ans[i])

print(ciphertext)
print(ord(c)^^ans[i])&lt;/pre&gt;
  &lt;p id=&quot;atfK&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;QfcH&quot;&gt;The challenge prints many 512-bit outputs from Python’s &lt;code&gt;random&lt;/code&gt; (MT19937). Each printed 512-bit block contains 16 little-endian 32-bit MT outputs; by untempering the last 624 32-bit outputs we recover the full MT19937 internal state, reconstruct the PRNG, re-generate the exact sequence used to build the (broken) polynomial construction and derived mask, then XOR that mask with the ciphertext to get the flag.&lt;/p&gt;
  &lt;p id=&quot;80r0&quot;&gt;The generator is Python’s &lt;code&gt;random.Random()&lt;/code&gt; — MT19937 — which is not cryptographically secure and is fully recoverable from 624 consecutive 32-bit outputs.&lt;/p&gt;
  &lt;p id=&quot;uo8S&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;Bbkp&quot;&gt;Attack: &lt;/p&gt;
  &lt;p id=&quot;4zso&quot;&gt;Read the file of printed numbers. The file contains many 512-bit integers (one per line) and a final printed ciphertext line like &lt;code&gt;bytearray(b&amp;#x27;...&amp;#x27;)&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;e9mN&quot;&gt;Use the &lt;em&gt;last 39&lt;/em&gt; printed 512-bit numbers. Split each 512-bit integer into 16 32-bit words (word 0 = least significant 32 bits, word 1 next, …) — &lt;strong&gt;little-endian by words&lt;/strong&gt;. Concatenate the 16×39 words → 624 32-bit outputs.&lt;/p&gt;
  &lt;p id=&quot;m1Sx&quot;&gt;Undo MT19937 tempering for each 32-bit output (standard invert tempering routine) to obtain the 624 internal &lt;code&gt;state&lt;/code&gt; words.&lt;/p&gt;
  &lt;p id=&quot;gNe0&quot;&gt;Create a &lt;code&gt;random.Random()&lt;/code&gt; instance and set the recovered state so that it resumes from the same place the challenge RNG was at (so future &lt;code&gt;getrandbits&lt;/code&gt; generate identical values).&lt;/p&gt;
  &lt;p id=&quot;EIWe&quot;&gt;Recompute the exact sequence the challenge generated:&lt;/p&gt;
  &lt;ul id=&quot;uamf&quot;&gt;
    &lt;li id=&quot;EhOm&quot;&gt;&lt;code&gt;mo = r.getrandbits(512)&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;D2kx&quot;&gt;&lt;code&gt;co = [ r.getrandbits(512) for _ in range(1, s) ]&lt;/code&gt; (coefficients)&lt;/li&gt;
    &lt;li id=&quot;Rxtv&quot;&gt;&lt;code&gt;p = [ r.getrandbits(512) % mo for _ in range(s) ]&lt;/code&gt; (points)&lt;/li&gt;
    &lt;li id=&quot;v1wU&quot;&gt;Evaluate the product &lt;code&gt;prod((am*pm - 1) % mo)&lt;/code&gt; for each &lt;code&gt;pm&lt;/code&gt; and collect values &lt;code&gt;v&lt;/code&gt;.&lt;/li&gt;
    &lt;li id=&quot;dkC9&quot;&gt;Multiply those &lt;code&gt;v&lt;/code&gt; values modulo &lt;code&gt;mo&lt;/code&gt; → &lt;code&gt;bp&lt;/code&gt;.&lt;/li&gt;
    &lt;li id=&quot;YU8F&quot;&gt;Convert &lt;code&gt;hex(bp)[2:-10]&lt;/code&gt; into bytes (the original program slices off the last 10 hex digits — replicate that behavior).&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;XDgZ&quot;&gt;XOR &lt;code&gt;bp&lt;/code&gt;-derived bytes with the ciphertext bytes to get plaintext; decode the flag.&lt;/p&gt;
  &lt;p id=&quot;vYvw&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;yVQt&quot;&gt;Solver: &lt;/p&gt;
  &lt;pre id=&quot;61kS&quot;&gt;import random
import re
import ast

MASK32 = (1 &amp;lt;&amp;lt; 32) - 1

def undo_right(y, s):
    x = y
    for _ in range(10):
        x = y ^ (x &amp;gt;&amp;gt; s)
    return x &amp;amp; MASK32

def undo_left(y, s, mask):
    x = y
    for _ in range(10):
        x = y ^ ((x &amp;lt;&amp;lt; s) &amp;amp; mask)
    return x &amp;amp; MASK32

def untemper(y):
    y = undo_right(y, 18)
    y = undo_left(y, 15, 0xEFC60000)
    y = undo_left(y, 7,  0x9D2C5680)
    y = undo_right(y, 11)
    return y &amp;amp; MASK32

def split512_le(x):
    return [(x &amp;gt;&amp;gt; (32 * i)) &amp;amp; MASK32 for i in range(16)]

def recover_rng_after_print(nums_512):
    outs = []
    for n in nums_512[-39:]:
        outs.extend(split512_le(n))
    state_words = [untemper(o) for o in outs]

    r = random.Random()
    r.setstate((3, tuple(state_words) + (624,), None))
    return r

def prod_mod(lst, mod):
    res = 1
    for x in lst:
        res = (res * x) % mod
    return res

def main():
    with open(&amp;quot;ChristmasRing1.txt&amp;quot;, &amp;quot;r&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:
        lines = [ln.strip() for ln in f.readlines() if ln.strip()]

    nums = list(map(int, lines[:-1]))

    m = re.match(r&amp;quot;bytearray\((b[&amp;#x27;\&amp;quot;].*[&amp;#x27;\&amp;quot;])\)&amp;quot;, lines[-1])
    if not m:
        raise ValueError(&amp;quot;Bad ciphertext line&amp;quot;)
    ct = bytearray(ast.literal_eval(m.group(1)))

    r = recover_rng_after_print(nums)

    s = 4096
    mo = r.getrandbits(512)
    co = [r.getrandbits(512) for _ in range(1, s)]
    coeffs_mod = [c % mo for c in co]
    p = [r.getrandbits(512) % mo for _ in range(s)]

    # evaluate
    v = []
    for pm in p:
        val = 1
        for am in coeffs_mod:
            t = (am * pm) % mo
            t = (t - 1) % mo
            val = (val * t) % mo
        v.append(val)

    bp = prod_mod(v, mo)

    hs = hex(bp)[2:-10]
    if len(hs) % 2 == 1:
        hs = &amp;quot;0&amp;quot; + hs
    a = bytearray.fromhex(hs)

    pt = bytes(ct[i] ^ a[i] for i in range(len(ct)))
    print(pt.decode())

if __name__ == &amp;quot;__main__&amp;quot;:
    main()
&lt;/pre&gt;
  &lt;p id=&quot;R7rb&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;gRGm&quot;&gt;Flag: &lt;strong&gt;grodno{You_are_the_lord_of_the_chocolate_ring}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;N1Pz&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;vZ2l&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;29S0&quot;&gt;RSA?&lt;/h1&gt;
  &lt;blockquote id=&quot;zNOj&quot;&gt;On New Year&amp;#x27;s Eve, Santa Claus makes a wish to give flags to all good hackers. But in his haste, he mixed up the operators in his magical encryption program, and instead of the reliable RSA, he got some strange code.&lt;/blockquote&gt;
  &lt;p id=&quot;v6rr&quot;&gt;&lt;/p&gt;
  &lt;pre id=&quot;H0DK&quot;&gt;A = __import__(&amp;#x27;random&amp;#x27;).SystemRandom().randint
 def R():
while True:
S = (ZZ^2).0
while S.norm() &amp;lt; 5^55:
a,b = ((-1)^A(0,1)*A(1,7^y) for y in (2,1))
S *= matrix([[a,b],[123*b,-a]])
S += (ZZ^2).0
S *= diagonal_matrix((1,123)) * S
if is_pseudoprime(S): return S
 n = prod(R() for _ in &amp;#x27;yyyyyyy&amp;#x27;)
print(f&amp;#x27;{n = :#x}&amp;#x27;)
 m = int.from_bytes(open(&amp;#x27;flag.txt&amp;#x27;,&amp;#x27;rb&amp;#x27;).read().strip(), &amp;#x27;big&amp;#x27;)
c = int(pow(m, 1|2^2^2^2, n))
print(f&amp;#x27;{c = :#x}&amp;#x27;&lt;/pre&gt;
  &lt;p id=&quot;3uYM&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;V0Vx&quot;&gt;The challenge builds an RSA-like modulus &lt;code&gt;n&lt;/code&gt; as a product of several specially constructed pseudoprimes coming from a quadratic-form / complex multiplication (CM) construction, then raises the message &lt;code&gt;m&lt;/code&gt; to some weird exponent modulo &lt;code&gt;n&lt;/code&gt;. Because the modulus factors are chosen with structure amenable to &lt;strong&gt;CM-style elliptic curve factorization&lt;/strong&gt;, we can factor &lt;code&gt;n&lt;/code&gt; efficiently by constructing elliptic curves with prescribed CM (via Hilbert class polynomials) and doing scalar multiplications with large smooth exponents until a division-by-zero occurs modulo a composite — that failure reveals a nontrivial gcd and yields a factor. After factoring we compute the private exponent (inverse of the public exponent modulo ∏(p_i − 1) or lcm) and recover the flag.&lt;/p&gt;
  &lt;p id=&quot;Co6t&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;BCgm&quot;&gt;Attack:&lt;/p&gt;
  &lt;p id=&quot;7DyC&quot;&gt;&lt;strong&gt;ECM / CM principle&lt;/strong&gt;: Elliptic-curve methods factor a composite &lt;code&gt;m&lt;/code&gt; by performing group operations on elliptic curves defined modulo &lt;code&gt;m&lt;/code&gt;. If during the group arithmetic you need to invert a denominator which is not invertible modulo &lt;code&gt;m&lt;/code&gt;, then that denominator shares a nontrivial factor with &lt;code&gt;m&lt;/code&gt;. Taking &lt;code&gt;gcd(denominator, m)&lt;/code&gt; yields a factor. Practical ECMs try many random curves and multiply by a large smooth scalar until failure.&lt;/p&gt;
  &lt;p id=&quot;C6HB&quot;&gt;&lt;strong&gt;Hilbert class polynomials&lt;/strong&gt;: Curves with complex multiplication by orders of imaginary quadratic fields have known j-invariants given by roots of Hilbert class polynomials. Building curves via Hilbert class polynomials gives curves whose orders are constrained — we can choose a smooth exponent &lt;code&gt;L&lt;/code&gt; such that the (putative) group order on a prime factor divides &lt;code&gt;L&lt;/code&gt;. Multiplying a random point by &lt;code&gt;L&lt;/code&gt; reduces it to the identity modulo a prime factor; on the composite modulus the calculation often triggers a division failure revealing a factor.&lt;/p&gt;
  &lt;p id=&quot;uXhY&quot;&gt;The solver uses that trick: for an appropriate discriminant &lt;code&gt;-d&lt;/code&gt; it constructs the Hilbert class polynomial &lt;code&gt;H_{-d}&lt;/code&gt; modulo &lt;code&gt;m&lt;/code&gt; (working in &lt;code&gt;Z/mZ[x]&lt;/code&gt; and then in &lt;code&gt;S = R / (H_{-d})&lt;/code&gt;) and picks random symbolic points; then it repeatedly multiplies those points by large smooth exponents &lt;code&gt;e = L^i&lt;/code&gt;. When a denominator becomes non-invertible during these scalar multiplications it raises &lt;code&gt;ZeroDivisionError&lt;/code&gt; — the exception embeds values that let us compute &lt;code&gt;gcd&lt;/code&gt; with the composite &lt;code&gt;m&lt;/code&gt; and thus factor it.&lt;/p&gt;
  &lt;p id=&quot;k0Yc&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;F6Nt&quot;&gt;Solver:&lt;/p&gt;
  &lt;pre id=&quot;LWN7&quot;&gt;import re
proof.all(False)

d = 123
n,c = map(ZZ, re.findall(&amp;#x27;0x[0-9a-f]+&amp;#x27;, open(&amp;#x27;output.txt&amp;#x27;).read()))

def inv(f):
    ff = f.lift().change_ring(QQ)
    gg = f.parent().modulus().change_ring(QQ)
    hh = xgcd(ff,gg)           
    assert hh[0] == 1
    return f.parent()(hh[1])   

def xDBLADD(a,b,X1,X2,X3):
    Z1 = Z2 = Z3 = 1
    if X1 == (): X1,Z1 = 1,0
    if X2 == (): X2,Z2 = 1,0
    if X3 == (): X3,Z3 = 1,0
    X4 = (X2^2-a*Z2^2)^2-8*b*X2*Z2^3
    Z4 = 4*(X2*Z2*(X2^2+a*Z2^2)+b*Z2^4)
    R = 2*(X2*Z3+X3*Z2)*(X2*X3+a*Z2*Z3)+4*b*Z2^2*Z3^2
    S = (X2*Z3-X3*Z2)^2
    X5 = R-S*X1
    Z5 = S
    return (X4*inv(Z4) if Z4 else ()), (X5*inv(Z5) if Z5 else ())

def xMUL(a,b,n,P):
    Q,PQ = (),P
    for bit in map(int,f&amp;#x27;{n:b}&amp;#x27;):
        if bit:
            P,Q = xDBLADD(a,b,PQ,P,Q)
        else:
            Q,P = xDBLADD(a,b,PQ,Q,P)
    return Q


ls = list(sorted({u^2+d*v^2 for u in range(49+1) for v in range(7+1)} - {0}))
L = lcm(ls)     

def fac(m, path=&amp;#x27;&amp;#x27;):

    print(f&amp;#x27;\x1b[33m    {&amp;quot;[&amp;quot;+path+&amp;quot;]&amp;quot;:9} ({int(m).bit_length()} bits)\x1b[0m&amp;#x27;)

    m = ZZ(m)
    if is_pseudoprime(m):                
        print(f&amp;#x27;--&amp;gt; \x1b[36m{m:#x}\x1b[0m&amp;#x27;); print()
        return [m]

    R.&amp;lt;x&amp;gt; = Zmod(m)[]
    S.&amp;lt;J&amp;gt; = R.quotient(hilbert_class_polynomial(-d))               
    a0, b0 = (-3*J^2+3*1728*J), (-2*J^3+4*1728*J^2-2*1728^2*J)    
    for i in range(1,20):

        e = L^i                           
        P = J.parent().random_element()     
        try:
            Q = xMUL(a0,b0,e, P)
            print(f&amp;#x27;{i:2} nope&amp;#x27;)
            continue
        except ZeroDivisionError as e:     
            vs = list(map(ZZ, re.findall(&amp;#x27;[0-9]+&amp;#x27;, e.args[0])))

        f = gcd(vs[0],m)
        if 1 &amp;lt; f &amp;lt; m:
            print(f&amp;#x27;\x1b[34m{m:#x} = {f:#x} * {m//f:#x}\x1b[0m&amp;#x27;); print()
            return fac(f, path+&amp;#x27;L&amp;#x27;) + fac(m//f, path+&amp;#x27;R&amp;#x27;) 

    print(&amp;#x27;\x1b[31mFAILED\x1b[0m&amp;#x27;)
    exit(1)


fs = fac(n)

print(&amp;#x27;    \x1b[1;4mfactorization\x1b[0m&amp;#x27;)
for f in fs:
    print(f&amp;#x27;\x1b[36m{f:#x}\x1b[0m&amp;#x27;)
print()

privkey = int(~Mod(65537, prod(f-1 for f in fs)))
print(f&amp;#x27;\x1b[35md = {privkey:#x}\x1b[0m&amp;#x27;); print()

flag = ZZ(pow(c, privkey, n))
flag = bytes.fromhex(f&amp;#x27;{flag:x}&amp;#x27;).decode(errors=&amp;#x27;replace&amp;#x27;)
print(f&amp;#x27;--&amp;gt; \x1b[1;32m{flag}\x1b[0m&amp;#x27;); print()&lt;/pre&gt;
  &lt;p id=&quot;HoJq&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;CgPj&quot;&gt;Flag: &lt;strong&gt;grodno{S1mpl3_s@g3_scr1pt_c@n_sc@r3_u}&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>beaverszero:NYCTFStegano2026</id><link rel="alternate" type="text/html" href="https://teletype.in/@beaverszero/NYCTFStegano2026?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=beaverszero"></link><title>Stegano Writeups</title><published>2026-01-13T21:46:53.744Z</published><updated>2026-01-13T21:47:28.933Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img1.teletype.in/files/8c/be/8cbecfc8-f1a3-4de2-9457-bc1c0daabb73.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img2.teletype.in/files/5e/0f/5e0f7fdc-c6e5-465a-97c2-1886713c585a.png&quot;&gt;Write-ups for Stegano tasks by Kata. English is not my main language, so I might have problems with explanations.</summary><content type="html">
  &lt;h2 id=&quot;8eSy&quot;&gt;Preview&lt;/h2&gt;
  &lt;p id=&quot;zyB9&quot;&gt;Write-ups for Stegano tasks by Kata. English is not my main language, so I might have problems with explanations.&lt;/p&gt;
  &lt;p id=&quot;U6MD&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;5UKP&quot;&gt;babyStegano&lt;/h2&gt;
  &lt;blockquote id=&quot;xSx3&quot;&gt;There are snowflakes on the window, but I think there is something behind them.&lt;/blockquote&gt;
  &lt;figure id=&quot;Xy1b&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/5e/0f/5e0f7fdc-c6e5-465a-97c2-1886713c585a.png&quot; width=&quot;900&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;P16x&quot;&gt;babyStegano implies that this is indeed a simple task, so let&amp;#x27;s go to the zsteg and see the flag:&lt;/p&gt;
  &lt;figure id=&quot;K9qA&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/c1/c1/c1c1b4b0-87f7-48c4-8525-1140e9304d7b.png&quot; width=&quot;1219&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;NhVW&quot;&gt;Flag:&lt;strong&gt; grodno{happy_new_year_ctfers}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;vFI2&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;PJva&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;iA92&quot;&gt;UltraLastChristmas&lt;/h1&gt;
  &lt;blockquote id=&quot;8139&quot;&gt;Did you know that elves can even hear ultrasound?&lt;/blockquote&gt;
  &lt;p id=&quot;pj4m&quot;&gt;This is also a very simple chall. The first thing I personally do when I see files in audio format is go to SonicVisualizer and look at the spectrogram.&lt;/p&gt;
  &lt;figure id=&quot;VmdB&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/0e/eb/0eebfc06-41f5-43ee-9eb2-b1f6a09ed735.png&quot; width=&quot;1440&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;6ktZ&quot;&gt;Flag: &lt;strong&gt;grodno{laaaast_christmaaas_i_geiv_u_ma_ha}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;FDOx&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;LMsH&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;IkTG&quot;&gt;Santa’s Report&lt;/h1&gt;
  &lt;blockquote id=&quot;gfrs&quot;&gt;We intercepted an encrypted report on Santa Claus’s activities.&lt;br /&gt;All that remains is to determine his favorite day of the year.&lt;/blockquote&gt;
  &lt;p id=&quot;x2BB&quot;&gt;We have an attached docs file that looks like the encoding is broken.&lt;/p&gt;
  &lt;figure id=&quot;uFTs&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/45/fa/45faa2e0-cb74-44e9-9023-f1b57583793c.png&quot; width=&quot;1032&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;TvpE&quot;&gt;There is a row in the document highlighted in bold that looks like a flag.&lt;/p&gt;
  &lt;p id=&quot;stLU&quot;&gt;I, as the author of the сhal, will not fix the encoding, since this line and the entire file is a Wikipedia article about Santa Claus and was left to distract you.&lt;/p&gt;
  &lt;p id=&quot;69Zd&quot;&gt;So let&amp;#x27;s move straight to the correct solution.&lt;/p&gt;
  &lt;p id=&quot;rMEU&quot;&gt;Some people may not know this, but DOCS and ODF formats are zip archives of XML files.&lt;br /&gt;So let&amp;#x27;s just change the file extension to zip and see what&amp;#x27;s inside.&lt;/p&gt;
  &lt;figure id=&quot;XaHc&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/4a/96/4a96f1ff-e0b5-4368-af52-7557946f0a7a.png&quot; width=&quot;1095&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;hX5v&quot;&gt;After searching through the files we can&amp;#x27;t find the flag.&lt;/p&gt;
  &lt;p id=&quot;ORrE&quot;&gt;We have only one image left in the folder Thumbnails &lt;/p&gt;
  &lt;figure id=&quot;rspJ&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/07/31/0731857b-6d4d-4f2b-ac0a-b4909aef37a3.png&quot; width=&quot;273&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;5LQs&quot;&gt;Let&amp;#x27;s open a hex editor and examine the image.&lt;/p&gt;
  &lt;figure id=&quot;bSHm&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/03/c5/03c5763e-c0ed-4518-be08-28ba067aa028.png&quot; width=&quot;1123&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;aYif&quot;&gt;Flag: &lt;strong&gt;grodno{my_lovley_06_12}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;eoHR&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;LZzU&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;0ADp&quot;&gt;Admin Vibes&lt;/h2&gt;
  &lt;blockquote id=&quot;xjam&quot;&gt;I always try to be serious while creating tasks. Right?&lt;/blockquote&gt;
  &lt;p id=&quot;GNmL&quot;&gt;Attached to the task is an audio file (my favorite song, I recommend everyone listen to it in full)&lt;/p&gt;
  &lt;p id=&quot;I4QP&quot;&gt;&lt;em&gt;Danika House on YouTube)&lt;/em&gt;&lt;/p&gt;
  &lt;p id=&quot;ACfO&quot;&gt;This audio has an embedded image signature using LSB that can be extracted using a simple Python script.&lt;/p&gt;
  &lt;p id=&quot;6Wrh&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;zGJz&quot;&gt;Let&amp;#x27;s extract the image using binwalk: &lt;/p&gt;
  &lt;figure id=&quot;Di4B&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/0a/2f/0a2fa902-16fe-44e7-8985-0e4e83cb1b85.png&quot; width=&quot;1670&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;tONo&quot;&gt;Here is the photo itself:&lt;/p&gt;
  &lt;figure id=&quot;l2CD&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/f1/d0/f1d0a423-b40b-46b0-a059-b88c0208c569.png&quot; width=&quot;648&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;qO1r&quot;&gt;Let&amp;#x27;s try zsteg again:&lt;/p&gt;
  &lt;figure id=&quot;7Alc&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/da/2d/da2d98f0-a0a1-4d7c-8ae4-81e9b19d6eca.png&quot; width=&quot;884&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;R76p&quot;&gt;Flag: &lt;strong&gt;grodno{1&amp;#x27;m_s@n3_!_pr0m1s3}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;B9dw&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;mrBR&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;F2gD&quot;&gt;BinaryChess&lt;/h2&gt;
  &lt;blockquote id=&quot;f3Tn&quot;&gt;In this chess game, a smart guy hid a flag. Find it. Pay attention to the moves. 0 and 1 are significant.&lt;/blockquote&gt;
  &lt;p id=&quot;cAlC&quot;&gt;This chall was made using &lt;a href=&quot;https://github.com/WintrCat/chessencryption&quot; target=&quot;_blank&quot;&gt;https://github.com/WintrCat/chessencryption&lt;/a&gt;&lt;br /&gt;The encoding script reads the file as a bit stream and encodes them into chess moves.&lt;br /&gt;In each position, it takes all legal moves, numbers them, and assigns each a binary number. The next portion of bits from the file selects which move to make. The sequence of moves is written to the PGN—this is the encoded data.&lt;/p&gt;
  &lt;p id=&quot;91wP&quot;&gt;Decoder:&lt;/p&gt;
  &lt;pre id=&quot;8pxf&quot;&gt;from time import time&lt;/pre&gt;
  &lt;pre id=&quot;nwK0&quot;&gt;from math import log2&lt;/pre&gt;
  &lt;pre id=&quot;A86e&quot;&gt;from chess import pgn, Board&lt;/pre&gt;
  &lt;pre id=&quot;UJMU&quot;&gt;from util import get_pgn_games&lt;/pre&gt;
  &lt;pre id=&quot;gEmn&quot;&gt;def decode(pgn_string: str, output_file_path: str):&lt;/pre&gt;
  &lt;pre id=&quot;Y2AP&quot;&gt;start_time = time()&lt;/pre&gt;
  &lt;pre id=&quot;rewf&quot;&gt;total_move_count = 0&lt;/pre&gt;
  &lt;pre id=&quot;Kae4&quot;&gt;games: list[pgn.Game] = get_pgn_games(pgn_string)&lt;/pre&gt;
  &lt;pre id=&quot;xYI0&quot;&gt;with open(output_file_path, &amp;quot;w&amp;quot;) as output_file:&lt;/pre&gt;
  &lt;pre id=&quot;YJMS&quot;&gt;output_file.write(&amp;quot;&amp;quot;)&lt;/pre&gt;
  &lt;pre id=&quot;TEej&quot;&gt;output_file = open(output_file_path, &amp;quot;ab&amp;quot;)&lt;/pre&gt;
  &lt;pre id=&quot;7uoW&quot;&gt;output_data = &amp;quot;&amp;quot;&lt;/pre&gt;
  &lt;pre id=&quot;UoV1&quot;&gt;for game_index, game in enumerate(games):&lt;/pre&gt;
  &lt;pre id=&quot;h08r&quot;&gt;chess_board = Board()&lt;/pre&gt;
  &lt;pre id=&quot;56w1&quot;&gt;game_moves = list(game.mainline_moves())&lt;/pre&gt;
  &lt;pre id=&quot;gqOy&quot;&gt;total_move_count += len(game_moves)&lt;/pre&gt;
  &lt;pre id=&quot;VyqJ&quot;&gt;for move_index, move in enumerate(game_moves):&lt;/pre&gt;
  &lt;pre id=&quot;4McS&quot;&gt;legal_move_ucis = [&lt;/pre&gt;
  &lt;pre id=&quot;xeFa&quot;&gt;legal_move.uci()&lt;/pre&gt;
  &lt;pre id=&quot;qoS8&quot;&gt;for legal_move in list(chess_board.generate_legal_moves())&lt;/pre&gt;
  &lt;pre id=&quot;EWyq&quot;&gt;]&lt;/pre&gt;
  &lt;pre id=&quot;oL4y&quot;&gt;move_binary = bin(&lt;/pre&gt;
  &lt;pre id=&quot;qbLR&quot;&gt;legal_move_ucis.index(move.uci())&lt;/pre&gt;
  &lt;pre id=&quot;Nx5Q&quot;&gt;)[2:]&lt;/pre&gt;
  &lt;pre id=&quot;DQUb&quot;&gt;if (&lt;/pre&gt;
  &lt;pre id=&quot;goVS&quot;&gt;game_index == len(games) - 1&lt;/pre&gt;
  &lt;pre id=&quot;q4Ko&quot;&gt;and move_index == len(game_moves) - 1&lt;/pre&gt;
  &lt;pre id=&quot;ghmN&quot;&gt;):&lt;/pre&gt;
  &lt;pre id=&quot;YJqP&quot;&gt;max_binary_length = min(&lt;/pre&gt;
  &lt;pre id=&quot;UQPF&quot;&gt;int(log2(&lt;/pre&gt;
  &lt;pre id=&quot;CQwd&quot;&gt;len(legal_move_ucis)&lt;/pre&gt;
  &lt;pre id=&quot;CK5N&quot;&gt;)),&lt;/pre&gt;
  &lt;pre id=&quot;SAxm&quot;&gt;8 - (len(output_data) % 8)&lt;/pre&gt;
  &lt;pre id=&quot;oHC8&quot;&gt;)&lt;/pre&gt;
  &lt;pre id=&quot;lCgA&quot;&gt;else:&lt;/pre&gt;
  &lt;pre id=&quot;sOY4&quot;&gt;max_binary_length = int(log2(&lt;/pre&gt;
  &lt;pre id=&quot;r9bS&quot;&gt;len(legal_move_ucis)&lt;/pre&gt;
  &lt;pre id=&quot;efc9&quot;&gt;))&lt;/pre&gt;
  &lt;pre id=&quot;AZ6l&quot;&gt;required_padding = max(0, max_binary_length - len(move_binary))&lt;/pre&gt;
  &lt;pre id=&quot;1xdL&quot;&gt;move_binary = (&amp;quot;0&amp;quot; * required_padding) + move_binary&lt;/pre&gt;
  &lt;pre id=&quot;kOZ2&quot;&gt;chess_board.push_uci(move.uci())&lt;/pre&gt;
  &lt;pre id=&quot;wf6k&quot;&gt;output_data += move_binary&lt;/pre&gt;
  &lt;pre id=&quot;ZA5r&quot;&gt;if len(output_data) % 8 == 0:&lt;/pre&gt;
  &lt;pre id=&quot;YgDM&quot;&gt;output_file.write(&lt;/pre&gt;
  &lt;pre id=&quot;pZ9i&quot;&gt;bytes([&lt;/pre&gt;
  &lt;pre id=&quot;WSnV&quot;&gt;int(output_data[i * 8 : i * 8 + 8], 2)&lt;/pre&gt;
  &lt;pre id=&quot;zw91&quot;&gt;for i in range(len(output_data) // 8)&lt;/pre&gt;
  &lt;pre id=&quot;VpxR&quot;&gt;])&lt;/pre&gt;
  &lt;pre id=&quot;ETLO&quot;&gt;)&lt;/pre&gt;
  &lt;pre id=&quot;Tccu&quot;&gt;output_data = &amp;quot;&amp;quot;&lt;/pre&gt;
  &lt;pre id=&quot;ANhe&quot;&gt;print(&lt;/pre&gt;
  &lt;pre id=&quot;IF3Z&quot;&gt;&amp;quot;\nsuccessfully decoded pgn with &amp;quot;&lt;/pre&gt;
  &lt;pre id=&quot;ocSm&quot;&gt;+ f&amp;quot;{len(games)} game(s), {total_move_count} total move(s)&amp;quot;&lt;/pre&gt;
  &lt;pre id=&quot;o8au&quot;&gt;+ f&amp;quot;({round(time() - start_time, 3)}s).&amp;quot;&lt;/pre&gt;
  &lt;pre id=&quot;TvOc&quot;&gt;)&lt;/pre&gt;
  &lt;pre id=&quot;LnSc&quot;&gt;if __name__ == &amp;quot;__main__&amp;quot;:&lt;/pre&gt;
  &lt;pre id=&quot;wp9M&quot;&gt;PGN_FILE_PATH = &amp;quot;output.pgn&amp;quot;&lt;/pre&gt;
  &lt;pre id=&quot;9hQF&quot;&gt;OUTPUT_FILE_PATH = &amp;quot;decoded_output.bin&amp;quot;&lt;/pre&gt;
  &lt;pre id=&quot;hOT8&quot;&gt;with open(PGN_FILE_PATH, &amp;quot;r&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as pgn_file:&lt;/pre&gt;
  &lt;pre id=&quot;BpUM&quot;&gt;pgn_text = pgn_file.read()&lt;/pre&gt;
  &lt;pre id=&quot;Rxub&quot;&gt;decode(pgn_text, OUTPUT_FILE_PATH)&lt;/pre&gt;
  &lt;pre id=&quot;nLmH&quot;&gt;print(f&amp;quot;Decoded binary saved to: {OUTPUT_FILE_PATH} (from: {PGN_FILE_PATH})&amp;quot;)&lt;/pre&gt;
  &lt;p id=&quot;x6zJ&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;IX3R&quot;&gt;Аnd we get the flag: &lt;strong&gt;grodno{wh@t_d0_u_me@n_st3g0_1n_ch3ss}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;hGuU&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;NuE7&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;fD5E&quot;&gt;Chupakabra&lt;/h2&gt;
  &lt;blockquote id=&quot;1Xpu&quot;&gt;Under the influence of various New Year&amp;#x27;s treats and drinks, I came up with the idea to write my own implementation of /dev/urandom/. But I recorded some very important data with it, and now I can&amp;#x27;t get it back. Flag format inside the grodno file{a-z0-9}&lt;/blockquote&gt;
  &lt;p id=&quot;fhhe&quot;&gt;The most terrifying task in my opinion. You can try typing &lt;/p&gt;
  &lt;p id=&quot;QjAZ&quot;&gt;&lt;strong&gt;&lt;em&gt;cat /dev/urandom&lt;/em&gt;&lt;/strong&gt; in your terminal and see what happens.&lt;/p&gt;
  &lt;p id=&quot;FTuo&quot;&gt;I can&amp;#x27;t say what the author intended for this task, but I solved it with the help of AI.&lt;/p&gt;
  &lt;p id=&quot;k7Wj&quot;&gt;The gist of my solver:&lt;/p&gt;
  &lt;p id=&quot;DQtb&quot;&gt;The program reads the file in chunks (block_size = 1 MiB) and calculates the MD5 hash for each block.&lt;/p&gt;
  &lt;p id=&quot;x0FS&quot;&gt;For each hash, it counts how many times it appears (counts) and stores up to four positions where the hash appears (positions).&lt;/p&gt;
  &lt;p id=&quot;JLY1&quot;&gt;It finds all hashes that appear exactly once—these are &amp;quot;singleton&amp;quot; blocks. The first such block is considered flag_hash (the supposed block with the flag) and its position is taken as flag_pos.&lt;/p&gt;
  &lt;p id=&quot;c0iC&quot;&gt;It looks for a &amp;quot;clean&amp;quot; block (clean_hash)—the most frequent hash in the file, different from flag_hash. It takes its position as clean_pos. The idea is that the file contains many identical (original) blocks and one different block, which contains the flag.&lt;/p&gt;
  &lt;p id=&quot;XTKA&quot;&gt;It reads the bytes of both blocks (read_block reads the block by index) and XORs them bitwise: xor_bytes = flag_block XOR clean_block.&lt;/p&gt;
  &lt;p id=&quot;itWw&quot;&gt;The result is searched using a regular expression for the byte pattern b&amp;quot;grodno\{[a-z0-9]+\}&amp;quot;—that is, a string like grodno{...} with lowercase letters and numbers. If found, a flag is printed.&lt;/p&gt;
  &lt;pre id=&quot;kg1M&quot;&gt;import hashlib
import os
import re
import sys


def read_block(path, index, size):
    with open(path, &amp;quot;rb&amp;quot;) as f:
        f.seek(index * size)
        return f.read(size)


def main():
    path = sys.argv[1] if len(sys.argv) &amp;gt; 1 else &amp;quot;/home/marcus/Загрузки/challenge.bin&amp;quot;
    block_size = 1024 * 1024
    counts = {}
    positions = {}
    total_blocks = 0
    with open(path, &amp;quot;rb&amp;quot;) as f:
        while True:
            data = f.read(block_size)
            if not data:
                break
            h = hashlib.md5(data).digest()
            counts[h] = counts.get(h, 0) + 1
            if len(positions.get(h, [])) &amp;lt; 4:
                positions.setdefault(h, []).append(total_blocks)
            total_blocks += 1
    singles = [h for h, c in counts.items() if c == 1]
    if not singles:
        print(&amp;quot;no singleton block found&amp;quot;)
        return
    flag_hash = singles[0]
    flag_pos = positions[flag_hash][0]
    clean_hash = max((h for h in counts if h != flag_hash), key=lambda k: counts[k])
    clean_pos = positions[clean_hash][0]
    flag_block = read_block(path, flag_pos, block_size)
    clean_block = read_block(path, clean_pos, block_size)
    xor_bytes = bytes(a ^ b for a, b in zip(flag_block, clean_block))
    m = re.search(b&amp;quot;grodno\\{[a-z0-9]+\\}&amp;quot;, xor_bytes)
    if not m:
        print(&amp;quot;flag pattern not found&amp;quot;)
        return
    print(m.group(0).decode())


if __name__ == &amp;quot;__main__&amp;quot;:
    main()
&lt;/pre&gt;
  &lt;p id=&quot;m6sr&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;95x5&quot;&gt;Let&amp;#x27;s wait a little and get the flag:&lt;br /&gt;&lt;strong&gt;grodno{deadlyparkourkillerdarkbrawlstarsassasinstalkersniper1998rus}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;zsN0&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;dV2E&quot;&gt;New Year&amp;#x27;s Quantization&lt;/h1&gt;
  &lt;blockquote id=&quot;4ZG6&quot;&gt;Think beyond pixels, sometimes the storage format of an image matters.&lt;/blockquote&gt;
  &lt;figure id=&quot;G9E2&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/a1/ba/a1bac4aa-ddc6-4ec6-b36c-b6f96a3c2e25.png&quot; width=&quot;261&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Q6Ry&quot;&gt;Now let&amp;#x27;s move on to my Magnum Opus.&lt;br /&gt;The idea for this task came to me about six months ago when I came across an article about the JPEG format on Wikipedia.&lt;/p&gt;
  &lt;p id=&quot;wBa6&quot;&gt;First, I&amp;#x27;ll tell you what the task about, and then I&amp;#x27;ll move on to the solution itself.&lt;/p&gt;
  &lt;p id=&quot;7NMH&quot;&gt;The script hides the payload bitwise in the parity bit (LSB) of the quantized DCT coefficients of the luminance (Y) component of a JPEG. It:&lt;/p&gt;
  &lt;ul id=&quot;OLZ1&quot;&gt;
    &lt;li id=&quot;RMvd&quot;&gt;Divides the image into 8x8 blocks,&lt;/li&gt;
    &lt;li id=&quot;EkTa&quot;&gt;performs an orthonormal DCT on each block,&lt;/li&gt;
    &lt;li id=&quot;EeLv&quot;&gt;divides the coefficients by the quantization table entries (rounds them) to obtain integer quantized coefficients,&lt;/li&gt;
    &lt;li id=&quot;mQlG&quot;&gt;changes the parity (±1) of the required AC coefficients so that it matches the payload bit,&lt;/li&gt;
    &lt;li id=&quot;27s2&quot;&gt;restores (multiply by the quantization table → inverse DCT → level shift), and saves the image as a JPEG.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;3dnr&quot;&gt;The hint says that the flag itself is located in the Y component.&lt;/p&gt;
  &lt;pre id=&quot;XWAN&quot;&gt;import sys
from PIL import Image
import numpy as np
import math

def build_dct_matrix(N=8):
    C = np.zeros((N, N), dtype=np.float64)
    for k in range(N):
        for n in range(N):
            if k == 0:
                a = math.sqrt(1.0 / N)
            else:
                a = math.sqrt(2.0 / N)
            C[k, n] = a * math.cos(math.pi * (2*n + 1) * k / (2.0 * N))
    return C

def block_dct(block):
    return C8 @ block @ C8_T

def quality_scale_table(q):
    q = max(1, min(100, q))
    if q &amp;lt; 50:
        scale = 5000.0 / q
    else:
        scale = 200.0 - 2.0 * q
    qt = ((STD_QT * scale) + 50.0) // 100.0
    qt[qt &amp;lt; 1] = 1
    return qt

ZIGZAG = [
 0,  1,  5,  6, 14, 15, 27, 28,
 2,  4,  7, 13, 16, 26, 29, 42,
 3,  8, 12, 17, 25, 30, 41, 43,
 9, 11, 18, 24, 31, 40, 44, 53,
10, 19, 23, 32, 39, 45, 52, 54,
20, 22, 33, 38, 46, 51, 55, 60,
21, 34, 37, 47, 50, 56, 59, 61,
35, 36, 48, 49, 57, 58, 62, 63
]
ZIGZAG_RC = [(idx // 8, idx % 8) for idx in ZIGZAG]

STD_QT = np.array([
 [16,11,10,16,24,40,51,61],
 [12,12,14,19,26,58,60,55],
 [14,13,16,24,40,57,69,56],
 [14,17,22,29,51,87,80,62],
 [18,22,37,56,68,109,103,77],
 [24,35,55,64,81,104,113,92],
 [49,64,78,87,103,121,120,101],
 [72,92,95,98,112,100,103,99]
], dtype=np.float64)

C8 = build_dct_matrix(8)
C8_T = C8.T

def extract_bits_for_quality(stego_path, q):
    im = Image.open(stego_path).convert(&amp;#x27;YCbCr&amp;#x27;)
    y, cb, cr = im.split()
    y_arr = np.array(y, dtype=np.float64)
    h0, w0 = y_arr.shape
    qt = quality_scale_table(q)
    H = ((h0 + 7) // 8) * 8
    W = ((w0 + 7) // 8) * 8
    y_p = np.pad(y_arr, ((0, H - h0), (0, W - w0)), mode=&amp;#x27;edge&amp;#x27;)
    blocks_h = H // 8
    blocks_w = W // 8
    bits = []
    for br in range(blocks_h):
        for bc in range(blocks_w):
            r0 = br * 8
            c0 = bc * 8
            block = y_p[r0:r0+8, c0:c0+8] - 128.0
            D = block_dct(block)
            Q = np.rint(D / qt).astype(int)
            for zind in range(1,64):
                rr, cc = ZIGZAG_RC[zind]
                bits.append(int(Q[rr,cc]) &amp;amp; 1)
    return bits

def bits_to_bytes(bits):
    if len(bits) &amp;lt; 32:
        return None
    blen = 0
    for i in range(32):
        blen = (blen &amp;lt;&amp;lt; 1) | bits[i]
    need = 32 + blen*8
    if need &amp;gt; len(bits):
        return None
    out = bytearray()
    for i in range(32, need):
        if (i-32) % 8 == 0:
            out.append(0)
        out[-1] = (out[-1] &amp;lt;&amp;lt; 1) | bits[i]
    return bytes(out)

def try_all_qualities(stego_path, qmin=40, qmax=95):
    for q in range(qmin, qmax+1):
        try:
            bits = extract_bits_for_quality(stego_path, q)
            payload = bits_to_bytes(bits)
            if payload is None:
                continue
            if b&amp;#x27;flag{&amp;#x27; in payload or all(32 &amp;lt;= b &amp;lt; 127 for b in payload[:min(50,len(payload))]):
                print(f&amp;quot;[+] Possibly quality={q}, payload_len={len(payload)} bytes&amp;quot;)
                print(payload[:200])
                open(f&amp;quot;recovered_q{q}.bin&amp;quot;,&amp;quot;wb&amp;quot;).write(payload)
        except Exception as e:
            pass

if __name__ == &amp;quot;__main__&amp;quot;:
    if len(sys.argv) &amp;lt; 2:
        print(&amp;quot;Usage: try_bruteforce_quality.py stego.jpg&amp;quot;)
        sys.exit(1)
    try_all_qualities(sys.argv[1], 40, 95)
&lt;/pre&gt;
  &lt;p id=&quot;egLO&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;m6ex&quot;&gt;The script isn&amp;#x27;t very pretty and it could be optimized and made better.&lt;/p&gt;
  &lt;p id=&quot;Q4sp&quot;&gt;The decoder tries to extract hidden bits from the quantized DCT coefficients of the luminance (Y) channel, assuming that:&lt;/p&gt;
  &lt;ul id=&quot;1DmH&quot;&gt;
    &lt;li id=&quot;Xnav&quot;&gt;the data is hidden in the LSB (least significant bit) of the coefficients,&lt;/li&gt;
    &lt;li id=&quot;4riD&quot;&gt;the standard JPEG quantization table is used,&lt;/li&gt;
    &lt;li id=&quot;zsf1&quot;&gt;the JPEG quality is unknown, so it is brute-forced,&lt;/li&gt;
    &lt;li id=&quot;I86G&quot;&gt;the first 4 bytes (32 bits) are the payload length (big-endian).&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;loYZ&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;7UKH&quot;&gt;This is how we get a flag: &lt;strong&gt;grodno{Qu@ntizat10n_1nfel1c1tq_m@tters}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;yZXv&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;lXvg&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;uube&quot;&gt;NorthWind Secrets&lt;/h1&gt;
  &lt;blockquote id=&quot;kZiS&quot;&gt;While investigating the coordinator&amp;#x27;s Telegram account, we discovered that he is a member of a group with an unusual name: &lt;strong&gt;NYHEIST2026&lt;/strong&gt;. A very strange base64 file was found in it. Decrypt the group&amp;#x27;s hidden plans.&lt;/blockquote&gt;
  &lt;p id=&quot;v4Fy&quot;&gt;After going to the group, we find a TXT file with 2.1 million characters in base64.&lt;/p&gt;
  &lt;p id=&quot;RhhK&quot;&gt;With the help of CyberChef, we find out that there&amp;#x27;s a hidden zip archive in this base64.&lt;/p&gt;
  &lt;p id=&quot;gwhP&quot;&gt;By writing a simple Python script, we will restore the archive itself and see that it is password-protected.&lt;/p&gt;
  &lt;p id=&quot;zybD&quot;&gt;Lovley John the Ripper quickly cracks the password (654321) and we see 512 pieces of the image.&lt;/p&gt;
  &lt;p id=&quot;MZqF&quot;&gt;Using Chatagpt, let&amp;#x27;s write a script to restore the full image.&lt;/p&gt;
  &lt;pre id=&quot;cH46&quot;&gt;
import os
import argparse
from PIL import Image

def parse_args():
    p = argparse.ArgumentParser(description=&amp;quot;Rebuild image from 512 parts.&amp;quot;)
    p.add_argument(&amp;quot;parts_dir&amp;quot;, help=&amp;quot;Directory with part1..part512 images&amp;quot;)
    p.add_argument(&amp;quot;output&amp;quot;, help=&amp;quot;Output image path&amp;quot;)
    p.add_argument(&amp;quot;--cols&amp;quot;, type=int, default=32)
    p.add_argument(&amp;quot;--rows&amp;quot;, type=int, default=16)
    p.add_argument(&amp;quot;--prefix&amp;quot;, default=&amp;quot;part&amp;quot;)
    p.add_argument(&amp;quot;--ext&amp;quot;, default=&amp;quot;png&amp;quot;)
    return p.parse_args()

def main():
    args = parse_args()

    cols = args.cols
    rows = args.rows
    prefix = args.prefix
    ext = args.ext
    parts_dir = args.parts_dir

    total = cols * rows

    first_path = os.path.join(parts_dir, f&amp;quot;{prefix}1.{ext}&amp;quot;)
    if not os.path.exists(first_path):
        raise FileNotFoundError(f&amp;quot;Missing {first_path}&amp;quot;)

    first = Image.open(first_path)
    tile_w, tile_h = first.size
    mode = first.mode

    out = Image.new(mode, (tile_w * cols, tile_h * rows))

    index = 1
    for r in range(rows):
        for c in range(cols):
            part_name = f&amp;quot;{prefix}{index}.{ext}&amp;quot;
            part_path = os.path.join(parts_dir, part_name)

            if not os.path.exists(part_path):
                raise FileNotFoundError(f&amp;quot;Missing {part_path}&amp;quot;)

            tile = Image.open(part_path)
            out.paste(tile, (c * tile_w, r * tile_h))
            index += 1

    out.save(args.output)
    print(f&amp;quot;[+] Rebuilt image saved as {args.output}&amp;quot;)

if __name__ == &amp;quot;__main__&amp;quot;:
    main()
&lt;/pre&gt;
  &lt;p id=&quot;MXoL&quot;&gt;Afterwards, we open the resulting image:&lt;/p&gt;
  &lt;figure id=&quot;ZB3c&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/bd/e7/bde78b36-5e53-43ce-bf51-5e5200bfe7f5.png&quot; width=&quot;1280&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;RTgV&quot;&gt;Flag: &lt;strong&gt;grodno{1_knew_th@t_th1s_w0uldn&amp;#x27;t_w0rk}&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>beaverszero:NYCTFOsint2026</id><link rel="alternate" type="text/html" href="https://teletype.in/@beaverszero/NYCTFOsint2026?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=beaverszero"></link><title>OSINT Writeups</title><published>2026-01-13T18:59:06.156Z</published><updated>2026-01-13T21:56:28.067Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/73/63/73634b3f-3a33-4ee1-8947-c9a53a71ef82.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/08/88/088882f6-13a1-414a-924f-4eddaf97eb8b.png&quot;&gt;Write-ups for Osint tasks by Kata. English is not my main language, so I might have problems with explanations.</summary><content type="html">
  &lt;h2 id=&quot;74Kd&quot;&gt;Preview&lt;/h2&gt;
  &lt;p id=&quot;ctOE&quot;&gt;Write-ups for Osint tasks by Kata. English is not my main language, so I might have problems with explanations.&lt;/p&gt;
  &lt;h2 id=&quot;zi7v&quot;&gt;&lt;/h2&gt;
  &lt;h1 id=&quot;ZgWJ&quot;&gt;Admin&amp;#x27;s Fuel&lt;/h1&gt;
  &lt;pre id=&quot;UeJf&quot;&gt;Rumor has it that our admins can survive solely on this wonderful drink.&lt;/pre&gt;
  &lt;pre id=&quot;JrUV&quot;&gt;This blend of malt and hops is known throughout Belarus.&lt;/pre&gt;
  &lt;pre id=&quot;NZK8&quot;&gt;Can you find the place where our fuel is produced?&lt;/pre&gt;
  &lt;pre id=&quot;yN9y&quot;&gt;​&lt;/pre&gt;
  &lt;pre id=&quot;niYS&quot;&gt;Flag example: grodno{grodno{Сity_Main_Square_1}&lt;/pre&gt;
  &lt;p id=&quot;Aufg&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;RW4x&quot;&gt;From the description it becomes clear that we are talking about a brewery. There is also an interesting pinned message in the Telegram group: &lt;/p&gt;
  &lt;blockquote id=&quot;iEbF&quot;&gt;‼️Аліварыя - лепшае беларускае піва‼️&lt;/blockquote&gt;
  &lt;p id=&quot;7LYT&quot;&gt;which translates as &lt;strong&gt;Olivariya - the best Belarusian beer&lt;/strong&gt;&lt;/p&gt;
  &lt;figure id=&quot;7GJv&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/08/88/088882f6-13a1-414a-924f-4eddaf97eb8b.png&quot; width=&quot;1162&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;afCI&quot;&gt;Flag format is grodno{Сity_Main_Square_1} &lt;br /&gt;So the flag is grodno{Minsk_Kiseleva_Street_30}&lt;/p&gt;
  &lt;p id=&quot;KIQ4&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;gB9L&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;PK6p&quot;&gt;Regular GoogleMaps&lt;/h1&gt;
  &lt;pre id=&quot;S5Y2&quot;&gt;I was browsing Google Maps looking for interesting views, when I suddenly saw THIS.&lt;/pre&gt;
  &lt;pre id=&quot;I3jn&quot;&gt;I wanted to share this find with friends, but I ABSOLUTELY accidentally closed the tab, cleared my history, and completely forgot where it was.&lt;/pre&gt;
  &lt;pre id=&quot;eC2W&quot;&gt;Can you help me find it?&lt;/pre&gt;
  &lt;pre id=&quot;joqW&quot;&gt;Flag format: grodno{State_City_Street}&lt;/pre&gt;
  &lt;figure id=&quot;AzO4&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/d4/0e/d40ee209-bc07-481f-8b67-0ebca15de345.png&quot; width=&quot;948&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;ebF5&quot;&gt;Using RIS (Reverse Image Search) we can see that this image is present in a large number of TikToks and Reels, in the comments of which I found the place itself: Moorpark, Arroyo Drive&lt;/p&gt;
  &lt;figure id=&quot;mLCh&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/3c/3c/3c3c60ad-a335-41da-b40c-40f8fc9c99f8.png&quot; width=&quot;783&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;VLdH&quot;&gt;The flag is &lt;strong&gt;grodno{California_Moorpark_Arroyo_Drive}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;3QjC&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;HGLf&quot;&gt;New Year&amp;#x27;s Eve&lt;/h1&gt;
  &lt;blockquote id=&quot;L7pA&quot;&gt;This is the last photo I took in 2025.&lt;br /&gt;It&amp;#x27;s hard to describe in words how many emotions you can get from celebrating the New Year in such a stunning place.&lt;br /&gt;And you need to find the official website of this ski resort)&lt;/blockquote&gt;
  &lt;figure id=&quot;l3d7&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/28/9a/289a392f-c2ff-4289-ba34-5ad4dac07317.png&quot; width=&quot;706&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;eeWf&quot;&gt;By the way, this is really my last photo in 2025) But where was it taken?&lt;/p&gt;
  &lt;p id=&quot;0ruB&quot;&gt;The photo shows a ski resort, it remains to be seen which one&lt;/p&gt;
  &lt;p id=&quot;zhVa&quot;&gt;Our favorite Reverse Search shows: &lt;/p&gt;
  &lt;figure id=&quot;BxSs&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/6a/e7/6ae7cda2-a291-4e48-9e63-6433c4cf9eb3.png&quot; width=&quot;1166&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;GnUF&quot;&gt;So we learn that there are several ski resorts in Belarus, and we can simply look through their websites in the flag, but if we google the most popular ones, we can find the exact location where the photo was taken.&lt;/p&gt;
  &lt;figure id=&quot;BysK&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/0a/e7/0ae7aaba-6c20-4f66-a383-a71d2c3c7e9c.png&quot; width=&quot;2500&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;niou&quot;&gt;This is the Logoisk ski resort and their official website: &lt;a href=&quot;https://logoisk.by/&quot; target=&quot;_blank&quot;&gt;https://logoisk.by/&lt;/a&gt;&lt;br /&gt;And the flag: &lt;strong&gt;grodno {&lt;a href=&quot;https://logoisk.by&quot; target=&quot;_blank&quot;&gt;https://logoisk.by&lt;/a&gt;}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;ZD6J&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;Gqoq&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;pXyi&quot;&gt;Flight of the grinch&lt;/h1&gt;
  &lt;p id=&quot;ErGx&quot;&gt;&lt;/p&gt;
  &lt;pre id=&quot;2qsg&quot;&gt;After Santa took off to deliver Christmas presents around the world, something strange appeared in the sky.&lt;/pre&gt;
  &lt;pre id=&quot;XKTl&quot;&gt;The Grinch decided to leave his own “holiday decoration”  not on a Christmas tree, but high above the ground, visible only to those who were watching the skies carefully.&lt;/pre&gt;
  &lt;pre id=&quot;XPnw&quot;&gt;The drawing was made by an aircraft, leaving a distinctive track that formed a recognizable shape.&lt;/pre&gt;
  &lt;pre id=&quot;vfiU&quot;&gt;You need is to determine the exact date when the pilot who created this sky drawing received their pilot certificate.&lt;/pre&gt;
  &lt;pre id=&quot;LIXk&quot;&gt;​&lt;/pre&gt;
  &lt;pre id=&quot;JOM6&quot;&gt;Flag format: grodno{MM.DD.YYYY}&lt;/pre&gt;
  &lt;p id=&quot;oEup&quot;&gt;&lt;/p&gt;
  &lt;figure id=&quot;ynLm&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/ca/8f/ca8f15d9-9c33-408f-8d3c-a615ef23ecf0.png&quot; width=&quot;1280&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;2IOG&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;VGRj&quot;&gt;And again, the reverse search gives us the result.&lt;/p&gt;
  &lt;figure id=&quot;yxC7&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/a5/ed/a5ed3422-aa27-455d-b086-391903a44ccc.png&quot; width=&quot;1206&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;X33x&quot;&gt;By entering the tail number on the website &lt;a href=&quot;https://www.flightaware.com/live/flight/N6914W&quot; target=&quot;_blank&quot;&gt;https://www.flightaware.com/live/flight/N6914W&lt;/a&gt; we can find out that it is registered to Timothy M. Pearson&lt;/p&gt;
  &lt;p id=&quot;QRxl&quot;&gt;The next step is to go to the FAA and search for the pilot by name:&lt;/p&gt;
  &lt;figure id=&quot;3TvT&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/7e/60/7e608bcb-c022-42e8-ac74-810bab043f4d.png&quot; width=&quot;1207&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;dRYR&quot;&gt;And we see the pilot we need TIMOTHY MARTIN PEARSON&lt;/p&gt;
  &lt;figure id=&quot;C5vs&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/96/2f/962fad21-2311-40c3-b4ae-e0bcc84efa0c.png&quot; width=&quot;1132&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;U1Mt&quot;&gt;Date of issue was 01.29.2021&lt;/p&gt;
  &lt;p id=&quot;XydS&quot;&gt;The flag is &lt;strong&gt;grodno{01.29.2021}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;RMVL&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;uNv9&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;mcXl&quot;&gt;Visiting Belorussian Grandfather&lt;/h1&gt;
  &lt;pre id=&quot;DGQH&quot;&gt;I visited my grandfather this summer while he was on vacation. He started working in December, so I wanted to come visit him and help him out, but I forgot where his house is. I have a photo of his assistant, and I remember his house is somewhere on the border  in the forest.&lt;/pre&gt;
  &lt;pre id=&quot;oC4t&quot;&gt;Can you help me?&lt;/pre&gt;
  &lt;pre id=&quot;r3Hc&quot;&gt;​&lt;/pre&gt;
  &lt;pre id=&quot;eDvj&quot;&gt;Flag format: grodno{Name_of_the_Forest}&lt;/pre&gt;
  &lt;p id=&quot;A1w2&quot;&gt;&lt;/p&gt;
  &lt;figure id=&quot;y6oI&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/08/76/0876f8ae-2534-48c4-bbe2-1c87bca21ea6.png&quot; width=&quot;960&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;xNT0&quot;&gt;It&amp;#x27;s very easy chall so I don&amp;#x27;t want to waste your time or mine. RIS shows us that this place is in Belovezhskaya Pushcha.&lt;/p&gt;
  &lt;p id=&quot;csfv&quot;&gt;Flag:&lt;strong&gt; grodno{Belovezhskaya_Pushcha}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;qnK8&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;svPb&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;yrli&quot;&gt;Beginning of the NorthWind&lt;/h1&gt;
  &lt;pre id=&quot;IVSA&quot;&gt;During preparations for the holiday, an unknown APT group called “NorthWind”  kidnapped Santa Claus, and now New Year&amp;#x27;s is in danger.&lt;/pre&gt;
  &lt;pre id=&quot;YBRc&quot;&gt;We have found the contact of the person coordinating the operation. His current nickname on Telegram is @vvanuss&lt;/pre&gt;
  &lt;pre id=&quot;9qmE&quot;&gt;Find the information hidden in his previous nickname; it should help us move forward with the investigation.&lt;/pre&gt;
  &lt;p id=&quot;Dbyy&quot;&gt;We have a Telegram account username @vvanuss&lt;/p&gt;
  &lt;p id=&quot;vDG6&quot;&gt;Telegram has a HUGE number of OSINT bots, so we&amp;#x27;ll use one of them for this solution: @Funstat_fbot&lt;br /&gt;&lt;br /&gt;Let&amp;#x27;s enter the user&amp;#x27;s ID and get our flag:&lt;/p&gt;
  &lt;figure id=&quot;D1l8&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/28/88/2888c61c-abe7-418a-b7bc-af58323b1dda.png&quot; width=&quot;682&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;TqCA&quot;&gt;Flag:&lt;strong&gt; grodno{f1rst_st3p_0f_1nvest1g@tion}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;ZJdE&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;Pirh&quot;&gt;Admin&amp;#x27;s Setup&lt;/h1&gt;
  &lt;blockquote id=&quot;SeQb&quot;&gt;Author: @vvanuss&lt;/blockquote&gt;
  &lt;blockquote id=&quot;cqIW&quot;&gt;Many people already know almost everything about me, but can you find the name of my laptop processor?&lt;/blockquote&gt;
  &lt;blockquote id=&quot;YyVK&quot;&gt;Flag format: grodno{Pentium_4}&lt;/blockquote&gt;
  &lt;p id=&quot;b2S4&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;XTnF&quot;&gt;The author of this task has a Telegram channel, which you can scroll to find a screenshot from the btop.&lt;/p&gt;
  &lt;figure id=&quot;gwjq&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/d7/30/d7309f9e-5728-4e7b-a816-65cdccd8ee3f.png&quot; width=&quot;1280&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;dxp2&quot;&gt;And we see the full name of the processor: Ryzen 7 6800H&lt;br /&gt;&lt;br /&gt;I admit that there is a problem with the flag format, since based on it the flag will be: grodno{Ryzen_7} but the real flag is &lt;strong&gt;grodno{Ryzen_7_6800H}&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;MDnk&quot;&gt;&lt;/p&gt;
  &lt;h1 id=&quot;wXjP&quot;&gt;babyOSINT&lt;/h1&gt;
  &lt;pre id=&quot;ANb6&quot;&gt;On December 31st, an employee of one company posted a photo from a corporate party with the caption:&lt;/pre&gt;
  &lt;pre id=&quot;jiC4&quot;&gt;​&lt;/pre&gt;
  &lt;pre id=&quot;A1fE&quot;&gt;&amp;quot;Best New Year in 10 years 🎅&amp;quot; &lt;/pre&gt;
  &lt;pre id=&quot;GbuY&quot;&gt;​&lt;/pre&gt;
  &lt;pre id=&quot;QDuT&quot;&gt;The account was deleted a day later.&lt;/pre&gt;
  &lt;pre id=&quot;Mf6F&quot;&gt;​&lt;/pre&gt;
  &lt;pre id=&quot;lWcZ&quot;&gt;Find the address of this establishment:&lt;/pre&gt;
  &lt;pre id=&quot;E9ee&quot;&gt;Flag format: grodno{Main_Square_1A}&lt;/pre&gt;
  &lt;p id=&quot;smKd&quot;&gt;&lt;/p&gt;
  &lt;figure id=&quot;HhqP&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/16/81/1681aa4b-0c74-4a98-bb8a-2917176b94e0.png&quot; width=&quot;1169&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Od1P&quot;&gt;RIS shows that this photo was taken at the bar named Spichki.&lt;/p&gt;
  &lt;figure id=&quot;BbkF&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/6b/cd/6bcd3b16-d995-4b28-a911-a179a216a9f1.png&quot; width=&quot;654&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;9oGi&quot;&gt;CTF is held in Grodno, so it&amp;#x27;s logical that the bar is also located in Grodno.&lt;/p&gt;
  &lt;figure id=&quot;KxpD&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/8d/a6/8da61112-f4d2-47b2-8d21-12a21e785158.png&quot; width=&quot;754&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;GlMu&quot;&gt;So the flag: grodno{Sovetskaya_Square_2A}&lt;/p&gt;

</content></entry></feed>