Как стать автором
Обновить

За кулисами Android: что-то, чего вы можете не знать

Время на прочтение 14 мин
Количество просмотров 150K


0. Оглавление


  • 1. Предисловие
  • 2. Хак eMMC памяти HTC Desire HD с целью изменения идентификационной информации телефона
  • 3. Создание телефона-оборотня с использованием криптографии
  • 4. Ложная безопасность: обзор угроз несанкционированного доступа к данным
  • 5. Заключение


1. Предисловие


Мобильные гаджеты стали неотъемлемой частью нашей повседневной жизни, мы доверяем им свои самые сокровенные тайны, а утрата такого устройства может привести к серьезным последствиям. Сегодня много внимания уделяется освещению вопросов мобильной безопасности: проводятся конференции, встречи, крупные игроки выпускают комплексные продукты для персональной и корпоративной защиты мобильных устройств. Но насколько такие средства эффективны, когда устройство уже утрачено? Насколько комфортны они в повседневном использовании – постоянные неудобства с дополнительным ПО, повышенный расход батареи, увеличенный риск системных ошибок. Какие советы можно дать беспокоящимся за сохранность своих мобильных данных? Не хранить ничего важного на смартфоне? Тогда зачем он такой нужен – не птичек же в космос отправлять, в самом деле?
Сегодня я хочу поговорить с вами об устройствах под управлением ОС Android, созданной глубокоуважаемой мною компанией Google. В качестве примера я использую неплохой смартфон прошлых лет от компании HTC – Desire HD. Почему его? Во-первых, именно с него мы начали свою исследовательскую деятельность в области безопасности Android-устройств, во-вторых – это все еще актуальный смартфон с полным набором функций среднестатистического гуглофона. Он поддерживает все версии Android, в нем стандартный взгляд HTC на организацию файловой системы и стандартная же раскладка разделов внутренней памяти. В общем, идеальный тренажер для защиты и нападения.
С этим докладом я выступил на вот-вот только прошедшей конференции ZeroNights 2012 и теперь хочу презентовать его хабрасообществу. Надеюсь он будет вам интересен и даже немного полезен.

2. От А до Я: история низкоуровневого взлома read-only области eMMC памяти смартфона HTC Desire HD


Как нам известно, eMMC память устройства разделена на блоки, каждый из которых несет в себе определенную информацию, вот так выглядит примерное древо блоков памяти смартфона HTC Desire HD



Нас интересует содержимое /dev/block/mmcblk0p7 и как назло именно туда нам обычный S-OFF хак лезть не разрешает. Точнее посмотреть можно, а вот изменить – извините.
Что же в нем такого, что даже отсутствие проверки цифровой подписи не позволяет туда писать?
В partition 7 HTC упрятали важнейшую информацию устройства: CID, состояние флага “S-ON\S-OFF”, IMEI и настройки радиомодуля. Уверенные в непробиваемости своей защиты, HTC хранят эти данные открытым текстом.
Проведя глубокий анализ рынка устройств для «восстановления» андроид-смартфонов, было найдено и куплено устройство HXC Dongle(www.hxcdongle.com), созданное безвестными китайскими умельцами. Устройство обещало все виды манипуляций с практически всеми моделями андроид-телефонов производства компании HTC.

Продукт состоял из USB-ридера смарт-карт и самой смарткарты формата sim. С сайта производителя (hxcdongle.com) скачивалось приложение, которое используя смарткарту для проверки своей подлинности совершала любые манипуляции на стоковом не рутованном аппарате, включая пресловутый S-OFF, смену CID и кое-что ещё.
При этом сам аппарат подключался к ПК обычным USB-miniUSB шнурком и все операции проходили через ADB (Android Debug Bridge). Это навело на мысли о том, что вся «магия» творится на самом устройстве.
Потребовался реверс-инжиниринг процесса, для этого нужно было перехватить USB-обмен по ADB. Надо отдать должное мастерам из Поднебесной – они предусмотрели такой вариант событий, что при использовании большинства USB-снифферов программа прямо говорила о наличии перехватывающего драйвера или же система просто падала в BSOD.
Тем, не менее, нас это не остановило и после непродолжительного research мы наконец-то увидели весь процесс, а так же вмешались и получили заветные дампы.

