Проект Vulners за пару лет превратился из агрегатора данных о багах в довольно популярный инструмент пентестера. Это уже полноценный тулкит для поиска эксплоитов, API для Python, плагины для Burp Suite и Chrome, серверный агент. Vulners удобно встраивается в сценарии атак, на нем пишут сканеры уязвимостей. В этой статье мы посмотрим, что может предложить Vulners, а также научимся использовать всю мощь его Python API в повседневных задачах.
Если ты до этого не сталкивался с Vulners, почитай эту статью. Она даст базовое понимание, о чем вообще речь и зачем нужен Vulners.
Также посмотри этот материал о консольной тулзе для поиска сплоитов getsploit. Мы же займемся более низкоуровневым жонглированием API из Python.
Чем дальше развивался Vulners, тем больше мы получали писем с просьбами дать описание нашего API.
Конечно, начали мы с самого простого — сделали Swagger. Но вскоре заметили, что даже мы сами, используя свой API, изобретаем велосипеды по два раза в день. Каждый разработчик внутри проекта создал свой враппер, чтобы обращаться к Vulners из своих утилит. В конце концов нас самих достал такой зоопарк, и мы решили унифицировать работу с API хотя бы для Python. Вот так и появился публичный питоний API.
Для начала мы собрали статистику обращений к API и выяснили, что самые часто запрашиваемые функции — это:
Ну что же, какие вызовы надо реализовать в будущем враппере, стало понятно. Но ведь пользователи Python-библиотек совсем не хотят разбираться, что в какой параметр отправляется (для этого API-либа и нужна). Так что основная задача — сделать API удобным и простым. Время программировать!
Как и в любом враппере, начали мы с велосипеда — сделали простую обертку на базе requests для того, чтобы обращаться к JSON API.
Тут все просто: создается opener, который потом обрабатывает все запросы твоего приложения в рамках одной сессии. И сюда же вынесены основные URL, чтобы можно было потом добавлять новые пути для запросов.
class Vulners(object): def __init__(self, proxies=None): """ Set default URLs and create session object :param proxies: {} dict for proxy supporting. Example: {"https": "myproxy.com:3128"} """ # Default URL's for the Vulners API self.__vulners_urls = { 'search': "https://vulners.com/api/v3/search/lucene/", 'software': "https://vulners.com/api/v3/burp/software/", 'id': "https://vulners.com/api/v3/search/id/", 'suggest': "https://vulners.com/api/v3/search/suggest/", 'ai': "https://vulners.com/api/v3/ai/scoretext/", 'archive': "https://vulners.com/api/v3/archive/collection/" } # Default search parameters self.__search_size = 100 # Requests opener self.__opener = requests.session() self.__opener.headers = {'User-Agent': 'Vulners Python API %s' % api_version} if proxies is not None: if not isinstance(proxies, dict): raise TypeError("Proxies must be a dict type") self.__opener.proxies.update(proxies)
Вторым «подкапотным» помощником стали обертки для инициации GET- и POST-запросов. Они просто перекидывают переданный dict по URL из списка, определенного в init.
После получения результата функция adapt_response_content или возвращает dict, сформированный из JSON-ответа сервера, или отдает его в raw-виде в случае не JSON-ответа.
def __vulners_get_request(self, vulners_url_key, json_parameters): """ Tech wrapper for the unified :param vulners_url_key: Key for the self.vulners_urls dict :param json_parameters: {} dict for the API call :return: 'data' key from the response """ # Return result response = self.__opener.get(self.__vulners_urls[vulners_url_key], params=json_parameters) return self.__adapt_response_content(response) def __vulners_post_request(self, vulners_url_key, json_parameters): """ Tech wrapper for the unified :param vulners_url_key: Key for the self.vulners_urls dict :param json_parameters: {} dict for the API call :return: 'data' key from the response """ # Return result response = self.__opener.post(self.__vulners_urls[vulners_url_key], json=json_parameters) return self.__adapt_response_content(response)
Ты спросишь: разве это лучше, чем самому дергать питоний requests? Ведь с такими вызовами все еще не разобраться без знания API Vulners. И будешь прав. Поэтому мы сделали публичные вызовы-обертки, которые реализуют функциональность Vulners без лишних приседаний.
Вот, например, как выглядит поиск уязвимостей по CPE-строке. И пользователю абсолютно не нужно знать, что и куда потом раскладывается в запросе и в каком виде надо подавать параметры.
def cpeVulnerabilities(self, cpeString): """ Find software vulnerabilities using CPE string. See CPE references at https://cpe.mitre.org/specification/ :param cpe: CPE software string, see https://cpe.mitre.org/specification/ :return: {merged by family dict} """ dataDocs = {} if len(cpeString.split(":")) <= 4: raise ValueError("Malformed CPE string. Please, refer to the https://cpe.mitre.org/specification/. Awaiting like 'cpe:/a:cybozu:garoon:4.2.1'") version = cpeString.split(":")[4] results = self.__burpSoftware(cpeString, version, type='cpe') for element in results.get('search'): elementData = element.get('_source') dataDocs[elementData.get('bulletinFamily')] = dataDocs.get(elementData.get('bulletinFamily'), []) + [elementData] return dataDocs
Соответственно, организация кода внутри библиотеки получилась достаточно прозрачной.
Все методы, доступные без двойного подчеркивания, публичные и организуют запросы на внутренние реализации методов, которые формируют необходимые запросы к API и обеспечивают проверку типизации и контента.
Но ведь что там под капотом у нас, как это работает, обычно никому не интересно! Библиотека должна дать результат — удобные и практичные вызовы, которые решают поставленные задачи: ищут сплоиты и инфу об уязвимостях. Для того чтобы тебе не пришлось ставить ее руками с GitHub, мы поместили ее в репозиторий PyPI.
Ставим:
pip install -U vulners
И импортируем библиотеку в своем коде:
import vulners vulners_api = vulners.Vulners()
Вот и все, что тебе понадобится для использования Vulners API. Теперь можно попробовать решить твои задачи, используя этот инструмент.
Cтатьи из последних выпусков журнала можно покупать отдельно только через два месяца после публикации. Чтобы читать эту статью, необходимо купить подписку.
Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта, включая эту статью. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке
1 год4790 р. Экономия 1400 рублей! |
1 месяц515 р. 25-30 статей в месяц |
Уже подписан?
Читайте также
Последние новости