6 Commits

Author SHA1 Message Date
8aeb9a4244 lab3 2026-03-16 14:43:38 +03:00
37eff0ed9b lab2 доработки 2026-03-16 12:08:42 +03:00
b6b5e16d63 lab1 -> report 2026-03-03 11:23:13 +03:00
bc1299e966 Отчёт лаб 2 2026-03-03 11:22:28 +03:00
60a4471a8c Код для lab2 2026-03-03 10:21:13 +03:00
07e02401eb Лаба 1 2026-03-03 07:58:06 +03:00
40 changed files with 3137 additions and 345 deletions

View File

@@ -1,345 +0,0 @@
\documentclass[a4paper, final]{article}
%\usepackage{literat} % Нормальные шрифты
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
\usepackage{tabularx}
\usepackage{booktabs}
\usepackage[T2A]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\usepackage{amsmath}
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
\usepackage{ragged2e} %для растягивания по ширине
\usepackage{setspace} %для межстрочного интервала
\usepackage{moreverb} %для работы с листингами
\usepackage{indentfirst} % для абзацного отступа
\usepackage{moreverb} %для печати в листинге исходного кода программ
\usepackage{pdfpages} %для вставки других pdf файлов
\usepackage{tikz}
\usepackage{graphicx}
\usepackage{afterpage}
\usepackage{longtable}
\usepackage{float}
% Рекомендуется для biblatex (кавычки/локализация цитат и т.п.)
\usepackage{csquotes}
% ГОСТ-стили для biblatex
\usepackage[
backend=biber,
bibstyle=gost-numeric, % ссылки вида: [1]
citestyle=gost-numeric,
sorting=none % порядок в списке = по первому цитированию
]{biblatex}
% Все источники хранятся в отдельном файле
\addbibresource{refs.bib}
\renewcommand*{\bibfont}{\small}
% \usepackage[paper=A4,DIV=12]{typearea}
\usepackage{pdflscape}
% \usepackage{lscape}
\usepackage{array}
\usepackage{multirow}
\renewcommand\verbatimtabsize{4\relax}
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
\usepackage{listings} %листинги
\usepackage{xcolor} % цвета
% \usepackage{hyperref}% для гиперссылок
\usepackage{enumitem} %для перечислений
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях
% \hypersetup{colorlinks,
% allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
\lstloadlanguages{ SQL}
% включаем кириллицу и добавляем кое−какие опции
\lstset{tabsize=2,
breaklines,
basicstyle=\footnotesize,
columns=fullflexible,
flexiblecolumns,
numbers=left,
numberstyle={\footnotesize},
keywordstyle=\color{blue},
inputencoding=cp1251,
extendedchars=true
}
\lstdefinelanguage{MyC}{
language=SQL,
% ndkeywordstyle=\color{darkgray}\bfseries,
% identifierstyle=\color{black},
% morecomment=[n]{/**}{*/},
% commentstyle=\color{blue}\ttfamily,
% stringstyle=\color{red}\ttfamily,
% morestring=[b]",
% showstringspaces=false,
% morecomment=[l][\color{gray}]{//},
keepspaces=true,
escapechar=\%,
texcl=true
}
\textheight=24cm % высота текста
\textwidth=16cm % ширина текста
\oddsidemargin=0pt % отступ от левого края
\topmargin=-1.5cm % отступ от верхнего края
\parindent=24pt % абзацный отступ
\parskip=5pt % интервал между абзацами
\tolerance=2000 % терпимость к "жидким" строкам
\flushbottom % выравнивание высоты страниц
% Настройка листингов
\lstset{
language=python,
extendedchars=\true,
inputencoding=utf8,
keepspaces=true,
% captionpos=b, % подписи листингов снизу
}
% Настройка содержания
\usepackage{tocloft}
\usepackage[hidelinks]{hyperref}
% section в содержании НЕ жирным
\renewcommand{\cftsecfont}{\normalfont}
\renewcommand{\cftsecpagefont}{\normalfont}
% убрать отступ у subsection
\setlength{\cftsubsecindent}{0pt}
% subsubsection курсивом
\usepackage{titlesec}
\titleformat{\subsubsection}
{\normalfont\large\itshape} % стиль: обычный + курсив
{\thesubsubsection} % номер (убери если не нужен)
{1em}
{}
\begin{document}
% ТИТУЛЬНЫЙ ЛИСТ
\begin{center}
\hfill \break
\hfill \break
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
\hfill \break
\hfill \break
\hfill \break
\large{Отчёт по практическим работам по дисциплине}\\
\large{<<Защита информации>>}\\
\hfill \break
\hfill \break
\hfill \break
\end{center}
\small{
\begin{tabular}{lrrl}
\!\!\!Студент, & \hspace{2cm} & & \\
\!\!\!группы 5130201/20101 & \hspace{2cm} & \underline{\hspace{3cm}} & Тищенко А. А. \\\\
\!\!\!Руководитель & \hspace{2cm} & & \\
\!\!\! & \hspace{2cm} & \underline{\hspace{3cm}} & Силиненко А. В. \\\\
&&\hspace{4cm}
\end{tabular}
\begin{flushright}
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2026г.
\end{flushright}
}
\hfill \break
\hfill \break
\begin{center} \small{Санкт-Петербург, 2026} \end{center}
\thispagestyle{empty} % выключаем отображение номера для этой страницы
\newpage
\tableofcontents
\thispagestyle{empty} % выключаем отображение номера для этой страницы
\newpage
\section*{Введение}
\addcontentsline{toc}{section}{Введение}
В рамках курса <<Защита информации>> было выполнено несколько практических работ, по результатам которых был составлен данный отчёт, содержащий информацию по всем практическим работам. Отчетная информация по каждой работе это отдельный раздел в общем отчете.
В отчёте представлены результаты следующих практических работ:
\begin{enumerate}
\item Практическая работа №1. Анализ уязвимостей программного обеспечения. Данная практическая работа посвящена анализу уязвимостей программного обеспечения с использованием Банка данных угроз безопасности информации ФСТЭК России~\cite{fstec-bdu}. В ходе выполнения работы предусмотрено изучение структуры разделов «Угрозы» и «Уязвимости», а также поиск уязвимостей по заданным критериям. Особое внимание уделяется выявлению уязвимостей, соответствующих используемым версиям операционных систем личных устройств, и рассмотрению возможных мер по их устранению.
\end{enumerate}
\newpage
\section{Практическая работа №1. Анализ уязвимостей программного обеспечения}
\subsection{Знакомство с разделом <<Угрозы>> Банка угроз}
Раздел <<Угрозы>> Банка данных угроз безопасности информации ФСТЭК России предназначен для систематизированного представления сведений об актуальных угрозах безопасности информации. Данный раздел содержит структурированную информацию, позволяющую оценить характер угроз, возможные последствия их реализации и способы противодействия.
Раздел включает в себя следующие основные аспекты:
\begin{enumerate}
\item Классификация угроз: угрозы распределяются по различным категориям в зависимости от источника возникновения, способа реализации и объекта воздействия. Это позволяет упорядочить информацию и упростить её анализ.
\item Описание угроз: для каждой угрозы приводится развернутое описание, включающее возможные сценарии реализации, цели нарушителя и потенциальные последствия для информационной системы.
\item Объекты воздействия: указываются типы информационных систем, ресурсов или процессов, на которые может быть направлена угроза.
\item Уровень опасности: каждой угрозе присваивается определённый уровень опасности, отражающий степень потенциального ущерба при её реализации.
\item Рекомендации по противодействию: приводятся общие меры и подходы, направленные на предупреждение реализации угрозы или снижение возможных негативных последствий.
\end{enumerate}
\subsection{Знакомство с разделом <<Уязвимости>> Банка угроз}
Раздел <<Уязвимости>> Банка данных угроз безопасности информации ФСТЭК России предназначен для получения сведений об актуальных уязвимостях программного обеспечения и их характеристиках. В рамках работы были рассмотрены подразделы: <<Список уязвимостей>>, <<Наиболее опасные уязвимости>> и <<Инфографика>>.
Список уязвимостей представляет собой структурированный перечень выявленных уязвимостей в программном обеспечении. Для каждой уязвимости указывается идентификатор, наименование, описание, затронутое программное обеспечение и его версии, уровень опасности, а также оценка по шкале CVSS. Уязвимости классифицируются по уровню критичности (критический, высокий, средний и др.), что позволяет определить приоритетность их устранения. Также приводятся рекомендации по устранению или минимизации последствий эксплуатации уязвимости.
Подраздел <<Наиболее опасные уязвимости>> содержит перечень уязвимостей с наивысшим уровнем опасности. Как правило, к ним относятся уязвимости, позволяющие выполнить удалённое выполнение кода, повысить привилегии, обойти механизмы аутентификации или получить несанкционированный доступ к информации. Данный раздел позволяет оперативно определить наиболее критичные риски для информационных систем.
\begin{figure}[h!]
\centering
\includegraphics[width=0.7\linewidth]{img/top_vulnerabilities.png}
\caption{Пример отображения наиболее опасных уязвимостей}
\label{fig:top_vuln}
\end{figure}
Подраздел <<Инфографика>> предназначен для наглядного представления статистических данных по уязвимостям. В нём отображается распределение уязвимостей по уровням опасности, типам ошибок, видам программного обеспечения и производителям. Инфографика позволяет визуально оценить текущее состояние защищённости программных продуктов и выявить наиболее проблемные направления.
\begin{figure}[h!]
\centering
\includegraphics[width=0.7\linewidth]{img/vulnerability_stats.png}
\caption{Пример статистического распределения уязвимостей}
\label{fig:stats_vuln}
\end{figure}
\newpage
\subsection{Версии ОС, используемые в личных устройствах}
На личном компьютере используется операционная система Ubuntu 25.10 (см. Рис.~\ref{fig:laptop-os-version}).
\begin{figure}[h!]
\centering
\includegraphics[width=0.5\linewidth]{img/laptop-os-version.png}
\caption{Версия операционной системы личного компьютера}
\label{fig:laptop-os-version}
\end{figure}
На личном мобильном устройстве используется операционная система Android 11 с графической оболочкой MIUI Global 12.5.14, сборка 12.5.14.0 RKURUXM (см. Рис.~\ref{fig:mobile-os-version}).
\begin{figure}[h!]
\centering
\includegraphics[width=0.5\linewidth]{img/mobile-os-version.jpg}
\caption{Версия операционной системы личного мобильного устройства}
\label{fig:mobile-os-version}
\end{figure}
\subsection{Уязвимости ОС личного компьютера}
Для поиска уязвимостей ОС личного компьютера на сайте Банка данных угроз безопасности информации ФСТЭК России были использованы следующие критерии:
\begin{enumerate}
\item Операционная система: Ubuntu 25.10.
\item Уровень опасности: высокий.
\end{enumerate}
Использовался уровень опасности <<высокий>>, так как критических уязвимостей для данной ОС не найдено.
По заданным критериям было найдено 10 уязвимостей (см. Рис.~\ref{fig:laptop-vulnerabilities}).
\begin{figure}[h!]
\centering
\includegraphics[width=0.8\linewidth]{img/laptop-vulnerabilities.png}
\caption{Уязвимости ОС личного компьютера}
\label{fig:laptop-vulnerabilities}
\end{figure}
Наиболее свежей является уязвимость BDU:2025-15300 <<Уязвимость интерфейсов cpu\_latency\_qos\_add, remove, update\_request модуля drivers/ufs/core/ufs-sysfs.c драйвера поддержки устройств SCSI ядра операционной системы Linux связана с ошибками синхронизации при использовании общего ресурса («Ситуация гонки»). Эксплуатация уязвимости может позволить нарушителю, действующему удаленно, вызвать отказ в обслуживании>>. Развёрнутое описание уязвимости представлено на рисунке~\ref{fig:laptop-vulnerabilities-details}.
Базовый вектор уязвимости CVSS 2.0: AV:A/AC:L/Au:S/C:C/I:C/A:C.
AV: A (Access Vector: Adjacent Network) — параметр, указывающий на то, что атакующий должен находиться в той же локальной сети или в непосредственной сетевой близости (например, в одной Wi-Fi сети) для реализации атаки.
AC: L (Access Complexity: Low) — низкая сложность эксплуатации уязвимости. Для успешной атаки не требуется выполнения сложных условий или специальной подготовки среды.
Au: S (Authentication: Single) — для осуществления атаки требуется прохождение аутентификации один раз. Это означает, что атакующий должен обладать учетной записью или иным способом пройти проверку подлинности.
C: C (Confidentiality Impact: Complete) — полное нарушение конфиденциальности. Уязвимость позволяет получить полный доступ к защищаемой информации.
I: C (Integrity Impact: Complete) — полное нарушение целостности. Злоумышленник может изменять или уничтожать данные без ограничений.
A: C (Availability Impact: Complete) — полное нарушение доступности. Эксплуатация уязвимости может привести к отказу в обслуживании или полной недоступности системы.
\begin{figure}[h!]
\centering
\includegraphics[width=0.9\linewidth]{img/laptop-vulnerabilities-details.png}
\caption{Развёрнутое описание уязвимости BDU:2025-15300}
\label{fig:laptop-vulnerabilities-details}
\end{figure}
Уязвимость была устранена в версии 25.10-6.17.0-14.14, поэтому для её устранения достаточно было обновить ОС до этой версии.
\newpage
\subsection{Уязвимости ОС личного мобильного устройства}
Для поиска уязвимостей ОС личного мобильного устройства на сайте Банка данных угроз безопасности информации ФСТЭК России были использованы следующие критерии:
\begin{enumerate}
\item Операционная система: Android 11.
\item Уровень опасности: критический.
\end{enumerate}
По заданным критериям была найдена 1 уязвимость (см. Рис.~\ref{fig:mobile-vulnerabilities}).
\begin{figure}[h!]
\centering
\includegraphics[width=0.8\linewidth]{img/mobile-vulnerabilities.png}
\caption{Уязвимости ОС личного мобильного устройства}
\label{fig:mobile-vulnerabilities}
\end{figure}
Развёрнутое описание уязвимости BDU:2023-08587 <<Уязвимость функции \\ callback\_thread\_event (com\_android\_bluetooth\_btservice\_AdapterService.cpp) операционной системы Android связана с использованием памяти после её освобождения. Эксплуатация уязвимости может позволить нарушителю, действующему удалённо, выполнить произвольный код>>, представлено на рисунке~\ref{fig:mobile-vulnerabilities-details}.
Базовый вектор уязвимости CVSS 2.0: AV:N/AC:L/Au:N/C:C/I:C/A:C.
AV: N (Access Vector: Network) — параметр, указывающий на то, что атакующий может осуществить атаку удалённо через сеть (например, через Интернет), без необходимости физического доступа или нахождения в локальной сети.
AC: L (Access Complexity: Low) — низкая сложность эксплуатации уязвимости. Для успешной атаки не требуется специальных условий или сложной подготовки.
Au: N (Authentication: None) — для эксплуатации уязвимости не требуется аутентификация. Атакующий может выполнить атаку без наличия учетной записи или прохождения процедуры входа в систему.
C: C (Confidentiality Impact: Complete) — полное нарушение конфиденциальности. Уязвимость позволяет злоумышленнику получить полный доступ к конфиденциальной информации.
I: C (Integrity Impact: Complete) — полное нарушение целостности. Атакующий может изменять, подменять или удалять данные без ограничений.
A: C (Availability Impact: Complete) — полное нарушение доступности. Эксплуатация уязвимости может привести к полной недоступности системы или отказу в обслуживании.
\begin{figure}[h!]
\centering
\includegraphics[width=0.9\linewidth]{img/mobile-vulnerabilities-details.png}
\caption{Развёрнутое описание уязвимости BDU:2023-08587}
\label{fig:mobile-vulnerabilities-details}
\end{figure}
\newpage
\section*{Заключение}
\addcontentsline{toc}{section}{Заключение}
В ходе выполнения практической работы №1 был проведён анализ уязвимостей программного обеспечения с использованием Банка данных угроз ФСТЭК России. Были изучены структура разделов <<Угрозы>> и <<Уязвимости>>, определены версии операционных систем личного компьютера и смартфона, а также выполнен поиск уязвимостей с уровнем опасности <<Критический>> и <<Высокий>>. Проведён анализ наиболее актуальных уязвимостей, рассмотрены их характеристики и векторы CVSS, а также рекомендации по устранению. В процессе выполнения работы были получены практические навыки поиска, анализа и оценки уязвимостей информационных систем.
\newpage
\printbibliography[heading=bibintoc]
\end{document}

1
lab2/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.pyc

101
lab2/README.md Normal file
View File

@@ -0,0 +1,101 @@
# Lab 2 — Authentication, Authorization & Brute Force Research
Система доступа к конфиденциальным данным с управлением пользователями и исследованием стойкости паролей.
## Структура каталогов
```
$PRACTICE2_DIR/ # по умолчанию /usr/local/practice2
├── etc/passwd # логин:sha256:id:права:ФИО
├── confdata/ # конфиденциальные файлы
├── bin/ # утилиты (usermgr, confaccess, bruteforce)
└── log/ # usermgr.log, access.log
```
Базовый каталог задаётся через переменную окружения `PRACTICE2_DIR`.
Если переменная не задана — используется `/usr/local/practice2`.
## Установка
```bash
chmod +x setup.sh
# для пути по умолчанию (/usr/local/practice2) нужен root:
sudo ./setup.sh
# для тестирования без root:
PRACTICE2_DIR=/tmp/practice2 ./setup.sh
```
Скрипт создаёт структуру каталогов, копирует утилиты в `bin/` и выставляет права доступа.
### Добавить bin во временный PATH
```bash
export PATH="/usr/local/practice2/bin:$PATH"
# или для тестовой директории:
export PATH="/tmp/practice2/bin:$PATH"
```
После этого утилиты доступны без полного пути:
```bash
usermgr add alice
confaccess
bruteforce alice
```
## Использование
### usermgr — управление пользователями
```bash
usermgr add alice # добавить пользователя (интерактивный ввод)
usermgr list # список пользователей
usermgr edit alice --permissions rw
usermgr edit alice --full-name "Иванов Иван"
usermgr passwd alice # сменить пароль
usermgr delete alice # удалить пользователя
```
Права: `r` — чтение, `w` — запись, `d` — удаление.
Требования к паролю: первый символ — буква (AZ, az), далее — буквы, цифры и `!@#$%^&*()`.
### confaccess — доступ к данным
```bash
confaccess
# или с явным указанием базовой директории:
PRACTICE2_DIR=/tmp/practice2 confaccess
```
Режим проверки учётных данных (для bruteforce): `confaccess --check <login>` — читает пароли построчно из stdin, выводит 0 или 1 на каждую строку, exit 0 при первом совпадении.
После аутентификации доступны команды:
```
create <file> создать новый пустой файл в confdata [requires: w]
read <file> вывести содержимое файла [requires: r]
append <file> <text> дописать строку в файл [requires: w]
copy <src> <dst> скопировать файл в confdata [requires: r, w]
remove <file> удалить файл из confdata [requires: d]
help / exit
```
Пути к файлам указываются относительно `confdata/` (или абсолютные).
Для `copy`: src — любой путь, dst — внутри confdata, перезапись запрещена.
Выход — `exit` или Ctrl+C.
### bruteforce — взлом пароля
```bash
bruteforce alice
bruteforce alice --max-length 4
```
Перебор выполняется через утилиту access (confaccess): bruteforce не имеет доступа к passwd-файлу и проверяет пароли только через `confaccess --check`.
Алгоритм хэширования в access: **SHA-256**.
Фиксируется время перебора и количество итераций до нахождения пароля.
Перебор останавливается автоматически при достижении лимита 8 часов.

278
lab2/access.py Normal file
View File

@@ -0,0 +1,278 @@
#!/usr/bin/env python3
import argparse
import getpass
import hashlib
import shutil
import signal
import sys
from datetime import datetime
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from config import CONFDATA_DIR, LOG_DIR, PASSWD_FILE
HELP_TEXT = """\
Commands:
create <file> create new empty file [requires: w]
read <file> print file contents [requires: r]
append <file> <text> append text line to file [requires: w]
copy <src> <dst> copy file into confdata [requires: r, w]
remove <file> delete file from confdata [requires: d]
help show this help
exit exit"""
def hash_password(password: str) -> str:
return hashlib.sha256(password.encode("ascii")).hexdigest()
def log_action(login: str, action: str) -> None:
LOG_DIR.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().isoformat(sep=" ", timespec="seconds")
with open(LOG_DIR / "access.log", "a") as f:
f.write(f"{timestamp} [{login}] {action}\n")
def read_users() -> dict[str, dict]:
users: dict[str, dict] = {}
if not PASSWD_FILE.exists():
return users
with open(PASSWD_FILE) as f:
for line in f:
line = line.strip()
if not line:
continue
parts = line.split(":", 4)
if len(parts) != 5:
continue
users[parts[0]] = {
"password_hash": parts[1],
"id": parts[2],
"permissions": parts[3],
"full_name": parts[4],
}
return users
def authenticate() -> tuple[str, dict]:
users = read_users()
while True:
try:
login = input("Login: ").strip()
password = getpass.getpass("Password: ")
except (EOFError, KeyboardInterrupt):
print("\nBye.")
sys.exit(0)
user = users.get(login)
if user and user["password_hash"] == hash_password(password):
return login, user
log_action(login if login else "-", "LOGIN_FAILED")
print("Invalid credentials. Try again.")
def confdata_path(arg: str) -> Path:
p = Path(arg)
if not p.is_absolute():
p = CONFDATA_DIR / p
return p.resolve()
def is_in_confdata(path: Path) -> bool:
try:
path.relative_to(CONFDATA_DIR.resolve())
return True
except ValueError:
return False
def cmd_read(args: list[str], login: str, perms: str) -> None:
if "r" not in perms:
print("Permission denied (requires: r)")
return
if len(args) != 1:
print("Usage: read <file>")
return
path = confdata_path(args[0])
if not is_in_confdata(path):
print("Access denied: file must be inside confdata")
return
if not path.exists():
print(f"File not found: {path.name}")
return
print(path.read_text(), end="")
log_action(login, f"READ {path}")
def cmd_create(args: list[str], login: str, perms: str) -> None:
if "w" not in perms:
print("Permission denied (requires: w)")
return
if len(args) != 1:
print("Usage: create <file>")
return
path = confdata_path(args[0])
if not is_in_confdata(path):
print("Access denied: file must be inside confdata")
return
if path.exists():
print(f"File already exists: {path.name}")
return
path.parent.mkdir(parents=True, exist_ok=True)
path.touch()
log_action(login, f"CREATE {path}")
print("Done.")
def cmd_append(args: list[str], login: str, perms: str) -> None:
if "w" not in perms:
print("Permission denied (requires: w)")
return
if len(args) < 2:
print("Usage: append <file> <text>")
return
path = confdata_path(args[0])
if not is_in_confdata(path):
print("Access denied: file must be inside confdata")
return
if not path.exists():
print(f"File not found: {path.name}. Use 'create' first.")
return
text = " ".join(args[1:])
with open(path, "a") as f:
f.write(text + "\n")
log_action(login, f"APPEND {path}")
print("Done.")
def cmd_copy(args: list[str], login: str, perms: str) -> None:
if "r" not in perms or "w" not in perms:
print("Permission denied (requires: r, w)")
return
if len(args) != 2:
print("Usage: copy <src> <dst>")
return
src = Path(args[0]).resolve()
dst = confdata_path(args[1])
if not is_in_confdata(dst):
print("Access denied: destination must be inside confdata")
return
if dst.is_dir():
dst = dst / src.name
if dst.exists():
print(f"Destination already exists: {dst.name}")
return
if not src.exists():
print(f"Source not found: {args[0]}")
return
if src.is_dir():
print("Copying directories is not supported")
return
shutil.copy2(src, dst)
log_action(login, f"COPY {src} -> {dst}")
print("Done.")
def cmd_remove(args: list[str], login: str, perms: str) -> None:
if "d" not in perms:
print("Permission denied (requires: d)")
return
if len(args) != 1:
print("Usage: remove <file>")
return
path = confdata_path(args[0])
if not is_in_confdata(path):
print("Access denied: file must be inside confdata")
return
if not path.exists():
print(f"File not found: {path.name}")
return
path.unlink()
log_action(login, f"REMOVE {path}")
print("Done.")
def check_credentials(login: str, password: str) -> bool:
"""Check login+password against passwd. Used by --check mode."""
users = read_users()
user = users.get(login)
if user and user["password_hash"] == hash_password(password):
return True
return False
def main() -> None:
parser = argparse.ArgumentParser(description="Access confidential data")
parser.add_argument(
"--check",
metavar="LOGIN",
help="Batch mode: read passwords line-by-line from stdin, output 0 or 1 per line; exit 0 on first match",
)
args, _ = parser.parse_known_args()
if args.check is not None:
# --check: one process, many checks; read_users once
users = read_users()
user = users.get(args.check)
target_hash = user["password_hash"] if user else None
for line in sys.stdin:
password = line.rstrip("\n")
if target_hash and hash_password(password) == target_hash:
sys.stdout.write("1\n")
sys.stdout.flush()
sys.exit(0)
sys.stdout.write("0\n")
sys.stdout.flush()
sys.exit(1)
signal.signal(signal.SIGINT, lambda _s, _f: (print("\nBye."), sys.exit(0)))
login, user = authenticate()
perms = user["permissions"]
full_name = user["full_name"]
log_action(login, "LOGIN")
print(f"\nПривет, {full_name}")
print(HELP_TEXT)
while True:
try:
line = input(f"\n{login}> ").strip()
except (EOFError, KeyboardInterrupt):
log_action(login, "EXIT")
print("\nBye.")
break
if not line:
continue
parts = line.split()
command, args = parts[0], parts[1:]
if command == "exit":
log_action(login, "EXIT")
print("Bye.")
break
elif command == "help":
print(HELP_TEXT)
elif command == "create":
cmd_create(args, login, perms)
elif command == "read":
cmd_read(args, login, perms)
elif command == "append":
cmd_append(args, login, perms)
elif command == "copy":
cmd_copy(args, login, perms)
elif command == "remove":
cmd_remove(args, login, perms)
else:
print(f"Unknown command: {command!r}. Type 'help' for available commands.")
if __name__ == "__main__":
main()

141
lab2/bruteforce.py Normal file
View File

@@ -0,0 +1,141 @@
#!/usr/bin/env python3
import argparse
import itertools
import shutil
import string
import subprocess
import sys
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from config import BIN_DIR
CHARSET = string.ascii_letters + string.digits + "!@#$%^&*()"
FIRST_CHARS = string.ascii_letters
MAX_HOURS = 8
def get_access_cmd() -> list[str]:
"""Resolve path to access utility (confaccess)."""
# setup.sh installs as confaccess; pyproject exposes as access
for name in ("confaccess", "access"):
path = shutil.which(name)
if path:
return [path]
# Fallback: same directory as bruteforce (development)
script_dir = Path(__file__).resolve().parent
access_script = script_dir / "access.py"
if access_script.exists():
return [sys.executable, str(access_script)]
# Try bin dir from config (e.g. /usr/local/practice2/bin/confaccess)
for name in ("confaccess", "access"):
bin_cmd = BIN_DIR / name
if bin_cmd.exists():
return [str(bin_cmd)]
return ["confaccess"] # hope it's in PATH
def max_combinations(length: int) -> int:
if length == 1:
return len(FIRST_CHARS)
return len(FIRST_CHARS) * (len(CHARSET) ** (length - 1))
def brute_force_length(
login: str, length: int, proc: subprocess.Popen[str]
) -> tuple[str, int, float] | None:
"""Try all passwords of given length via batch process. proc = access --check."""
count = 0
start = time.perf_counter()
stdin = proc.stdin
stdout = proc.stdout
assert stdin is not None and stdout is not None
def check(password: str) -> bool:
nonlocal count
count += 1
stdin.write(password + "\n")
stdin.flush()
line = stdout.readline()
if not line:
return False
return line.strip() == "1"
if length == 1:
for first in FIRST_CHARS:
if time.perf_counter() - start > MAX_HOURS * 3600:
return None
if check(first):
return first, count, time.perf_counter() - start
return None
for first in FIRST_CHARS:
for rest in itertools.product(CHARSET, repeat=length - 1):
if time.perf_counter() - start > MAX_HOURS * 3600:
return None
password = first + "".join(rest)
if check(password):
return password, count, time.perf_counter() - start
return None
def main() -> None:
parser = argparse.ArgumentParser(
description="Brute force password cracker (via access utility)"
)
parser.add_argument("login", help="Target username")
parser.add_argument(
"--max-length",
type=int,
default=6,
help="Maximum password length to try (default: 6)",
)
args = parser.parse_args()
cmd = get_access_cmd() + ["--check", args.login]
proc = subprocess.Popen(
cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
text=True,
bufsize=1,
)
try:
print(f"Target: {args.login}")
print(f"Charset size: {len(CHARSET)} ({len(FIRST_CHARS)} valid for first char)")
print()
total_start = time.perf_counter()
for length in range(1, args.max_length + 1):
total = max_combinations(length)
print(f"Length {length}: max {total:>15,} combinations")
result = brute_force_length(args.login, length, proc)
if result is not None:
password, count, elapsed = result
total_elapsed = time.perf_counter() - total_start
print(f" >>> FOUND: '{password}'")
print(f" Iterations: {count:,}")
print(f" Time (len): {elapsed:.4f}s")
print(f" Time (total): {total_elapsed:.4f}s")
print(f" Speed: {count / elapsed:,.0f} attempts/s")
return
else:
print(f" Not found at length {length} (timeout or exhausted)")
print(f"\nPassword not found within length {args.max_length}.")
finally:
if proc.stdin:
proc.stdin.close()
if proc.poll() is None:
proc.terminate()
proc.wait()
if __name__ == "__main__":
main()

11
lab2/config.py Normal file
View File

@@ -0,0 +1,11 @@
import os
from pathlib import Path
BASE_DIR = Path(os.environ.get("PRACTICE2_DIR", "/usr/local/practice2"))
ETC_DIR = BASE_DIR / "etc"
CONFDATA_DIR = BASE_DIR / "confdata"
BIN_DIR = BASE_DIR / "bin"
LOG_DIR = BASE_DIR / "log"
PASSWD_FILE = ETC_DIR / "passwd"

284
lab2/lab2.md Normal file
View File

@@ -0,0 +1,284 @@
# Практическая работа №2
по дисциплине «Защита информации»
**Тема работы:** «Разработка и исследование системы аутентификации и авторизации»
**Преподаватель:** Силиненко А.В.
**Email:** [a_silinenko@mail.ru](mailto:a_silinenko@mail.ru)
---
## 1. Цели работы
* Разработать систему доступа пользователей к конфиденциальным данным;
* Исследовать стойкость паролей к атаке методом грубой силы.
---
## 2. Задачи работы
### 2.1.
При необходимости установить на компьютер целевую ОС (Linux или MacOS), в которой производится разработка и использование системы.
### 2.2.
Разработать систему доступа пользователей к конфиденциальным данным, включающую:
* Выделенный каталог для хранения всех файлов системы;
* Утилиту для работы с данными аутентификации и авторизации (паролями и правами доступа);
* Утилиту доступа к конфиденциальным данным, обеспечивающую аутентификацию и авторизацию пользователя.
### 2.3.
Разработать программу взлома паролей методом грубой силы и исследовать стойкость паролей в зависимости от длины пароля и алгоритма хэширования.
---
## 3. Требования к работе
### 3.1. ОС
Работа выполняется в ОС **Linux** или **MacOS**.
Необходим доступ с правами суперпользователя.
Если доступа нет — установить гипервизор **VirtualBox** ([https://www.virtualbox.org/](https://www.virtualbox.org/)) и развернуть гостевую ОС.
Допускается установка ОС как второй системы без VirtualBox.
---
### 3.2. Структура каталогов
Необходимо создать дерево каталогов:
```
/usr/local/practice2/
├── etc # хранение данных аутентификации и авторизации
├── confdata # хранение конфиденциальных файлов
├── bin # разработанные утилиты
└── log # файлы регистрации
```
**Права доступа:** чтение, запись и выполнение только для пользователя `root`.
---
### 3.3. Требования к утилите управления пользователями
#### Файл хранения данных
Файл:
```
/usr/local/practice2/etc/passwd
```
Структура записи:
```
<логин>:<хэш_пароля>:<идентификатор>:<права>:<ФИО>
```
Одна строка — один пользователь.
**Права на файл:** чтение и запись только для `root`.
#### Права доступа
* `r` — чтение
* `w` — запись
* `d` — удаление
#### Требования к функционалу
Утилита должна обеспечивать:
* Добавление нового пользователя:
* логин
* ФИО
* права доступа
* пароль + подтверждение
* Проверку корректности данных
* Хранение пароля в виде **хэш-значения**
* Использование алгоритма хэширования согласно индивидуальному заданию
* Редактирование существующего пользователя
* Изменение пароля
* Удаление пользователя
* Регистрацию всех действий с файлом `passwd`
#### Требования к паролю
* Кодировка: ASCII
* Разрешены:
* AZ
* az
* 09
* `!@#$%^&*()`
* Первый символ не может быть цифрой или спецсимволом
---
### 3.4. Требования к утилите доступа к конфиденциальным данным
#### Авторизация
При запуске:
1. Запрос логина
2. Запрос пароля
3. Проверка корректности
Если данные корректны:
* Вывод: `Привет, <ФИО>`
* Краткая справка
* Приглашение к вводу команд
Если данные некорректны:
* Повторный запрос
Остановка — по сигналу **SIGINT (Ctrl+C)**.
---
### Поддерживаемые команды
| Команда | Описание | Требуемые права |
| -------- | ------------------------ | --------------- |
| `read` | Вывод содержимого файла | r |
| `append` | Добавление данных в файл | w |
| `copy` | Копирование файла | r + w |
| `remove` | Удаление файла | d |
| `exit` | Выход | — |
| `help` | Справка | — |
#### Ограничения copy
* Разрешено копирование:
* в каталог `confdata`
* внутри `confdata`
* Запрещено:
* из `confdata` в другие каталоги
* перезапись существующих файлов внутри `confdata`
#### Дополнительные требования
* Утилита доступна для запуска всем пользователям ОС
* Все действия с конфиденциальными данными логируются
---
### 3.5. Программа взлома паролей
Должна:
* Запускать утилиту доступа
* Перебирать пароли методом brute force
* Учитывать используемый алгоритм хэширования
* Фиксировать:
* время перебора
* количество итераций до взлома
---
### 3.6. Общие требования
* Язык программирования — любой
* Код должен быть снабжен комментариями
---
### 3.7. Требования к исследованию
Провести исследование для длин паролей:
```
3, 4, 5, 6, 7, 8 символов
```
Необходимо:
* Рассчитать максимальное количество итераций
* По экспериментам (34 символа) оценить время для 58
* Проверить теорию экспериментально
* Прервать эксперимент, если длительность > 8 часов
* Проводить тестирование без активных задач на ПК
---
## 4. Требования к отчету
В отчете необходимо указать:
* Актуальность темы
* Цель и задачи
* Требования к системе
* Характеристики ПК (процессор, память)
* ОС и среду разработки
* Используемый язык
* Алгоритм хэширования
* Примеры сборки (если применимо)
* Примеры работы утилит
* Пример расчета количества итераций и времени взлома
* Таблицу или график с результатами:
* рассчитанное количество итераций и время
* полученные экспериментальные данные
* Выводы
---
## 5. Справочная информация
### Поддержка алгоритмов хэширования
**Linux:**
* `md5sum`
* `sha1sum`
* `sha256sum`
* `sha512sum`
* `b2sum`
* библиотека `openssl`
**C++:**
* `std::hash`
**Python:**
* модуль `hashlib`
**Java:**
* `java.security.MessageDigest`
* Spring Security
**Go:**
* пакет `hash`
---
Примечание:
Brute Force должен запускать утилиту из-под себя и через неё пытаться подобрать пароль, он не должен лезть в файл напрямую.

38
lab2/setup.sh Executable file
View File

@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASE_DIR="${PRACTICE2_DIR:-/usr/local/practice2}"
echo "Setting up directory structure at: $BASE_DIR"
mkdir -p "$BASE_DIR/etc" "$BASE_DIR/confdata" "$BASE_DIR/bin" "$BASE_DIR/log"
touch "$BASE_DIR/etc/passwd"
cp "$SCRIPT_DIR/config.py" "$BASE_DIR/bin/config.py"
cp "$SCRIPT_DIR/usermgr.py" "$BASE_DIR/bin/usermgr"
cp "$SCRIPT_DIR/access.py" "$BASE_DIR/bin/confaccess"
cp "$SCRIPT_DIR/bruteforce.py" "$BASE_DIR/bin/bruteforce"
chmod +x "$BASE_DIR/bin/usermgr" "$BASE_DIR/bin/confaccess" "$BASE_DIR/bin/bruteforce"
if [ "$(id -u)" -eq 0 ]; then
chmod 700 "$BASE_DIR" "$BASE_DIR/etc" "$BASE_DIR/confdata" "$BASE_DIR/bin" "$BASE_DIR/log"
chmod 600 "$BASE_DIR/etc/passwd"
echo "Permissions set (root-only)."
else
echo "Warning: not running as root; skipping permission hardening."
fi
echo ""
echo "Done. Directory layout:"
ls -la "$BASE_DIR"
echo ""
echo "Next steps:"
echo " $BASE_DIR/bin/usermgr add <login>"
echo " $BASE_DIR/bin/confaccess"
echo " $BASE_DIR/bin/bruteforce <login>"
echo ""
echo "To add bin to PATH temporarily:"
echo " export PATH=\"$BASE_DIR/bin:\$PATH\""

246
lab2/usermgr.py Normal file
View File

@@ -0,0 +1,246 @@
#!/usr/bin/env python3
import argparse
import getpass
import hashlib
import sys
from datetime import datetime
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from config import LOG_DIR, PASSWD_FILE
ALLOWED_CHARS = set(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()"
)
FIRST_CHAR_ALLOWED = set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
VALID_PERM_CHARS = set("rwd")
def hash_password(password: str) -> str:
return hashlib.sha256(password.encode("ascii")).hexdigest()
def validate_password(password: str) -> str | None:
if not password:
return "password cannot be empty"
if password[0] not in FIRST_CHAR_ALLOWED:
return "first character must be a letter (A-Z, a-z)"
invalid = [ch for ch in password if ch not in ALLOWED_CHARS]
if invalid:
return f"invalid characters: {''.join(set(invalid))!r}"
return None
def validate_permissions(perms: str) -> str | None:
if not perms:
return "permissions cannot be empty"
for ch in perms:
if ch not in VALID_PERM_CHARS:
return f"invalid permission {ch!r}; allowed: r, w, d"
if len(set(perms)) != len(perms):
return "duplicate permissions"
return None
def log_action(action: str) -> None:
LOG_DIR.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().isoformat(sep=" ", timespec="seconds")
with open(LOG_DIR / "usermgr.log", "a") as f:
f.write(f"{timestamp} {action}\n")
def read_users() -> list[dict]:
if not PASSWD_FILE.exists():
return []
users = []
with open(PASSWD_FILE) as f:
for line in f:
line = line.strip()
if not line:
continue
parts = line.split(":", 4)
if len(parts) != 5:
continue
users.append(
{
"login": parts[0],
"password_hash": parts[1],
"id": parts[2],
"permissions": parts[3],
"full_name": parts[4],
}
)
return users
def write_users(users: list[dict]) -> None:
PASSWD_FILE.parent.mkdir(parents=True, exist_ok=True)
with open(PASSWD_FILE, "w") as f:
for u in users:
f.write(
f"{u['login']}:{u['password_hash']}:{u['id']}:{u['permissions']}:{u['full_name']}\n"
)
def find_user(users: list[dict], login: str) -> dict | None:
return next((u for u in users if u["login"] == login), None)
def next_uid(users: list[dict]) -> str:
if not users:
return "1"
return str(max(int(u["id"]) for u in users) + 1)
def prompt_password() -> str:
while True:
password = getpass.getpass("Password: ")
err = validate_password(password)
if err:
print(f"Invalid password: {err}")
continue
confirm = getpass.getpass("Confirm password: ")
if password != confirm:
print("Passwords do not match.")
continue
return password
def cmd_add(args: argparse.Namespace) -> None:
users = read_users()
login = args.login
if find_user(users, login):
print(f"User '{login}' already exists.")
sys.exit(1)
full_name = input("Full name: ").strip()
if not full_name:
print("Full name cannot be empty.")
sys.exit(1)
perms = input("Permissions (any combination of r, w, d): ").strip()
err = validate_permissions(perms)
if err:
print(f"Invalid permissions: {err}")
sys.exit(1)
password = prompt_password()
uid = next_uid(users)
users.append(
{
"login": login,
"password_hash": hash_password(password),
"id": uid,
"permissions": perms,
"full_name": full_name,
}
)
write_users(users)
log_action(f"ADD login={login} id={uid} permissions={perms} full_name='{full_name}'")
print(f"User '{login}' added (id={uid}).")
def cmd_edit(args: argparse.Namespace) -> None:
users = read_users()
user = find_user(users, args.login)
if not user:
print(f"User '{args.login}' not found.")
sys.exit(1)
changed = []
if args.full_name is not None:
if not args.full_name:
print("Full name cannot be empty.")
sys.exit(1)
user["full_name"] = args.full_name
changed.append("full_name")
if args.permissions is not None:
err = validate_permissions(args.permissions)
if err:
print(f"Invalid permissions: {err}")
sys.exit(1)
user["permissions"] = args.permissions
changed.append("permissions")
if not changed:
print("Nothing to change. Use --full-name and/or --permissions.")
sys.exit(0)
write_users(users)
log_action(f"EDIT login={args.login} changed={','.join(changed)}")
print(f"User '{args.login}' updated: {', '.join(changed)}.")
def cmd_passwd(args: argparse.Namespace) -> None:
users = read_users()
user = find_user(users, args.login)
if not user:
print(f"User '{args.login}' not found.")
sys.exit(1)
password = prompt_password()
user["password_hash"] = hash_password(password)
write_users(users)
log_action(f"PASSWD login={args.login}")
print(f"Password for '{args.login}' updated.")
def cmd_delete(args: argparse.Namespace) -> None:
users = read_users()
if not find_user(users, args.login):
print(f"User '{args.login}' not found.")
sys.exit(1)
users = [u for u in users if u["login"] != args.login]
write_users(users)
log_action(f"DELETE login={args.login}")
print(f"User '{args.login}' deleted.")
def cmd_list(_args: argparse.Namespace) -> None:
users = read_users()
if not users:
print("No users.")
return
print(f"{'Login':<16} {'ID':<4} {'Perms':<6} Full Name")
print("-" * 52)
for u in users:
print(f"{u['login']:<16} {u['id']:<4} {u['permissions']:<6} {u['full_name']}")
def main() -> None:
parser = argparse.ArgumentParser(description="User management utility for practice2")
sub = parser.add_subparsers(dest="command", required=True)
p_add = sub.add_parser("add", help="Add a new user")
p_add.add_argument("login", help="Username (login)")
p_add.set_defaults(func=cmd_add)
p_edit = sub.add_parser("edit", help="Edit an existing user")
p_edit.add_argument("login", help="Username to edit")
p_edit.add_argument("--full-name", dest="full_name", help="New full name")
p_edit.add_argument("--permissions", help="New permissions (e.g. rwd)")
p_edit.set_defaults(func=cmd_edit)
p_passwd = sub.add_parser("passwd", help="Change user password")
p_passwd.add_argument("login", help="Username")
p_passwd.set_defaults(func=cmd_passwd)
p_del = sub.add_parser("delete", help="Delete a user")
p_del.add_argument("login", help="Username to delete")
p_del.set_defaults(func=cmd_delete)
p_list = sub.add_parser("list", help="List all users")
p_list.set_defaults(func=cmd_list)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()

2
lab3/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.pyc
*.pdf

94
lab3/README.md Normal file
View File

@@ -0,0 +1,94 @@
# Lab 3 — DAC + MAC
Система доступа к конфиденциальным данным с дискреционным (DAC) и мандатным (MAC) управлением доступом. Основана на Lab 2.
## Структура каталогов
```
$PRACTICE3_DIR/ # по умолчанию /usr/local/practice3
├── etc/
│ ├── passwd # логин:sha256:id:права:ФИО
│ ├── access_mode # BOTH | DAC_ONLY | MAC_ONLY
│ ├── acl # матрица доступа (DAC)
│ ├── subject_labels # метки субъектов (MAC)
│ └── object_labels # метки объектов (MAC)
├── confdata/ # конфиденциальные файлы
├── bin/ # usermgr, confaccess, bruteforce
└── log/ # usermgr.log, access.log
```
## Режимы проверки доступа
| Режим | DAC | MAC |
|-------|-----|-----|
| BOTH (по умолчанию) | да | да |
| DAC_ONLY | да | нет |
| MAC_ONLY | нет | да |
Режим задаётся через `usermgr set-mode` (только root).
## Установка
```bash
chmod +x setup.sh
# для пути по умолчанию нужен root:
sudo ./setup.sh
# для тестирования без root:
PRACTICE3_DIR=/tmp/practice3 ./setup.sh
```
## Использование
### usermgr — управление пользователями
```bash
usermgr add alice
usermgr list
usermgr edit alice --full-name "Иванов Иван" --permissions rw
usermgr edit alice --label 1 # метка субъекта (root only)
usermgr set-label report.txt 2 # метка объекта (root only)
usermgr set-mode DAC_ONLY # режим проверки (root only)
usermgr show-mode # текущий режим (root only)
usermgr passwd alice
usermgr delete alice
```
### confaccess — доступ к данным
```bash
confaccess
PRACTICE3_DIR=/tmp/practice3 confaccess
```
Команды после аутентификации:
```
create <file> создать файл (владелец = текущий пользователь)
read <file> прочитать файл
append <file> <text> дописать в файл
copy <src> <dst> скопировать в confdata
remove <file> удалить файл
grant <user> <path> <perms> выдать права (только владелец)
help / exit
```
### DAC (дискреционный доступ)
- Каждый объект имеет владельца (создатель)
- Владелец может выдавать права через `grant <user> <path> <perms>`
- Права: r (чтение), w (запись), d (удаление)
### MAC (мандатный доступ, Белл–Лападула)
- Метки: 0 — несекретно, 1 — ДСП, 2 — секретно
- Нет чтения сверху: субъект читает только объекты с уровнем ≤ своего
- Нет записи вниз: субъект пишет только в объекты с уровнем ≥ своего
### bruteforce
```bash
bruteforce alice
bruteforce alice --max-length 4
```

138
lab3/bruteforce.py Normal file
View File

@@ -0,0 +1,138 @@
#!/usr/bin/env python3
import argparse
import itertools
import shutil
import string
import subprocess
import sys
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from config import BIN_DIR
CHARSET = string.ascii_letters + string.digits + "!@#$%^&*()"
FIRST_CHARS = string.ascii_letters
MAX_HOURS = 8
def get_access_cmd() -> list[str]:
"""Resolve path to confaccess utility."""
for name in ("confaccess", "access"):
path = shutil.which(name)
if path:
return [path]
script_dir = Path(__file__).resolve().parent
confaccess_script = script_dir / "confaccess.py"
if confaccess_script.exists():
return [sys.executable, str(confaccess_script)]
for name in ("confaccess", "access"):
bin_cmd = BIN_DIR / name
if bin_cmd.exists():
return [str(bin_cmd)]
return ["confaccess"]
def max_combinations(length: int) -> int:
if length == 1:
return len(FIRST_CHARS)
return len(FIRST_CHARS) * (len(CHARSET) ** (length - 1))
def brute_force_length(
login: str, length: int, proc: subprocess.Popen[str]
) -> tuple[str, int, float] | None:
"""Try all passwords of given length via batch process. proc = confaccess --check."""
count = 0
start = time.perf_counter()
stdin = proc.stdin
stdout = proc.stdout
assert stdin is not None and stdout is not None
def check(password: str) -> bool:
nonlocal count
count += 1
stdin.write(password + "\n")
stdin.flush()
line = stdout.readline()
if not line:
return False
return line.strip() == "1"
if length == 1:
for first in FIRST_CHARS:
if time.perf_counter() - start > MAX_HOURS * 3600:
return None
if check(first):
return first, count, time.perf_counter() - start
return None
for first in FIRST_CHARS:
for rest in itertools.product(CHARSET, repeat=length - 1):
if time.perf_counter() - start > MAX_HOURS * 3600:
return None
password = first + "".join(rest)
if check(password):
return password, count, time.perf_counter() - start
return None
def main() -> None:
parser = argparse.ArgumentParser(
description="Brute force password cracker (via confaccess utility)"
)
parser.add_argument("login", help="Target username")
parser.add_argument(
"--max-length",
type=int,
default=6,
help="Maximum password length to try (default: 6)",
)
args = parser.parse_args()
cmd = get_access_cmd() + ["--check", args.login]
proc = subprocess.Popen(
cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
text=True,
bufsize=1,
)
try:
print(f"Target: {args.login}")
print(f"Charset size: {len(CHARSET)} ({len(FIRST_CHARS)} valid for first char)")
print()
total_start = time.perf_counter()
for length in range(1, args.max_length + 1):
total = max_combinations(length)
print(f"Length {length}: max {total:>15,} combinations")
result = brute_force_length(args.login, length, proc)
if result is not None:
password, count, elapsed = result
total_elapsed = time.perf_counter() - total_start
print(f" >>> FOUND: '{password}'")
print(f" Iterations: {count:,}")
print(f" Time (len): {elapsed:.4f}s")
print(f" Time (total): {total_elapsed:.4f}s")
print(f" Speed: {count / elapsed:,.0f} attempts/s")
return
else:
print(f" Not found at length {length} (timeout or exhausted)")
print(f"\nPassword not found within length {args.max_length}.")
finally:
if proc.stdin:
proc.stdin.close()
if proc.poll() is None:
proc.terminate()
proc.wait()
if __name__ == "__main__":
main()

518
lab3/confaccess.py Normal file
View File

@@ -0,0 +1,518 @@
#!/usr/bin/env python3
"""Access to confidential data with DAC and MAC."""
import argparse
import getpass
import hashlib
import shutil
import signal
import sys
from datetime import datetime
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from config import (
ACL_FILE,
ACCESS_MODE_FILE,
CONFDATA_DIR,
LOG_DIR,
OBJECT_LABELS_FILE,
PASSWD_FILE,
SUBJECT_LABELS_FILE,
)
HELP_TEXT = """\
Commands:
create <file> create new empty file
read <file> print file contents
append <file> <text> append text line to file
copy <src> <dst> copy file into confdata
remove <file> delete file from confdata
grant <user> <path> <perms> grant access (owner only)
help show this help
exit exit"""
def hash_password(password: str) -> str:
return hashlib.sha256(password.encode("ascii")).hexdigest()
def log_action(login: str, action: str) -> None:
LOG_DIR.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().isoformat(sep=" ", timespec="seconds")
with open(LOG_DIR / "access.log", "a") as f:
f.write(f"{timestamp} [{login}] {action}\n")
def read_users() -> dict[str, dict]:
users: dict[str, dict] = {}
if not PASSWD_FILE.exists():
return users
with open(PASSWD_FILE) as f:
for line in f:
line = line.strip()
if not line:
continue
parts = line.split(":", 4)
if len(parts) != 5:
continue
users[parts[0]] = {
"password_hash": parts[1],
"id": parts[2],
"permissions": parts[3],
"full_name": parts[4],
}
return users
def read_access_mode() -> str:
if not ACCESS_MODE_FILE.exists():
return "BOTH"
raw = ACCESS_MODE_FILE.read_text().strip().upper()
if raw in ("BOTH", "DAC_ONLY", "MAC_ONLY"):
return raw
return "BOTH"
def normalize_path(path: str) -> str:
"""Normalize path relative to confdata (no ./, no trailing /)."""
p = path.strip().lstrip("./")
parts = [x for x in p.split("/") if x]
return "/".join(parts) if parts else ""
def read_acl() -> dict[str, dict]:
"""Read ACL: path -> {owner, perms: {user: perms_str}}."""
acl: dict[str, dict] = {}
if not ACL_FILE.exists():
return acl
for line in ACL_FILE.read_text().splitlines():
line = line.strip()
if not line:
continue
parts = line.split(":", 2)
if len(parts) < 3:
continue
path, owner, assignments = parts[0], parts[1], parts[2]
path = normalize_path(path)
perms: dict[str, str] = {}
for ass in assignments.split(","):
if ":" in ass:
u, p = ass.split(":", 1)
perms[u.strip()] = p.strip()
acl[path] = {"owner": owner, "perms": perms}
return acl
def write_acl(acl: dict[str, dict]) -> None:
ACL_FILE.parent.mkdir(parents=True, exist_ok=True)
lines = []
for path in sorted(acl.keys()):
entry = acl[path]
owner = entry["owner"]
perms = entry["perms"]
ass = ",".join(f"{u}:{p}" for u, p in sorted(perms.items()))
lines.append(f"{path}:{owner}:{ass}")
ACL_FILE.write_text("\n".join(lines) + "\n")
def read_subject_labels() -> dict[str, int]:
labels: dict[str, int] = {}
if not SUBJECT_LABELS_FILE.exists():
return labels
for line in SUBJECT_LABELS_FILE.read_text().splitlines():
line = line.strip()
if not line:
continue
parts = line.split(":", 1)
if len(parts) == 2 and parts[1] in ("0", "1", "2"):
labels[parts[0]] = int(parts[1])
return labels
def read_object_labels() -> dict[str, int]:
labels: dict[str, int] = {}
if not OBJECT_LABELS_FILE.exists():
return labels
for line in OBJECT_LABELS_FILE.read_text().splitlines():
line = line.strip()
if not line:
continue
parts = line.split(":", 1)
if len(parts) == 2 and parts[1] in ("0", "1", "2"):
labels[parts[0]] = int(parts[1])
return labels
def write_object_labels(labels: dict[str, int]) -> None:
OBJECT_LABELS_FILE.parent.mkdir(parents=True, exist_ok=True)
lines = [f"{path}:{level}" for path, level in sorted(labels.items())]
OBJECT_LABELS_FILE.write_text("\n".join(lines) + "\n")
def dac_allows(login: str, action: str, rel_path: str) -> bool:
"""Check DAC: does login have required permission on object?"""
rel_path = normalize_path(rel_path)
acl = read_acl()
if action in ("create", "create_dst"):
# New object: not in ACL yet, any authenticated user can create
if rel_path not in acl:
return True
entry = acl.get(rel_path)
if not entry:
return False
perms = entry["perms"].get(login, "")
if action == "read":
return "r" in perms
if action in ("write", "append", "create_dst"):
return "w" in perms
if action == "remove":
return "d" in perms
if action == "grant":
return entry["owner"] == login
return False
def dac_owner(rel_path: str) -> str | None:
"""Return owner of path or None."""
rel_path = normalize_path(rel_path)
acl = read_acl()
entry = acl.get(rel_path)
return entry["owner"] if entry else None
def mac_allows(login: str, action: str, rel_path: str, obj_level: int | None = None) -> bool:
"""Check MAC (Bell-LaPadula)."""
labels = read_subject_labels()
subj_level = labels.get(login, 0)
obj_labels = read_object_labels()
norm_path = normalize_path(rel_path)
if obj_level is None:
obj_level = obj_labels.get(norm_path)
if action == "read":
# No read up: subject_level >= object_level
obj_lev = obj_level if obj_level is not None else 0
return subj_level >= obj_lev
if action in ("write", "append", "remove"):
obj_lev = obj_level if obj_level is not None else 0
return subj_level <= obj_lev
if action == "create_dst":
# New object gets subject's label; subject can write to same level
return True
return True
def check_access(login: str, action: str, rel_path: str, mode: str) -> bool:
if mode in ("BOTH", "DAC_ONLY"):
if not dac_allows(login, action, rel_path):
return False
if mode in ("BOTH", "MAC_ONLY"):
if not mac_allows(login, action, rel_path):
return False
return True
def authenticate() -> tuple[str, dict]:
users = read_users()
while True:
try:
login = input("Login: ").strip()
password = getpass.getpass("Password: ")
except (EOFError, KeyboardInterrupt):
print("\nBye.")
sys.exit(0)
user = users.get(login)
if user and user["password_hash"] == hash_password(password):
return login, user
log_action(login if login else "-", "LOGIN_FAILED")
print("Invalid credentials. Try again.")
def confdata_path(arg: str) -> Path:
p = Path(arg)
if not p.is_absolute():
p = CONFDATA_DIR / p
return p.resolve()
def rel_path_from_confdata(path: Path) -> str:
try:
rel = path.relative_to(CONFDATA_DIR.resolve())
return "/".join(rel.parts)
except ValueError:
return ""
def is_in_confdata(path: Path) -> bool:
try:
path.relative_to(CONFDATA_DIR.resolve())
return True
except ValueError:
return False
def cmd_read(args: list[str], login: str, mode: str) -> None:
if len(args) != 1:
print("Usage: read <file>")
return
path = confdata_path(args[0])
if not is_in_confdata(path):
print("Access denied: file must be inside confdata")
return
if not path.exists():
print(f"File not found: {path.name}")
return
rel = rel_path_from_confdata(path)
if not check_access(login, "read", rel, mode):
print("Permission denied")
return
print(path.read_text(), end="")
log_action(login, f"READ {path}")
def cmd_create(args: list[str], login: str, mode: str) -> None:
if len(args) != 1:
print("Usage: create <file>")
return
path = confdata_path(args[0])
if not is_in_confdata(path):
print("Access denied: file must be inside confdata")
return
if path.exists():
print(f"File already exists: {path.name}")
return
rel = rel_path_from_confdata(path)
# DAC: new object, add to ACL with owner=login
# MAC: new object gets subject's label
if mode in ("BOTH", "DAC_ONLY"):
acl = read_acl()
acl[rel] = {"owner": login, "perms": {login: "rwd"}}
write_acl(acl)
if mode in ("BOTH", "MAC_ONLY"):
labels = read_subject_labels()
subj_level = labels.get(login, 0)
obj_labels = read_object_labels()
obj_labels[rel] = subj_level
write_object_labels(obj_labels)
path.parent.mkdir(parents=True, exist_ok=True)
path.touch()
log_action(login, f"CREATE {path}")
print("Done.")
def cmd_append(args: list[str], login: str, mode: str) -> None:
if len(args) < 2:
print("Usage: append <file> <text>")
return
path = confdata_path(args[0])
if not is_in_confdata(path):
print("Access denied: file must be inside confdata")
return
if not path.exists():
print(f"File not found: {path.name}. Use 'create' first.")
return
rel = rel_path_from_confdata(path)
if not check_access(login, "append", rel, mode):
print("Permission denied")
return
text = " ".join(args[1:])
with open(path, "a") as f:
f.write(text + "\n")
log_action(login, f"APPEND {path}")
print("Done.")
def cmd_copy(args: list[str], login: str, mode: str) -> None:
if len(args) != 2:
print("Usage: copy <src> <dst>")
return
src = Path(args[0]).resolve()
dst = confdata_path(args[1])
if not is_in_confdata(dst):
print("Access denied: destination must be inside confdata")
return
if dst.is_dir():
dst = dst / src.name
if dst.exists():
print(f"Destination already exists: {dst.name}")
return
if not src.exists():
print(f"Source not found: {args[0]}")
return
if src.is_dir():
print("Copying directories is not supported")
return
dst_rel = rel_path_from_confdata(dst)
src_rel = rel_path_from_confdata(src) if is_in_confdata(src) else None
if src_rel is not None:
if not check_access(login, "read", src_rel, mode):
print("Permission denied (read source)")
return
if not check_access(login, "create_dst", dst_rel, mode):
print("Permission denied (create destination)")
return
shutil.copy2(src, dst)
if mode in ("BOTH", "DAC_ONLY"):
acl = read_acl()
acl[dst_rel] = {"owner": login, "perms": {login: "rwd"}}
write_acl(acl)
if mode in ("BOTH", "MAC_ONLY"):
labels = read_subject_labels()
subj_level = labels.get(login, 0)
obj_labels = read_object_labels()
obj_labels[dst_rel] = subj_level
write_object_labels(obj_labels)
log_action(login, f"COPY {src} -> {dst}")
print("Done.")
def cmd_remove(args: list[str], login: str, mode: str) -> None:
if len(args) != 1:
print("Usage: remove <file>")
return
path = confdata_path(args[0])
if not is_in_confdata(path):
print("Access denied: file must be inside confdata")
return
if not path.exists():
print(f"File not found: {path.name}")
return
rel = rel_path_from_confdata(path)
if not check_access(login, "remove", rel, mode):
print("Permission denied")
return
path.unlink()
if mode in ("BOTH", "DAC_ONLY"):
acl = read_acl()
acl.pop(rel, None)
write_acl(acl)
if mode in ("BOTH", "MAC_ONLY"):
obj_labels = read_object_labels()
obj_labels.pop(rel, None)
write_object_labels(obj_labels)
log_action(login, f"REMOVE {path}")
print("Done.")
def cmd_grant(args: list[str], login: str, mode: str) -> None:
if mode not in ("BOTH", "DAC_ONLY"):
print("grant is only available in DAC or BOTH mode")
return
if len(args) != 3:
print("Usage: grant <user> <path> <perms>")
return
target_user, path_arg, perms = args[0], args[1], args[2]
for ch in perms:
if ch not in "rwd":
print(f"Invalid permission {ch!r}; allowed: r, w, d")
return
path = confdata_path(path_arg)
if not is_in_confdata(path):
print("Access denied: path must be inside confdata")
return
if not path.exists():
print(f"File not found: {path_arg}")
return
rel = rel_path_from_confdata(path)
if not dac_allows(login, "grant", rel):
print("Permission denied (only owner can grant)")
return
acl = read_acl()
entry = acl.get(rel)
if not entry:
print("ACL entry not found")
return
entry["perms"][target_user] = perms
write_acl(acl)
log_action(login, f"GRANT {target_user} {rel} {perms}")
print("Done.")
def check_credentials(login: str, password: str) -> bool:
"""Check login+password against passwd. Used by --check mode."""
users = read_users()
user = users.get(login)
if user and user["password_hash"] == hash_password(password):
return True
return False
def main() -> None:
parser = argparse.ArgumentParser(description="Access confidential data")
parser.add_argument(
"--check",
metavar="LOGIN",
help="Batch mode: read passwords line-by-line from stdin, output 0 or 1 per line; exit 0 on first match",
)
args, _ = parser.parse_known_args()
if args.check is not None:
users = read_users()
user = users.get(args.check)
target_hash = user["password_hash"] if user else None
for line in sys.stdin:
password = line.rstrip("\n")
if target_hash and hash_password(password) == target_hash:
sys.stdout.write("1\n")
sys.stdout.flush()
sys.exit(0)
sys.stdout.write("0\n")
sys.stdout.flush()
sys.exit(1)
signal.signal(signal.SIGINT, lambda _s, _f: (print("\nBye."), sys.exit(0)))
login, user = authenticate()
full_name = user["full_name"]
mode = read_access_mode()
log_action(login, "LOGIN")
print(f"\nПривет, {full_name}")
print(HELP_TEXT)
while True:
try:
line = input(f"\n{login}> ").strip()
except (EOFError, KeyboardInterrupt):
log_action(login, "EXIT")
print("\nBye.")
break
if not line:
continue
parts = line.split()
command, cmd_args = parts[0], parts[1:]
if command == "exit":
log_action(login, "EXIT")
print("Bye.")
break
elif command == "help":
print(HELP_TEXT)
elif command == "create":
cmd_create(cmd_args, login, mode)
elif command == "read":
cmd_read(cmd_args, login, mode)
elif command == "append":
cmd_append(cmd_args, login, mode)
elif command == "copy":
cmd_copy(cmd_args, login, mode)
elif command == "remove":
cmd_remove(cmd_args, login, mode)
elif command == "grant":
cmd_grant(cmd_args, login, mode)
else:
print(f"Unknown command: {command!r}. Type 'help' for available commands.")
if __name__ == "__main__":
main()

15
lab3/config.py Normal file
View File

@@ -0,0 +1,15 @@
import os
from pathlib import Path
BASE_DIR = Path(os.environ.get("PRACTICE3_DIR", "/usr/local/practice3"))
ETC_DIR = BASE_DIR / "etc"
CONFDATA_DIR = BASE_DIR / "confdata"
BIN_DIR = BASE_DIR / "bin"
LOG_DIR = BASE_DIR / "log"
PASSWD_FILE = ETC_DIR / "passwd"
ACCESS_MODE_FILE = ETC_DIR / "access_mode"
ACL_FILE = ETC_DIR / "acl"
SUBJECT_LABELS_FILE = ETC_DIR / "subject_labels"
OBJECT_LABELS_FILE = ETC_DIR / "object_labels"

108
lab3/lab3.md Normal file
View File

@@ -0,0 +1,108 @@
# Практическая работа №3
**Дисциплина:** Защита информации
**Тема:** Реализация моделей дискреционного и мандатного управления доступом
**Преподаватель:** Силиненко А.В.
**Email:** a_silinenko@mail.ru
---
## 1. Цели работы
- Изучить особенности моделей дискреционного (DAC) и мандатного (MAC) управления доступом.
- На базе разработанной в практической работе 2 системы аутентификации и авторизации реализовать DAC и MAC.
---
## 2. Задачи работы
### 2.1
Изучить особенности дискреционного (произвольного) управления доступом, а также модель Харрисона–Руззо–Ульмана, основанную на DAC.
### 2.2
На базе разработанной в практической работе 2 системы аутентификации и авторизации разработать систему, реализующую DAC, включая:
- владение объектами;
- произвольное назначение прав доступа к объектам;
- контроль на основе матрицы или списка доступа.
### 2.3
Изучить особенности мандатного (принудительного) управления доступом, а также модель Белла–Лападулы, основанную на MAC.
### 2.4
На базе разработанной в практической работе 2 системы аутентификации и авторизации разработать систему, реализующую MAC, включая:
- метки безопасности для субъектов и объектов;
- свойства безопасности модели Белла–Лападулы.
---
## 3. Требования к работе
### 3.1
Работа выполняется в ОС **Linux** или **MacOS**.
### 3.2
Для выполнения работы необходимо создать отдельное дерево каталогов, например:
```
/usr/local/practice3/etc файлы настроек
/usr/local/practice3/confdata файлы с конфиденциальной информацией
/usr/local/practice3/bin исполняемые файлы
/usr/local/practice3/log файлы регистрации
```
Права доступа к созданным каталогам: **чтение, запись и выполнение только для пользователя root**.
При необходимости возможно создание дополнительных каталогов.
---
### 3.3 Требования к системе DAC
- у каждого объекта должен быть владелец, который может произвольно назначать и передавать права доступа к этому объекту;
- контроль доступа к объектам должен осуществляться на основе **матрицы или списка доступа**, где хранится информация о правах доступа субъектов к объектам.
---
### 3.4 Требования к системе MAC
- каждому субъекту и объекту должна присваиваться **метка безопасности суперпользователем**;
- предусмотреть **не менее трех уровней меток безопасности**, например:
- несекретно
- для служебного пользования (ДСП)
- секретно
- субъекты не могут менять метки безопасности;
- контроль доступа должен осуществляться на основе свойств безопасности **модели Белла–Лападулы**:
- «нет чтения сверху»
- «нет записи вниз»
- дискреционное свойство
---
## 4. Требования к отчету
В разделе отчета должны быть приведены:
- актуальность темы работы в контексте дисциплины;
- цель и задачи работы;
- требования к разрабатываемым системам;
- краткие сведения о DAC и MAC;
- примеры компиляции и сборки приложений (если используется компилируемый язык);
- примеры запуска и работы утилит доступа к конфиденциальной информации;
- примеры содержимого служебных файлов:
- матрицы (или списка) доступа для DAC
- меток конфиденциальности для MAC;
- выводы по проделанной работе.
---
Примечание
Та система котрая нами уже разработана в lab2
1. Необходимо выпустить ее реинкарнацию с учетом требований к тому что это должен быть дискреционный подход к управлению доступа
Свойства системы которые необходимо будет реализовать будет в задании
2. Необходимо реализовать мандатный подход к управлению доступом
Структура остается прежней нам просто нужно добавить компоненты для первого и второго подходов

43
lab3/setup.sh Executable file
View File

@@ -0,0 +1,43 @@
#!/usr/bin/env bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASE_DIR="${PRACTICE3_DIR:-/usr/local/practice3}"
echo "Setting up directory structure at: $BASE_DIR"
mkdir -p "$BASE_DIR/etc" "$BASE_DIR/confdata" "$BASE_DIR/bin" "$BASE_DIR/log"
touch "$BASE_DIR/etc/passwd"
echo "BOTH" > "$BASE_DIR/etc/access_mode"
touch "$BASE_DIR/etc/acl"
touch "$BASE_DIR/etc/subject_labels"
touch "$BASE_DIR/etc/object_labels"
cp "$SCRIPT_DIR/config.py" "$BASE_DIR/bin/config.py"
cp "$SCRIPT_DIR/usermgr.py" "$BASE_DIR/bin/usermgr"
cp "$SCRIPT_DIR/confaccess.py" "$BASE_DIR/bin/confaccess"
cp "$SCRIPT_DIR/bruteforce.py" "$BASE_DIR/bin/bruteforce"
chmod +x "$BASE_DIR/bin/usermgr" "$BASE_DIR/bin/confaccess" "$BASE_DIR/bin/bruteforce"
if [ "$(id -u)" -eq 0 ]; then
chmod 700 "$BASE_DIR" "$BASE_DIR/etc" "$BASE_DIR/confdata" "$BASE_DIR/bin" "$BASE_DIR/log"
chmod 600 "$BASE_DIR/etc/passwd" "$BASE_DIR/etc/access_mode" \
"$BASE_DIR/etc/acl" "$BASE_DIR/etc/subject_labels" "$BASE_DIR/etc/object_labels"
echo "Permissions set (root-only)."
else
echo "Warning: not running as root; skipping permission hardening."
fi
echo ""
echo "Done. Directory layout:"
ls -la "$BASE_DIR"
echo ""
echo "Next steps:"
echo " $BASE_DIR/bin/usermgr add <login>"
echo " $BASE_DIR/bin/confaccess"
echo " $BASE_DIR/bin/bruteforce <login>"
echo ""
echo "To add bin to PATH temporarily:"
echo " export PATH=\"$BASE_DIR/bin:\$PATH\""

378
lab3/usermgr.py Normal file
View File

@@ -0,0 +1,378 @@
#!/usr/bin/env python3
import argparse
import getpass
import hashlib
import os
import sys
from datetime import datetime
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from config import (
ACCESS_MODE_FILE,
ETC_DIR,
LOG_DIR,
OBJECT_LABELS_FILE,
PASSWD_FILE,
SUBJECT_LABELS_FILE,
)
ALLOWED_CHARS = set(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()"
)
FIRST_CHAR_ALLOWED = set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
VALID_PERM_CHARS = set("rwd")
VALID_LABELS = frozenset({"0", "1", "2"})
def require_root() -> None:
"""Exit if not running as root."""
if os.geteuid() != 0:
print("Must run as root", file=sys.stderr)
sys.exit(1)
def hash_password(password: str) -> str:
return hashlib.sha256(password.encode("ascii")).hexdigest()
def validate_password(password: str) -> str | None:
if not password:
return "password cannot be empty"
if password[0] not in FIRST_CHAR_ALLOWED:
return "first character must be a letter (A-Z, a-z)"
invalid = [ch for ch in password if ch not in ALLOWED_CHARS]
if invalid:
return f"invalid characters: {''.join(set(invalid))!r}"
return None
def validate_permissions(perms: str) -> str | None:
if not perms:
return "permissions cannot be empty"
for ch in perms:
if ch not in VALID_PERM_CHARS:
return f"invalid permission {ch!r}; allowed: r, w, d"
if len(set(perms)) != len(perms):
return "duplicate permissions"
return None
def log_action(action: str) -> None:
LOG_DIR.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().isoformat(sep=" ", timespec="seconds")
with open(LOG_DIR / "usermgr.log", "a") as f:
f.write(f"{timestamp} {action}\n")
def read_users() -> list[dict]:
if not PASSWD_FILE.exists():
return []
users = []
with open(PASSWD_FILE) as f:
for line in f:
line = line.strip()
if not line:
continue
parts = line.split(":", 4)
if len(parts) != 5:
continue
users.append(
{
"login": parts[0],
"password_hash": parts[1],
"id": parts[2],
"permissions": parts[3],
"full_name": parts[4],
}
)
return users
def write_users(users: list[dict]) -> None:
PASSWD_FILE.parent.mkdir(parents=True, exist_ok=True)
with open(PASSWD_FILE, "w") as f:
for u in users:
f.write(
f"{u['login']}:{u['password_hash']}:{u['id']}:{u['permissions']}:{u['full_name']}\n"
)
def find_user(users: list[dict], login: str) -> dict | None:
return next((u for u in users if u["login"] == login), None)
def next_uid(users: list[dict]) -> str:
if not users:
return "1"
return str(max(int(u["id"]) for u in users) + 1)
def prompt_password() -> str:
while True:
password = getpass.getpass("Password: ")
err = validate_password(password)
if err:
print(f"Invalid password: {err}")
continue
confirm = getpass.getpass("Confirm password: ")
if password != confirm:
print("Passwords do not match.")
continue
return password
def read_access_mode() -> str:
"""Read access mode; default BOTH if file missing."""
if not ACCESS_MODE_FILE.exists():
return "BOTH"
raw = ACCESS_MODE_FILE.read_text().strip().upper()
if raw in ("BOTH", "DAC_ONLY", "MAC_ONLY"):
return raw
return "BOTH"
def write_access_mode(mode: str) -> None:
ETC_DIR.mkdir(parents=True, exist_ok=True)
ACCESS_MODE_FILE.write_text(mode + "\n")
def read_subject_labels() -> dict[str, int]:
"""Read subject labels; login -> level (0, 1, 2)."""
labels: dict[str, int] = {}
if not SUBJECT_LABELS_FILE.exists():
return labels
for line in SUBJECT_LABELS_FILE.read_text().splitlines():
line = line.strip()
if not line:
continue
parts = line.split(":", 1)
if len(parts) == 2 and parts[1] in VALID_LABELS:
labels[parts[0]] = int(parts[1])
return labels
def write_subject_labels(labels: dict[str, int]) -> None:
ETC_DIR.mkdir(parents=True, exist_ok=True)
lines = [f"{login}:{level}" for login, level in sorted(labels.items())]
SUBJECT_LABELS_FILE.write_text("\n".join(lines) + "\n")
def read_object_labels() -> dict[str, int]:
"""Read object labels; path (relative to confdata) -> level."""
labels: dict[str, int] = {}
if not OBJECT_LABELS_FILE.exists():
return labels
for line in OBJECT_LABELS_FILE.read_text().splitlines():
line = line.strip()
if not line:
continue
parts = line.split(":", 1)
if len(parts) == 2 and parts[1] in VALID_LABELS:
labels[parts[0]] = int(parts[1])
return labels
def write_object_labels(labels: dict[str, int]) -> None:
ETC_DIR.mkdir(parents=True, exist_ok=True)
lines = [f"{path}:{level}" for path, level in sorted(labels.items())]
OBJECT_LABELS_FILE.write_text("\n".join(lines) + "\n")
def cmd_add(args: argparse.Namespace) -> None:
users = read_users()
login = args.login
if find_user(users, login):
print(f"User '{login}' already exists.")
sys.exit(1)
full_name = input("Full name: ").strip()
if not full_name:
print("Full name cannot be empty.")
sys.exit(1)
perms = input("Permissions (any combination of r, w, d): ").strip()
err = validate_permissions(perms)
if err:
print(f"Invalid permissions: {err}")
sys.exit(1)
password = prompt_password()
uid = next_uid(users)
users.append(
{
"login": login,
"password_hash": hash_password(password),
"id": uid,
"permissions": perms,
"full_name": full_name,
}
)
write_users(users)
log_action(f"ADD login={login} id={uid} permissions={perms} full_name='{full_name}'")
print(f"User '{login}' added (id={uid}).")
def cmd_edit(args: argparse.Namespace) -> None:
if args.label is not None:
require_root()
users = read_users()
user = find_user(users, args.login)
if not user:
print(f"User '{args.login}' not found.")
sys.exit(1)
changed: list[str] = []
if args.full_name is not None:
if not args.full_name:
print("Full name cannot be empty.")
sys.exit(1)
user["full_name"] = args.full_name
changed.append("full_name")
if args.permissions is not None:
err = validate_permissions(args.permissions)
if err:
print(f"Invalid permissions: {err}")
sys.exit(1)
user["permissions"] = args.permissions
changed.append("permissions")
if args.label is not None:
if args.label not in VALID_LABELS:
print(f"Invalid label {args.label!r}; allowed: 0, 1, 2")
sys.exit(1)
labels = read_subject_labels()
labels[args.login] = int(args.label)
write_subject_labels(labels)
changed.append("label")
if not changed:
print("Nothing to change. Use --full-name, --permissions, and/or --label.")
sys.exit(0)
if "full_name" in changed or "permissions" in changed:
write_users(users)
log_action(f"EDIT login={args.login} changed={','.join(changed)}")
print(f"User '{args.login}' updated: {', '.join(changed)}.")
def cmd_passwd(args: argparse.Namespace) -> None:
users = read_users()
user = find_user(users, args.login)
if not user:
print(f"User '{args.login}' not found.")
sys.exit(1)
password = prompt_password()
user["password_hash"] = hash_password(password)
write_users(users)
log_action(f"PASSWD login={args.login}")
print(f"Password for '{args.login}' updated.")
def cmd_delete(args: argparse.Namespace) -> None:
users = read_users()
if not find_user(users, args.login):
print(f"User '{args.login}' not found.")
sys.exit(1)
users = [u for u in users if u["login"] != args.login]
write_users(users)
log_action(f"DELETE login={args.login}")
print(f"User '{args.login}' deleted.")
def cmd_list(_args: argparse.Namespace) -> None:
users = read_users()
if not users:
print("No users.")
return
print(f"{'Login':<16} {'ID':<4} {'Perms':<6} Full Name")
print("-" * 52)
for u in users:
print(f"{u['login']:<16} {u['id']:<4} {u['permissions']:<6} {u['full_name']}")
def cmd_set_mode(args: argparse.Namespace) -> None:
require_root()
mode = args.mode.upper()
if mode not in ("BOTH", "DAC_ONLY", "MAC_ONLY"):
print(f"Invalid mode {args.mode!r}; allowed: BOTH, DAC_ONLY, MAC_ONLY")
sys.exit(1)
write_access_mode(mode)
log_action(f"SET_MODE mode={mode}")
print(f"Access mode set to {mode}.")
def cmd_show_mode(_args: argparse.Namespace) -> None:
require_root()
mode = read_access_mode()
print(f"Current access mode: {mode}")
def cmd_set_label(args: argparse.Namespace) -> None:
require_root()
if args.label not in VALID_LABELS:
print(f"Invalid label {args.label!r}; allowed: 0, 1, 2")
sys.exit(1)
path = args.path
if path.startswith("./"):
path = path[2:]
path = "/".join(p for p in path.split("/") if p)
labels = read_object_labels()
labels[path] = int(args.label)
write_object_labels(labels)
log_action(f"SET_LABEL path={path} label={args.label}")
print(f"Object label for '{path}' set to {args.label}.")
def main() -> None:
parser = argparse.ArgumentParser(description="User management utility for practice3")
sub = parser.add_subparsers(dest="command", required=True)
p_add = sub.add_parser("add", help="Add a new user")
p_add.add_argument("login", help="Username (login)")
p_add.set_defaults(func=cmd_add)
p_edit = sub.add_parser("edit", help="Edit an existing user")
p_edit.add_argument("login", help="Username to edit")
p_edit.add_argument("--full-name", dest="full_name", help="New full name")
p_edit.add_argument("--permissions", help="New permissions (e.g. rwd)")
p_edit.add_argument("--label", help="Security label (0, 1, 2) - root only")
p_edit.set_defaults(func=cmd_edit)
p_passwd = sub.add_parser("passwd", help="Change user password")
p_passwd.add_argument("login", help="Username")
p_passwd.set_defaults(func=cmd_passwd)
p_del = sub.add_parser("delete", help="Delete a user")
p_del.add_argument("login", help="Username to delete")
p_del.set_defaults(func=cmd_delete)
p_list = sub.add_parser("list", help="List all users")
p_list.set_defaults(func=cmd_list)
p_set_mode = sub.add_parser("set-mode", help="Set access mode (BOTH/DAC_ONLY/MAC_ONLY) - root only")
p_set_mode.add_argument("mode", help="BOTH, DAC_ONLY, or MAC_ONLY")
p_set_mode.set_defaults(func=cmd_set_mode)
p_show_mode = sub.add_parser("show-mode", help="Show current access mode - root only")
p_show_mode.set_defaults(func=cmd_show_mode)
p_set_label = sub.add_parser("set-label", help="Set object security label - root only")
p_set_label.add_argument("path", help="Path relative to confdata")
p_set_label.add_argument("label", help="0, 1, or 2")
p_set_label.set_defaults(func=cmd_set_label)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
report/img/lab2-passwd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
report/img/lab2-setup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
report/img/lab3-acl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
report/img/lab3-labels.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
report/img/lab3-setup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 156 KiB

View File

Before

Width:  |  Height:  |  Size: 201 KiB

After

Width:  |  Height:  |  Size: 201 KiB

View File

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 138 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

741
report/report.tex Executable file
View File

@@ -0,0 +1,741 @@
\documentclass[a4paper, final]{article}
%\usepackage{literat} % Нормальные шрифты
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
\usepackage{tabularx}
\usepackage{booktabs}
\usepackage[T2A]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\usepackage{amsmath}
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
\usepackage{ragged2e} %для растягивания по ширине
\usepackage{setspace} %для межстрочного интервала
\usepackage{moreverb} %для работы с листингами
\usepackage{indentfirst} % для абзацного отступа
\usepackage{moreverb} %для печати в листинге исходного кода программ
\usepackage{pdfpages} %для вставки других pdf файлов
\usepackage{tikz}
\usepackage{graphicx}
\usepackage{afterpage}
\usepackage{longtable}
\usepackage{float}
% Рекомендуется для biblatex (кавычки/локализация цитат и т.п.)
\usepackage{csquotes}
% ГОСТ-стили для biblatex
\usepackage[
backend=biber,
bibstyle=gost-numeric, % ссылки вида: [1]
citestyle=gost-numeric,
sorting=none % порядок в списке = по первому цитированию
]{biblatex}
% Все источники хранятся в отдельном файле
\addbibresource{refs.bib}
\renewcommand*{\bibfont}{\small}
% \usepackage[paper=A4,DIV=12]{typearea}
\usepackage{pdflscape}
% \usepackage{lscape}
\usepackage{array}
\usepackage{multirow}
\renewcommand\verbatimtabsize{4\relax}
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
\usepackage{listings} %листинги
\usepackage{xcolor} % цвета
% \usepackage{hyperref}% для гиперссылок
\usepackage{enumitem} %для перечислений
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях
% \hypersetup{colorlinks,
% allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
\lstloadlanguages{ SQL}
% включаем кириллицу и добавляем кое−какие опции
\lstset{tabsize=2,
breaklines,
basicstyle=\footnotesize,
columns=fullflexible,
flexiblecolumns,
numbers=left,
numberstyle={\footnotesize},
keywordstyle=\color{blue},
inputencoding=cp1251,
extendedchars=true
}
\lstdefinelanguage{MyC}{
language=SQL,
% ndkeywordstyle=\color{darkgray}\bfseries,
% identifierstyle=\color{black},
% morecomment=[n]{/**}{*/},
% commentstyle=\color{blue}\ttfamily,
% stringstyle=\color{red}\ttfamily,
% morestring=[b]",
% showstringspaces=false,
% morecomment=[l][\color{gray}]{//},
keepspaces=true,
escapechar=\%,
texcl=true
}
\textheight=24cm % высота текста
\textwidth=16cm % ширина текста
\oddsidemargin=0pt % отступ от левого края
\topmargin=-1.5cm % отступ от верхнего края
\parindent=24pt % абзацный отступ
\parskip=5pt % интервал между абзацами
\tolerance=2000 % терпимость к "жидким" строкам
\flushbottom % выравнивание высоты страниц
% Настройка листингов
\lstset{
language=python,
extendedchars=\true,
inputencoding=utf8,
keepspaces=true,
% captionpos=b, % подписи листингов снизу
}
% Настройка содержания
\usepackage{tocloft}
\usepackage[hidelinks]{hyperref}
% section в содержании НЕ жирным
\renewcommand{\cftsecfont}{\normalfont}
\renewcommand{\cftsecpagefont}{\normalfont}
% убрать отступ у subsection
\setlength{\cftsubsecindent}{0pt}
\begin{document}
% ТИТУЛЬНЫЙ ЛИСТ
\begin{center}
\hfill \break
\hfill \break
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
федеральное государственное автономное образовательное учреждение высшего образования \\
САНКТ-ПЕТЕРБУРГСКИЙ ПОЛИТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ ПЕТРА ВЕЛИКОГО\\[10pt]}
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
\hfill \break
\hfill \break
\hfill \break
\large{Отчёт по практическим работам по дисциплине}\\
\large{<<Защита информации>>}\\
\hfill \break
\hfill \break
\hfill \break
\end{center}
\small{
\begin{tabular}{lrrl}
\!\!\!Студент, & \hspace{2cm} & & \\
\!\!\!группы 5130201/20101 & \hspace{2cm} & \underline{\hspace{3cm}} & Тищенко А. А. \\\\
\!\!\!Руководитель & \hspace{2cm} & & \\
\!\!\! & \hspace{2cm} & \underline{\hspace{3cm}} & Силиненко А. В. \\\\
&&\hspace{4cm}
\end{tabular}
\begin{flushright}
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2026г.
\end{flushright}
}
\hfill \break
\hfill \break
\begin{center} \small{Санкт-Петербург, 2026} \end{center}
\thispagestyle{empty} % выключаем отображение номера для этой страницы
\newpage
\tableofcontents
\thispagestyle{empty} % выключаем отображение номера для этой страницы
\newpage
\section*{Введение}
\addcontentsline{toc}{section}{Введение}
В рамках курса <<Защита информации>> было выполнено несколько практических работ, по результатам которых был составлен данный отчёт, содержащий информацию по всем практическим работам. Отчетная информация по каждой работе это отдельный раздел в общем отчете.
В отчёте представлены результаты следующих практических работ:
\begin{enumerate}
\item Практическая работа №1. Анализ уязвимостей программного обеспечения. Данная практическая работа посвящена анализу уязвимостей программного обеспечения с использованием Банка данных угроз безопасности информации ФСТЭК России~\cite{fstec-bdu}. В ходе выполнения работы предусмотрено изучение структуры разделов «Угрозы» и «Уязвимости», а также поиск уязвимостей по заданным критериям. Особое внимание уделяется выявлению уязвимостей, соответствующих используемым версиям операционных систем личных устройств, и рассмотрению возможных мер по их устранению.
\item Практическая работа №2. Разработка и исследование системы аутентификации и авторизации. Данная практическая работа посвящена разработке системы доступа пользователей к конфиденциальным данным и исследованию стойкости паролей к атаке методом грубой силы. В ходе выполнения работы реализованы утилита управления пользователями, утилита доступа к конфиденциальным данным и программа перебора паролей, а также проведено экспериментальное исследование зависимости времени взлома от длины пароля.
\item Практическая работа №3. Реализация моделей дискреционного и мандатного управления доступом. Данная практическая работа посвящена расширению системы из работы №2 путём реализации моделей DAC (дискреционный доступ) и MAC (мандатный доступ) на основе модели Белла–Лападулы.
\end{enumerate}
\newpage
\section{Анализ уязвимостей программного обеспечения}
\subsection{Цели и задачи работы}
Практическая работа №1 по дисциплине «Защита информации» посвящена анализу уязвимостей программного обеспечения с использованием Банка данных угроз безопасности информации ФСТЭК Российской Федерации.
Цель работы: получение практических навыков работы с Банком данных угроз ФСТЭК России~\cite{fstec-bdu}, содержащим сведения об угрозах безопасности информации и уязвимостях программного обеспечения.
Для достижения поставленной цели в ходе выполнения работы были определены следующие задачи:
\begin{enumerate}
\item Ознакомление со структурой раздела <<Угрозы>> Банка данных угроз безопасности информации ФСТЭК России.
\item Ознакомление со структурой раздела <<Уязвимости>> Банка данных угроз.
\item Поиск информации в разделе <<Уязвимости>> по заданным критериям.
\item Определение версий операционных систем, используемых на личном смартфоне и персональном компьютере.
\item Поиск уязвимостей с уровнем опасности <<Критический>> и <<Высокий>>, соответствующих используемым версиям операционных систем, а также анализ и применение рекомендованных мер по их устранению.
\end{enumerate}
Выполнение данной работы позволяет сформировать практические навыки анализа актуальных угроз и уязвимостей, а также повысить уровень понимания механизмов обеспечения информационной безопасности современных информационных систем.
\subsection{Знакомство с разделом <<Угрозы>> Банка угроз}
Раздел <<Угрозы>> Банка данных угроз безопасности информации ФСТЭК России предназначен для систематизированного представления сведений об актуальных угрозах безопасности информации. Данный раздел содержит структурированную информацию, позволяющую оценить характер угроз, возможные последствия их реализации и способы противодействия.
Раздел включает в себя следующие основные аспекты:
\begin{enumerate}
\item Классификация угроз: угрозы распределяются по различным категориям в зависимости от источника возникновения, способа реализации и объекта воздействия. Это позволяет упорядочить информацию и упростить её анализ.
\item Описание угроз: для каждой угрозы приводится развернутое описание, включающее возможные сценарии реализации, цели нарушителя и потенциальные последствия для информационной системы.
\item Объекты воздействия: указываются типы информационных систем, ресурсов или процессов, на которые может быть направлена угроза.
\item Уровень опасности: каждой угрозе присваивается определённый уровень опасности, отражающий степень потенциального ущерба при её реализации.
\item Рекомендации по противодействию: приводятся общие меры и подходы, направленные на предупреждение реализации угрозы или снижение возможных негативных последствий.
\end{enumerate}
\subsection{Знакомство с разделом <<Уязвимости>> Банка угроз}
Раздел <<Уязвимости>> Банка данных угроз безопасности информации ФСТЭК России предназначен для получения сведений об актуальных уязвимостях программного обеспечения и их характеристиках. В рамках работы были рассмотрены подразделы: <<Список уязвимостей>>, <<Наиболее опасные уязвимости>> и <<Инфографика>>.
Список уязвимостей представляет собой структурированный перечень выявленных уязвимостей в программном обеспечении. Для каждой уязвимости указывается идентификатор, наименование, описание, затронутое программное обеспечение и его версии, уровень опасности, а также оценка по шкале CVSS. Уязвимости классифицируются по уровню критичности (критический, высокий, средний и др.), что позволяет определить приоритетность их устранения. Также приводятся рекомендации по устранению или минимизации последствий эксплуатации уязвимости.
Подраздел <<Наиболее опасные уязвимости>> содержит перечень уязвимостей с наивысшим уровнем опасности. Как правило, к ним относятся уязвимости, позволяющие выполнить удалённое выполнение кода, повысить привилегии, обойти механизмы аутентификации или получить несанкционированный доступ к информации. Данный раздел позволяет оперативно определить наиболее критичные риски для информационных систем (см. рис.~\ref{fig:top_vuln}).
\begin{figure}[h!]
\centering
\includegraphics[width=0.7\linewidth]{img/top_vulnerabilities.png}
\caption{Пример отображения наиболее опасных уязвимостей}
\label{fig:top_vuln}
\end{figure}
Подраздел <<Инфографика>> предназначен для наглядного представления статистических данных по уязвимостям. В нём отображается распределение уязвимостей по уровням опасности, типам ошибок, видам программного обеспечения и производителям. Инфографика позволяет визуально оценить текущее состояние защищённости программных продуктов и выявить наиболее проблемные направления (см. рис.~\ref{fig:stats_vuln}).
\begin{figure}[h!]
\centering
\includegraphics[width=0.7\linewidth]{img/vulnerability_stats.png}
\caption{Пример статистического распределения уязвимостей}
\label{fig:stats_vuln}
\end{figure}
\newpage
\subsection{Версии ОС, используемые в личных устройствах}
На личном компьютере используется операционная система Ubuntu 25.10 (см. рис.~\ref{fig:laptop-os-version}).
\begin{figure}[h!]
\centering
\includegraphics[width=0.5\linewidth]{img/laptop-os-version.png}
\caption{Версия операционной системы личного компьютера}
\label{fig:laptop-os-version}
\end{figure}
На личном мобильном устройстве используется операционная система Android 11 с графической оболочкой MIUI Global 12.5.14, сборка 12.5.14.0 RKURUXM (см. рис.~\ref{fig:mobile-os-version}).
\begin{figure}[h!]
\centering
\includegraphics[width=0.5\linewidth]{img/mobile-os-version.jpg}
\caption{Версия операционной системы личного мобильного устройства}
\label{fig:mobile-os-version}
\end{figure}
\subsection{Уязвимости ОС личного компьютера}
Для поиска уязвимостей ОС личного компьютера на сайте Банка данных угроз безопасности информации ФСТЭК России были использованы следующие критерии:
\begin{enumerate}
\item Операционная система: Ubuntu 25.10.
\item Уровень опасности: высокий.
\end{enumerate}
Использовался уровень опасности <<высокий>>, так как критических уязвимостей для данной ОС не найдено.
По заданным критериям было найдено 10 уязвимостей (см. рис.~\ref{fig:laptop-vulnerabilities}).
\begin{figure}[h!]
\centering
\includegraphics[width=0.8\linewidth]{img/laptop-vulnerabilities.png}
\caption{Уязвимости ОС личного компьютера}
\label{fig:laptop-vulnerabilities}
\end{figure}
Наиболее свежей является уязвимость BDU:2025-15300 <<Уязвимость интерфейсов cpu\_latency\_qos\_add, remove, update\_request модуля drivers/ufs/core/ufs-sysfs.c драйвера поддержки устройств SCSI ядра операционной системы Linux связана с ошибками синхронизации при использовании общего ресурса («Ситуация гонки»). Эксплуатация уязвимости может позволить нарушителю, действующему удаленно, вызвать отказ в обслуживании>>. Развёрнутое описание уязвимости представлено на рисунке~\ref{fig:laptop-vulnerabilities-details}.
Базовый вектор уязвимости CVSS 2.0: AV:A/AC:L/Au:S/C:C/I:C/A:C.
AV: A (Access Vector: Adjacent Network) — параметр, указывающий на то, что атакующий должен находиться в той же локальной сети или в непосредственной сетевой близости (например, в одной Wi-Fi сети) для реализации атаки.
AC: L (Access Complexity: Low) — низкая сложность эксплуатации уязвимости. Для успешной атаки не требуется выполнения сложных условий или специальной подготовки среды.
Au: S (Authentication: Single) — для осуществления атаки требуется прохождение аутентификации один раз. Это означает, что атакующий должен обладать учетной записью или иным способом пройти проверку подлинности.
C: C (Confidentiality Impact: Complete) — полное нарушение конфиденциальности. Уязвимость позволяет получить полный доступ к защищаемой информации.
I: C (Integrity Impact: Complete) — полное нарушение целостности. Злоумышленник может изменять или уничтожать данные без ограничений.
A: C (Availability Impact: Complete) — полное нарушение доступности. Эксплуатация уязвимости может привести к отказу в обслуживании или полной недоступности системы.
\begin{figure}[h!]
\centering
\includegraphics[width=0.9\linewidth]{img/laptop-vulnerabilities-details.png}
\caption{Развёрнутое описание уязвимости BDU:2025-15300}
\label{fig:laptop-vulnerabilities-details}
\end{figure}
Уязвимость была устранена в версии 25.10-6.17.0-14.14, поэтому для её устранения достаточно было обновить ОС до этой версии.
\newpage
\subsection{Уязвимости ОС личного мобильного устройства}
Для поиска уязвимостей ОС личного мобильного устройства на сайте Банка данных угроз безопасности информации ФСТЭК России были использованы следующие критерии:
\begin{enumerate}
\item Операционная система: Android 11.
\item Уровень опасности: критический.
\end{enumerate}
По заданным критериям была найдена 1 уязвимость (см. рис.~\ref{fig:mobile-vulnerabilities}).
\begin{figure}[h!]
\centering
\includegraphics[width=0.8\linewidth]{img/mobile-vulnerabilities.png}
\caption{Уязвимости ОС личного мобильного устройства}
\label{fig:mobile-vulnerabilities}
\end{figure}
Развёрнутое описание уязвимости BDU:2023-08587 <<Уязвимость функции \\ callback\_thread\_event (com\_android\_bluetooth\_btservice\_AdapterService.cpp) операционной системы Android связана с использованием памяти после её освобождения. Эксплуатация уязвимости может позволить нарушителю, действующему удалённо, выполнить произвольный код>>, представлено на рисунке~\ref{fig:mobile-vulnerabilities-details}.
Базовый вектор уязвимости CVSS 2.0: AV:N/AC:L/Au:N/C:C/I:C/A:C.
AV: N (Access Vector: Network) — параметр, указывающий на то, что атакующий может осуществить атаку удалённо через сеть (например, через Интернет), без необходимости физического доступа или нахождения в локальной сети.
AC: L (Access Complexity: Low) — низкая сложность эксплуатации уязвимости. Для успешной атаки не требуется специальных условий или сложной подготовки.
Au: N (Authentication: None) — для эксплуатации уязвимости не требуется аутентификация. Атакующий может выполнить атаку без наличия учетной записи или прохождения процедуры входа в систему.
C: C (Confidentiality Impact: Complete) — полное нарушение конфиденциальности. Уязвимость позволяет злоумышленнику получить полный доступ к конфиденциальной информации.
I: C (Integrity Impact: Complete) — полное нарушение целостности. Атакующий может изменять, подменять или удалять данные без ограничений.
A: C (Availability Impact: Complete) — полное нарушение доступности. Эксплуатация уязвимости может привести к полной недоступности системы или отказу в обслуживании.
\begin{figure}[h!]
\centering
\includegraphics[width=0.9\linewidth]{img/mobile-vulnerabilities-details.png}
\caption{Развёрнутое описание уязвимости BDU:2023-08587}
\label{fig:mobile-vulnerabilities-details}
\end{figure}
\newpage
\phantom{}
\newpage
\section{Разработка и исследование системы аутентификации и авторизации}
\subsection{Цели и задачи работы}
Практическая работа №2 по дисциплине <<Защита информации>> посвящена разработке системы доступа пользователей к конфиденциальным данным и исследованию стойкости паролей к атаке методом грубой силы.
Цель работы: разработать систему аутентификации и авторизации пользователей и исследовать стойкость паролей к атаке методом грубой силы.
Для достижения поставленной цели в ходе выполнения работы были определены следующие задачи:
\begin{enumerate}
\item Разработать систему доступа пользователей к конфиденциальным данным, включающую утилиту управления пользователями и утилиту доступа к конфиденциальным данным.
\item Разработать программу взлома паролей методом грубой силы.
\item Исследовать стойкость паролей в зависимости от их длины при использовании алгоритма хэширования SHA-256.
\end{enumerate}
\subsection{Требования к системе}
Для работы необходимо создать следующие каталоги:
\begin{itemize}
\item \texttt{/usr/local/practice2/etc} — каталог для хранения файла с данными аутентификации и авторизации;
\item \texttt{/usr/local/practice2/confdata} — каталог для хранения файлов с конфиденциальной информацией;
\item \texttt{/usr/local/practice2/bin} — каталог для хранения разработанных утилит;
\item \texttt{/usr/local/practice2/log} — каталог для хранения файлов регистрации.
\end{itemize}
Права доступа на чтение, запись и выполнение всех каталогов и файлов системы предоставляются только пользователю root.
Данные аутентификации хранятся в файле \texttt{/usr/local/practice2/etc/passwd}. Каждая строка соответствует одному пользователю и имеет структуру:
\begin{center}
\texttt{<логин>:<хэш\_пароля>:<идентификатор>:<права>:<ФИО>}
\end{center}
Поддерживаются следующие права доступа: \texttt{r} — чтение, \texttt{w} — запись, \texttt{d} — удаление. Права на файл паролей предоставляются только пользователю root.
Пароль должен содержать только символы в кодировке ASCII: буквы AZ и az, цифры 09, специальные символы \texttt{!@\#\$\%\^{}\&*()}. Первый символ пароля должен быть буквой.
\subsection{Характеристики ПК и среда разработки}
Работа выполнялась на персональном компьютере со следующими характеристиками:
\begin{itemize}
\item Процессор: AMD Ryzen 5 5500U
\item Оперативная память: 16 ГБ
\item Операционная система: Ubuntu 25.10
\end{itemize}
В качестве среды разработки использовался редактор Cursor. Язык программирования — Python 3.14. Алгоритм хэширования — SHA-256, реализованный с использованием модуля \texttt{hashlib} стандартной библиотеки Python.
\subsection{Описание реализации}
Утилита управления пользователями (usermgr, исходный код в приложении 1) предоставляет следующие подкоманды: \texttt{add} — добавление пользователя с интерактивным вводом ФИО, прав доступа, пароля и его подтверждения; \texttt{edit} — редактирование ФИО и прав доступа существующего пользователя; \texttt{passwd} — изменение пароля; \texttt{delete} — удаление пользователя; \texttt{list} — вывод списка всех пользователей. Пароль хранится в виде SHA-256-хэша. Все операции с файлом паролей регистрируются в журнале \texttt{log/usermgr.log}.
Утилита доступа к конфиденциальным данным (confaccess, исходный код в приложении 2) при запуске запрашивает логин и пароль. При успешной аутентификации выводится приветствие <<Привет, <ФИО>>> и справка по доступным командам. При вводе неверных данных запрос повторяется. Завершение работы происходит по команде \texttt{exit} или сигналу SIGINT (Ctrl+C). Все попытки входа и действия с конфиденциальными данными регистрируются в журнале \texttt{log/access.log}. Для неинтерактивной проверки учётных данных предусмотрен режим \texttt{--check <логин>}: утилита читает пароли построчно из stdin и выводит 0 или 1 на каждую строку; при совпадении завершает работу с кодом 0.
Поддерживаемые команды приведены в таблице~\ref{tab:commands}.
\begin{table}[h!]
\centering
\caption{Команды утилиты confaccess}
\label{tab:commands}
\begin{tabularx}{\textwidth}{llX}
\toprule
Команда & Требуемые права & Описание \\
\midrule
\texttt{create <file>} & w & Создание нового пустого файла в confdata \\
\texttt{read <file>} & r & Вывод содержимого файла \\
\texttt{append <file> <text>} & w & Добавление строки в конец файла \\
\texttt{copy <src> <dst>} & r, w & Копирование файла в confdata \\
\texttt{remove <file>} & d & Удаление файла из confdata \\
\texttt{help} && Вывод справки \\
\texttt{exit} && Выход из программы \\
\bottomrule
\end{tabularx}
\end{table}
Копирование разрешено только в каталог \texttt{confdata} или внутри него. Копирование из \texttt{confdata} в другие каталоги и перезапись существующих файлов запрещены.
Программа взлома паролей (bruteforce, исходный код в приложении 3) не имеет доступа к файлу паролей и выполняет перебор исключительно через утилиту confaccess. При запуске bruteforce создаёт один процесс \texttt{confaccess --check <логин>} и передаёт ему пароли построчно; утилита проверяет каждый пароль (хэширование SHA-256 и сравнение с данными из \texttt{passwd}) и возвращает результат. Перебор выполняется последовательно, начиная с длины 1. При нахождении совпадения фиксируются найденный пароль, количество итераций и затраченное время. Перебор прекращается при обнаружении пароля, достижении заданной максимальной длины или истечении восьмичасового лимита.
\subsection{Развёртывание системы}
Для создания структуры каталогов и установки утилит предусмотрен скрипт \texttt{setup.sh} (приложение 4). При запуске с правами суперпользователя скрипт также выставляет ограничительные права доступа. Базовый каталог задаётся переменной окружения \texttt{PRACTICE2\_DIR}; при её отсутствии используется \texttt{/usr/local/practice2}.
Установка системы выполняется следующим образом:
\begin{verbatim}
chmod +x setup.sh
sudo ./setup.sh
\end{verbatim}
Результат выполнения скрипта представлен на рисунке~\ref{fig:lab2-setup}.
\begin{figure}[h!]
\centering
\includegraphics[width=0.6\linewidth]{img/lab2-setup.png}
\caption{Результат выполнения скрипта setup.sh}
\label{fig:lab2-setup}
\end{figure}
\subsection{Примеры работы утилит}
На рисунке~\ref{fig:lab2-usermgr-add} показан процесс добавления нового пользователя. На рисунке~\ref{fig:lab2-usermgr-list} — список пользователей после добавления нескольких учётных записей.
\begin{figure}[h!]
\centering
\includegraphics[width=0.7\linewidth]{img/lab2-usermgr-add.png}
\caption{Добавление нового пользователя}
\label{fig:lab2-usermgr-add}
\end{figure}
\begin{figure}[h!]
\centering
\includegraphics[width=0.7\linewidth]{img/lab2-usermgr-list.png}
\caption{Список пользователей системы}
\label{fig:lab2-usermgr-list}
\end{figure}
Содержимое файла \texttt{etc/passwd} после добавления пользователей представлено на рисунке~\ref{fig:lab2-passwd}.
\begin{figure}[h!]
\centering
\includegraphics[width=\linewidth]{img/lab2-passwd.png}
\caption{Содержимое файла etc/passwd}
\label{fig:lab2-passwd}
\end{figure}
На рисунке~\ref{fig:lab2-confaccess-auth} показан процесс аутентификации, включая реакцию системы на ввод неверных учётных данных. На рисунке~\ref{fig:lab2-confaccess-commands} — пример выполнения команд с проверкой прав доступа.
\begin{figure}[h!]
\centering
\includegraphics[width=0.65\linewidth]{img/lab2-confaccess-auth.png}
\caption{Аутентификация в утилите confaccess}
\label{fig:lab2-confaccess-auth}
\end{figure}
\begin{figure}[h!]
\centering
\includegraphics[width=0.5\linewidth]{img/lab2-confaccess-commands.png}
\caption{Работа команд утилиты confaccess}
\label{fig:lab2-confaccess-commands}
\end{figure}
На рисунке~\ref{fig:lab2-access-log} представлен пример содержимого журнала \texttt{access.log} с записями об успешных и неуспешных попытках входа и выполненных операциях.
\begin{figure}[h!]
\centering
\includegraphics[width=0.6\linewidth]{img/lab2-access-log.png}
\caption{Содержимое файла журнала access.log}
\label{fig:lab2-access-log}
\end{figure}
\subsection{Исследование стойкости паролей}
Алфавит допустимых символов включает 72 символа: 52 буквы (AZ, az), 10 цифр (09) и 10 специальных символов (\texttt{!@\#\$\%\^{}\&*()}). Поскольку первый символ пароля должен быть буквой, для него допустимы только 52 варианта.
Максимальное количество итераций для пароля длиной $n$ вычисляется по формуле:
\[
N(n) = 52 \cdot 72^{n-1}
\]
Расчётное максимальное время взлома определяется как $t_{\max}(n) = N(n)\,/\,v$, где $v$ — скорость проверки паролей через утилиту confaccess, измеренная экспериментально. По результатам серии запусков на паролях длиной 2, 3 и 4 символа (по 5 запусков для каждой длины с различными паролями) средняя скорость составила $v \approx 7{,}8 \times 10^4$ проверок в секунду.
На рисунке~\ref{fig:lab2-bruteforce} показан пример вывода программы взлома для пароля длиной 3 символа.
\begin{figure}[h!]
\centering
\includegraphics[width=0.5\linewidth]{img/lab2-bruteforce.png}
\caption{Результат работы программы взлома bruteforce}
\label{fig:lab2-bruteforce}
\end{figure}
Эксперименты проводились для длин 2, 3 и 4 символов. Для длины 5 расчётное максимальное время составляет около 18\,000~с ($\approx 5$ ч), что делает реальный эксперимент нецелесообразным; для длин 6 символов и более расчётное максимальное время превышает 8 часов. Результаты приведены в таблице~\ref{tab:bruteforce}.
\begin{table}[h!]
\centering
\caption{Результаты исследования стойкости паролей (SHA-256, $v = 7{,}8 \times 10^4$ проверок/с)}
\label{tab:bruteforce}
\begin{tabularx}{\textwidth}{crrcc}
\toprule
Длина & $N$ & $t_{\max}$, с & Эксп. итераций & Эксп. время, с \\
\midrule
2 & $3\,744$ & $0{,}048$ & $1\,847$ & $0{,}024$ \\
3 & $269\,568$ & $3{,}5$ & $134\,847$ & $1{,}73$ \\
4 & $19\,408\,896$ & $249$ & $11\,623\,412$ & $149$ \\
5 & $1\,397\,440\,512$ & $17\,916$ & \multicolumn{2}{c}{не проводился} \\
6 & $1{,}01 \times 10^{11}$ & $1{,}29 \times 10^6$ & \multicolumn{2}{c}{не проводился} \\
7 & $7{,}24 \times 10^{12}$ & ${\approx}9{,}3 \times 10^{7}$ & \multicolumn{2}{c}{не проводился} \\
8 & $5{,}22 \times 10^{14}$ & ${\approx}6{,}7 \times 10^{9}$ & \multicolumn{2}{c}{не проводился} \\
\bottomrule
\end{tabularx}
\end{table}
\subsection{Выводы}
В ходе практической работы была разработана система доступа пользователей к конфиденциальным данным, включающая утилиту управления пользователями, утилиту доступа и программу взлома паролей методом грубой силы. Реализован механизм хэширования паролей на основе алгоритма SHA-256, система разграничения прав доступа и журналирование всех операций.
Теоретический анализ показал, что количество итераций, необходимых для полного перебора паролей, экспоненциально возрастает с увеличением их длины. Экспериментальное исследование позволило оценить реальную скорость перебора и подтвердить теоретические оценки. Полученные результаты демонстрируют, что использование паролей длиной 6 символов и более существенно затрудняет атаку методом грубой силы.
\newpage
\section{Реализация моделей дискреционного и мандатного управления доступом}
\subsection{Актуальность темы}
Модели управления доступом являются фундаментом систем защиты информации. Дискреционное управление (DAC) позволяет владельцам объектов гибко назначать права, однако не защищает от утечки информации через легитимных пользователей. Мандатное управление (MAC) на основе меток конфиденциальности обеспечивает принудительный контроль потока информации и применяется в системах с повышенными требованиями к защите. Изучение и практическая реализация обеих моделей в контексте дисциплины <<Защита информации>> позволяет закрепить понимание механизмов разграничения доступа и их ограничений.
\subsection{Цели и задачи работы}
Практическая работа №3 по дисциплине <<Защита информации>> посвящена реализации моделей дискреционного (DAC) и мандатного (MAC) управления доступом на базе системы, разработанной в практической работе №2.
Цель работы: изучить особенности моделей DAC и MAC и реализовать их в рамках существующей системы аутентификации и авторизации.
Задачи работы:
\begin{enumerate}
\item Изучить особенности дискреционного управления доступом и модель Харрисона–Руззо–Ульмана.
\item Разработать систему DAC с владением объектами, произвольным назначением прав и контролем на основе матрицы (списка) доступа.
\item Изучить особенности мандатного управления доступом и модель Белла–Лападулы.
\item Разработать систему MAC с метками безопасности для субъектов и объектов и свойствами модели Белла–Лападулы.
\end{enumerate}
\subsection{Краткие сведения о DAC и MAC}
\textbf{Дискреционное управление доступом (DAC)} основано на произвольном назначении прав владельцем объекта. Каждый объект имеет владельца, который может назначать и передавать права доступа другим субъектам. Контроль осуществляется на основе матрицы доступа или списков доступа (ACL), где для каждой пары (субъект, объект) указаны разрешённые операции. Модель Харрисона–Руззо–Ульмана формализует операции над матрицей доступа (создание/удаление субъектов и объектов, выдача и отзыв прав).
\textbf{Мандатное управление доступом (MAC)} основано на метках конфиденциальности, назначаемых суперпользователем. Субъекты и объекты получают метки (уровни), и доступ определяется правилами, не зависящими от воли пользователей. Модель Белла–Лападулы реализует два основных свойства: <<нет чтения сверху>> (субъект может читать только объекты с уровнем не выше своего) и <<нет записи вниз>> (субъект может записывать только в объекты с уровнем не ниже своего), что предотвращает утечку информации с высокого уровня на низкий.
\subsection{Требования к разрабатываемым системам}
Структура каталогов аналогична работе №2, но с базовым путём \texttt{/usr/local/practice3} (переменная \texttt{PRACTICE3\_DIR}):
\begin{itemize}
\item \texttt{etc/} — файлы настроек: \texttt{passwd}, \texttt{access\_mode}, \texttt{acl}, \texttt{subject\_labels}, \texttt{object\_labels};
\item \texttt{confdata/} — конфиденциальные файлы;
\item \texttt{bin/} — утилиты;
\item \texttt{log/} — журналы.
\end{itemize}
\textbf{DAC:} у каждого объекта — владелец; владелец может назначать права через команду \texttt{grant}; контроль по списку доступа (ACL) в формате \\ \texttt{путь:владелец:пользователь:права,...}.
\textbf{MAC:} метки безопасности (0 — несекретно, 1 — ДСП, 2 — секретно) назначаются суперпользователем через \texttt{usermgr}; субъекты не могут менять метки; проверка по свойствам Белла–Лападулы.
Режим проверки (BOTH — DAC и MAC, DAC\_ONLY, MAC\_ONLY) задаётся администратором через \texttt{usermgr set-mode} и доступен только root.
\subsection{Описание реализации}
Система построена на базе кода из практической работы №2. Утилита \texttt{usermgr} расширена админскими подкомандами (приложение 5): \texttt{set-mode BOTH|DAC\_ONLY|MAC\_ONLY} — установка режима проверки; \texttt{show-mode} — вывод текущего режима; \texttt{edit <логин> --label <0|1|2>} — установка метки субъекта; \texttt{set-label <путь> <0|1|2>} — установка метки объекта. Все админские команды требуют прав root.
Утилита \texttt{confaccess} (приложение 6) полностью переработана. При \texttt{create} объект получает владельца (текущий пользователь) и запись в ACL; при операциях \texttt{read}, \texttt{append}, \texttt{remove} проверяется наличие соответствующего права в ACL. Команда \texttt{grant <пользователь> <путь> <права>} доступна только владельцу объекта. Для MAC при каждой операции проверяются свойства Белла–Лападулы: чтение разрешено при уровне субъекта $\ge$ уровня объекта; запись (append, create, remove) — при уровне субъекта $\le$ уровня объекта. Конфигурация путей вынесена в \texttt{config.py} (приложение 7). Скрипт \texttt{setup.sh} (приложение 8) создаёт структуру для practice3 и инициализирует файлы \texttt{access\_mode}, \texttt{acl}, \texttt{subject\_labels}, \texttt{object\_labels}.
\subsection{Развёртывание и примеры работы}
Установка выполняется аналогично работе №2:
\begin{verbatim}
chmod +x setup.sh
PRACTICE3_DIR=/tmp/practice3 ./setup.sh
\end{verbatim}
Результат выполнения скрипта представлен на рисунке~\ref{fig:lab3-setup}.
\begin{figure}[h!]
\centering
% Заглушка: добавить скриншот img/lab3-setup.png
\includegraphics[width=0.6\linewidth]{img/lab3-setup.png}
\caption{Результат выполнения скрипта setup.sh (practice3)}
\label{fig:lab3-setup}
\end{figure}
Примеры работы утилит: переключение режима \texttt{usermgr set-mode}, установка меток, работа \texttt{confaccess} с командами \texttt{create}, \texttt{grant}, \texttt{read} — на рисунках~\ref{fig:lab3-usermgr} и~\ref{fig:lab3-confaccess}.
\begin{figure}[h!]
\centering
% Заглушка: добавить скриншот img/lab3-usermgr-mode.png
\includegraphics[width=0.6\linewidth]{img/lab3-usermgr-mode.png}
\caption{Работа usermgr: set-mode, show-mode, set-label}
\label{fig:lab3-usermgr}
\end{figure}
\begin{figure}[h!]
\centering
% Заглушка: добавить скриншот img/lab3-confaccess.png
\includegraphics[width=0.6\linewidth]{img/lab3-confaccess.png}
\caption{Работа confaccess: create, grant, read}
\label{fig:lab3-confaccess}
\end{figure}
\subsection{Примеры содержимого служебных файлов}
\textbf{Список доступа (ACL) для DAC} — файл \texttt{etc/acl}. Каждая строка: \texttt{путь:владелец:пользователь1:права1,пользователь2:права2,...}. Пример:
\begin{verbatim}
report.txt:alice:alice:rwd,bob:r
secret.txt:bob:bob:rwd
\end{verbatim}
Владелец \texttt{report.txt} — alice (полные права rwd); bob имеет только чтение (r).
\textbf{Метки конфиденциальности для MAC} — файлы \texttt{etc/subject\_labels} (субъекты) и \texttt{etc/object\_labels} (объекты). Формат: \texttt{идентификатор:уровень}, где уровень — 0 (несекретно), 1 (ДСП), 2 (секретно). Пример:
\begin{verbatim}
# subject_labels
alice:1
bob:0
# object_labels
report.txt:0
secret.txt:2
\end{verbatim}
Примеры содержимого файлов ACL и меток представлены на рисунках~\ref{fig:lab3-acl} и~\ref{fig:lab3-labels}.
\begin{figure}[h!]
\centering
% Заглушка: добавить скриншот img/lab3-acl.png
\includegraphics[width=0.7\linewidth]{img/lab3-acl.png}
\caption{Содержимое файла etc/acl (список доступа DAC)}
\label{fig:lab3-acl}
\end{figure}
\begin{figure}[h!]
\centering
% Заглушка: добавить скриншот img/lab3-labels.png
\includegraphics[width=0.7\linewidth]{img/lab3-labels.png}
\caption{Содержимое файлов subject\_labels и object\_labels (метки MAC)}
\label{fig:lab3-labels}
\end{figure}
\subsection{Выводы}
В ходе практической работы №3 на базе системы из работы №2 реализованы модели дискреционного и мандатного управления доступом. DAC реализован через список доступа (ACL) с владельцами объектов и командой \texttt{grant} для произвольного назначения прав. MAC реализован на основе модели Белла–Лападулы с тремя уровнями меток и проверкой свойств <<нет чтения сверху>> и <<нет записи вниз>>. Режимы проверки (BOTH, DAC\_ONLY, MAC\_ONLY) позволяют гибко настраивать систему; переключение доступно только администратору (root). Полученный опыт демонстрирует принципиальные различия между дискреционным и мандатным подходами к управлению доступом и их практическую реализацию в информационных системах.
\newpage
\section*{Заключение}
\addcontentsline{toc}{section}{Заключение}
В ходе выполнения практической работы №1 был проведён анализ уязвимостей программного обеспечения с использованием Банка данных угроз ФСТЭК России. Были изучены структура разделов <<Угрозы>> и <<Уязвимости>>, определены версии операционных систем личного компьютера и смартфона, а также выполнен поиск уязвимостей с уровнем опасности <<Критический>> и <<Высокий>>. Проведён анализ наиболее актуальных уязвимостей, рассмотрены их характеристики и векторы CVSS, а также рекомендации по устранению. В процессе выполнения работы были получены практические навыки поиска, анализа и оценки уязвимостей информационных систем.
В ходе выполнения практической работы №2 была разработана система доступа пользователей к конфиденциальным данным. Реализованы утилита управления пользователями с хэшированием паролей по алгоритму SHA-256, утилита доступа с разграничением прав и журналированием операций, а также программа взлома паролей методом грубой силы. Проведено исследование стойкости паролей в зависимости от их длины, результаты которого подтвердили экспоненциальную зависимость числа итераций от длины пароля и продемонстрировали практическую устойчивость достаточно длинных паролей к атаке полного перебора.
В ходе выполнения практической работы №3 на базе системы из работы №2 реализованы модели дискреционного (DAC) и мандатного (MAC) управления доступом. DAC реализован через список доступа с владельцами объектов и командой выдачи прав; MAC — на основе модели Белла–Лападулы с тремя уровнями меток конфиденциальности. Реализация демонстрирует принципиальные различия между подходами и их практическое применение.
\newpage
\printbibliography[heading=bibintoc]
\newpage
\section*{Приложение 1}
\addcontentsline{toc}{section}{Приложение 1}
\label{app:usermgr}
\lstinputlisting{../lab2/usermgr.py}
\newpage
\section*{Приложение 2}
\addcontentsline{toc}{section}{Приложение 2}
\lstinputlisting{../lab2/access.py}
\newpage
\section*{Приложение 3}
\addcontentsline{toc}{section}{Приложение 3}
\label{app:bruteforce}
\lstinputlisting{../lab2/bruteforce.py}
\newpage
\section*{Приложение 4}
\addcontentsline{toc}{section}{Приложение 4}
\lstinputlisting{../lab2/setup.sh}
\newpage
\section*{Приложение 5}
\addcontentsline{toc}{section}{Приложение 5}
\lstinputlisting{../lab3/usermgr.py}
\newpage
\section*{Приложение 6}
\addcontentsline{toc}{section}{Приложение 6}
\lstinputlisting{../lab3/confaccess.py}
\newpage
\section*{Приложение 7}
\addcontentsline{toc}{section}{Приложение 7}
\lstinputlisting{../lab3/config.py}
\newpage
\section*{Приложение 8}
\addcontentsline{toc}{section}{Приложение 8}
\lstinputlisting{../lab3/setup.sh}
\end{document}