Главная страница
Навигация по странице:

  • int userid, fd; // File descriptor

  • userid = getuid(); // Получить фактический ID пользователя. // Запись данных if(write(fd, userid, 4) == -1) // Записать ID пользователя перед данными.

  • fatal(“in main() while writing buffer to file”); write(fd, “\n”, 1); // Добавить перевод строки.

  • 0x284 Структуры

  • Хакинг. Хакинг__искусство_эксплоита_2_е_469663841. Книга дает полное представление о программировании, машин ной архитектуре, сетевых соединениях и хакерских приемах


    Скачать 2.5 Mb.
    НазваниеКнига дает полное представление о программировании, машин ной архитектуре, сетевых соединениях и хакерских приемах
    АнкорХакинг
    Дата16.06.2022
    Размер2.5 Mb.
    Формат файлаpdf
    Имя файлаХакинг__искусство_эксплоита_2_е_469663841.pdf
    ТипКнига
    #595131
    страница13 из 51
    1   ...   9   10   11   12   13   14   15   16   ...   51

    109
    пользователь reader запустит uid_demo, эффективным идентификато- ром пользователя станет 0 для root, а это значит, что программа сможет обращаться к файлам как root. Именно так программа chsh позволяет любому пользователю изменить свою запускаемую оболочку, которая указана в /etc/passwd.
    Эту же технологию можно применить в многопользовательской про- грамме записи заметок. Следующая программа является модификаци- ей программы simplenote: вместе с каждой заметкой она будет записы- вать ID ее автора. Кроме того, в ней будет представлен новый синтаксис для #include.
    Функции ec_malloc() и fatal() пригодились во многих наших програм- мах. Чтобы не копировать их текст в очередную новую программу, мож- но поместить их в отдельный включаемый файл.
    hacking.h
    // Функция для вывода сообщения об ошибке и завершения работы программы void fatal(char *message) {
    char error_message[100];
    strcpy(error_message, “[!!] Fatal Error “);
    strncat(error_message, message, 83);
    perror(error_message);
    exit(-1);
    }
    // Оболочка для malloc() с проверкой ошибки void *ec_malloc(unsigned int size) {
    void *ptr;
    ptr = malloc(size);
    if(ptr == NULL)
    fatal(“in ec_malloc() on memory allocation”);
    return ptr;
    }
    В новой программе hacking.h эти функции можно разместить во вклю- чаемом файле. В C, если имя файла в директиве #include заключено в угловые скобки < и >, компилятор ищет файл по стандартным пу- тям включаемых файлов, таких как /usr/include/. Если же имя файла заключено в кавычки, компилятор ищет его в текущем каталоге. По- этому если hacking.h находится в том же каталоге, что и программа, его можно включить директивой #include «hacking.h».
    Строки, изменившиеся в новой программе notetaker (notetaker.c), выде- лены полужирным.
    notetaker.c
    #include
    #include

    110
    0x200 Программирование
    #include
    #include
    #include
    #include “hacking.h”
    void usage(char *prog_name, char *filename) {
    printf(“Usage: %s \n”, prog_name, filename);
    exit(0);
    }
    void fatal(char *); // Функция обработки критических ошибок void *ec_malloc(unsigned int); // Оболочка для malloc() с проверкой ошибок int main(int argc, char *argv[]) {
    int userid, fd; // File descriptor
    char *buffer, *datafile;
    buffer = (char *) ec_malloc(100);
    datafile = (char *) ec_malloc(20);
    strcpy(datafile, “/var/notes”);
    if(argc < 2) // Если аргументов командной строки нет,
    usage(argv[0], datafile); // вывести сообщение о правилах вызова
    // и завершить работу.
    strcpy(buffer, argv[1]); // Копировать в буфер.
    printf(“[DEBUG] buffer @ %p: \’%s\’\n”, buffer, buffer);
    printf(“[DEBUG] datafile @ %p: \’%s\’\n”, datafile, datafile);
    // Открытие файла fd = open(datafile, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR);
    if(fd == -1)
    fatal(“in main() while opening file”);
    printf(“[DEBUG] file descriptor is %d\n”, fd);
    userid = getuid(); // Получить фактический ID пользователя.
    // Запись данных
    if(write(fd, &userid, 4) == -1) // Записать ID пользователя перед данными.
    fatal(«in main() while writing userid to file»);
    write(fd, «\n», 1); // Добавить перевод строки.
    if(write(fd, buffer, strlen(buffer)) == -1) // Записать заметку.
    fatal(“in main() while writing buffer to file”);
    write(fd, “\n”, 1); // Добавить перевод строки.
    // Закрытие файла if(close(fd) == -1)
    fatal(“in main() while closing file”);

    0x280 Опираясь на основы
    111
    printf(“Note has been saved.\n”);
    free(buffer);
    free(datafile);
    }
    Файлом для вывода теперь служит не /var/notes, а /tmp/notes, чтобы данные хранились в более надежном месте. Для получения фактиче- ского идентификатора пользователя применяется функция getuid().
    ID записывается в файл данных перед тем, как в него выводится строка заметки. Поскольку функция write() принимает в качестве аргумента указатель на источник, с помощью оператора & получаем адрес целого числа userid.
    reader@hacking:

    /booksrc $ gcc -o notetaker notetaker.c reader@hacking:/booksrc $ sudo chown root:root ./notetaker reader@hacking:/booksrc $ sudo chmod u+s ./notetaker reader@hacking:/booksrc $ ls -l ./notetaker
    -rwsr-xr-x 1 root root 9015 2007-09-07 05:48 ./notetaker reader@hacking:/booksrc $ ./notetaker “this is a test of multiuser notes”
    [DEBUG] buffer @ 0x804a008: ‘this is a test of multiuser notes’
    [DEBUG] datafile @ 0x804a070: ‘/var/notes’
    [DEBUG] file descriptor is 3
    Note has been saved.
    reader@hacking:/booksrc $ ls -l /var/notes
    -rw------- 1 root reader 39 2007-09-07 05:49 /var/notes reader@hacking:/booksrc $
    Здесь показан результат компиляции программы notetaker, изменения ее владельца на root и установки флага setuid. Теперь при запуске про- граммы она работает от имени пользователя root, поэтому владельцем файла /var/notes, когда он будет создан, также станет root.
    reader@hacking:/booksrc $ cat /var/notes cat: /var/notes: Permission denied reader@hacking:/booksrc $ sudo cat /var/notes
    ?
    this is a test of multiuser notes reader@hacking:/booksrc $ sudo hexdump -C /var/notes
    00000000 e7 03 00 00 0a 74 68 69 73 20 69 73 20 61 20 74 |.....this is a t|
    00000010 65 73 74 20 6f 66 20 6d 75 6c 74 69 75 73 65 72 |est of multiuser|
    00000020 20 6e 6f 74 65 73 0a | notes.|
    00000027
    reader@hacking:/booksrc $ pcalc 0x03e7 999 0x3e7 0y1111100111
    reader@hacking:/booksrc $
    В файле /var/notes записаны ID пользователя reader (999) и заметка.
    Поскольку в данной архитектуре установлен порядок байтов «сначала младший», 4 байта целого числа 999 при выводе в шестнадцатеричном виде появляются в обратном порядке (выделены полужирным).

    112
    0x200 Программирование
    Чтобы обычный пользователь мог читать заметки из файла, ему нужна программа с флагом setuid root. Программа notesearch.c будет читать файл заметок и отображать только заметки, записанные пользовате- лем, у которого тот же ID. Кроме того, появился необязательный аргу- мент командной строки, задающий строку для поиска. Если он задан, то отображаются только те заметки, где есть искомая строка.
    notesearch.c
    #include
    #include
    #include
    #include
    #include “hacking.h”
    #define FILENAME “/var/notes”
    int print_notes(int, int, char *); // Функция для вывода заметок.
    int find_user_note(int, int); // Поиск заметки пользователя
    // в файле.
    int search_note(char *, char *); // Функция поиска ключевого слова.
    void fatal(char *); // Обработчик критических ошибок.
    int main(int argc, char *argv[]) {
    int userid, printing=1, fd; // Дескриптор файла.
    char searchstring[100];
    if(argc > 1) // Если есть аргумент,
    strcpy(searchstring, argv[1]); // это строка для поиска;
    else // в противном случае searchstring[0] = 0; // строка для поиска пуста.
    userid = getuid();
    fd = open(FILENAME, O_RDONLY); // Открыть файл только для чтения.
    if(fd == -1)
    fatal(“in main() while opening file for reading”);
    while(printing)
    printing = print_notes(fd, userid, searchstring);
    printf(“-------[ end of note data ]-------\n”);
    close(fd);
    }
    // Функция для вывода заметок с заданным ID пользователя,
    // соответствующих строке поиска, если она задана;
    // возвращает 0, если достигнут конец файла, и 1, если есть другие заметки.
    int print_notes(int fd, int uid, char *searchstring) {
    int note_length;
    char byte=0, note_buffer[100];
    note_length = find_user_note(fd, uid);

    0x280 Опираясь на основы
    113
    if(note_length == -1) // Если достигнут конец файла,
    return 0; // вернуть 0.
    read(fd, note_buffer, note_length); // Прочитать данные заметки.
    note_buffer[note_length] = 0; // Символ конца строки.
    if(search_note(note_buffer, searchstring)) // Если строка найдена,
    printf(note_buffer); // вывести заметку.
    return 1;
    }
    // Функция для поиска следующей заметки с заданным ID пользователя;
    // возвращает -1, если достигнут конец файла;
    // в противном случае возвращает длину найденной заметки.
    int find_user_note(int fd, int user_uid) {
    int note_uid=-1;
    unsigned char byte;
    int length;
    while(note_uid != user_uid) { // Повторять цикл,
    // пока не найдется user_uid.
    if(read(fd, ¬e_uid, 4) != 4) // Прочитать данные для uid.
    return -1; // Если 4 байта не прочитаны, вернуть код конца файла.
    if(read(fd, &byte, 1) != 1) // Прочитать символ перевода строки.
    return -1;
    byte = length = 0;
    while(byte != ‘\n’) { // Узнать, сколько байт осталось
    // до конца строки.
    if(read(fd, &byte, 1) != 1) // Прочитать один байт.
    return -1; // Если не удалось прочитать байт,
    // вернуть код конца файла.
    length++;
    }
    }
    lseek(fd, length * -1, SEEK_CUR); // Переместить текущую позицию чтения
    // назад на length байт.
    printf(“[DEBUG] found a %d byte note for user id %d\n”, length, note_uid);
    return length;
    }
    // Функция для поиска заметки по данному ключевому слову;
    // возвращает 1, если заметка найдена, и 0, если не найдена.
    int search_note(char *note, char *keyword) {
    int i, keyword_length, match=0;
    keyword_length = strlen(keyword);
    if(keyword_length == 0) // Если строка поиска не задана,
    return 1; // всегда “совпадение”.

    114
    0x200 Программирование for(i=0; i < strlen(note); i++) { // Цикл по всем байтам заметки.
    if(note[i] == keyword[match]) // Если совпадает с байтом
    // ключевого слова,
    match++; // приготовиться к проверке следующего байта;
    else { // в противном случае if(note[i] == keyword[0]) // если байт совпадает с первым байтом
    // ключевого слова,
    match = 1; // в счетчик повторений записывается 1.
    else match = 0; // В противном случае - 0.
    }
    if(match == keyword_length) // Если найдено полное совпадение,
    return 1; // вернуть код совпадения.
    }
    return 0; // Вернуть код несовпадения.
    }
    В основном этот код должен быть ясен, но в нем есть ряд новых поня- тий. В начале определено имя файла, чтобы не размещать его в куче.
    Далее функция lseek() используется для перемещения текущей пози- ции чтения из файла. Вызов lseek(fd, length * -1, SEEK_CUR); сообщает программе, что позицию чтения файла нужно переместить вперед на length
    * -1 байт. Поскольку это число отрицательное, позиция переме- щается назад на length байт.
    reader@hacking:/booksrc $ gcc -o notesearch notesearch.c reader@hacking:/booksrc $ sudo chown root:root ./notesearch reader@hacking:/booksrc $ sudo chmod u+s ./notesearch reader@hacking:/booksrc $ ./notesearch
    [DEBUG] found a 34 byte note for user id 999
    this is a test of multiuser notes
    -------[ end of note data ]------- reader@hacking:/booksrc $
    После компиляции и установки флага setuid для root программа
    notesearch работает так, как предполагалось. Но пока у нас только один пользователь – что будет, когда с программами notetaker и notesearch станет работать другой пользователь?
    reader@hacking:/booksrc $ sudo su jose jose@hacking:/home/reader/booksrc $ ./notetaker “This is a note for jose”
    [DEBUG] buffer @ 0x804a008: ‘This is a note for jose’
    [DEBUG] datafile @ 0x804a070: ‘/var/notes’
    [DEBUG] file descriptor is 3
    Note has been saved.
    jose@hacking:/home/reader/booksrc $ ./notesearch
    [DEBUG] found a 24 byte note for user id 501
    This is a note for jose
    -------[ end of note data ]------- jose@hacking:/home/reader/booksrc $

    0x280 Опираясь на основы
    115
    Когда эти программы запустит пользователь jose, фактическим ID пользователя будет 501. Эта величина будет добавляться ко всем за- меткам, сделанным с помощью notetaker, а программа notesearch будет отображать только заметки с совпадающим ID пользователя.
    reader@hacking:/booksrc $ ./notetaker “This is another note for the reader user”
    [DEBUG] buffer @ 0x804a008: ‘This is another note for the reader user’
    [DEBUG] datafile @ 0x804a070: ‘/var/notes’
    [DEBUG] file descriptor is 3
    Note has been saved.
    reader@hacking:/booksrc $ ./notesearch
    [DEBUG] found a 34 byte note for user id 999
    this is a test of multiuser notes
    [DEBUG] found a 41 byte note for user id 999
    This is another note for the reader user
    -------[ end of note data ]------- reader@hacking:/booksrc $
    Аналогично все заметки пользователя reader помечены идентификато- ром 999. Несмотря на то что для обеих программ notetaker и notesearch установлен бит suid root и у них есть полный доступ на чтение и запись к файлу /var/notes, логика программы notesearch не дает запустивше- му ее пользователю просматривать заметки других авторов. Это очень напоминает то, как в файле /etc/passwd хранятся данные обо всех поль- зователях, но программы, вроде chsh и passwd, разрешают любому поль- зователю изменить только свою оболочку или пароль.
    0x284 Структуры
    Иногда требуется объединить несколько переменных в группу и обра- щаться с ней как с одной переменной. В C есть понятие структуры – пе- ременной, которая может хранить в себе другие переменные. Структу- ры часто используются в системных функциях и библиотеках, поэто- му для работы с этими функциями необходимо понимать структуры.
    Рассмотрим простой пример. Многие функции, работающие со време- нем, используют структуру, которая называется tm и определена в
    /usr/
    include/time.h. Определена эта структура так:
    struct tm {
    int tm_sec; /* секунды */
    int tm_min; /* минуты */
    int tm_hour; /* часы */
    int tm_mday; /* число месяца */
    int tm_mon; /* месяц */
    int tm_year; /* год */
    int tm_wday; /* день недели */
    int tm_yday; /* день года */
    int tm_isdst; /* летнее время */
    };

    116
    0x200 Программирование
    После того как структура tm определена, она становится допустимым типом переменной и можно объявлять переменные и указатели с ти- пом данных tm. Это иллюстрирует программа time_example.c. После включения заголовочного файла time.h тип tm становится определен- ным и далее используется для объявления переменных current_time и time_ptr.
    time_example.c
    #include
    #include
    int main() {
    long int seconds_since_epoch;
    struct tm current_time, *time_ptr;
    int hour, minute, second, day, month, year;
    seconds_since_epoch = time(0); // Передать функции time в качестве аргумента нулевой указатель.
    printf(“time() - seconds since epoch: %ld\n”, seconds_since_epoch);
    time_ptr = ¤t_time; // Поместить в time_ptr адрес
    // структуры current_time.
    localtime_r(&seconds_since_epoch, time_ptr);
    // Три способа доступа к элементам структуры:
    hour = current_time.tm_hour; // Прямой доступ minute = time_ptr->tm_min; // Доступ по указателю second = *((int *) time_ptr); // Хакерский доступ по указателю printf(“Current time is: %02d:%02d:%02d\n”, hour, minute, second);
    }
    Функция time() возвращает количество секунд, прошедших с 1 января
    1970 года. В UNIX-системах время отсчитывается относительно этого произвольно выбранного момента времени, называемого также нача-
    лом эпохи (epoch). Функция localtime_r() принимает в качестве аргу- ментов два указателя: один – на количество секунд, прошедших с на- чала эпохи, другой – на структуру tm. Указатель time_ptr уже установ- лен на адрес current_time – незаполненной структуры tm. С помощью оператора адреса получается указатель на секунды с начала эпохи, принимаемый в качестве другого аргумента функции localtime_r(), ко- торая заполняет элементы tm. К элементам структур можно обращать- ся тремя разными способами: первые два допустимы при доступе к эле- ментам структуры, а третий является хакерским решением. Если пе- ременная имеет тип структуры, доступ к ее элементам осуществляет- ся путем дописывания имен элементов к имени переменной через точ- ку. Следовательно, current_time.tm_hour предоставляет доступ к эле- менту tm_hour структуры current_time типа tm. Часто прибегают к указа-

    0x280 Опираясь на основы
    117
    телям на структуры, потому что гораздо эффективнее передать указа- тель размером в четыре байта, чем передавать всю структуру данных.
    Указатели на структуру встречаются настолько часто, что в C включен встроенный метод для доступа к элементам структуры по указателю на нее, что устраняет необходимость разыменования. При использовании указателя на структуру вроде time_ptr можно также обращаться к эле- менту структуры по имени, но через символы, напоминающие стрелку вправо. Например, time_ptr->tm_min – адрес элемента tm_min структуры типа tm, указателем на которую служит time_ptr. К секундам (элемент tm_sec структуры типа tm) можно обратиться с помощью любого из этих двух законных методов, но в программе применен иной способ. Догада- етесь ли вы, как работает этот третий метод?
    reader@hacking:/booksrc $ gcc time_example.c reader@hacking:/booksrc $ ./a.out time() - seconds since epoch: 1189311588
    Current time is: 04:19:48
    reader@hacking:/booksrc $ ./a.out time() - seconds since epoch: 1189311600
    Current time is: 04:20:00
    reader@hacking:/booksrc $
    Программа работает так, как предполагалось, но каким же образом из структуры tm извлекаются секунды? Вспомним, что в конечном сче- те все сводится к памяти. Поскольку структура tm начинается с tm_sec, соответствующее целочисленное значение тоже находится в нача- ле. В строке second = *((int *) time_ptr) для переменной time_ptr вы- полняется приведение типа из указателя на структуру tm в указатель на целый тип. После этого приведенный указатель разыменовывает- ся и возвращает данные, находящиеся по адресу указателя. Так как адрес структуры tm является также адресом ее первого элемента, мы получим целое число – значение элемента tm_sec структуры. Дополне- ние, которое мы ввели в код time_example.c (time_example2.c), выводит также байты current_time. Из этого видно, что элементы tm примыкают друг к другу в памяти. Последующие элементы структуры также могут быть получены по указателю простым прибавлением соответствующе- го числа к адресу указателя.
    1   ...   9   10   11   12   13   14   15   16   ...   51


    написать администратору сайта