Что такое Maltego?
Maltego – это программа для автоматизации сбора информации из различных источников и дальнейшего представления в удобном графическом формате, где наглядно видны связи между разными объектами. Инструмент также применяется в расследованиях и разведке на основе открытых/закрытых источников.
Есть несколько вариантов лицензий Maltego:
- Бесплатные Maltego CaseFile, Maltego Community Edition
- Платные Maltego Professional, Maltego Enterprise
Вся информация в Maltego представлена в виде графа (схемы с указанием узлов и связей).

В графе отображены различные объекты (Entities). Объектом может быть любая единица информации: IP-адрес, домен, email, номер телефона, ФИО, организация и т.д. Объекты соединяются связями (Links).

Добавлять объекты и связи можно как вручную, так и автоматически, используя трансформации (Transforms) – скрипты, которые ищут данные о выбранном объекте в различных источниках и выводят её на граф в виде объектов.
Также в Maltego есть набор встроенных трансформаций. Например, для получения информации об IP-адресе есть большой набор встроенных трансформаций: получение данных Whois, местоположения, проверка по базам фродов и спискам нод TOR.

Трансформации можно скачивать и устанавливать дополнительно из маркетплейса Transforms Hub в самой программе.

Для скачивания некоторых трансформаций из хаба потребуется API-ключ для сервисов, откуда запрашивается информация. Для этого потребуется зарегистрироваться на самих этих сервисах.

Можно также писать собственные трансформации, что пригодится для поиска информации по внутренним источникам.
Применение в расследованиях
Возможности Maltego полезны для расследований компьютерных инцидентов, например, когда нужно проанализировать большие объёмы информации из логов и баз данных, выявить взаимосвязи и найти улики. Давайте разберём, что может программа на примере вымышленного, но типичного расследования.
Расследование: найти создателя фишинговых сайтов
Сценарий: Поступила жалоба на фишинговый сайт, который имитирует сайт банка и ворует данные кредитных карт. Доменное имя сайта MOB-VTB24.RU похоже на домен банка ВТБ. Сайт, расположенный на этом домене внешне похож на сайт банка а также запрашивает данные банковской карты, а значит используется для фишинга. Сразу предположим, что злоумышленник вряд ли мог ограничиться одним доменом, к тому же он мог завести несколько разных аккаунтов.
Цель расследования: Найти все домены злоумышленника, удостовериться что они фишинговые, и заблокировать его аккаунты. Домены отправить на идентификацию данных администратора.
Гипотезы: Из логов мы можем как минимум узнать IP-адреса, значения сookie, User-Agent браузера и другие параметры системы, таким образом получить цифровой отпечаток злоумышленника. Потому что при создании нового аккаунта в Личном кабинете для регистрации домена необходимо указать email, телефон, а также в некоторых случаях ФИО, адрес и паспортные данные.
Более того, мошенник может забыть скрыть свой настоящий IP-адрес, используя proxy или VPN, и тем самым выдать себя. Или в одной сессии зайти в два своих аккаунта, благодаря чему мы легко найдём связь между ними. Он может указать одинаковые регистрационные данные, такие как email и телефон. Также косвенные признаки, например, использование специфичного почтового сервиса для регистрации большого количества email.

Поэтому обратимся к Maltego. Создадим новый граф и сразу добавим на него доменное имя MOB-VTB-24.RU.

В Entity Palette
доступен список типов объектов, которые можно перетащить на граф. Найдём там объект Domain
и перетащим его в основное окно графа и зададим доменное имя.

Вставлять объекты также можно из буфера обмена. Типы объектов Maltego распознаёт автоматически. Если распознавание прошло некорректно, можно изменить тип объекта, выбрав в контекстном меню Change Type
.

В этом же контекстном меню можно выбирать и установленные трансформации.

Добавление новых типов Entities
В первую очередь нам нужно получить регистрационную информацию домена (ФИО, email и телефон) и аккаунт его владельца из базы данных. Для этого нужно создать тип объекта User
(для отображения аккаунта), а также написать трансформации для поиска домена по базе. Все запросы в базу, указанные ниже, изменены и упрощены, и служат только для образовательных целей.
Найдём во вкладке Entities
мастер создания типов объектов.

Создадим тип объекта User
и настроим основное свойство объекта: user_id