Из дампов была восстановлена последовательность действий, которая представляла собой команды shell, также, в устройство закачивались некие файлы. Большая часть файлов оказалась известными утилитами (хоть и хитро переименованными) – root-exploit, busybox и дампами /dev/block/mmcblk0p7 до и после изменения + скрипт зачистки, убирающий следы.

Но среди них была и собственно утилита, которая используя уникальный ключ, сгенерированный ядром по таймстэмпу, снимала защиту от записи. Команда strings на этот файл дала нам текстовые строки сообщений об ошибках, по которым Google вывел нас на исходный код утилиты gfree за авторством Scotty2(известная в XDA-сообществе личность, талантливый хакер), которая умела кратковременно (до перезагрузки) снимать защиту микросхемы и менять Secure_Flag все в том же mmcblk0p7
Поиски контакта с автором привели меня на канал #g2root IRC-сети freenode.net, где я узнал, что Scotty2 уже давно никто не видел, однако на связь со мной вышел его товарищ, хакер под ником Guhl, который оказался соавтором gfree. Его сначала очень разозлило, что с помощью их труда китайцы зарабатывают деньги, тем более даже не упомянув имен создателей и вырезав все копирайты из тела программы. Затем, ознакомившись с результатами нашего исследования, он заинтересовался изменениями, которые китайцы внесли в модуль wpthis.ko, тем самым сделав RW доступ к partition 7 постоянным.
Каким же образом организована столь хитрая защита от записи и почему gfree умеет её отключать (это ключевой момент в получении S-OFF)?
Partition 7 защищается от записи в 2 этапа:
-Программная защита в секторе памяти
-Защитная инструкция в ядре, вызывающая перезагрузку при обнаружении попытки записи

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

Как известно из ядра мы имеем прямой доступ к железу и модуль wpthis.ko подает на контроллер микросхемы памяти eMMC (Quallcomm MSM7XXX) управляющий сигнал на цикл перезагрузки (reset_and_init_emmc) через подачу напряжения на 88 ножку:

void powercycle_emmc()
{
    gpio_tlmm_config(PCOM_GPIO_CFG(88, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), 0);

    // turn off.
    gpio_set_value(88, 0);
    mdelay(200);

    // turn back on.
    gpio_set_value(88, 1);
    mdelay(200);
}


