Как я получил пожизненный запас чесночной пиццы с помощью Python и Selenium

Автор оригинала: Sanjeet Chatterjee
  • Перевод

История голодного студента с пытливым умом


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


Papa John’s (с) Заголовок письма с опросом

Бесплатная еда! Мне определённо нужно было пройти этот опрос…

Опрос



Papa John’s (с) Завершающая страница опроса

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

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

Я мог делать это вечно! Но это не самое эффективное использование моего времени, так что давайте применим немного магии Selenium.

Бот


Selenium Webdriver — это фреймворк автоматизации действий в браузере, который в основном используется для тестирования. Я выбрал Python как язык программирования и решил попробовать Selenium для создания опросного бота.

Установка


Для начала запускаем pip install selenium и pip install fake_useragent. Что такое user-agent? Документация MDN определяют его следующим образом:

Заголовок запроса User-Agent — это строка, позволяющая серверам и сетевым узлам идентифицировать приложение, операционную систему, поставщика и/или версию агента, который отправил запрос.

Мне показалось хорошей идеей рандомизировать этот заголовок, чтобы обойти любой возможный механизм фильтрации/блокировки — вот где появляется fake_useragent.

Кроме того, нужно было скачать ChromeDriver, чтобы взаимодействовать с браузером Chrome.

Код


Базовая настройка Selenium выглядит следующим образом (инициализация с помощью случайного user-agent):

from fake_useragent import UserAgent
from selenium import webdriver
from random import randrange
import time

ua = UserAgent(verify_ssl=False)
user_agent = ua.random

print("USER AGENT: " + user_agent)

chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("user-agent=" + user_agent)
driver = webdriver.Chrome(chrome_options=chrome_options)

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

id = randrange(100000000000000)
url = "https://www.papajohnsfeedback.com/GBR?GUID=" + str(id)

print(url)

driver.get(url)
time.sleep(1)

driver.find_element_by_id('NextButton').click()
time.sleep(1)

driver.find_element_by_id('NextButton').click()
time.sleep(1)

driver.find_element_by_xpath("//div[contains(@class, 'Opt1')]/span").click()
time.sleep(1)

Часть скрипта бота

XPath


XPath — это язык запросов для выбора узлов из документа HTML или XML. Для каждого из вопросов опроса я использовал инструмент тестирования XPath реального времени для выбора правильных узлов, на которые нажимает бот. Конечно, я ставил Papa John’s оценки 5 звёзд по всем пунктам.


Тестирование XPath

Всегда пожалуйста!


И, наконец, получаем код валидации.


Papa John's (с) Завершающая страница опроса

driver.find_element_by_id('NextButton').click()
time.sleep(1)

code = driver.find_element_by_class_name('ValCode').get_attribute("innerHTML").split(' ')[2]

Извлечение кода валидации

Празднование


Через полчаса программирования python-бот был готов. Вот gist с кодом, а вот он в действии:


Спасибо, Papa John’s


Я ввёл все сгенерированные коды при расчёте в корзине Papa John's. И вот он — потенциально бесконечный запас чесночных пицца-палочек.

Бесплатная пицца для меня и отличные отзывы для отдела маркетинга Papa John's. Кажется, беспроигрышная ситуация! Для этого бота мне даже пришлось изучить XPath и отточить свои навыки Selenium.



Конечно, я ничего не заказал. Как честный человек, я уведомил Papa John's об уязвимости вместе с видеодоказательством. На момент написания статьи они не ответили. Но опрос больше не работает, так что я думаю, что они получили сообщение.

Кстати, я мог бы запрограммировать заказ 1000 чесночных пицц из каждого магазина Papa John's по всей Великобритании и в одиночку повергнуть сеть Papa John's в безумие. Можете себе представить, какой возник бы хаос?

Возможно, я сделал это в параллельной вселенной.