Теперь добавим трансформацию, которая будет выводить данные домена и аккаунт владельца
Библиотека для написания трансформаций maltego_trx
Установить библиотеку можно командой:
pip install maltego-trx
После установки создаём новый проект:
maltego-trx start new_project
После создания проекта файлы для наших трансформаций будут располагаться в папке new_project/transforms/
Пример: трансформация GreetPerson
В качестве примера рассмотрим трансформацию GreetPerson
, которая уже есть в папке, она выводит приветствие:
from maltego_trx.entities import Phrase
from maltego_trx.transform import DiscoverableTransform
class GreetPerson(DiscoverableTransform):
"""
Returns a phrase greeting a person on the graph.
"""
@classmethod
def create_entities(cls, request, response):
person_name = request.Value
response.addEntity(Phrase, "Hi %s, nice to meet you!" % person_name)
Создание трансформации SearchDomain
Для трансформации нужно создать новый класс и унаследовать его от DiscoverableTransform
, переопределить метод create_entities
, в котором и будет происходить вся логика по поиску данных и добавления объектов на граф.
Добавление объектов на граф выполняется с помощью метода response.addEntity
.
from maltego_trx.transform import DiscoverableTransform
import MySQLdb
# Наследуем наш класс от DiscoverableTransform
class SearchDomain(DiscoverableTransform):
"""
Выполняет поиск домена.
"""
# Переопределяем метод create_entities
@classmethod
def create_entities(cls, request, response):
# Получаем входящее значение
email = request.Value
# Инициализируем подключение к базе
db = MySQLdb.connect(host='database.host',
port='3306',
user='user',
passwd='password',
db='db',
charset="utf8")
cursor = db.cursor(MySQLdb.cursors.DictCursor)
# Выполняем поиск по таблице domains
cursor.execute("""select user_id, name, phone, email
from domain
where domain = %s
""", [domain])
rows = cursor.fetchall()
# В зависимости от поля добавляем на граф
# объекты соответствующих типов
for row in rows:
response.addEntity('yourorganization.User', row['user_id'])
response.addEntity('maltego.Person', row['name'])
response.addEntity('maltego.PhoneNumber', row['phone'])
response.addEntity('maltego.EmailAddress', row['email'])
Unique Type Name
, которое мы указали при создании (yourorganization.User
). Для встроенных объектов можно импортировать соответствующие классы из библиотеки Maltego. from maltego_trx.entities import Person
...
response.addEntity(Person, row['name'])
Для удобства можно сделать маппинг полей таблицы и объектов Maltego. Это будет полезно, когда разных объектов много.
field_map = {
'user_id': 'yourorganization.User',
'name': 'maltego.Person',
'phone': 'maltego.PhoneNumber',
'email': 'maltego.EmailAddress'
}
for row in rows:
for field in row:
if field_map.get(field):
response.addEntity(field_map[field], row[field])
Добавление трансформации в Maltego
Скрипт нашей трансформации готов, теперь нужно его добавить в Maltego.
Во вкладке Transforms
найдём меню New Local Transform
и заполним все необходимые параметры.

В значении Input Entity Type
нужно выбрать тип объекта, над которым мы будем выполнять эту трансформацию. В этом примере это Person
.

Зададим путь к интерпретатору Python3:
- project.py – это основной скрипт проекта.
- local означает, что трансформация будет работать локально (maltego_trx также позволяет запускать сервер трансформаций
python project.py runserver
). - searchdomain – это имя нашей трансформации, в зависимости от того как мы назвали класс.
- Убедиться, что класс создан правильно и вывести список всех трансформаций проекта можно в директории проекта, выполнив команду
python3 project.py list
.
Запуск трансформаций
Наконец мы добрались до момента, когда можно запустить нашу трансформацию.


В окне Output
можно увидеть логи, а также туда можно вывести отладочную информацию, если нужно найти ошибку в скрипте. Это можно сделать в настройках трансформации в Transform Manager
, указав галочку Show debug info
.

Каждый объект можно пометить флагом и комментарием:

Аналогичное действие можно сделать в коде трансформации при добавлении объекта:
- добавить флажок;
from maltego_trx.maltego import BOOKMARK_CLRS
...
...
me = response.addEntity(
'yourorganization.User', user_id
)
me.setBookmark(BOOKMARK_CLRS["red"])
- добавить заметку.
me.setNote("Регистрирует фишинговые домены")
Итак, мы нашли аккаунт пользователя, телефон, email и ФИО. Можем выполнить обратный поиск по этим данным. Для этого нужно добавить соответствующие трансформации по аналогии с SearchEmail
.
Обратный поиск

Поиск по номеру телефона выдал нам новые домены и другой аккаунт. Видно, что все домены так или иначе вызывают подозрения и связаны они не только с банками, но и с сервисом BlaBlaCar.
Повторив поиск, только по одному номеру телефона мы выявили ещё 88 доменов.

Выгрузив данные по найденным аккаунтам, получаем дополнительные адреса электронной почты и телефоны. Подключив в качестве источника, например, логи авторизации, найдём также IP-адреса, с которых был вход в найденные аккаунты.

Повторяя шаги по новым объектам, мы можем выявить таким образом целую сеть.

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

