Codeby Games Codeby Games - Мастер Рандома [Writeup]

xverizex

Green Team
29.12.2022
25
14
BIT
227
Задание должно было решиться легко, но я столкнулся с проблемой, которую долго не мог решить. Открыв приложение в гидре можно увидеть, что доступен флаг debug, если в меню указать пункт 'D'. После включения флага, переходим в check_pass. Декомпилятор гидры выдал вот такой код. Ниже приведен код функции check_pass, и я решил, написать свой генератор кода, но генерировался совсем не тот код, который бы подошел.
C++:
void check_pass(void)

{
  int iVar1;
  tm *__tp;
  char local_118 [88];
  time_t local_c0;
  char our_password [65];
  char generated_password [65];
  int index;
 
  local_c0 = time((time_t *)0x0);
  srand((uint)local_c0);
  for (index = 0; index < 0x40; index = index + 1) {
    iVar1 = rand();
    generated_password[index] = (char)iVar1 + (char)(iVar1 / 0x1a) * -0x1a + 'A';
  }
  generated_password[64] = '\0';
  if (is_debug == '\x01') {
    __tp = gmtime(&local_c0);
    strftime(local_118,0x50,"%Y-%m-%d %H:%M:%S",__tp);
    printf(s_[%s]_-_LOG:_00102020,local_118);
    puts("...ubuntu3.6_amd64");
    printf("_IO_2_1_stdin_: %#lx\n",our_password._48_8_);
  }
  printf(s__:_0010206d);
  fflush(stdout);
  fgets(our_password,0x41,stdin);
  iVar1 = strcmp(generated_password,our_password);
  if (iVar1 == 0) {
    give_me_data();
  }
  else {
    puts(&DAT_0010208b);
  }
  return;
}

Долго провозившись, я начал обдумывать другие решения. Что ещё может остаться в данном случае, чтобы попытаться решить задачу. Мне пришел верный вариант. Мы можем использовать эту же программу для генерации кода.
Итак, я зашел на сервер и дошел до места ввода пароля. Было указано время. [2024-04-13 11:18:51].
С помощью вот такого сишного кода я смог сгенерировать нужное время.
C:
        time_t t = time (0);
        tm = gmtime (&t);
        tm->tm_hour = 11 + 5;
        tm->tm_min = 18;
        tm->tm_sec = 51;
        t = mktime (tm);
        printf ("time: %s\n", t);

Почему я добавил к 11 ещё 5, это стало ясно далее, когда я попытался сгенерировать пароль. Уж пришлось так сделать, потому что время отставало на 5 часов. Запишем число, которое выдала утилита.
Что делаем дальше? Заходим с помощью отладчика с помощью radare2.
Bash:
r2 -d task.elf
Переходим в функцию main, затем переход в режим просмотра и переход в функцию check_pass.
Bash:
s main
V
листаем вниз и встречаем функцию check_pass.
Смотрим какое число справа от check_pass (у меня это было 2)
Нажимаем 2 и мы переходим в код этой функции.
Я приведу пример простого дизассемблера, потому что так проще. Будет без картинок, но вы держитесь. Так тоже можно объяснить. Посмотрите на этот код.
Код:
;-- check_pass:
            0x0000121a  push rbp
            0x0000121b  mov rbp, rsp
            0x0000121e  sub rsp, 0x110
            0x00001225  mov edi, 0
            0x0000122a  call sym.imp.time
            0x0000122f   mov qword [rbp - 0xb8], rax
            0x00001236  mov rax, qword [rbp - 0xb8]
            0x0000123d  mov edi, eax
            0x0000123f   call sym.imp.srand
            0x00001244  mov dword [rbp - 4], 0
     ┌─<0x0000124b      eb38           jmp 0x1285
      │    0x0000124d      e88efeffff     call sym.imp.rand
Как мы видим, по адресу 0x122f мы записываем результат rax в локальную переменную. Тут то нам и нужно поменять значение rax. Но для начала переходим в режим просмотра отладчика.
Код:
V!!
нажимаем ':'
db 0x122f

Так мы поставили наш первый бряк на место в памяти. Осталось поставить бряк на место проверки пароля, чтобы посмотреть сгенерированный пароль.
Код:
            0x00001363      488d9550ffff.  lea rdx, [rbp - 0xb0]
            0x0000136a      488d45a0       lea rax, [rbp - 0x60]
            0x0000136e      4889d6           mov rsi, rdx
            0x00001371      4889c7           mov rdi, rax
            0x00001374      e817fdffff        call sym.imp.strcmp
Опять нажимаем ':' и ставим бряк на адрес 0x1374.
Код:
db 0x1374
Далее пишем в отладчике
Код:
dc
и начинает выполняться программа. Включаем отладку, чтобы удостовериться, что время такое как на сервере (тот самый сервер, который на codeby.games - мастер рандома). Затем переходим в вести пароль. бряк останавливает выполнение.
Записываем в rax новое значение. Если вы забыли, то в rax сейчас содержится время в секундах. Его мы меняем на то, что нам пишет утилита (та, которая генерирует число mktime из даты).
Код:
dr rax=0x.... <- здесь число в шестнадцатеричной системе счисления
Теперь можем продолжить выполнение программы.
Код:
dc
Далее он предлагает указать пароль. Пишем всякий вздор и нажимаем enter. Бряк останавливает нас на проверке. Отсюда мы достаем нашу строку.
Код:
ps @rdi
Эта строка и есть пароль к серверу, которая завязана на времени. Время генерируется при каждом новом вызове функции, поэтому важно не затупить, а то придется несколько операций заново производить.

Всем спасибо.
 

nitrotek

New member
01.05.2024
2
0
BIT
10
Можно без отладчика, достаточно развернуть функцию генерации сида и на вход ей подать дату и в ремя, которые сервер в режиме D показывает
C:
#include <iostream>

using namespace std;

const int SecondsPerMinute = 60;
const int SecondsPerHour = 3600;
const int SecondsPerDay = 86400;
const int DaysOfMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

bool IsLeapYear(short year)
{
    if (year % 4 != 0) return false;
    if (year % 100 != 0) return true;
    return (year % 400) == 0;
}

time_t mkgmtime(short year, short month, short day, short hour, short minute, short second)
{
    time_t secs = 0;
    for (short y = 1970; y < year; ++y)
        secs += (IsLeapYear(y) ? 366 : 365) * SecondsPerDay;
    for (short m = 1; m < month; ++m) {
        secs += DaysOfMonth[m - 1] * SecondsPerDay;
        if (m == 2 && IsLeapYear(year)) secs += SecondsPerDay;
    }
    secs += (day - 1) * SecondsPerDay;
    secs += hour * SecondsPerHour;
    secs += minute * SecondsPerMinute;
    secs += second;
    return secs;
}

int main()
{
    time_t seed = mkgmtime(2024, 5, 1, 5, 41, 7);
    unsigned int d = ((unsigned int*)&seed)[0];
    srand(d);
    char bytes[65];
    for (int x = 0; x < 64; x++) {
        bytes[x] = rand() % 26 + 65;
    }
    bytes[64] = 0;

    printf("%s\n", bytes);
    return 0;
}
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!