Введение
В процессе работы в Red Team часто приходится проводить разведку облачных сервисов. Для этого я решил написать инструмент AWScanner, который автоматически собирает и анализирует диапазоны IP Amazon Web Services с помощью Masscan и TLS-сканирования. В этой статье я разберу ход своих мыслей при создании сканера, объясню каждую функцию, чтобы даже новичок понял, как это работает, чтобы он смог создавать в будущем свои инструменты по ИБ.
Советую ознакомиться ещё с мой статьёй в которой разбирается сканирование облачных сервисов Статья - Жёстко сканируем и хакаем облачные сервисы Amazon
Это сделало инструмент простым в использовании, особенно для тех, кто только начинает заниматься пентестингом.
Это простой класс для цветного вывода в терминале. Например, зелёный цвет (GREEN) используется для успешных сообщений, а красный (RED) - для ошибок. Это делает интерфейс более читаемым.
Функция print_available_regions
Эта функция просто выводит список регионов с зелёным тире перед каждым, чтобы было красиво.
Это помогает пользователю увидеть, какие регионы доступны.
Функция run_command
Эта функция выполняет команды в оболочке и обрабатывает ошибки. Если команда не сработает, она выведет красное сообщение и завершит скрипт.
Функция is_file_empty
Эта функция проверяет, существует ли файл и пустой ли он. Например, если после сканирования файл с открытыми IP пуст, мы не будем пытаться его обрабатывать.
Функция scan_region
Загружаем IP-диапазоны для региона с помощью wget и jq, сохраняем в файл.
wget загружает JSON-файл с публичными IP-диапазонами AWS.
Запускаем masscan для сканирования этих IP на порт 443 с заданной скоростью и выводим, что результат сохранён в файл
Извлекаем IP с открытым портом 443 с помощью awk.
Тут собираем данные о сертификатах (например, Common Name) для найденных IP.
Функция convert_to_csv
Эта функция преобразует JSON в CSV, но я добавил несколько вариантов команд jq, потому что структура JSON может отличаться на разных регионах. Например, иногда поле называется
Основная функция main
Это главная функция, которая запускает интерактивный цикл. Сначала проверяет, запущен ли скрипт от root, затем ждёт ввода команд. Если пользователь введёт scan eu-central-1 10000, скрипт вызовет scan_region с этими параметрами.
Разбивает строку по пробелам и присваивает значения переменным:
_ - игнорирует первый элемент ("scan"), так как он не нужен.
region - сохраняет название региона (например, us-west-1).
rate - сохраняет скорость сканирования (например, 10000).
Функция print_help
Эта функция выводит помощь, показывая доступные команды.
Разработка AWScanner была интересным процессом, который помог мне глубже понять Python, т.к я не программист.
Этот скрипт можно улушать! Как? Предлагайте идеи или форкайте проект
Исходники и дополнения:
В процессе работы в Red Team часто приходится проводить разведку облачных сервисов. Для этого я решил написать инструмент AWScanner, который автоматически собирает и анализирует диапазоны IP Amazon Web Services с помощью Masscan и TLS-сканирования. В этой статье я разберу ход своих мыслей при создании сканера, объясню каждую функцию, чтобы даже новичок понял, как это работает, чтобы он смог создавать в будущем свои инструменты по ИБ.
Советую ознакомиться ещё с мой статьёй в которой разбирается сканирование облачных сервисов Статья - Жёстко сканируем и хакаем облачные сервисы Amazon
Определение целей
Когда я задумался о написании сканера, у меня были следующие требования:- Скорость - облака масштабируемы, IP-диапазоны огромны, поэтому необходимо использовать быстрые инструменты.
- Автоматизация - минимальное количество ручных действий.
- Информативность - важно не только найти открытые порты, но и извлечь полезную информацию, например, домены сертификатов.
Структура скрипта
Я сделал AWScanner в виде командной строки с интерактивным интерфейсом. Пользователь может вводить команды, такие какlist_regions
для просмотра доступных регионов, scan <region> <rate>
для запуска сканирования. Это сделало инструмент простым в использовании, особенно для тех, кто только начинает заниматься пентестингом.
Реализация функций
Теперь давайте разберём каждую функцию, чтобы понять, как она работает и почему я её добавил.Цветной вывод и удобство использования
Сначала добавил поддержку цветного вывода, чтобы пользователю было проще ориентироваться:
Python:
class Colors:
RED = '\033[91m'
GREEN = '\033[92m'
RESET = '\033[0m'
UNDERLINE = '\033[4m'
Список регионов
Отфильтровал в огромнос json файле при помощи команд для обработки строк, таких как jq, awk и прочее.
Python:
AVAILABLE_REGIONS = [
"af-south-1", "ap-east-1", "ap-east-2", ..., "us-west-2"
]
Функция print_available_regions
Python:
def print_available_regions():
print("Available regions:")
for region in AVAILABLE_REGIONS:
print(f" {Colors.GREEN}-{Colors.RESET} {region}")
Это помогает пользователю увидеть, какие регионы доступны.
Функция run_command
Python:
def run_command(command):
try:
subprocess.run(command, shell=True, check=True, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError as e:
print(f"{Colors.RED}[ERROR]{Colors.RESET} Command failed: {command}")
sys.exit(1)
check=True
- если команда завершится с кодом возврата != 0 (ошибка), Python вызовет исключение CalledProcessError.stderr=subprocess.DEVNUL
- перенаправляет стандартный поток ошибок (stderr) в /dev/null (ошибки не выводятся).Функция is_file_empty
Python:
def is_file_empty(file_path):
return not os.path.exists(file_path) or os.path.getsize(file_path) == 0
os.path.exists(file_path)
- Возвращает False, если файл не существуетos.path.getsize(file_path) == 0
- Если файл существует, проверяет его размер.Функция scan_region
Python:
def scan_region(region, rate):
ip_ranges_command = (
f"wget -qO- https://ip-ranges.amazonaws.com/ip-ranges.json | "
f"jq '.prefixes[] | select(.region == \"{region}\") | .ip_prefix' -r | "
f"sort -u > {region}-range.txt"
)
run_command(ip_ranges_command)
wget загружает JSON-файл с публичными IP-диапазонами AWS.
- jq фильтрует данные;
- prefixes[] - перебирает массив CIDR-блоков;
- select(.region == "region") - выбирает блоки, привязанные к указанному региону (например, eu-central-1);
- ip_prefix - извлекает значение поля (например, 52.93.244.0/24);
- -r - выводит результат в raw-формате (без кавычек).
- sort -u удаляет дубликаты и сохраняет уникальные значения в файл {region}-range.txt;
Python:
print(f"{Colors.GREEN}[+]{Colors.RESET} IP ranges for {region} saved to {region}-range.txt")
masscan_command = f"masscan -iL {region}-range.txt -oL {region}-range.masscan -p 443 --rate {rate}"
run_command(masscan_command)
print(f"{Colors.GREEN}[+]{Colors.RESET} Masscan results saved to {region}-range.masscan")
- -iL - указывает файл со списком целей (CIDR-блоков из {region}-range.txt);
- -oL - сохраняет результаты в файл {region}-range.masscan;
- -p 443 - сканирует только порт 443;
- --rate - задает скорость сканирования (пакетов/секунду).
Python:
tls_open_command = f"awk '/open/ {{print $4}}' {region}-range.masscan > {region}-range.tlsopen"
run_command(tls_open_command)
print(f"{Colors.GREEN}[+]{Colors.RESET} Open TLS IPs saved to {region}-range.tlsopen")
- awk парсит файл {region} - range.masscan;
- /open/ - ищет строки с состоянием open;
- print $4 - извлекает 4-е поле (IP-адрес);
- Результат сохраняется в {region}-range.tlsopen.
Python:
if not is_file_empty(f"{region}-range.tlsopen"):
tls_scan_command = f"cat {region}-range.tlsopen | tls-scan --port=443 -o {region}-range-tlsinfo.json"
run_command(tls_scan_command)
convert_to_csv(region)
else:
print(f"{Colors.RED}[WARNING]{Colors.RESET} TLS open IPs file is empty, skipping CSV conversion.")
- tls-scan подключается к каждому IP из {region}-range.tlsopen на порту 443;
- Извлекает информацию о TLS-сертификате (версия, алгоритмы, Common Name и др.);
- Сохраняет результат в JSON-файл {region}-range-tlsinfo.json.
Функция convert_to_csv
Python:
def convert_to_csv(region):
"""Convert TLS info JSON to CSV format for different JSON structures."""
json_file = f"{region}-range-tlsinfo.json"
csv_file = f"{region}-range-tlsinfo.csv"
if is_file_empty(json_file):
print(f"{Colors.RED}[WARNING]{Colors.RESET} TLS info file {json_file} is empty or missing, skipping CSV conversion.")
return
# Attempt to convert JSON to CSV using various jq commands
csv_commands = [
f"jq -r '.[] | {{ip, commonName: .cert.subject.commonName}} | [.ip, .commonName] | @csv' {json_file} > {csv_file}",
f"jq -r '.[] | {{ip, commonName: .cert.subject.commonName}} | [.ip, .commonName] | @csv' {json_file} > {csv_file}",
f"jq -r '[.ip, .cert.subject.commonName] | @csv' {json_file} > {csv_file}",
f"jq -r '[.ip, .commonName] | @csv' {json_file} > {csv_file}"
]
for command in csv_commands:
try:
run_command(command)
print(f"{Colors.GREEN}[+]{Colors.RESET} TLS info converted to CSV and saved to {csv_file}")
return
except:
continue # Try the next command if the current one fails
print(f"{Colors.RED}[ERROR]{Colors.RESET} Failed to convert {json_file} to CSV.")
.cert.subject.commonName
, а иногда просто .commonName
. Поэтому я сделал массив csv_commands, я его сделал, потому что одной командой не получилось спарсить и перевести все запросы в табличку (да, я тестировал на большинстве регионах, чтобы отследить ошибки в моей программе).Основная функция main
Python:
def main():
if os.geteuid() != 0:
print(f"{Colors.RED}[ERROR]{Colors.RESET} You must run this script as the root user.")
sys.exit(1)
while True:
print(f"{Colors.UNDERLINE}awscan{Colors.RESET} > ", end='')
user_input = input()
if user_input == "help":
print_help()
elif user_input == "list_regions":
print_available_regions()
elif user_input.startswith("scan"):
_, region, rate = user_input.split()
if region not in AVAILABLE_REGIONS:
print(f"{Colors.RED}[ERROR]{Colors.RESET} You must specify a correct region.")
continue
scan_region(region, int(rate))
elif user_input == "exit":
print("See you later!")
exit(0)
Python:
_, region, rate = user_input.split()
_ - игнорирует первый элемент ("scan"), так как он не нужен.
region - сохраняет название региона (например, us-west-1).
rate - сохраняет скорость сканирования (например, 10000).
Функция print_help
Python:
def print_help():
"""Display help information for commands."""
print(f"""Description: AWS IP range scanner with Masscan and TLS analysis made by q1ncite
Core commands
=============
Command Description
------------- -------------
list_regions Show available regions for scanning.
scan <region> <rate> Start scan of region (e.g., {Colors.RED}scan eu-central-1 10000{Colors.RESET}).
""")
Таблица: Основные функции и их назначение
Функция | Назначение |
---|---|
print_available_regions | Выводит список доступных регионов AWS. |
run_command | Выполняет команды в оболочке и обрабатывает ошибки. |
is_file_empty | Проверяет, пустой ли файл или не существует. |
scan_region | Выполняет сканирование региона: загрузка IP, masscan, анализ TLS. |
convert_to_csv | Преобразует JSON с информацией TLS в CSV, учитывая разные структуры. |
main | Запускает интерактивный цикл и обрабатывает команды пользователя. |
print_help | Показывает помощь с описанием команд. |
Заключение
Я надеюсь, что этот подробный разбор поможет вам, особенно если вы только начинаете, понять, как создавать подобные инструменты.Разработка AWScanner была интересным процессом, который помог мне глубже понять Python, т.к я не программист.
Этот скрипт можно улушать! Как? Предлагайте идеи или форкайте проект

Исходники и дополнения: