• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Гостевая статья Анализ данных Wi-Fi с помощью Jupyter Notebook

Когда дело доходит до прослушивания Wi-Fi, является кроссплатформенным и способным захватывать огромные объемы данных. Осмысление этих данных - еще одна задача. Вот тут-то и появляется Jupyter Notebook. Он может помочь проанализировать пакеты Wi-Fi и определить, к каким сетям ранее подключался конкретный телефон, что позволяет нам узнать личность владельца.

В целом, данные могут вводить в заблуждение, особенно когда их много, что является и благословением, и проклятием. Это повышает вероятность включения важных паттернов, но также увеличивает вероятность их затемнения. Поиск значимых шаблонов в необработанных данных может быть похож на поиск иголки в стоге сена, но бесплатные инструменты для анализа больших данных, такие как Jupyter Notebook, упрощают работу.

Wireshark для данных Wi-Fi
Wireshark - это невероятный инструмент для сбора данных Wi-Fi, который может быстро заполнить экран информацией. Эти данные могут многое вам сказать, в зависимости от того, что вы ищете, но часто слишком много говорят, чтобы можно было легко распознать шаблоны. В Wireshark есть встроенные способы анализа данных, но обмен результатами может быть затруднен, и встроенные в Wireshark инструменты могут не сказать вам, что вам нужно.

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

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

Блокнот Jupyter для анализа данных
Собрав информацию в Wireshark, мы можем экспортировать ее в виде файла CSV и импортировать в блокнот Jupyter. Преимущество этого состоит в том, что мы можем быстро срезать данные, сгенерированные Wireshark, используя библиотеку Pandas Python. Эта библиотека может работать с CSV-файлами в качестве фреймов данных, которые могут легко составлять графики и диаграммы данных для отображения взаимосвязей.

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

В нашем примере мы будем использовать Jupyter Notebook для изучения захвата Wireshark на общественном поезде. Во время захвата мы записываем устройства пассажиров, отвечающие на эскиз Arduino « », который создает 40 поддельных точек доступа Wi-Fi с именами популярных открытых сетей. Любое устройство, которое подключилось к точке доступа с тем же именем, что и одно из поддельных, попытается подключиться. Мы можем записать и построить график этих ответов в Jupyter Notebook, чтобы узнать, какие поддельные сети вызывают большинство телефонов и к каким из поддельных сетевых имен каждый телефон подключался в прошлом.

То, что тебе понадобится. Чтобы следовать за ним, вам понадобится компьютер с Wireshark. Он помогает иметь карту, которую можно перевести в режим монитора, но вы также можете использовать данные из захвата на GitHub, чтобы следить, если вы не можете сгенерировать свои собственные. Вы можете загрузить образец набора данных CSV, клонируя репозиторий, переходя в его каталог и перечислив его содержимое.

Код:
~# git clone https://github.com/skickar/Research.git

Cloning into 'Research'...
remote: Enumerating objects: 21, done.
remote: Counting objects: 100% (21/21), done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 107 (delta 9), reused 0 (delta 0), pack-reused 86
Receiving objects: 100% (107/107), 1.18 MiB | 4.91 MiB/s, done.
Resolving deltas: 100% (47/47), done.

~# cd Research
~/Research# ls

commands.json     jsonpayload.json                             sash.json
dataCleaner.py    MetroWilshireVermonttoHollywoodHighland.csv  SocalDecloaker.ino
fades.py          NullByteSpecial.ipynb                        twitter0.json
FindWiFiDistance  README.md                                    twitter1.json
json.json         RedLineResearch.ipynb

Вам также понадобится Python3 для запуска Jupyter, поэтому убедитесь, что в вашей системе он есть, прежде чем продолжить.

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

Затем вам нужно будет записывать в Wireshark только по указанному вами каналу. Запишите весь трафик, пока работает Beacon Spammer, чтобы отслеживать устройства, пытающиеся подключиться. Полученные данные должны хорошо работать для анализа.

Шаг 1 - Экспортируйте захват Wireshark в виде файла CSV
Сначала мы возьмем файл PCAP в Wireshark и превратим его в файл CSV для использования в Jupyter Notebook. Для этого откройте свой захват в Wireshark и добавьте все необходимые данные из пакетов в столбцы, щелкнув правой кнопкой мыши поле, которое вы хотите добавить, и выберите «Применить как столбец».
analyze-wi-fi-data-captures-with-jupyter-notebook.w1456.jpg

Затем добавьте любые фильтры отображения, которые вы хотите удалить нежелательные пакеты. Здесь я говорю, что хочу только пакеты с адресом назначения, совпадающим с частичным MAC-адресом «6c: 3f: 23». Этот фильтр захвата также использует [0: 3], чтобы указать, что я хочу посмотреть от начала до третьего октета MAC-адреса.
analyze-wi-fi-data-captures-with-jupyter-notebook.w1456 (2).jpg

Когда фильтр перехвата включен, видны только пакеты, отправленные спаммеру-маяку. Каждый раз, когда мы запускаем Beacon Spammer, он выбирает MAC-адрес и затем изменяет последнюю половину MAC для каждой ложной сети, которую он создает. Это облегчает выбор всех пакетов, отправляемых в наши поддельные сети, сообщая Wireshark только о включении пакетов, отправляемых на адреса, имеющие одинаковый частичный MAC-адрес.

Наконец, пришло время экспортировать наши выбранные пакеты. В разделе «Файл» нажмите «Экспортировать пакетные сечения» и выберите «Как CSV» для экспорта в нужном формате.
analyze-wi-fi-data-captures-with-jupyter-notebook.w1456 (3).jpg

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

analyze-wi-fi-data-captures-with-jupyter-notebook.w1456 (4).jpg

Как только это будет сделано, CSV-файл, содержащий данные, должен быть сохранен на вашем компьютере и готов к импорту в Jupyter Notebook.

Шаг 2 - Установите Jupyter и откройте новый ноутбук
Jupyter - это программа для анализа данных, написанная на Python, которая упрощает установку на любой компьютер с установленным Python3. Вы можете сделать это, выполнив следующую команду в новом окне терминала.
Код:
~# pip3 install jupyter