Комментарии 29

    +20
    Вот он, незамутненный, рафинированный пример программиста, кодящего за еду.
      0

      Даже не за еду: он же не сделал заказ.

        +18
        Тут уровень выше — кодить за мысль о еде.
          0
          Следующая ступень:
          мыслить о коде за мысль о еде…
            +1
            Это уже комментаторы.
      0
      Лучшее, что я прочитал за сегодня
        +7

        Баг в том, что они разрешают делать заказ, когда у тебя в корзине только "подарочная" еда. Обычно такие подарки делают в дополнение к следующему заказу. Тогда как бы им все равно, генерируй коды сколько угодно, если это все равно стимулирует их продажи.

          0

          Додо позволяет вводить промо коды только с подарочной пиццой.

            0
            Баг в том, что они не отслеживают IP и не добавили хотя бы минимальной капчи.
              +1
              Не решит проблему, просто сделает автоматизацию атаки сложнее.
            +1
            Было дело, заказал несколько раз по «вечному» промо-коду stickermule несколько наклеек, используя промо-код из ссылки после первой покупки. Его можно было применять многократно, суммируя с другими акциями, за счёт чего сумма заказа иногда становилась отрицательной.
            image
            После четвёртого раза мне стало стыдно и я отписался в техподдержку. Мне тоже ничего не ответили.
            Но потом я заметил, что больше оно так не работает.

            А вообще, будьте честными.
              0

              Как минимум у Сбера по моему оплатить заказ если он отрицательныц не выйдет, валидация не даст, это если картой платить такой заказ

                0
                В случае оплаты отрицательного заказа, было так же, как и с нулевым — оплачивать не требовалось и заказ сразу переходил в обработку. Полагаю, у stickermule есть легальные промо-коды, позволяющие оплатить всю сумму.
              +1
              я мог бы запрограммировать заказ 1000 чесночных пицц из каждого магазина Papa John's по всей Великобритании
              Возможно, отменили бы заказ и вернули деньги
                0
                Извините, а где тут уязвимость? Я так понял тут просто обычная автоматизация прохождения опроса. Причём настолько элементарная, что обычно даётся в качестве тестового задания тестировщикам.
                  –1
                  Нет простейших способов отфильтровать уже прошедших — капча, валидация по IP, т.п.
                    0
                    Все эти способы только поднимают планку автоматизации чуть выше. Я вообще не могу придумать ни одного адекватного способа, который был делал автоматизацию невозможной.
                    Впрочем соглашусь, даже капча отсекла бы любопытных студентов, а люди, которые могут это обойти стали бы заморачиваться только искусства ради. А это маловероятно для временной акции.
                    Но и разработчиков/дизайнеров/менеджеров (или кто там это всё делал и придумывал?) тоже понимаю. Для создания таких ограничений тоже нужно больше компетенций и/или трудозатрат, что в конечном итоге поднимают стоимость и приводит к невозможности проведений этих акций. Ну и возможно они научились на этой истории чему-нибудь.
                      0
                      Если учесть, что на стороне сервера сидит человек и вручную эти заказы собирает, то это вообще не уязвимость. Чувака бы просто нафик послали со всеми его промокодами.
                  0
                  В работе с Вебом (Selenium) вместо time.sleep(1) рекомендую использовать wait.until(EC.visibility_of_element_located((By.ID, «Блаблавиджет»)))
                    0
                    Плюсую, всегда коробит от этих слипов в коде, на что они рассчитывают? Что каждая строка кода будет идеально вписываться в этот слип?
                      0

                      так можно зависнуть навсегда, не всё так просто с wait until

                        0
                        если изучать selenium и применять его то сначала ты прописываешь сколько максимально ждать загрузки элемента, например 50
                        wait = WebDriverWait(driver, 50)
                        wait.until(EC.visibility_of_element_located((By.ID, «login-logo»)))
                      0

                      По поводу использования задержки для имитации кликов: автор установил значение в 1 секунду, хотя можно было бы рандомизировать это значение.
                      Кстати, если вы планируете использовать задержку для загрузки элементов или ajax, то в selenium есть специальная функция для этого. Привожу пример в с#:
                      Browser.Manage().Timeouts().ImplicityWait( TimeSpan.FromSeconds(10) );
                      В таком случае браузер будет ждать 10 секунд на загрузку элемента, прежде чем выдаст исключение. Это значение можно менять на ходу и это очень удобно.

                        +1
                        Хакер в столовой пиццерии. Помню раньше на сайте папа джонс форма отправки заказа иногда становилась неактивной при выборе определенных сочетаний данных. Приходилось ручками менять код формы, что кнопка отправки становилась доступной.
                          0
                          Странно, что GUID никак не был привязан к кукам. Можно было хранить состояние опроса на сервере, увязав его с сессией. Очередной IDOR.
                            0
                            Куки почистить можно. Надо было просто хранить в БД список разосланных гуидов и с ним сверять. Видимо гуид в базу попадал после прохождения опроса уже.
                            0
                            В пападжонсе платежная инфа (номер карты, cvc) идет через сайт пападжонса, а не банка, вот где фейспалм) а они какие-то скидочные коды подбирают. Я бы вообще не советовал у них ничего заказывать.
                              0
                              Две чесночные пиццы этому человеку!
                                0

                                Как вариант, можно было заглянуть в консоль разработчика, посмотреть какой API запрос шлёт фронт беку после заполнения опроса и не заморачиваться с Selenium.

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

                                Самое читаемое