solutions

Разбор сервиса sijang с Runderground CTF #2

Общая информация о сервисе


Сервис sijang представлял собой бинарный сервис, состоящий из двух частей.

Первая часть являлась клиентом для некоторого магазина оружия, а вторая представляла контролер базы данных, которая была представлена в виде файла.

Концепция задания заключалась в том, что участники не могли напрямую взаимодействовать с контролером базы данных, и все их запросы проводились клиентом.
Обе части сервиса являлись бинарными файлами формата ELF и архитектуры x86-64.

Исследование сервиса


Функционал сервиса не такой большой. В клиенте пользователю доступно всего 6 базовых функций после регистрации и/или авторизации.


Кратко опишем имеющиеся опции.

1. Посмотреть профайл пользователя


Отображает базовую информацию о пользователе: имя, количество монет, текущее оружие в руках и инвентарь с оружием.


2. Купить оружие


Отображает маркет и последние 10 записей, которые в него поступили. Также предоставляет дополнительное меню на 3 действия, с помощью которых можно купить оружие разным способом: например, по прямому токену или индексу на текущей странице, а также запросить другую страницу маркета.


3. Продать оружие


Позволяет продать оружие, если оно в текущий момент лежит в вашем инвентаре.
При продаже вы должны будете подтвердить действие, а также указать описание, если хотите, и выбрать вариант продажи оружия (в архив или в паблик). Продажей в архив пользовался чекер, потому что оружие в архиве нельзя было купить, но, при наличии достаточного количества денег для покупки, можно было увидеть его описание из-за логической ошибки. При продаже оружия в паблик его могут купить и оно будет удалено из маркета.

Также при продаже рассчитывается стоимость оружия, и ценообразование на маркете абсолютно нечестное: вы продаёте оружие и получаете 10% от его стоимости, а продаётся оно за 1000% от его стоимости. Это явно намекает на то, что создание множества аккаунтов и перепродажа самому себе ни к чему не приведёт.


После того, как оружие действительно будет добавлено в маркет, вы получите сообщение о том, что оно было успешно добавлено, а также увидите его токен, по которому вы можете обращаться к нему для изменения статуса (архив/паблик) и покупки обратно (если вам хватит денег).


Также токен оружия служил attack_data, чтобы можно было удобнее покупать оружие с флагами в описании.

4. Изменить статус


Данная опция позволяла поменять статус оружия (архив/паблик), владельцем которого вы являетесь, но которое уже находится в маркете.


5. Установка оружия


Данная опция позволяла вам установить одно оружие как основное, при этом оно временно удалялось из вашего инвентаря и его нельзя было продать.

6. Снятие оружия


Опция обратная предыдущей: при снятии оружие возвращается в инвентарь.

Уязвимость


Изначально закладывалась одна уязвимость, с помощью которой можно было получить большое количество денег и иметь возможность покупать любой товар.

Однако, в ходе соревнований, одной команде удалось получить себе отрицательный баланс, что также сломало логику покупки. Но, к сожалению, это команда сама не поняла, как это сделала, и смогла это сделать только на одной команде.

В целом, на многих маркетах кто-то смог сделать невалидные объекты, при покупке которых в некоторых случаях можно было получить баланс. К сожалению, команда, которая получила отрицательный баланс не знала об attack_data и не смогла сдать хотя бы один флаг.

Заложенная уязвимость была в гонке между продажей оружия и его установкой как основного.

Если внимательно посмотреть код продажи оружия, то станет понятно, что при продаже создаётся отдельный поток, который пытается продать оружие. При этом основной поток выполнения не дожидается завершения работы потока продажи и позволяет сразу же выполнить действия.

При рассмотрении потока продажи можно было найти интересный участок кода.


Смысл данного кода заключается в том, что если клиент не смог получить доступ к маркету на добавление нового элемента (то есть, в данный момент маркет обслуживает другого клиента), то поток будет жать 0x30d40 миллисекунд в течение 7 раз.

При этом до данного момента структура нашего оружия, которое мы продаём, ещё не была освобождена и оружие не было удалено из инвентаря, т.к. оно по факту не продано и, возможно, не будет продано, если не получится получить доступ к маркету.

В момент блокировки потока мы можем сделать действие по установке оружия как основного, после чего оно окажется у нас в профиле (в руках) и будет удалено из инвентаря.