Requirement already satisfied: jupyter in /usr/local/lib/python3.7/dist-packages (1.0.0)
Requirement already satisfied: notebook in /usr/local/lib/python3.7/dist-packages (from jupyter) (6.0.2)
Requirement already satisfied: jupyter-console in /usr/local/lib/python3.7/dist-packages (from jupyter) (6.0.0)
Requirement already satisfied: nbconvert in /usr/local/lib/python3.7/dist-packages (from jupyter) (5.6.1)
Requirement already satisfied: ipywidgets in /usr/local/lib/python3.7/dist-packages (from jupyter) (7.5.1)
Requirement already satisfied: ipykernel in /usr/local/lib/python3.7/dist-packages (from jupyter) (5.1.3)
Requirement already satisfied: qtconsole in /usr/local/lib/python3.7/dist-packages (from jupyter) (4.6.0)
Requirement already satisfied: jupyter-client>=5.3.4 in /usr/local/lib/python3.7/dist-packages (from notebook->jupyter) (5.3.4)
Requirement already satisfied: Send2Trash in /usr/local/lib/python3.7/dist-packages (from notebook->jupyter) (1.5.0)
Requirement already satisfied: prometheus-client in /usr/local/lib/python3.7/dist-packages (from notebook->jupyter) (0.7.1)
Requirement already satisfied: pyzmq>=17 in /usr/local/lib/python3.7/dist-packages (from notebook->jupyter) (18.1.1)
Requirement already satisfied: terminado>=0.8.1 in /usr/local/lib/python3.7/dist-packages (from notebook->jupyter) (0.8.3)
Requirement already satisfied: jupyter-core>=4.6.0 in /usr/local/lib/python3.7/dist-packages (from notebook->jupyter) (4.6.1)
Requirement already satisfied: nbformat in /usr/lib/python3/dist-packages (from notebook->jupyter) (4.4.0)
Requirement already satisfied: traitlets>=4.2.1 in /usr/lib/python3/dist-packages (from notebook->jupyter) (4.3.2)
Requirement already satisfied: jinja2 in /usr/lib/python3/dist-packages (from notebook->jupyter) (2.10)
Requirement already satisfied: tornado>=5.0 in /usr/lib/python3/dist-packages (from notebook->jupyter) (5.1.1)
Requirement already satisfied: ipython-genutils in /usr/lib/python3/dist-packages (from notebook->jupyter) (0.2.0)
Requirement already satisfied: prompt-toolkit<2.1.0,>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from jupyter-console->jupyter) (2.0.10)
Requirement already satisfied: pygments in /usr/lib/python3/dist-packages (from jupyter-console->jupyter) (2.3.1)
Requirement already satisfied: ipython in /usr/local/lib/python3.7/dist-packages (from jupyter-console->jupyter) (7.10.2)
Requirement already satisfied: testpath in /usr/local/lib/python3.7/dist-packages (from nbconvert->jupyter) (0.4.4)
Requirement already satisfied: mistune<2,>=0.8.1 in /usr/local/lib/python3.7/dist-packages (from nbconvert->jupyter) (0.8.4)
Requirement already satisfied: pandocfilters>=1.4.1 in /usr/local/lib/python3.7/dist-packages (from nbconvert->jupyter) (1.4.2)
Requirement already satisfied: entrypoints>=0.2.2 in /usr/lib/python3/dist-packages (from nbconvert->jupyter) (0.3)
Requirement already satisfied: defusedxml in /usr/local/lib/python3.7/dist-packages (from nbconvert->jupyter) (0.6.0)
Requirement already satisfied: bleach in /usr/local/lib/python3.7/dist-packages (from nbconvert->jupyter) (3.1.0)
Requirement already satisfied: widgetsnbextension~=3.5.0 in /usr/local/lib/python3.7/dist-packages (from ipywidgets->jupyter) (3.5.1)
Requirement already satisfied: python-dateutil>=2.1 in /usr/lib/python3/dist-packages (from jupyter-client>=5.3.4->notebook->jupyter) (2.7.3)
Requirement already satisfied: ptyprocess; os_name != "nt" in /usr/local/lib/python3.7/dist-packages (from terminado>=0.8.1->notebook->jupyter) (0.6.0)
Requirement already satisfied: six>=1.9.0 in /usr/lib/python3/dist-packages (from prompt-toolkit<2.1.0,>=2.0.0->jupyter-console->jupyter) (1.12.0)
Requirement already satisfied: wcwidth in /usr/local/lib/python3.7/dist-packages (from prompt-toolkit<2.1.0,>=2.0.0->jupyter-console->jupyter) (0.1.7)
Requirement already satisfied: pickleshare in /usr/local/lib/python3.7/dist-packages (from ipython->jupyter-console->jupyter) (0.7.5)
Requirement already satisfied: backcall in /usr/local/lib/python3.7/dist-packages (from ipython->jupyter-console->jupyter) (0.1.0)
Requirement already satisfied: decorator in /usr/lib/python3/dist-packages (from ipython->jupyter-console->jupyter) (4.3.0)
Requirement already satisfied: pexpect; sys_platform != "win32" in /usr/local/lib/python3.7/dist-packages (from ipython->jupyter-console->jupyter) (4.7.0)
Requirement already satisfied: setuptools>=18.5 in /usr/lib/python3/dist-packages (from ipython->jupyter-console->jupyter) (40.8.0)
Requirement already satisfied: jedi>=0.10 in /usr/local/lib/python3.7/dist-packages (from ipython->jupyter-console->jupyter) (0.15.2)
Requirement already satisfied: webencodings in /usr/lib/python3/dist-packages (from bleach->nbconvert->jupyter) (0.5.1)
Requirement already satisfied: parso>=0.5.2 in /usr/local/lib/python3.7/dist-packages (from jedi>=0.10->ipython->jupyter-console->jupyter) (0.5.2)
После установки Jupyter мы можем запустить его, чтобы создать веб-интерфейс и открыть его автоматически. Для этого введите следующее. Если вы запускаете с правами root, вы получите сообщение о том, что это не рекомендуется, и вы можете продолжить или сменить учетную запись.
Код:
~# jupyter notebook

