• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Написание сканера для поиска Reflected XSS

tabac

CPU register
Пользователь
Регистрация
30.09.2018
Сообщения
1 610
Решения
1
Реакции
3 332
В 2016 году я работал над проектом сканера веб-приложений, который очень похож на Burp Suite, - он проксировал HTTP-запросы от браузера и инструментов для автоматизации, вроде Selenium, и отправлял их в различные модули / плагины для сканирования на уязвимости. Большая часть приложения была написана на Python и имела модульную архитектуру, а интерфейс написан с использованием Django и Celery для асинхронных задач.

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

Перед началом программирования, нам стоит начать с теоритических основ и ответить на следующие вопросы:
  • Как это работает? - Создайте простую блок-схему того, как должен функционировать ваш сканер - начните с того, что он будет принимать на входе и что будет отдавать на выходе, а потом проработайте детали.
  • Какие технологии будут использованы? Выбор правильных технологий критически важен. Выбор технологий подразумевает выбор библиотек, с которыми вы будете работать, и предположения о том, как масштабировать проект в дальнейшем, отталкиваясь от потребностей. Особенно важную роль играет то, ощущаете ли вы себя в зоне комфорта, работая с тем или иным языком программирования. Я мог бы потратить 20 мучительных часов на написание кода на Golang и получить результат чуть лучше, чем если бы я писал свой проект на Python, который я бы завершил за 5 часов.
Условимся на том, что, как я и намекнул, для написания текущего проекта будет логичнее использовать Python. Предыдущие проекты я писал также на нем, - у меня имеется небольшой опыт. Итак, начнем с составления диаграммы - давайте поразмышляем о том, на чем основана сама XSS.


Как это работает
Весь сканер можно разделить на модули:
  • Парсер необработанных запросов
  • Первичный тестер
  • Контекстный анализатор
  • Генератор полезной нагрузки
  • Чекер наличия полезной нагрузки
c105d57f987795f2b3a8a.png

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

Для начала создадим виртуальное окружение для проекта:
Код:
pip3 install virtualenv
python3 -m virtualenv xss_env
Активируем его:
Код:
cd xss_env/Scripts && activate
Создадим новую папку вне папки virtualenv:
Код:
mkdir rxss

Парсер необработанных запросов
После настройки виртуальной среды, можно перейти к написанию кода. Первый модуль - это анализатор необработанных HTTP-запросов, который принимает входные данные из файла и преобразует их в объект запроса. Для его написания мы будем использовать библиотеку http:
Код:
from __future__ import absolute_import, unicode_literalsfrom http.server import BaseHTTPRequestHandler
from io import BytesIO
class HTTPRequest(BaseHTTPRequestHandler):
    def __init__(self, request_text):
        self.rfile = BytesIO(request_text)
        self.raw_requestline = self.rfile.readline()
        self.error_code = self.error_message = None
        self.parse_request()

    def send_error(self, code, message):
        self.error_code = code
        self.error_message = message
Приведенный выше класс берет необработанную HTTP-строку и преобразует ее в объект запроса.
Код:
POST /search.php?test=query HTTP/1.1
Host: testphp.vulnweb.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
Origin: http://testphp.vulnweb.com
Connection: close
Referer: http://testphp.vulnweb.com/search.php?test=query
Upgrade-Insecure-Requests: 1searchFor=asdas&goButton=go
Теперь сохраним его в request.txt
Код:
from __future__ import absolute_import, unicode_literals

from http.server import BaseHTTPRequestHandler
from io import BytesIO


class HTTPRequest(BaseHTTPRequestHandler):
    def __init__(self, request_text):
        self.rfile = BytesIO(request_text)
        self.raw_requestline = self.rfile.readline()
        self.error_code = self.error_message = None
        self.parse_request()

    def send_error(self, code, message):
        self.error_code = code
        self.error_message = message


with open("requests.txt", "rb") as f:
    request = HTTPRequest(f.read())
    if not request.error_code:
        print(request.command)  # отображает метод запроса
        print(request.path)  # отображаем путь
        print(request.headers.keys())  # отображаем заголовки запроса
        print(request.headers['host'])  # prints requests host
        content_len = int(request.headers.get('Content-Length'))
        print(request.rfile.read(content_len))  # prints request body
Попробуйте сами сохранить вышеуказанный код в файл request_parser.py и выполните его, используя следующую команду:
Код:
python3 request_parser.pyPOST

/search.php?test=query
['Host', 'User-Agent', 'Accept', 'Accept-Language', 'Accept-Encoding', 'Content-Type', 'Content-Length', 'Origin', 'Connection', 'Referer', 'Upgrade-Insecure-Requests']
testphp.vulnweb.com
b'searchFor=asdas&goButton=go'
Мы успешно взяли HTTP-запрос из файла и повторно отправили его в неизменном виде. Теперь нужно сделать еще одну вещь: преобразовать его тело и отправить параметры в DICT, чтобы получить возможность добавлять свою собственную полезную нагрузку и анализировать результаты.

Исходный код можно так же скопировать отсюда
Код:
from __future__ import absolute_import, unicode_literals

from http.server import BaseHTTPRequestHandler
from io import BytesIO
from urllib import parse


class Request:
    def __init__(self):
        self.headers = None
        self.params = None
        self.data = None
        self.path = None

    def replace(self, string, payload):

        for k, v in self.headers.items():
            k.replace(string, payload)
            v.replace(string, payload)
   
        for k, v in self.params.items():
            self.params[k] = self.params[k].replace(string, payload)
        for k, v in self.data.items():
            self.data[k] = self.data[k].replace(string, payload)
            print(self.data)


class RequestParser(object):

    def __init__(self, request_text):
        self.request = Request()
        try:
            self.raw_request = HTTPRequest(request_text)
            if self.raw_request.error_code:
                raise Exception("failed parsing request")
            self.request.method = self.raw_request.command
            self.request.path = self.construct_path()
            self.request.headers = self.raw_request.headers
            self.request.data = self.convert(self.construct_data())
            self.request.params = self.convert(self.construct_params())
        except Exception as e:
            raise e

    def convert(self, data):
        if isinstance(data, bytes):
            return data.decode()
        if isinstance(data, (str, int)):
            return str(data)
        if isinstance(data, dict):
            return dict(map(self.convert, data.items()))
        if isinstance(data, tuple):
            return tuple(map(self.convert, data))
        if isinstance(data, list):
            return list(map(self.convert, data))
        if isinstance(data, set):
           return set(map(self.convert, data))

    def construct_path(self):
        return parse.urlsplit(self.raw_request.path).path

    def construct_data(self):
    return dict(parse.parse_qsl(self.raw_request.rfile.read(int(self.raw_request.headers.get('content-length')))))

    def construct_params(self):
    return dict(parse.parse_qsl(parse.urlsplit(self.raw_request.path).query))



with open("requests.txt", "rb") as f:
    parser = RequestParser(f.read())
    print(parser.request.method) # prints method
    print(parser.request.path) # prints request.path
    print(parser.request.headers) # prints requests headers
    print(parser.request.data) # prints requests body
    print(parser.request.params) # prints requests params

Первичный тестер
Итак, мы закончили первый модуль, написав скрипт, который будет брать сырой запрос из текстового файла и отправлять его в неизменном виде, отображая ответ. Теперь нам нужно научиться редактировать запрос, вставляя XSS-локатор в параметры запроса и тело сообщения и проверять, будет ли он отображаться в ответе. Для этого мы будем использовать пакет requests в Python.
Код:
pip3 install requests
Создайте новый файл с именем create_insertions.py и вставьте в него следующий код:
Код:
import copy

class GetInsertionPoints:

    def __init__(self, request):
        self.request = request
        self.requests = []
        self.params(append=True)
        self.body(append=True)

    def params(self, append: bool = False) -> None:
        if self.request.params:
            for q in self.request.params:
                request = copy.deepcopy(self.request)
                if append:
                    request.params[q] = str(request.params[q])+" teyascan"
                else:
                    request.params[q] = "teyascan"
                    request.insertion = q
                    request.iplace = 'params'
                    self.requests.append(request)

    def body(self, append: bool = False) -> None:
        if self.request.data:
            for q in self.request.data:
                request = copy.deepcopy(self.request)
                if append:
                    request.data[q] = str(request.data[q])+" teyascan"
                else:
                    request.data[q] = "teyascan"
                    request.insertion = q
                    request.iplace = 'body'
                    self.requests.append(request)


with open("requests.txt", "rb") as f:
    parser = RequestParser(f.read())
    print(parser.request.method) # prints method
    print(parser.request.path) # prints request.path
    print(parser.request.headers) # prints requests headers
    print(parser.request.data) # prints requests body
    print(parser.request.params) # prints requests params
    i_p = GetInsertionPoints(parser.request)
    print(i_p.requests)
Данный код анализирует параметры и тело запроса для создания списка объектов запросов с флагами в качестве полезной нагрузки.
Код:
python3 create_insertions.py [<__main__.HTTPRequest object at 0x0000021E8AD34A30>, <__main__.HTTPRequest object at 0x0000021E8AD34B80>, <__main__.HTTPRequest object at 0x0000021E8AD34BB0>]
Теперь давайте отправим каждый запрос и проверим, отображаются ли наши параметры в ответе.
Код:
import requestsdef send_request(request, scheme):
    url = "{}://{}{}".format(scheme, request.headers.get("host"), request.path)
    req = requests.Request(request.method, url, params=request.params, data=request.data, headers=request.headers)
    r = req.prepare()
    s = requests.Session()
    response = s.send(r, allow_redirects=False, verify=False)
    return responsewith open("requests.txt", "rb") as f:
    parser = RequestParser(f.read())
    i_p = GetInsertionPoints(parser.request)

    for request in i_p.requests:
        response = send_request(request, "http")
        if "teyascan" in response.text:
            print("probe reflection found in "+request.insertion)
Итак, если наш пэйлоад обнаружится в ответе, то вы увидите что - то вроде этого:
Код:
python .\test.py
probe reflection found in searchFor
На этом все. Встретимся в следующей серии.

Оригинал
Перевод @Moody канал @ Cybred
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Сейчас у скрипт-кидди популярны штуки типа
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх