Здарова, щеглы, сегодня мы своими руками будем писать скрипт на Python. Нам понадобятся: интерпретатор
Ни одного комментария, какие-то import, непонятные аргументы командной строки и еще эти две последние строчки... Но будьте спокойны, все нормально, это я вам как мастер программирования на Python с 30-минутным стажем говорю. Тем более, как известно, Google не врет, а
Так что же мы все-таки сделали в вышеописанном фрагменте кода? Мы подключили модули для работы с аргументами коммандной строки, модули для логирования (
После этого мы, в соответствии с
Делаем перерыв и выпиваем глоток освежающей минеральной воды.
Поехали дальше.
В коде появился минимум комментариев. Это прогресс. Надо войти в кураж (не зря мы заготовили прамирацетам) и дописать одну единственную функцию, которая будет осуществлять, непосредственно, проверку. Ее имя уже упомянуто в коде выше: check_ip.
30 минут спустя
Хорошо-то как. Не зря я говорил, что понадобится час времени. Продолжим.
В общем-то весь наш скрипт готов. Приступаем к тестированию.
Неожиданно узнаем, что у блога есть альтернативный IP-адрес. И действительно:
Однако:
Какой-то левый хост обрабатывает запросы. Почему? Потому что это прокси, который реагирует на содержимое заголовка Host. В общем скрипт готов, по крайней мере альфа-версия скрипта. Если вам понравилось - подписывайтесь, ставьте лайки, шлите pull-реквесты на
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
под "какая-там-у-вас-ОС", текстовый редактор с
Код:
import argparse
import logging
import coloredlogs
import ssl
import concurrent.futures
import urllib.request
from netaddr import IPNetwork
from collections import deque
VERSION = 0.1
def setup_args():
parser = argparse.ArgumentParser(
description = 'Domain Seeker v' + str(VERSION) + ' (c) Kaimi (kaimi.io)',
epilog = '',
formatter_class = argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument(
'-d',
'--domains',
help = 'Domain list to discover',
type = str,
required = True
)
parser.add_argument(
'-i',
'--ips',
help = 'IP list (ranges) to scan for domains',
type = str,
required = True
)
parser.add_argument(
'--https',
help = 'Check HTTPS in addition to HTTP',
action = 'store_true'
)
parser.add_argument(
'--codes',
help = 'HTTP-codes list that will be considered as good',
type = str,
default = '200,301,302,401,403'
)
parser.add_argument(
'--separator',
help = 'IP/Domain/HTTP-codes list separator',
type = str,
default = ','
)
parser.add_argument(
'--include',
help = 'Show results containing provided string',
type = str
)
parser.add_argument(
'--exclude',
help = 'Hide results containing provided string',
type = str
)
parser.add_argument(
'--agent',
help = 'User-Agent value for HTTP-requests',
type = str,
default = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1'
)
parser.add_argument(
'--http-port',
help = 'HTTP port',
type = int,
default = 80
)
parser.add_argument(
'--https-port',
help = 'HTTPS port',
type = int,
default = 443
)
parser.add_argument(
'--timeout',
help = 'HTTP-request timeout',
type = int,
default = 5
)
parser.add_argument(
'--threads',
help = 'Number of threads',
type = int,
default = 2
)
args = parser.parse_args()
return args
if __name__ == '__main__':
main()
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
- это вообще неоспоримая истина.Так что же мы все-таки сделали в вышеописанном фрагменте кода? Мы подключили модули для работы с аргументами коммандной строки, модули для логирования (
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
между прочим!), модуль для работы с SSL (для одной мелочи, связанной с HTTPS-запросами), модуль для создания пула потоков, и, наконец, модули для совершения HTTP-запросов, работы с IP-адресами и двухсторонней очередью (по поводу различных типов импорта можно почитать Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
).После этого мы, в соответствии с
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
, создали вспомогательную функцию, которая будет обрабатывать аргументы, переданные скрипту при запуске из командной строки. Как видите, в скрипте будет предусмотрена работа со списком доменов/IP-диапазонов, а также возможность фильтрации результатов по ключевым словам и по Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
и еще пара мелочей, как, например, смена Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
и опциональная проверка HTTPS-версии искомого ресурса. Последние две строки в основном используются для разделения кода, который будет выполнен при запуске самого скрипта и при импортировании в другой скрипт. В общем тут все сложно, все так пишут. Мы тоже так будем писать. Можно было бы немного модифицировать этот код, например, добавив возврат разных статусов системе в зависимости от того, как отработала функция main, добавить argv в качестве аргумента, и так далее, но мы изучаем Python только 10 минут и ленимся вчитываться в документацию.Делаем перерыв и выпиваем глоток освежающей минеральной воды.
Поехали дальше.
Код:
def main():
# Обрабатываем аргументы и инициализируем логирование
# с блекджеком и цветными записями
args = setup_args()
coloredlogs.install()
# Сообщаем бесполезную информацию, а также запускаем цикл проверки
logging.info("Starting...")
try:
check_loop(args)
except Exception as exception:
logging.error(exception)
logging.info("Finished")
def check_loop(args):
# Создаем пул потоков, еще немного обрабатываем переданные аргументы
# и формируем очередь заданий
with concurrent.futures.ThreadPoolExecutor(max_workers = args.threads) as pool:
domains = args.domains.split(args.separator)
ips = args.ips.split(args.separator)
codes = args.codes.split(args.separator)
tasks = deque([])
for entry in ips:
ip_list = IPNetwork(entry)
for ip in ip_list:
for domain in domains:
tasks.append(
pool.submit(
check_ip, domain, ip, args, codes
)
)
# Обрабатываем результаты и выводим найденные пары домен-IP
for task in concurrent.futures.as_completed(tasks):
try:
result = task.result()
except Exception as exception:
logging.error(exception)
else:
if result != None:
data = str(result[0])
if(
( args.exclude == None and args.include == None )
or
( args.exclude and args.exclude not in data )
or
( args.include and args.include in data )
):
logging.critical("[+] " + args.separator.join(result[1:]))
30 минут спустя
Хорошо-то как. Не зря я говорил, что понадобится час времени. Продолжим.
Код:
def check_ip(domain, ip, args, codes):
# Преобразуем IP из числа в строку
# Магическая code-flow переменная для совершения двух проверок
# И бесполезное логирование
ip = str(ip)
check_https = False
logging.info("Checking " + args.separator.join([ip, domain]))
while True:
# Задаем порт и схему для запроса в зависимости от магической переменной
schema = 'https://' if check_https else 'http://';
port = str(args.https_port) if check_https else str(args.http_port)
request = urllib.request.Request(
schema + ip + ':' + port + '/',
data = None,
headers = {
'User-Agent': args.agent,
'Host': domain
}
)
# Совершаем запрос, и если получаем удовлетворительный код состояни HTTP,
# то возвращаем содержимое ответа сервера, а также домен и IP
try:
response = urllib.request.urlopen(
request,
data = None,
timeout = args.timeout,
context = ssl._create_unverified_context()
)
data = response.read()
return [data, ip, domain]
except urllib.error.HTTPError as exception:
if str(exception.code) in codes:
data = exception.fp.read()
return [data, ip, domain]
except Exception:
pass
if args.https and not check_https:
check_https = True
continue
return None
Неожиданно узнаем, что у блога есть альтернативный IP-адрес. И действительно:
Код:
curl -i 'http://188.226.181.47/' --header 'Host: kaimi.io'
Код:
HTTP/1.1 301 Moved Permanently
Server: nginx/1.4.6 (Ubuntu)
Date: Sun, 02 Oct 2016 13:52:43 GMT
Content-Type: text/html
Content-Length: 193
Connection: keep-alive
Location: https://kaimi.io/
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.4.6 (Ubuntu)</center>
</body>
</html>
Код:
curl -i 'https://188.226.181.47/' --header 'Host: kaimi.io'
Код:
curl: (51) SSL: certificate subject name (*.polygraph.io) does not match target host name '188.226.181.47'
Пожалуйста,
Вход
или
Регистрация
для просмотра содержимого URL-адресов!
.