Отсортировав в графе ненужные объекты, выгружаем список доменов и аккаунтов мошенника/ов. Далее, исходя из полученной информации, можно блокировать аккаунты, домены и противодействовать мошеннической активности.
Это только один из примеров, когда можно использовать Maltego для расследований и анализа большого количества взаимосвязанной информации. Можно продолжать добавлять источники и совершенствовать скрипты трансформаций.
Оптимизация запросов с учётом ограничений
Немного сгладить этот недостаток можно, усовершенствовав скрипты трансформаций и выводить объекты по порядку и, таким образом, уменьшить количество запросов в базу данных.
Как это сделать – в конце статьи, а пока продолжим поиски.
Итак, доработаем наши трансформации, чтобы выводить объекты по порядку по 12 штук. Оптимизируем следующим образом:
- При первом запуске трансформации делаем запрос в базу.
- Выводим на экран 12 объектов, остальные сохраняем во временном хранилище, например, в файле.
- При повторных запусках считываем объекты из файла по 12 штук. Для этого создадим общий класс для трансформаций, которые будут работать с кэшем. И остальные трансформации будем наследовать уже от него.
CacheTransform
from maltego_trx.transform import DiscoverableTransform
import zlib
import json
class CacheTransform(DiscoverableTransform):
# Переопределяем основной метод,
# который будет выводить объекты на экран
@classmethod
def create_entities(cls, request, response):
# Получаем входное значение
value = request.Value
# Кэшированные объекты будем сохранять
# в файле. Сгенерируем уникальное имя файла
# на основе имени класса и входного значения
transform = cls.__name__
# Входное значение может быть разной длины
# и содержать небезопасные символы.
# Поэтому посчитаем контрольную сумму
crc = zlib.crc32(bytes(value, 'utf-8'))
filename = f"/tmp/maltego_{transform}{crc}"
# Сначала проверим не было ли уже
# что-то записано в кэш
cache = cls.extract_from_cache(response, filename)
# Если в кэше что-то уже есть то читаем оттуда
if cache is not None and len(cache) > 0:
for entity in cache:
response.addEntity(entity["entity_type"], entity["value"])
# Если кэш не обнаружен, то выполняем поиск
else:
entities = cls.search_entities(value)
# Выводим на экран первые 12 объектов
head = entities[:12:]
for entity in head:
response.addEntity(entity["entity_type"], entity["value"])
# Остальные сохраняем в кэш
tail = entities[12::]
if len(tail) > 0:
# Выведем в окно Output сообщение о том,
# сколько элементов было добавлено в кэш
response.addUIMessage(f"Добавлено в кэш {len(tail)} штук")
with open(filename, 'w', encoding='utf-8') as w_file:
# Для безопасного сохранения объектов
# используем формат JSON
json.dump(tail, w_file)
@staticmethod
def extract_from_cache(response, filename):
"""
Читает первые 12 элементов и удаляет их из кэша
"""
try:
with open(f"{filename}", 'r', encoding='utf-8') as file:
entities = json.load(file)
if len(entities) > 0:
head = entities[:12:]
tail = entities[12::]
with open(f"{filename}", 'w', encoding='utf-8') as w_file:
response.addUIMessage(
f"Осталось в кэше {len(tail)} штук"
)
json.dump(tail, w_file)
return head
else:
return []
except FileNotFoundError:
return None
@staticmethod
def search_entities(value):
"""
Метод для поиска данных во внешних источниках
"""
entities = list()
for i in range(1, 15):
entities.append(
{
"entity_type": "maltego.Phrase",
"value": f"Test {i}"
}
)
return entities
Пробуем запустить:

Трансформация вывела на экран 12 объектов и 2 записала в кэш. Вот как выглядит файл с кэшем:

Трансформация SearchDomain (улучшенная)
Теперь мы можем немного модифицировать и ту трансформацию, что создали вначале.
from maltego_trx.transform import CacheTransform
import MySQLdb
# На этот раз наследуем уже
# от CacheTransform
class SearchDomain(CacheTransform):
"""
Выполняет поиск домена.
"""
# Переопределяем другой метод search_entities
@classmethod
def search_entities(value):
# Получаем входящее значение
email = value
# Инициализируем подключение к базе
db = MySQLdb.connect(host='database.host',
port='3306',
user='user',
passwd='password',
db='db',
charset="utf8")
cursor = db.cursor(MySQLdb.cursors.DictCursor)
# Выполняем поиск по таблице domains
cursor.execute("""select user_id, name, phone, email
from domain
where domain = %s
""", [domain])
rows = cursor.fetchall()
# В зависимости от поля добавляем на граф
# объекты соответствующих типов
field_map = {
'user_id': 'yourorganization.User',
'name': 'maltego.Person',
'phone': 'maltego.PhoneNumber',
'email': 'maltego.EmailAddress'
}
# Сохраняем найденные объекты в массив
# А вся логика по выводу и кэшированию
# выполнится в родительском классе
entities = list()
for row in rows:
for field in row:
if field_map.get(field):
entities.append({
"entity_type": field_map[field],
"value": row[field]
})
Теперь если поиск вернёт много объектов, то нам не придётся снова и снова выполнять запросы в базу, можно постепенно вывести все элементы на экран из кэша.
Maltego упрощает процесс расследования благодаря автоматизации поиска и представления данных в графическом виде. В статье мы рассмотрели далеко не весь функционал программы. Например, это удобные функции по анализу информации, уже представленной на графе, возможности экспорта данных, объединение трансформаций в так называемые «машины» для ещё большей оптимизации поиска. Всё это можно использовать не только для расследования но и для задач поиска информации по открытым источникам (OSINT).
Комментарии