[I 00:26:51.488 NotebookApp] JupyterLab extension loaded from /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/jupyterlab
[I 00:26:51.488 NotebookApp] JupyterLab application directory is /Library/Frameworks/Python.framework/Versions/3.6/share/jupyter/lab
[I 00:26:51.490 NotebookApp] Serving notebooks from local directory: /Users/skickar
[I 00:26:51.491 NotebookApp] The Jupyter Notebook is running at:
[I 00:26:51.491 NotebookApp] http://localhost:8888/?token=4de1f5eba5b656d903d08298a831a11ba97a581e3e575cda
[I 00:26:51.491 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 00:26:51.496 NotebookApp]

    To access the notebook, open this file in a browser:
        file:///Users/skickar/Library/Jupyter/runtime/nbserver-54785-open.html
    Or copy and paste one of these URLs:
        http://localhost:8888/?token=4de1f5eba5b656d903d08298a831a11ba97a581e3e575cda

Должен открыться веб-браузер, позволяющий выбрать предыдущие проекты или открыть новый.
analyze-wi-fi-data-captures-with-jupyter-notebook.w1456 (1).jpg


Нажмите «Новый», а затем «Python 3», чтобы открыть новый блокнот.
analyze-wi-fi-data-captures-with-jupyter-notebook.w1456 (5).jpg


Шаг 3 - Импорт данных в Jupyter
Давайте посмотрим на макет. Вверху мы увидим варианты сохранения, вставки ввода и запуска команд. Мы также увидим подсказку «In», ожидающую добавления Python 3.
analyze-wi-fi-data-captures-with-jupyter-notebook.w1456 (6).jpg

Теперь нам нужно импортировать наш файл Wireshark CSV во фрейм данных Pandas. Библиотеки Pandas и Matplotlib помогут нам легко работать с захваченными данными и манипулировать ими.

Сначала мы импортируем Pandas и будем называть его pd , делая то же самое с библиотекой Pyplot из Matplotlib и plt . Теперь, когда мы ссылаемся на pd, Python 3 знает, что мы говорим о Pandas, и то же самое с plt и Pyplot.

Код:
In [ ]: import pandas as pd
        from matplotlib import pyplot as plt
Далее мы будем использовать опцию Pandas read_csv, чтобы указать расположение CSV-файла, с которым мы работаем, тип символа, разделяющего данные (запятая в CSV-файлах или вкладка в TSV-файлах), тип кодировки и строка, в которой находится заголовок, содержащий имена ячеек. Все это будет помещено в переменную, которую я назвал wd или wireless data , но вы можете называть свою как угодно.

Если вы используете такие данные, как CSV из , он не использует стандартную строку 0 для заголовков. Вы можете изменить значение в этом последнем аргументе на строку, в которой расположены метки столбцов, чтобы правильно маркировать данные.

Наконец, мы сэмплируем 10 случайных точек данных, используя метод .sample (10), чтобы увидеть результаты.
Код:
In [ ]: wd = pd.read_csv('PATH/TO/FILE.csv', delimiter = ',', encoding='latin-1', header=0)
        wd.sample(10)
Теперь, когда мы импортировали наши данные, мы можем начать работать с ними, чтобы увидеть, что мы можем извлечь из шаблонов внутри них.


Шаг 4 - Изобразите основные данные на графике
Визуализация данных является одним из самых важных моментов при внедрении их в Jupyter, поэтому давайте углубимся в изучение отношений с графиками. Спуститесь ниже вывода к новому входу, и мы начнем разбирать данные.

Для этого нам нужно узнать, как получить доступ к данным внутри фрейма данных. Наш полный фрейм данных - это наша переменная wd , которая содержит всю информацию, которую мы импортировали из нашего CSV-файла. Чтобы получить доступ к части данных, мы будем использовать имена столбцов, которые мы узнали из выборки данных в последней команде, чтобы получить доступ к данным в этих столбцах.

Если мы хотим отобразить значения в столбце Time , я могу получить к ним доступ, используя wd [Time] .

Зная это, я могу создать график, сначала определив размер с помощью команды rcParams , а затем нанеся на график элементы, которые я хочу отобразить, с помощью команды plt.plot (wd [A], wd ) , добавив 'o' к указать, что мы хотим использовать точки, а не линии для построения наших данных. Наконец, мы можем установить цвет с помощью последней переменной цвета .
Код:
In [ ]: plt.rcParams["figure.figsize"] = (20,10)
        plt.plot(wd['Time'], wd['Transmitter address'], 'o', color='DarkGreen')
Последним шагом будет маркировка наших осей X и Y на графике.
Код:
In [ ]: plt.xlabel('Time')
        plt.ylabel('Real Clients Sending Directed Packets To Fake Networks')
Как только это будет сделано, мы можем добавить plt.show () для построения фигуры.
Код:
In [ ]: plt.show()
Теперь сделайте то же самое, чтобы построить столбец «Адрес получателя» в зависимости от времени. Готовый результат должен выглядеть примерно так.
Код:
In [ ]: ## Plotting directed packets (unmasked clients) connecting to fake networks over time
        # Here, we analyze when a clients is connecting to a fake network over time in the first figure.
        # In the second, we analyze which fake networks are recieving directed packets from unmasked clients over time.
        plt.rcParams["figure.figsize"] = (20,10)
        plt.plot(wd['Time'], wd['Transmitter address'], 'o', color='DarkGreen')
        plt.title('Clients Connecting to Fake Networks Over Time')
        plt.xlabel('Time')
        plt.ylabel('Real Clients Sending Directed Packets To Fake Networks')
        plt.show()
        plt.plot(wd['Time'], wd['Destination address'], 'o', color='DarkBlue')
        plt.title('Fake Networks Broadcasted by the Beacon Spammer Recieving Directed Packets')
        plt.xlabel('Time')
        plt.ylabel('Beacon Spammer MAC of Fake Networks Recieving Hit')
        plt.show()
Нажмите «Выполнить», пока выбран этот вход, чтобы увидеть результат.
analyze-wi-fi-data-captures-with-jupyter-notebook.w1456 (8).jpg

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

Шаг 5 - Нанесите назначение и передатчик друг на друга
Теперь мы собираемся расположить столбцы «Адрес получателя» и «Адрес передатчика» друг против друга. Это покажет, на какие сети реагирует каждое устройство и какая сеть вызвала реакцию большинства устройств.

Чтобы не допустить слишком длинных событий в метках графика, мы будем использовать метод .str, чтобы захватить только последние пять символов MAC-адреса в поле передатчика и получателя. Он должен быть достаточно уникальным для нашего набора данных. Чтобы получить доступ к последним пяти, мы можем использовать индекс -5, а затем a :, чтобы указать, что мы хотим перейти к концу строки. Если бы мы хотели обратного, мы могли бы указать : 5, чтобы взять первые пять. Когда мы вызываем wd [Адрес получателя] .str-5:, мы указываем последние пять символов в столбцах «Адрес получателя».

После обозначения осей X и Y код должен выглядеть следующим образом:
Код:
In [ ]: ## Plotting which client MAC addresses responds to which fake network MAC addresses
        ## Here, we see a fingerprint for every client device on the left.
        ## We can scan the row a device is in to determine which unique fake networks it will respond to.
        ## We can scan a column to find which fake networks cause the most client devices to respond.
        plt.rcParams["figure.figsize"] = (25,10)
        plt.plot(wd['Destination address'].str[-5:], wd['Transmitter address'].str[-5:], 'o',)
        plt.title('Clients Connecting to Fake Networks')
        plt.xlabel('Beacon Spammer Fake Networks')
        plt.ylabel('Real Clients Connecting')
        plt.show()
Нажмите «Выполнить», чтобы увидеть результаты.
analyze-wi-fi-data-captures-with-jupyter-notebook.w1456 (9).jpg

Вот это да! Мы видим, что у каждого телефона есть уникальный отпечаток. Мы также видим, что одна сеть, в частности, была намного более популярной, чем другие. Это означает, что хакер может использовать сеть для подключения большого количества телефонов.


Шаг 6 - Найдите самую популярную поддельную сеть
Чтобы отследить наиболее популярное поддельное имя сети, на которое реагировали близлежащие устройства, мы будем использовать тот же подход, что и сопоставление последних пяти символов MAC-адреса. В нашем наборе данных есть два типа пакетов: тестовые запросы и аутентификация . Зондовые запросы содержат SSID сети, а аутентификация - нет.

Мы собрали намного больше аутентификаций, чем тестовых запросов, поэтому, чтобы узнать имя сети, на которую реагировали все эти устройства, мы можем найти тестовые запросы с MAC-адресом назначения, который соответствует последним пяти символам нашей популярной сети "70 : 11" .

Мы сделаем это с помощью метода .str.contains () , который мы можем использовать как для сопоставления строки 70:11, так и строки фрейма зонда . Например, команда wd [wd ['Адрес получателя']. Str.contains ('70: 11 ') проверит, содержит ли поле Адрес получателя строку, которую мы ищем в MAC-адресе.

Собрав вместе, мы будем использовать переменную под названием Popular, в которой мы добавим все строки, которые имеют адрес получателя, совпадающий с адресом нашей загадочной сети, и слово Probe, указывающее, что это пробный запрос. Это должно составить список только проверочных запросов, направленных на поддельную сеть в популярной переменной.
Код:
In [ ]: ## Find the most popular network:
        popular = wd[wd['Destination address'].str.contains('70:11') & wd['Type/Subtype'].str.contains('Probe')]
Получив этот список тестовых запросов, мы можем отобразить данные в ячейке «SSID», содержащейся с помощью следующей команды.
Код:
In [ ]: popular['SSID'].value_counts().plot('barh')

Вот и мы! В результате нашего анализа мы определили, что SSID "attwifi" приведет к тому, что наибольшее количество соседних устройств автоматически подключится к нашей поддельной сети.
analyze-wi-fi-data-captures-with-jupyter-notebook.w1456 (10).jpg

Если мы хотим поделиться этими результатами с кем-либо еще, мы можем щелкнуть опцию экспорта, чтобы загрузить наш блокнот в различных форматах, включая PDF, уценку и собственный формат Jipyter .ipynb , который идеально отображается на GitHub.

Jupyter Notebook облегчает анализ Wi-Fi
Jupyter Notebook удачно подходит для анализа информации Wi-Fi, позволяя легко манипулировать массивами данных с помощью простых команд Python. Приложив немного больше усилий, можно импортировать целые файлы PCAP в необработанном виде, но использование фильтров захвата и столбцов в Wireshark для экспорта данных в формате CSV намного удобнее для начинающих. Я изучил Jupyter Notebook за один вечер, имея лишь небольшой опыт работы с Python, поэтому я призываю всех, кто ищет шаблоны в файлах CSV, попробовать этот бесплатный и простой в использовании ресурс.

Надеюсь, вам понравилось это руководство по получению идей от Jupyter Notebook и Wireshark! Если у вас есть какие-либо вопросы об этом руководстве, оставьте комментарий ниже.

-----------
 
Последнее редактирование модератором:
  • Нравится
Реакции: KPS5
Мы в соцсетях:

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