Далее, в случае если поток получит доступ к маркету, поток попробует добавить оружие и освободить его, а также удалить из инвентаря. Освобождение указателя будет корректным, но при удалении возникнет ошибка, которая обрабатывается недостаточно и ход выполнения программы продолжается.

После этих манипуляций мы получим указатель на освобождённый объект и сможем переместить его обратно в инвентарь. Однако, данный указатель всё ещё будет интерпретироваться программой, как указатель на объект оружия, и он должен иметь в определённых полях определённые типы значений.


Структуру для оружия можно было получить с помощью анализа функции генерации случайного оружия, которая вызывалась при регистрации нового пользователя.


Размер структуры 24 байта (но в данном случае будет выделено больше, т.к. вызывается функция-обёртка над calloc, которая добавляет 16 байт к любому размеру). Первое поле - это указатель на имя оружия, второе поле - указатель на описание оружия (в данном случае это NULL), третье поле является качеством оружия (от него рассчитывается стоимость продажи).

Освобождение данного указателя происходит в коде потока продажи оружия и после освобождения память аналогичного размера нигде не выделятся, а также у пользователя нет прямого примитива выделения памяти.

На данном моменте (по убеждению автора) наступает самая сложная часть, связанная с тем, что необходимо найти код, способный выделить память размером 24 байта (или немногим больше, но чтобы попасть в данный чанк) и иметь структуру, схожую с оружием (достаточно, чтобы первое поле было верным указателем).

Данный код можно найти в функции получения элементов маркета при покупке оружия (когда мы запрашиваем 10 элементов для их отображения в таблице). Функция - get_items_from_server


Здесь была некоторая подсказка, которую мог заметить внимательный ревёрсер. Дело в том, что в большей части кода используется собственная функция выделения памяти, которая по сути представляет маленькую обёртку над calloc, но в некоторых местах используется calloc напрямую. В частности, в моменте инициализации оружия пользователя в функции init_user, в которой и инициализируется структура оружия, которое будет продаваться.

Таким образом мы можем получить чанк верного размера, теперь надо понять будет ли он иметь правильную структуру.
Немного посмотрев код, можно понять, что в данную структуру первым полем помещается указатель на имя объекта, который пришёл от маркета.


То есть, такой объект нам подходит.

Теперь нужно внимательно посмотреть на код выхода из меню покупки. Здесь была ещё одна логическая уязвимость, которая заключалась в том, что если вы вводите опцию "4" то выходите из меню, и при этом чанки страницы освобождаются, и таким образом освобождается и наш чанк. Но, если вы вводите любую некорректную опцию, освобождения памяти не происходит.


Таким образом мы можем оставить наш объект не освобождённым.

Исходя из всего выше описанного, формируем план атаки.

  1. Создаём 10 (или более) потоков в которых будем пытаться блокировать маркет
  2. Во время работы потоков подключаемся и пытаемся продать оружие
  3. Сразу после подтверждения продажи отправляем команду "установить оружие", чтобы поставить себе в качестве оружия объект, который продаётся и будет освобождён
  4. Дожидаемся продажи оружия
  5. Передаём опцию "2" чтобы получить чанк, схожий по размеру и структуре с оружием
  6. Выходим из меню покупки через указание некорректной опции, чтобы оставить чанк не освобождённым
  7. Снимаем оружие через опцию "Unset weapon"
  8. Продаём снятое оружие и получаем множество монет (т.к. поле качество было переписано некоторым другим полем, с большим значением)

Некоторые действия из перечисленных были реализованы в PoC-эксплоите на нашем github-е - https://github.com/C4T-BuT-S4D/training-18-10-20/blob/master/sploits/sijang/race_condition.py

Но, после момента продажи, он переходит в интерактивный режим.

Полный пример эксплуатации.


Запускаем эксплоит и видим, что мы смогли установить себе оружие как основное, а потом оно было продано. Теперь передаём опцию "2", чтобы получить выделение чанка нужного размера.


Теперь выходим через опцию "9" (чтобы не было освобождения) и снимаем оружие.


Продаём оружие и видим, что цена будет огромной, а качество и вовсе отрицательное.
Теперь мы можем купить оружие по токену (взять это можно из attack_data) и получить флаг.


Можно использовать этот аккаунт для покупки остального (если команда которую вы атакуете не заметит это и не попытается его удалить).

Таким образом и решался данный сервис.
A/D Reverse PWN