Skip to content

Latest commit

 

History

History
executable file
·
106 lines (78 loc) · 7.76 KB

File metadata and controls

executable file
·
106 lines (78 loc) · 7.76 KB

PT Flag Installer

Author: @greg0r0

Desc

Вот вам флаг. Бесплатно! Вам нужно только установить его.

PTInstaller.exe

Flag

ptctf{w3ll_l0c4l_ch3ck_1s_b4d_1d34}

Solve

Перед нами обычный необфусцированный установщик флага без антидебага. Написан на C#+WinForms. Это означает что задача обратной разработки сводится к чтению исходного кода после декомпиляции при помощи специализированной тулзы. Один из лучших инструментов для этой задачи - dnspyEx. Правда при первоначальном анализе окажется что это классический бинарь PE/x64, так как проект скомпилирован в один исполняемый файл с данными в оверлее, в том числе библиотека, которую откроем в dnspy, лежит там же.

Но первым шагом нам надо вытащить из оверлея собственно dll с кодом на шарпах. Это можно сделать различными способами, например использовать binwalk -e. Или использовать DetectItEasу и использовать функцию экстрактора.

Интерес представляют файлы Form1.cs и LicenseKeyCheck.cs - в них основная и интересующая нас логика.

Из кода Form1.cs узнаем, что у нас есть четыре стадии установки - лицензия, лицензионный ключ, установка и завершение установки. Нам важно, как проверяется лицензионный ключ - его реализация находится в класе LicenseKeyCheck. Формально, класс делится на две части - проверку частей ключа и генерацию данных флага.

Из функций генерации данных флага можно увидеть, что первая часть (ptctf{) - просто лежит байтами в классе StaticStorage, а вот остальные части флага лежат там же, но зашифрованные примитивным четырехбайтовым ксором. В теории можно побрутить, так как всего 4 байта в 2023 году перебрать можно очень быстро. Но - это не интересно и особо не имеет смысла, т.к. семантика флага нам не известна и в таком случае придется брутить скорборд неправильными флагами. Поэтому наша цель - проанализировать первую часть чекера и выяснить алгоритмы проверки ключа.

Нулевой шаг находится в коде формы, так как в конструктор флага данные из форм передаются в другом порядке. Получается, нулевой шаг - это формирование ключа методом перестановки:

...
/*
    1 2 3 4
       V
    3 2 4 1
*/

string data_part1 = textBox_Lkey_3.Text.Trim();
string data_part2 = textBox_Lkey_2.Text.Trim();
string data_part3 = textBox_Lkey_4.Text.Trim();
string data_part4 = textBox_Lkey_1.Text.Trim();

checker = new(data_part1, data_part2, data_part3, data_part4);
...

Далее, при формировании ключа, происходит его проверка. Функции называются просто - check[1-4](). Делают они следующее:

  • check1() - Произведение байт должно равнятся фиксированному значению. Декомпилятор мог сбить немного с толку, так как маски вместо привычного вида 0xFF000000 имеют целочисленный вид 4278190080. Существует множество подходящих значений для этой проверки - поэтому кусочек ключа не используется для расшифрования флага.
  • check2() - Обычный xor.
  • check3() - стандартный шарповый AES. Эту часть надо локально перебирать при помощи аналогичного кода на C#, но опять же - всего-то 2 байта... Код вообще можно скопировать из декомпилятора и чуть подправить. UPD: два байта, тк эта часть ключа берется по маске 0x0000FFFF
  • check4() - Тут отдельная реализация генерации данных флага и чекера. В чекере идет проверка даты - в ключе необходима дата в формате UnixTimestamp, старше чем октябрь 2077 года. В генерации же ключ для расшифрования берется из лицензионного соглашения.

Подходящий ключ под эти проверки:

cac3422f 3315732d 561affb1 6d731337

После проверки имитируется процесс установки, но пользователь видит только "Готово!", без конкретного места установки флага.

Для того, чтобы узнать куда установился флаг, откроем опять код формы и увидим в функции обработки кнопки код, который создает объект StreamWriter для файла System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), который является стандартным путем "My Documents". Там и будет flag.txt с флагом.

P.S. Пример брутера:

using System.Security.Cryptography;

byte[] k = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
byte[] iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] enc = { 0x5a, 0xf1, 0xcf, 0x02, 0x68, 0x3d, 0x2d, 0x62, 0x6d, 0xb2, 0x2f, 0x9c, 0x8d, 0x90, 0xb5, 0x85 };

uint a = 0;
for (uint i = 0; i < uint.MaxValue; i++)
{
    a = uint.Parse(i.ToString("x8"), System.Globalization.NumberStyles.HexNumber) & 0x0000FFFF;
    byte[] encrypted;

    using (Aes aesAlg = Aes.Create())
    {
        aesAlg.Key = k;
        aesAlg.IV = iv;
        ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
        using (MemoryStream msEncrypt = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            {
                using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                {
                    swEncrypt.Write(a);
                }
                encrypted = msEncrypt.ToArray();
                if (encrypted.SequenceEqual(enc))
                {
                    System.Console.WriteLine("FOUND");
                    System.Console.WriteLine(a.ToString("x8"));
                }
            }
        }
    }

    
   

}

System.Console.WriteLine("Done");