Bootloader при загрузке устройства, выполняет конфигурирование микросхемы eMMC устанавливая в ней защиту от записи. Перезагрузка микросхемы eMMC приводит к сбросу настроек защиты от записи. Scotty удалось найти способ (88-ю ножку микросхемы, отключение и подача напряжения на которую, приводит к корректной перезагрузке контроллера eMMC.
После чего мы оказываемся в одном шаге от доступа к записи в /dev/block/ mmcblk0p7 в режиме online, нам мешает только ядро с защитной функцией. Для обхода этой защиты нам придется собрать свое ядро из исходника, внеся в него небольшие правки. Из набора исходников нам интересен файл drivers/mmc/card/block.c
В нём ищем строку #if 1 и просто меняем её на #if 0. Что любопытно – в режиме ядра нет родной команды на перезагрузку, поэтому используется BUG(), создающий ошибку, приводящую к перезагрузке.

#if 1 
 if (board_emmc_boot())
   if (mmc_card_mmc(card)) {
     if (brq.cmd.arg < 131073) {/* should not write any value before 131073 */
       pr_err("%s: pid %d(tgid %d)(%s)\n", func, (unsigned)(current->pid),           
                    (unsigned)(current->tgid), current->comm);
       pr_err("ERROR! Attemp to write radio partition start %d size %d\n",
                     brq.cmd.arg, blk_rq_sectors(req));
   
       BUG();
       return 0;
      }
#endif



Работает защита так: когда включены обе системы, то при попытке
#dd if=/sdcard/mmcblk0p7 of=/dev/block/mmcblk0p7

телефон просто перезагружается. Если ядро уже модифицировано, то запись пройдет, но на самом деле не запишется – перезагрузка вернет все вспять.
Какого рода эта уязвимость? Безусловно, это уязвимость аппаратная – производителем устройства оставлена лазейка для перезаписи памяти, которая всегда должна оставаться Read-Only, еще это уязвимость персональная – идентификационная информация телефона может быть изменена и это затруднит его поиск в случае утраты/кражи.
Но так же эта уязвимость – уязвимости операторов сотовой связи. Эксперименты с partition7 показали, что у операторов отсутствует фильтр IMEI номеров – в сети могут регистрироваться аппараты с любыми IMEI, включая дублирующиеся и явно не соответствующие реальности. Операторы, безусловно, в курсе проблемы, но возможно связаны по рукам и ногам…китайскими телефонами. Дело в том, что большая доля рынка телефонов принадлежит устройствам с одинаковым IMEI-номером. Отключение этих аппаратов от сети приведет к потере большого числа клиентов.
Ну а теперь мы поговорим о более приземленных вещах – защита нашего устройства от кражи хранящейся на нем информации. Сначала я расскажу о том, как защититься, а потом – от чего.

3. Paranoid Android: создание телефона-оборотня с использованием криптографии


С момента появления на рынке android-устройств энтузиасты искали способы максимально обезопасить свои данные и раскрыть потенциал сильно урезанного, но все еще Linux-ядра Android OS.
Началось все с The Guardian Project (https://guardianproject.info), которые выпустили порт известной linux-утилиты cryptsetup, реализующей возможности модуля ядра DM-crypt для прозрачного шифрования данных. Так как Android OS умеет использовать родное шифрование(пусть и очень криво да ненадежно), то этот модуль присутствует во всех стоковых ядрах. Столь мощный инструмент, однако, автором был использован в совершенно несерьезных целях – банальном создании криптоконтейнеров для хранения файлов и папок. Мы пошли дальше, найдя способ применить эту утилиту для шифрования пользовательских данных «на лету», но не только…
Итак, рассмотрим пошагово создание защищенного смартфона на базе ОС Андроид версий 2.3-4.1 на примере телефона HTC Desire HD.
Все важные данные хранятся в двух местах (это вытекает из доступных пользователю на запись областей):

/data/
/mnt/sdcard


Следовательно самый очевидный вариант – «просто» зашифровать их, используя cryptsetup. На деле все оказывается куда сложнее и интереснее: во-первых придется решать проблему открытия и монтирования криптоконтейнеров на этапе загрузки смартфона – нужно править ядро, добавляя в последовательность загрузки вызов интерактивного меню ввода пароля и операции с криптоконтейнерам, во-вторых нужно создавать собственно тач-меню для ввода пароля на этапе загрузки девайса. Подобные проекты были, но ни один из них не дошел до релиза – неизбежные проблемы порчи криптоконтейнера при отключении питания сводили на нет преимущества безопасности.
Мы пошли другим путем – крипто-ОС отделена от основной системы, таким образом, любые проблемы не помешают телефону включаться и функционировать и ядро мучить не надо.

Давайте рассмотрим пример пошагового исполнения нашего криптофона

Нам потребуется:
— Телефон на Android версии 2.3-4.1
— Root
— установленный пакет консольных утилит busybox
— установленный консольный менеджер криптоконтейнеров lm.cryptsetup
— включенный режим USB-Отладки
— бинарник reboot из состава ROM manager

Рекомендую использовать какой-нибудь кастом-ром, в котором уже все это есть, например Leedroid Rom для HTC Desire HD

1. Нам нужно создать контейнеры для хранения защищенных данных, начнем с /data/ контейнера:


В каталоге /data/ мы создадим пустой файл размером 800мб (напомню, в Desire HD нам доступен 1гб внутренней памяти, необходимо оставить хотя бы 200мб на функционирование открытой системы) —
#busybox dd if=/dev/zero of=/data/secure0 bs=1M count 800

Привяжем его к loopback-устройству –
#losetup /dev/block/loop3 /data/secure0

Отформатируем в Luks-формат используя 128-битное AES шифрование –
#lm.cryptsetup luksFormat –c aes-plain /dev/block/loop3

Откроем наш новый криптоконтейнер для дальнейшей настройки –
#lm.cryptsetup luksOpen /dev/block/loop3 data

Где data — выбранное нами название для крипто-контейнера.
Создадим в нашем крипто-контейнере файловую систему ext4 –
#mke2fs –T ext4 –L Secure0 -F /dev/mapper/data

Контейнер для /data/ готов, пока отложим его в сторону и займемся SD-картой, предварительно сохранив прогресс и правильно закрыв файл –
#lm.cryptsetup luksClose  data

Теперь нам нужна команда parted, она есть в составе кастомного рекавери-меню, там я ею и воспользуюсь (находясь в кастомном рекавери ClockWorkMod Recovery нам доступна работа в ADB с Root-правами), будем переразмечать SD-card, сделаем из одного раздела 2 – открытый и шифрованный –
parted /dev/block/mmcblk1

где mmcblk1 — наша SD-карта «в сборе»(в то время как mmcblk0 – это вся eMMC память)
Выводим содержимое –
print

Обращаем внимание на полный размер карты, запоминаем его. По-умолчанию с HTC DHD идет 8Гб карта, ее и будем рамечать.
Удаляем первый и единственный раздел SD-карты —
rm 1

Создаем два новых, сначала открытый в fat32, размером 4Гб —
mkpartfs primary fat32 0 4032

Затем заготовку для криптоконтейнера, делаем ее в ext2, но это не важно, все равно переформатируем —
mkpartfs primary ext2 4032 8065

Как видим первая цифра является границей первого раздела, вторая – границей памяти.
Выходим и перезагружаемся —
quit

Теперь в памяти SD-карты у нас 2 раздела, /dev/block/mmcblk1p1 – это 4гб открытой флешки, которая монтируется к ПК при подключении телефона usb-шнурком и /dev/block/mmcblk1p2 – это скрытый раздел на 4гб, который никуда не монтируется(linux, правда, вполне оперативно его распознает). Иными словами, при поверхностной оценке первая мысль – поддельная SD-карта, такое часто бывает – вполовину номинала.

Продолжаем работать с SD-картой. Теперь у нас вместо файла блоковое устройство (раздел карты), это значит loopback-устройство нам не понадобится, форматируем в Luks сразу весь раздел –
#lm.cryptsetup luksFormat –c aes-plain /dev/block/mmcblk1p2

Открываем созданный контейнер –
#lm.cryptsetup luksOpen /dev/block/mmcblk1p2 sdcard

Где sdcard – это выбранное имя для контейнера SD-карты
Форматируем открытый контейнер в fat32 –
#mkfs.vfat -n Seccard0 /dev/mapper/sdcard

Закрываем контейнер sd-карты, он полностью готов, и не трогаем его до того, как система окончательно заработает –
#lm.cryptsetup luksClose sdcard

Займемся наполнением data-контейнера, ведь сейчас он пуст, а в таком виде система работать не будет.
Выполним команды на открытие –
#losetup /dev/block/loop3 /data/secure0
#lm.cryptsetup luksOpen /dev/block/loop3 data

Создадим папку в корне(предварительно перемонтируя его в rw), куда примонтируем наш криптоконтейнер –
#mount –o remount,rw /
#mkdir /DATA
#mount –t ext4 /dev/mapper/data /DATA

На надо скопировать все папки из оригинальной /data/ кроме ../dalvik-cache/(содержимое программы создадут сами при первом запуске криптосистемы) и ../d/ ну и само собой без файла secure0 —
# cp -a /data/app /DATA
# cp -a /data/app-private /DATA
# cp -a /data/backup /DATA
# cp -a /data/data /DATA
# cp -a /data/dontpanic /DATA
# cp -a /data/drm /DATA
# cp -a /data/etc /DATA
# cp -a /data/htcfs /DATA
# cp -a /data/local /DATA
# cp -a /data/misc /DATA
# cp -a /data/property /DATA
# cp -a /data/secure /DATA
# cp -a /data/system /DATA
# cp -a /data/zipalign.log /DATA
# mkdir /DATA/d
# mkdir /DATA/dalvik-cache

Теперь у нас есть дубль нашей системы со всеми данными, отмонтируем и закрываем контейнер —
# umount /DATA
# lm.cryptsetup luksClose data


2. Теперь нам надо сделать из этих двух контейнеров с данными вторую ОС, загружаемую и выгружаемую по нашему желанию «на горячую»


Тут возможны варианты, я опишу самый первый, с которого мы ничинали.
Итак, что нужно сделать? Нам нужно на лету заменить содержимое /data/ и /mnt/sdcard/ на содержимое подготовленных криптоконтейнеров, а когда надо будет – вернуться обратно. Так как система живая, то просто так это сделать не получится. Мы используем команды, позволяющие останавливать видимую часть Андроид, сохраняя работоспособность ядра.
Вот пример одного из алгоритмов входа в крипто-режим:
#setprop ctl.stop zygote
#mount -o remount,rw rootfs /
#mkdir /DATA
#mkdir /mnt/SDCARD
#mount -o move /mnt/sdcard /mnt/SDCARD
#lm.cryptsetup luksOpen /dev/block/mmcblk1p2 sdcard
#mount -t vfat /dev/mapper/sdcard /mnt/sdcard
#mount -o remount,ro rootfs /
#mount /dev/block/mmcblk0p26 /DATA
#losetup /dev/block/loop5 /DATA/secure0
#lm.cryptsetup luksOpen /dev/block/loop5 data
#umount /data -l
#mount -t ext4 /dev/mapper/data /data
#setprop ctl.start zygote
#killall zygote


Алгоритм очень грубый и все можно сделать куда проще и эффективнее, но это лишь пример. В любом случае программу под андроид для обработки такого алгоритма либо придется писать, либо грубо пользоваться какой-нибудь программой для исполнения сценариев с поддержкой интерактива.
Выполнив этот сценарий, мы окажемся в крипторежиме, который пока 1 к 1 похож на оригинал. Чтобы не путаться, лучше сразу поменять «обои» и расположение значков. Потери в скорости и продолжительности работы смартфона практически неощутимы, что позволяет находиться в этом режиме постоянно.

3. Нам надо придумать способ безопасно завершать работу в крипторежиме.


Неправильное выключение в 1 случае из 10 приведет к порче файловой системы и сделает крипторежим не загружаемым (но все же все данные можно будет извлечь неповрежденными, правда копаясь вручную в контейнере), а значит стандартные опции выключения-перезагрузки нам не подходят.
Выключить телефон безболезненно нам поможет вот такой скриптик:
#sync
#setprop ctl.stop zygote
#setprop ctl.stop runtime
#setprop ctl.stop keystore
#fuser /data -m -k
#umount /data
#/lm.cryptsetup luksClose data
#/system/bin/reboot


За криптораздел на SD-карте переживать не стоит, с fat32 ничего не случится.

Теперь у нас есть телефон с двойным дном. Как его использовать? Можно прятать в крипто-режиме любовниц, деловых партнеров, инсайдерскую информацию, коллекцию немецких фильмов или инструментарий интернет-тролля. Очевидно, что при выборе пароля должной сложности взлом криптоконтейнера — задача не тривиальная. Так что ставим пинлок, отключаем ADB – и наши данные за каменной стеной.

4. Ложная безопасность: обзор способов извлечения данных из гуглофона.


Выше мы рассмотрели нетривиальный способ глухой обороны своих мобильных данных, но ни словом не обмолвились – от чего защищаемся? Очень много копий сломано в вопросах безопасности ОС Android, много докладов сделано, продуктов выпущено, но основные усилия по защите-обороне все же направлены на внешнего врага — удаленные взломы, вирусы, перехват трафика. Но самая высокая степень угрозы как всегда у физического доступа к устройству. Если аппарат попал в плохие руки, то с высокой долей вероятности информация покинет его темницы и порадует злоумышленника.
Поставим себя на место злоумышленника и попробуем составить алгоритм взлома устройства, которое мы можем потрогать руками. Представим, что у нас в руках снова HTC Desire HD, заблокированный графическим ключом или пинлоком, с чего начать?

1. Получаем доступ к данным


Способ получения доступа к данным зависит от начальных условий, но обязательно будет присутствовать два условия – наличие Android USB Debugging On и Root-доступа.
С учетом того, что огромное количество смартфонов рутуется и перепрошивается прямо после распаковки коробки, то велик шанс получить все на блюдечке с голубой каемочкой, ибо в большинстве кастомных прошивок ADB-доступ включен по-умолчанию, а в некоторых еще и убрана соответствующая индикация. Так что первый совет – проверьте статус USB Debugging и выключите его, если включен.
Следующая уязвимость S-OFF устройств – это кастомное рекавери. Если у вас есть S-OFF, то скорее всего вы прошиты кастомом, если вы прошиты кастомом, то скорее всего у вас есть кастомное рекавери, а кастомное рекавери – это одна сплошная дыра. Тут вам в одном флаконе и Root, и неотключаемый USB Debugging, а дальше делаем «mount –a» и пока-пока данные. Как бороться? Криптография, о которой говорили выше – единственный выход. Почему? Потому, как даже прошивка родного рекавери не поможет – злоумышленник точно так же снова прошьет fastboot’ом кастом, а дальше по инструкции.
Вариант посложнее: телефон S-ON, в ОС не попасть – пинлок, ADB выключен, рекавери стоковое и вообще рута нет. Безнадежный случай? Как бы не так.
Есть такая штука, зовётся XTC Clip, является клоном фабричного устройства HTC для низкоуровневой работы с телефонами разных моделей этого производителя (от Hero до Sensation). Позволяет сделать S-OFF без потери данных(в отличие от официального анлока бутлоадера) и ни разу не загрузившись в ОС.


Работа с устройством состоит из 3-х этапов:
  • 1. Подключение к ПК под управлением ОС Windows в виде USB-диска с программой
  • 2. Создание из SD-карты так называемой GoldCard(на карточку пишется служебная информация для считывания из инженерного меню телефона)
  • 3. Загрузки телефона в инженерное меню через кабель, вставляющийся в SIM-разъем телефона с последующим сбросом Secure Flag в 0 положение



После чего мы опять же шьем Recovery и переходим к пункту 2

2. Что брать?


После получения доступа в режиме Root к нутру смартфона мы можем его полностью вычистить. Например, полностью скопировав каталог /data/, мы получим, по сути, полную копию пользовательских настроек системы – аккаунты, программы, настройки софта, точки доступа и так далее. Это можно просто вставить в телефон той же модели с примерно той же версией ОС и получить клон атакуемого аппарата.
А можно брать только то, что интересно, например:
/data/system/accounts.db

Это база данных SQlite ваших аккаунтов, беда в том, что все пароли в ней лежат открытым текстом (гугл не считает это проблемой, так как получение доступа в этот раздел – это нарушение безопасности устройства, а не ОС), ставим на ПК программу вроде sqlitebrowser (http://sourceforge.net/projects/sqlitebrowser/)

Все ваши аккаунты с паролями в удобном читаемом виде, бери и пользуйся!
Что у нас есть еще интересного?
Телефонная книжка!
/data/data/com.android.providers.contacts/databases/contacts2.db

Тут у нас и сами контакты и история звонков.
Копаем дальше, смс –
/data/data/com.android.providers.telephony/databases/mmssms.db

Вся вкуснота в таблице Treads.
Ну а можно просто снять пинлок и пользоваться телефоном в штатном режиме, тоже через ADB –

adb shell
# sqlite3 /data/data/com.android.providers.settings/databases/settings.db
sqlite> update secure set value=65536 where name='lockscreen.password_type';
sqlite> .exit
# exit
adb reboot


Если sqlite3 отсутствует в нашем рекавери, то нужно его скачать отдельно и пропушить через ADB

3. Как защищаться?


  • 1. Не пользоваться Андроид
  • 2. Не брать телефоны, для которых существует Root и S-OFF
  • 3. Шифровать, шифровать и еще раз шифровать

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

Заключение


Сегодня мы с вами рассмотрели несколько вопросов уязвимости смартфонов на базе ОС Андроид, часть из них так же характерна и для планшетов под этой осью. Что касается уязвимости аппаратной, позволяющей менять идентификационные данные аппарата – это проблема вендора и операторов и им придется искать способы ее решать, нам же в свою очередь остается блюсти «информационную гигиену», знать опасности в лицо и соизмерять усилия по защите с ценностью защищаемых данных.

Всем, кто осилил — большое спасибо за внимание!
Теги:
Хабы:
+96
Комментарии 33
Комментарии Комментарии 33

Публикации

Истории

Работа

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн