Общие сетевые ресурсы в домене Active Directory используются для упрощения и централизации доступа к файлам, папкам, принтерам и другим ресурсам в корпоративной сети. Это может привести к нарушению одной из основ информационной безопасности – конфиденциальности
Специалистам по информационной безопасности необходимо выполнять сканирование общих сетевых папок в домене по данным причинам:
-
Обнаружение и мониторинг угроз
Сканирование папок позволяет выявлять несанкционированный доступ к данным и файлам. Это помогает рано обнаруживать потенциальные угрозы безопасности и предотвращать утечку чувствительных информационных ресурсов.
-
Контроль доступа
Сканирование может помочь определить, кто имеет доступ к данным и файлам. Это позволяет специалистам по информационной безопасности убедиться, что только сотрудники с соответствующими правами имеют доступ к конфиденциальной информации.
-
Соблюдение нормативов
Множество отраслевых и государственных регуляторов требуют соблюдения нормативов по защите данных и конфиденциальности. Сканирование помогает удостовериться, что организация соблюдает требования по уровню доступа и контролю к данным.
-
Аудит безопасности
Сканирование общих сетевых папок может быть включено в процессы аудита безопасности, позволяя проводить оценку уровня безопасности и выявлять потенциальные слабые места.
-
Аудит безопасности
Сканирование общих сетевых папок может быть включено в процессы аудита безопасности, позволяя проводить оценку уровня безопасности и выявлять потенциальные слабые места.
Текущие продукты по поиску открытых сетевых ресурсов
На просторах сети есть подобные программы, которые обладают куда большим функционалом, чем я планирую описать.
Как пример программа «SoftPerfect Network Scanner»
Или такая программа как «advanced ip scanner»
Данные программы позволяют сканировать подсети внутри компании и выдавать информацию по поводу открытых сетевых папок, и даже какие файлы там содержатся.
Также можно воспользоваться встроенными средствами Windows для поиска и просмотра открытых сетевых ресурсов на том же файловом сервере. И напрашивается вопрос — а зачем что-то изобретать по новому?
Ответ — не знаю, просто захотелось попробовать что-то свое сделать.
Описание работы программы
Изначально я попробовал сделать все только на PowerShell, сделать скрипт, затем батник, но решил, что это как-то просто.
Скрипт выглядит следующим образом:
# Установка английского языка для корректной работы $prevCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture [System.Threading.Thread]::CurrentThread.CurrentThread.CurrentCulture = 'en-US' # Получаем IP машины $ipAddress = (Test-Connection -ComputerName $env:COMPUTERNAME -Count 1).IPV4Address.IPAddressToString # Получаем имя компьютера $computerName = $env:COMPUTERNAME # Генерируем рандомное число $randomNumber = Get-Random -Minimum 1 -Maximum 10000 # Создадим локальный файл в Temp $localFolderPath = "C:\\Temp" $fileName = "$($randomNumber)_$($ipAddress)_$($computerName)_$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss').txt" $localFilePath = "$localFolderPath\$fileName" # Отладочная информация Write-Host "File will be created at $localFilePath" # Функция для получения прав доступа и замена на читабельные данные function Get-AccessRights { param($path) $accessRights = & icacls $path | Out-String $accessRights = $accessRights -replace '\(OI\)', '/Owner' # Owner object $accessRights = $accessRights -replace '\(CI\)', '/Container' # Container $accessRights = $accessRights -replace '\(F\)', '/Full access' # Full access accessRights = $accessRights -replace '\(RX\)', '/Read and execute' # Read and execute return $accessRights } # Получаем список общих сетевых папок на текущем компьютере $networkFolders = Get-WmiObject -Class Win32_Share | Where-Object { $_.Path -ne $null -and $_.Type -eq 0 } # Если есть открытые сетевые папки, записываем информацию в файл if ($networkFolders) { $outputData = @() $outputData += "$ipAddress \ $computerName \ `"Number of open folders:`" $($networkFolders.Count)" $outputData += "Path to folders:" foreach ($folder in $networkFolders) { folderPath = $folder.Path $outputData += "$folderPath" # Получаем информацию о правах доступа и сам процесс для записи в файл $accessRights = Get-AccessRights -path $folderPath $outputData += $accessRights } $outputData | Out-File -FilePath $localFilePath -Encoding UTF8 } else { # Если открытых сетевых папок нет, выводим сообщение об этом IP Address: $ipAddress `nComputer Name: $computerName `nNetwork folders not found." | Out-File -FilePath $localFilePath -Encoding UTF8 } # Выводим сообщение об успешном выполнении задачи Write-Host "Information gathered and saved to $localFilePath." # Восстановить предыдущую настройку кодировки [System.Threading.Thread]::CurrentThread.CurrentCulture = $prevCulture
Данный скрипт создает в папке C:\Temp файл с результатами и присваивает ему случайно сгенерированный номер, для дальнейшего поиска.
Для локального запуска можно заменить строку на какой-нибудь другой путь:
# Создадим локальный файл в Temp $localFolderPath = "C:\\Temp"
Но стоит вопрос централизованной проверки. То есть с одного компьютера или сервера можно сразу запустить на всех устройствах данный скрипт.
Поэтому я решил написать программу на Python для непосредственного использования на компьютера ( и все усложнить в 1000 раз)
import subprocess import os import datetime import shutil import re import tkinter as tk from tkinter import messagebox, scrolledtext # Функция для обновления лога в текстовом окне def log_message(log_window, message): log_window.insert(tk.END, message + '\n') log_window.see(tk.END) log_window.update_idletasks() # Функция для отображения окна с сообщением об ошибке def show_error_message(error_message): messagebox.showerror("Error", error_message) # Функция для отображения окна с успешным сообщением def show_success_message(file_path, file_name): messagebox.showinfo("Success", f"File created successfully!\n\nFile name: {file_name}\nNetwork path: {file_path}") # Основной код программы def main_program(log_window): try: # Путь к сетевой папке и учетные данные (замените на реальные данные в рабочей среде) network_folder_path = r'\\путь\' # Замените на реальный сетевой путь username = 'логин' # Замените на реальное имя пользователя домена password = 'пароль' # Замените на реальный пароль # Логирование подключения к сетевой папке log_message(log_window, "Attempting to disconnect old network connection...") disconnect_command = f"net use {network_folder_path} /delete" subprocess.run(disconnect_command, shell=True) log_message(log_window, "Attempting to connect to network folder...") net_use_command = f"net use {network_folder_path} /user:{username} {password}" result = subprocess.run(net_use_command, shell=True, capture_output=True, text=True, encoding='cp866') if result.returncode == 0: log_message(log_window, f"Successfully connected to the network folder: {network_folder_path}") else: error_message = result.stderr raise Exception(f"Error connecting to the network folder: {error_message}") # Генерация текущей даты и времени для именования файлов current_datetime = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S') # Путь к временному скрипту PowerShell (используем C:\Temp для избежания ошибок доступа) temp_dir = "C:\\Temp" if not os.path.exists(temp_dir): os.makedirs(temp_dir) ps_script_path = os.path.join(temp_dir, 'temp_script.ps1') # Логирование создания скрипта log_message(log_window, "Creating PowerShell script...") # Содержимое скрипта PowerShell для сбора данных ps_script_content = r''' # Установка культуры на английский для корректной работы $prevCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture [System.Threading.Thread]::CurrentThread.CurrentThread.CurrentCulture = 'en-US' # получаем IP машины $ipAddress = (Test-Connection -ComputerName $env:COMPUTERNAME -Count 1).IPV4Address.IPAddressToString # получаем имя компьютера $computerName = $env:COMPUTERNAME # генерируем рандомное число $randomNumber = Get-Random -Minimum 1 -Maximum 10000 # Создадим локальный файл в Temp $localFolderPath = "C:\\Temp" $fileName = "$($randomNumber)_$($ipAddress)_$($computerName)_$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss').txt" $localFilePath = "$localFolderPath\$fileName" # Отладочная информация Write-Host "File will be created at $localFilePath" # функция для получения прав доступа и замена на читабельные данные function Get-AccessRights { param($path) $accessRights = & icacls $path | Out-String $accessRights = $accessRights -replace '\(OI\)', '/Owner' # Owner object $accessRights = $accessRights -replace '\(CI\)', '/Container' # Container $accessRights = $accessRights -replace '\(F\)', '/Full access' # Full access $accessRights = $accessRights -replace '\(RX\)', '/Read and execute' # Read and execute return $accessRights } # Получаем список общих сетевых папок на текущем компьютере $networkFolders = Get-WmiObject -Class Win32_Share | Where-Object { $_.Path -ne $null -and $_.Type -eq 0 } # Если есть открытые сетевые папки, записываем информацию в файл if ($networkFolders) { $outputData = @() $outputData += "$ipAddress \ $computerName \ `"Number of open folders:`" $($networkFolders.Count)" $outputData += "Path to folders:" foreach ($folder in $networkFolders) { $folderPath = $folder.Path $outputData += "$folderPath" # Получаем информацию о правах доступа и сам процесс для записи в файл $accessRights = Get-AccessRights -path $folderPath $outputData += $accessRights } $outputData | Out-File -FilePath $localFilePath -Encoding UTF8 } else { # Если открытых сетевых папок нет, выводим сообщение об этом "IP Address: $ipAddress `nComputer Name: $computerName `nNetwork folders not found." | Out-File -FilePath $localFilePath -Encoding UTF8 } # Выводим сообщение об успешном выполнении задачи Write-Host "Information gathered and saved to $localFilePath." # Восстановить предыдущую настройку кодировки [System.Threading.Thread]::CurrentThread.CurrentCulture = $prevCulture ''' # Запись содержимого PowerShell скрипта во временный файл with open(ps_script_path, 'w', encoding='utf-8') as f: f.write(ps_script_content) log_message(log_window, "PowerShell script created. Executing script...") # Команда для выполнения скрипта PowerShell ps_command = f'powershell.exe -ExecutionPolicy Bypass -File "{ps_script_path}"' result = subprocess.run(ps_command, shell=True, capture_output=True, text=True) if result.returncode == 0: log_message(log_window, "PowerShell script executed successfully.") log_message(log_window, result.stdout) # Поиск случайного числа в выводе PowerShell match = re.search(r'(\d+)', result.stdout) if match: random_number = int(match.group()) log_message(log_window, f"Random number generated by PowerShell: {random_number}") # Проверяем, создался ли файл локально log_message(log_window, "Checking local Temp folder for the created file...") local_files = os.listdir(temp_dir) found_file = None for file_name in local_files: if file_name.startswith(str(random_number)): found_file = file_name break if found_file: local_file_path = os.path.join(temp_dir, found_file) log_message(log_window, f"File created locally: {local_file_path}") # Копируем файл в сетевую папку log_message(log_window, f"Copying file to network folder: {network_folder_path}") shutil.copy(local_file_path, network_folder_path) # Проверка файла в сетевой папке log_message(log_window, "Searching for the file in the network folder...") files_in_network_folder = os.listdir(network_folder_path) network_found_file = None for file_name in files_in_network_folder: if file_name.startswith(str(random_number)): network_found_file = file_name break if network_found_file: full_file_path = os.path.join(network_folder_path, network_found_file) log_message(log_window, f"Found file: {full_file_path}") # Чтение файла и анализ содержимого with open(full_file_path, 'r', encoding='utf-8') as output_file: output_data = output_file.read() # Лог содержимого файла для отладки log_message(log_window, f"File content:\n{output_data}") # Установка префикса в зависимости от содержимого файла new_prefix = "Empty_" # По умолчанию "Empty_" if "Number of open folders:" in output_data: open_folders_count = int(re.search(r'\d+', output_data).group()) log_message(log_window, f"Open folders count: {open_folders_count}") if open_folders_count > 0: # Проверяем наличие прав, связанных с "Everyone", "Все" или "All" if re.search(r'(Everyone|Все|All)([:/\\])', output_data): new_prefix = "Нарушение_" log_message(log_window, "Violation detected based on folder permissions.") else: new_prefix = "OpenShareFind_" log_message(log_window, "Open folders found, but no violations.") else: log_message(log_window, "No open folders found, setting prefix to 'Empty_'.") # Переименование файла new_filename = f"{new_prefix}{network_found_file}" new_file_path = os.path.join(network_folder_path, new_filename) os.rename(full_file_path, new_file_path) log_message(log_window, f"File renamed to: {new_file_path}") # Выводим сообщение об успешном создании файла show_success_message(network_folder_path, new_filename) else: raise Exception("File not found in the network folder.") else: raise Exception("File not found in local Temp folder.") else: raise Exception("Random number not found in PowerShell output.") else: raise Exception(f"Error executing PowerShell script: {result.stderr}") except Exception as e: show_error_message(str(e)) # Функция для запуска программы с интерфейсом def run_gui(): root = tk.Tk() root.title("Network File Creation Tool") # Окно для вывода лога log_window = scrolledtext.ScrolledText(root, wrap=tk.WORD, width=100, height=30) log_window.pack(padx=10, pady=10) # Запуск программы в отдельном потоке root.after(100, lambda: main_program(log_window)) root.mainloop() # Запуск программы run_gui()
Данный код сделан специально с отладкой, чтобы можно было сразу понять где появляется ошибка при подключении или другие ошибки.
Логика работы данной программы:
-
Подключение к сетевой папке:
-
Попытка отключения предыдущих подключений с помощью команды
net use /delete
. -
Попытка подключения к сетевой папке с использованием указанных учетных данных.
-
-
Создание временного PowerShell скрипта:
-
Генерация текущей даты и времени для именования файлов.
-
Создание временного каталога
C:\Temp
(если он не существует). -
Запись PowerShell скрипта во временный файл.
-
-
Выполнение PowerShell скрипта:
-
Сбор информации о сетевых папках и правах доступа.
-
Запись информации в файл.
-
-
Проверка и работа с файлом:
-
Поиск файла, созданного PowerShell скриптом в локальной папке
C:\Temp
. -
Копирование файла в сетевую папку.
-
Проверка содержимого файла и переименование файла на основе анализа прав доступа и наличия открытых папок.
-
-
Завершение и обработка ошибок:
-
Отображение сообщений об успехе или ошибках.
-
Для работы данной программы так же необходимо:
-
Создать учетную запись с правами администратора, которая будет вшита в данную программу ( нужно так же понимать, что для данной УЗ необходимо сделать ограничения на работу, кроме тех, которые выполняются в ходе работы программы).
-
Создать на сервере папку и расшарить доступ к ней для данной УЗ, Так же для данной папки необходимо сделать разрешения на чтение\запись.
Код программы без отладочных механизмов:
import subprocess import os import datetime import shutil import re import tkinter as tk from tkinter import messagebox, scrolledtext # Функция для обновления лога в текстовом окне def log_message(log_window, message): log_window.insert(tk.END, message + '\n') log_window.see(tk.END) log_window.update_idletasks() # Функция для отображения окна с сообщением об ошибке def show_error_message(error_message): messagebox.showerror("Error", error_message) # Функция для отображения окна с успешным сообщением def show_success_message(file_path, file_name): messagebox.showinfo("Success", f"File created successfully!\n\nFile name: {file_name}\nNetwork path: {file_path}") # Основной код программы #def main_program(log_window): def main_program(): try: # Путь к сетевой папке и учетные данные (замените на реальные данные в рабочей среде) network_folder_path = r'\\DESKTOP-N391HHM\Users\warde\Desktop\test' # Замените на реальный сетевой путь username = 'wardeathmor' # Замените на реальное имя пользователя домена password = '1351' # Замените на реальный пароль # Логирование подключения к сетевой папке #log_message(log_window, "Attempting to disconnect old network connection...") disconnect_command = f"net use {network_folder_path} /delete" subprocess.run(disconnect_command, shell=True) #log_message(log_window, "Attempting to connect to network folder...") net_use_command = f"net use {network_folder_path} /user:{username} {password}" result = subprocess.run(net_use_command, shell=True, capture_output=True, text=True, encoding='cp866') if result.returncode == 0: #log_message(log_window, f"Successfully connected to the network folder: {network_folder_path}") pass else: error_message = result.stderr raise Exception(f"Error connecting to the network folder: {error_message}") # Генерация текущей даты и времени для именования файлов current_datetime = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S') # Путь к временному скрипту PowerShell (используем C:\Temp для избежания ошибок доступа) temp_dir = "C:\\Temp" if not os.path.exists(temp_dir): os.makedirs(temp_dir) ps_script_path = os.path.join(temp_dir, 'temp_script.ps1') # Логирование создания скрипта #log_message(log_window, "Creating PowerShell script...") # Содержимое скрипта PowerShell для сбора данных ps_script_content = r''' # Установка культуры на английский для корректной работы $prevCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture [System.Threading.Thread]::CurrentThread.CurrentThread.CurrentCulture = 'en-US' # получаем IP машины $ipAddress = (Test-Connection -ComputerName $env:COMPUTERNAME -Count 1).IPV4Address.IPAddressToString # получаем имя компьютера $computerName = $env:COMPUTERNAME # генерируем рандомное число $randomNumber = Get-Random -Minimum 1 -Maximum 10000 # Создадим локальный файл в Temp $localFolderPath = "C:\\Temp" $fileName = "$($randomNumber)_$($ipAddress)_$($computerName)_$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss').txt" $localFilePath = "$localFolderPath\$fileName" # Отладочная информация Write-Host "File will be created at $localFilePath" # функция для получения прав доступа и замена на читабельные данные function Get-AccessRights { param($path) $accessRights = & icacls $path | Out-String $accessRights = $accessRights -replace '\(OI\)', '/Owner' # Owner object $accessRights = $accessRights -replace '\(CI\)', '/Container' # Container $accessRights = $accessRights -replace '\(F\)', '/Full access' # Full access $accessRights = $accessRights -replace '\(RX\)', '/Read and execute' # Read and execute return $accessRights } # Получаем список общих сетевых папок на текущем компьютере $networkFolders = Get-WmiObject -Class Win32_Share | Where-Object { $_.Path -ne $null -and $_.Type -eq 0 } # Если есть открытые сетевые папки, записываем информацию в файл if ($networkFolders) { $outputData = @() $outputData += "$ipAddress \ $computerName \ `"Number of open folders:`" $($networkFolders.Count)" $outputData += "Path to folders:" foreach ($folder in $networkFolders) { $folderPath = $folder.Path $outputData += "$folderPath" # Получаем информацию о правах доступа и сам процесс для записи в файл $accessRights = Get-AccessRights -path $folderPath $outputData += $accessRights } $outputData | Out-File -FilePath $localFilePath -Encoding UTF8 } else { # Если открытых сетевых папок нет, выводим сообщение об этом "IP Address: $ipAddress `nComputer Name: $computerName `nNetwork folders not found." | Out-File -FilePath $localFilePath -Encoding UTF8 } # Выводим сообщение об успешном выполнении задачи Write-Host "Information gathered and saved to $localFilePath." # Восстановить предыдущую настройку кодировки [System.Threading.Thread]::CurrentThread.CurrentCulture = $prevCulture ''' # Запись содержимого PowerShell скрипта во временный файл with open(ps_script_path, 'w', encoding='utf-8') as f: f.write(ps_script_content) #log_message(log_window, "PowerShell script created. Executing script...") # Команда для выполнения скрипта PowerShell ps_command = f'powershell.exe -ExecutionPolicy Bypass -File "{ps_script_path}"' result = subprocess.run(ps_command, shell=True, capture_output=True, text=True) if result.returncode == 0: #log_message(log_window, "PowerShell script executed successfully.") #log_message(log_window, result.stdout) # Поиск случайного числа в выводе PowerShell match = re.search(r'(\d+)', result.stdout) if match: random_number = int(match.group()) #log_message(log_window, f"Random number generated by PowerShell: {random_number}") # Проверяем, создался ли файл локально #log_message(log_window, "Checking local Temp folder for the created file...") local_files = os.listdir(temp_dir) found_file = None for file_name in local_files: if file_name.startswith(str(random_number)): found_file = file_name break if found_file: local_file_path = os.path.join(temp_dir, found_file) #log_message(log_window, f"File created locally: {local_file_path}") # Копируем файл в сетевую папку #log_message(log_window, f"Copying file to network folder: {network_folder_path}") shutil.copy(local_file_path, network_folder_path) # Проверка файла в сетевой папке #log_message(log_window, "Searching for the file in the network folder...") files_in_network_folder = os.listdir(network_folder_path) network_found_file = None for file_name in files_in_network_folder: if file_name.startswith(str(random_number)): network_found_file = file_name break if network_found_file: full_file_path = os.path.join(network_folder_path, network_found_file) #log_message(log_window, f"Found file: {full_file_path}") # Чтение файла и анализ содержимого with open(full_file_path, 'r', encoding='utf-8') as output_file: output_data = output_file.read() # Лог содержимого файла для отладки #log_message(log_window, f"File content:\n{output_data}") # Установка префикса в зависимости от содержимого файла new_prefix = "Empty_" # По умолчанию "Empty_" if "Number of open folders:" in output_data: open_folders_count = int(re.search(r'\d+', output_data).group()) #log_message(log_window, f"Open folders count: {open_folders_count}") if open_folders_count > 0: # Проверяем наличие прав, связанных с "Everyone", "Все" или "All" if re.search(r'(Everyone|Все|All)([:/\\])', output_data): new_prefix = "Нарушение_" #log_message(log_window, "Violation detected based on folder permissions.") else: new_prefix = "OpenShareFind_" #log_message(log_window, "Open folders found, but no violations.") else: #log_message(log_window, "No open folders found, setting prefix to 'Empty_'.") pass # Переименование файла new_filename = f"{new_prefix}{network_found_file}" new_file_path = os.path.join(network_folder_path, new_filename) os.rename(full_file_path, new_file_path) #log_message(log_window, f"File renamed to: {new_file_path}") # Выводим сообщение об успешном создании файла #show_success_message(network_folder_path, new_filename) else: raise Exception("File not found in the network folder.") else: raise Exception("File not found in local Temp folder.") else: raise Exception("Random number not found in PowerShell output.") else: raise Exception(f"Error executing PowerShell script: {result.stderr}") except Exception as e: show_error_message(str(e)) # Функция для запуска программы с интерфейсом #def run_gui(): #root = tk.Tk() #root.title("Network File Creation Tool") # Окно для вывода лога #log_window = scrolledtext.ScrolledText(root, wrap=tk.WORD, width=100, height=30) #log_window.pack(padx=10, pady=10) # Запуск программы в отдельном потоке #root.after(100, lambda: main_program(log_window)) #root.after(100, lambda: main_program()) #root.mainloop() # Запуск программы #run_gui() main_program()
Логика дополнительного усложнения
Зачем просматривать каждый файл с информацией, когда можно этого не делать? Именно поэтому я внедрил механизм переименовки файлов в зависимости от их содержимого. Это все регулируется в данном блоке и при желании можно отредактировать его:
# Установка префикса в зависимости от содержимого файла new_prefix = "Empty_" # По умолчанию "Empty_" if "Number of open folders:" in output_data: open_folders_count = int(re.search(r'\d+', output_data).group()) #log_message(log_window, f"Open folders count: {open_folders_count}") if open_folders_count > 0: # Проверяем наличие прав, связанных с "Everyone", "Все" или "All" if re.search(r'(Everyone|Все|All)([:/\\])', output_data): new_prefix = "Нарушение_" #log_message(log_window, "Violation detected based on folder permissions.") else: new_prefix = "OpenShareFind_" #log_message(log_window, "Open folders found, but no violations.")
В данном случае есть три варианта: Нарушение, открытая шара найдена и пусто. Нарушение ставится в случае, если найдена открытая шара, и есть доступ для всех.
К вопросу о том, зачем генерировать случайное число: Для функции переименовки файла программа должна найти файл, с которым надо работать, поэтому этот механизма как некий идентификатор для дальнейшей работы.
Для непосредственного удаленного запуска можно использовать как и встроенные средства в АД, так и Kaspersky Endpoint Security.
Заключение
Данная программа позволяет централизовано собирать данные по открытым сетевым ресурсам в компании. Да, есть программы куда лучше, куда функциональнее, но в данном случае захотелось попробовать сделать подобное с другим подходом, сделать централизованный сбор информации, вместо того, чтоб сидеть сканировать пул IP-адресов и ждать.
Надеюсь кому-нибудь пригодятся данные наработки в своей работе.
ссылка на оригинал статьи https://habr.com/ru/articles/841976/
Добавить комментарий