Хакинг. Хакинг__искусство_эксплоита_2_е_469663841. Книга дает полное представление о программировании, машин ной архитектуре, сетевых соединениях и хакерских приемах
Скачать 2.5 Mb.
|
0x200 Программирование функции и обработки критических ошибок. Функция usage() опреде- лена выше main(), поэтому ей не нужен прототип. simplenote.c #include #include #include #include #include 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 fd; // file descriptor char *buffer, *datafile; buffer = (char *) ec_malloc(100); datafile = (char *) ec_malloc(20); strcpy(datafile, “/tmp/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); strncat(buffer, “\n”, 1); // Добавить в конце перевод строки. // Открытие файла 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); // Запись данных if(write(fd, buffer, strlen(buffer)) == -1) fatal(“in main() while writing buffer to file”); // Закрытие файла if(close(fd) == -1) fatal(“in main() while closing file”); printf(“Note has been saved.\n”); free(buffer); 0x280 Опираясь на основы 101 free(datafile); } // Функция для вывода сообщения об ошибке и завершения работы программы 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; } За исключением необычных флагов в функции open(), код этой програм- мы должен быть вполне ясен. Здесь также используется ряд стандартных функций, не встречавшихся ранее. Функция strlen() принимает стро- ку и возвращает ее длину. Она используется вместе с функцией write(), которой нужно знать, сколько байт должно быть записано. Функция perror() (от print error) служит для вывода сообщения об ошибке и ис- пользуется в fatal() для вывода дополнительного сообщения об ошибке (если таковое имеется) и завершения работы программы. reader@hacking:/booksrc $ gcc -o simplenote simplenote.c reader@hacking:/booksrc $ ./simplenote Usage: ./simplenote reader@hacking:/booksrc $ ./simplenote “this is a test note” [DEBUG] buffer @ 0x804a008: ‘this is a test note’ [DEBUG] datafile @ 0x804a070: ‘/tmp/notes’ [DEBUG] file descriptor is 3 Note has been saved. reader@hacking:/booksrc $ cat /tmp/notes this is a test note reader@hacking:/booksrc $ ./simplenote “great, it works” [DEBUG] buffer @ 0x804a008: ‘great, it works’ [DEBUG] datafile @ 0x804a070: ‘/tmp/notes’ [DEBUG] file descriptor is 3 Note has been saved. reader@hacking:/booksrc $ cat /tmp/notes this is a test note great, it works reader@hacking:/booksrc $ 102 0x200 Программирование Вывод программы не вызывает вопросов, но кое-что в исходном коде требует пояснения. Файлы fcntl.h и sys/stat.h должны быть включены (#include), потому что в них определены флаги, используемые функци- ей open(). Первая группа флагов находится в fcntl.h и участвует в опре- делении режима доступа. В режиме доступа должен быть задан хотя бы один из следующих флагов: O_RDONLY Открыть файл только для чтения. O_WRONLY Открыть файл только для записи. O_RDWR Открыть файл для чтения и записи. Эти флаги можно использовать вместе с некоторыми другими, необя- зательными, соединяя их поразрядным ИЛИ. В число наиболее упо- требительных и полезных флагов входят: O_APPEND Записать данные в конец файла. O_TRUNC Если файл уже существует, сократить его длину до 0. O_CREAT Создать файл, если он еще не существует. Поразрядные операции действуют над битами, пропуская их через стандартные логические вентили, такие как ИЛИ и И. Если два бита поступают на вход вентиля ИЛИ, результат будет 1, если один или дру- гой бит равен 1. Если два бита поступают на вход вентиля И, результат будет 1, если оба бита равны 1. Над 32-разрядными числами можно вы- полнять поразрядные операции, применяя логические операторы к их соответствующим битам. Исходный код и вывод программы bitwise.c иллюстрируют эти поразрядные операции. bitwise.c #include int main() { int i, bit_a, bit_b; printf(“bitwise OR operator |\n”); for(i=0; i < 4; i++) { bit_a = (i & 2) / 2; // Взять второй бит. bit_b = (i & 1); // Взять первый бит. printf(“%d | %d = %d\n”, bit_a, bit_b, bit_a | bit_b); } printf(“\nbitwise AND operator &\n”); for(i=0; i < 4; i++) { bit_a = (i & 2) / 2; // Взять второй бит. bit_b = (i & 1); // Взять первый бит. printf(“%d & %d = %d\n”, bit_a, bit_b, bit_a & bit_b); } } 0x280 Опираясь на основы 103 Результат компиляции и выполнения bitwise.c: reader@hacking:/booksrc $ gcc bitwise.c reader@hacking:/booksrc $ ./a.out bitwise OR operator | 0 | 0 = 0 0 | 1 = 1 1 | 0 = 1 1 | 1 = 1 bitwise AND operator & 0 & 0 = 0 0 & 1 = 0 1 & 0 = 0 1 & 1 = 1 reader@hacking:/booksrc $ Значения флагов, используемых в функции open(), состоят из един- ственного бита. Благодаря этому можно объединять их с помощью по- разрядного ИЛИ, не теряя информации. Программа fcntl_flags.c и ее вывод позволяют изучить некоторые флаги, определенные в fcntl.h, и результат их объединения. fcntl_flags.c #include #include void display_flags(char *, unsigned int); void binary_print(unsigned int); int main(int argc, char *argv[]) { display_flags(“O_RDONLY\t\t”, O_RDONLY); display_flags(“O_WRONLY\t\t”, O_WRONLY); display_flags(“O_RDWR\t\t\t”, O_RDWR); printf(“\n”); display_flags(“O_APPEND\t\t”, O_APPEND); display_flags(“O_TRUNC\t\t\t”, O_TRUNC); display_flags(“O_CREAT\t\t\t”, O_CREAT); printf(“\n”); display_flags(“O_WRONLY|O_APPEND|O_CREAT”, O_WRONLY|O_APPEND|O_CREAT); } void display_flags(char *label, unsigned int value) { printf(“%s\t: %d\t:”, label, value); binary_print(value); printf(“\n”); } void binary_print(unsigned int value) { unsigned int mask = 0xff000000; // Маска для старшего байта. unsigned int shift = 256*256*256; // Смещение для старшего байта. 104 0x200 Программирование unsigned int byte, byte_iterator, bit_iterator; for(byte_iterator=0; byte_iterator < 4; byte_iterator++) { byte = (value & mask) / shift; // Выделить каждый байт. printf(“ “); for(bit_iterator=0; bit_iterator < 8; bit_iterator++) { // Вывести // биты байта. if(byte & 0x80) // Если старший бит в байте не 0, printf(“1”); // вывести 1. else printf(“0”); // Иначе вывести 0. byte *= 2; // Сдвинуть все биты влево на 1. } mask /= 256; // Переместить биты маски вправо на 8. shift /= 256; // Переместить биты смещения вправо на 8. } } Результат компиляции и выполнения fcntl_flags.c: reader@hacking:/booksrc $ gcc fcntl_flags.c reader@hacking:/booksrc $ ./a.out O_RDONLY : 0 : 00000000 00000000 00000000 00000000 O_WRONLY : 1 : 00000000 00000000 00000000 00000001 O_RDWR : 2 : 00000000 00000000 00000000 00000010 O_APPEND : 1024 : 00000000 00000000 00000100 00000000 O_TRUNC : 512 : 00000000 00000000 00000010 00000000 O_CREAT : 64 : 00000000 00000000 00000000 01000000 O_WRONLY|O_APPEND|O_CREAT : 1089 : 00000000 00000000 00000100 01000001 $ Применение битовых флагов и поразрядной логики – эффективный и распространенный прием. Поскольку каждый флаг является чис- лом, в котором может быть установлен в 1 только один определенный бит, выполнение поразрядного ИЛИ над такими величинами равно- сильно их сложению. В fcntl_flags.c 1 + 1024 + 64 = 1089. Разумеется это возможно, только если каждый флаг использует только один определенный бит. 0x282 Права доступа к файлам Если режим доступа в функции open() определяется флагом O_CREAT, ну- жен дополнительный аргумент, задающий права доступа к создавае- мому файлу. В этом аргументе участвуют флаги, определяемые в sys/ stat.h, которые можно комбинировать с помощью поразрядного ИЛИ. S_IRUSR Право чтения файла предоставляется пользователю (владельцу). S_IWUSR Право записи файла предоставляется пользователю (владельцу). S_IXUSR Право исполнения файла предоставляется пользователю (вла- дельцу). 0x280 Опираясь на основы 105 S_IRGRP Право чтения файла предоставляется группе. S_IWGRP Право записи в файл предоставляется группе. S_IXGRP Право выполнения файла предоставляется группе. S_IROTH Право чтения файла предоставляется всем (от other). S_IWOTH Право записи в файл предоставляется всем. S_IXOTH Право исполнения файла предоставляется всем. Тому, кто знаком с системой прав доступа к файлам в UNIX, эти фла- ги должны быть абсолютно понятны. Если они не понятны, рассмотрим вкратце права доступа к файлам в UNIX. У каждого файла есть владелец и группа. Увидеть их можно с помощью команды ls -l. reader@hacking:/booksrc $ ls -l /etc/passwd simplenote* -rw-r--r-- 1 root root 1424 2007-09-06 09:45 /etc/passwd -rwxr-xr-x 1 reader reader 8457 2007-09-07 02:51 simplenote -rw------- 1 reader reader 1872 2007-09-07 02:51 simplenote.c reader@hacking:/booksrc $ Владелец файла simplenote* – root, группа – тоже root. У других двух файлов simplenote владелец – reader и группа – reader. Права чтения, записи и исполнения можно включать и выключать в трех разных полях для пользователя, для группы и для всех. Права для пользователя задают, что может делать с файлом его владелец (чи- тать, писать и/или исполнять), права для группы задают, что могут де- лать пользователи, принадлежащие к этой группе, а права для всех за- дают, что могут делать все остальные. Эти поля отображены и в начале вывода команды ls -l. Сначала идут права на чтение/запись/исполнение для владельца (r – право на чтение, w – право на запись, x – право на исполнение, отсутствие права обозна- чено дефисом -). Следующие три символа – права группы, последние три – права для всех остальных. В приведенном примере у програм- мы simplenote включены все три права доступа для владельца. Каждо- му праву соответствует битовый флаг; 4 (двоичное 100) – для чтения, 2 (двоичное 010) – для записи, 1 (двоичное 001) – для исполнения. По- скольку бит для каждого значения уникален, поразрядное ИЛИ дает такой же результат, как сложение этих чисел. Значения можно склады- вать, чтобы задавать права доступа владельца, группы и всех осталь- ных в команде chmod. reader@hacking:/booksrc $ chmod 731 simplenote.c reader@hacking:/booksrc $ ls -l simplenote.c -rwx-wx--x 1 reader reader 1826 2007-09-07 02:51 simplenote.c reader@hacking:/booksrc $ chmod ugo-wx simplenote.c reader@hacking:/booksrc $ ls -l simplenote.c -r-------- 1 reader reader 1826 2007-09-07 02:51 simplenote.c reader@hacking:/booksrc $ chmod u+w simplenote.c 106 0x200 Программирование reader@hacking:/booksrc $ ls -l simplenote.c -rw------- 1 reader reader 1826 2007-09-07 02:51 simplenote.c reader@hacking:/booksrc $ Первая команда (chmod 731) дает владельцу права чтения, записи и ис- полнения, поскольку первое число 7 (4 + 2 + 1), группе – права запи- си и исполнения, поскольку второе число 3 (2 + 1), а всем остальным – только исполнения, потому что третье число 1. С помощью chmod мож- но также добавлять и отнимать права. В следующей команде chmod ар- гумент ugo-wx означает отнять права записи и исполнения у владельца, группы и всех остальных. В последней команде chmod u+w владельцу до- бавляется право записи. В программе simplenote дополнительным аргументом прав доступа для функции open() является S_IRUSR|S_IWUSR, вследствие чего права доступа к создаваемому файлу /tmp/notes будут ограничены правами на чтение и запись, предоставленными только его владельцу. reader@hacking:/booksrc $ ls -l /tmp/notes -rw------- 1 reader reader 36 2007-09-07 02:52 /tmp/notes reader@hacking:/booksrc $ 0x283 Идентификатор пользователя У каждого пользователя UNIX-системы есть уникальный числовой идентификатор (user ID, UID). Идентификатор пользователя можно узнать с помощью команды id. reader@hacking:/booksrc $ id reader uid=999(reader) gid=999(reader) groups=999(reader),4(adm),20(dialout),24(cdrom),25(floppy),29(audio), 30(dip),44(video),46(plugdev),104(scanner),112(netdev),113(lpadmin), 115(powerdev),117(admin) reader@hacking:/booksrc $ id matrix uid=500(matrix) gid=500(matrix) groups=500(matrix) reader@hacking:/booksrc $ id root uid=0(root) gid=0(root) groups=0(root) reader@hacking:/booksrc $ Пользователь root с идентификатором 0 представляет собой админи- стратора системы, обладающего полным доступом к ней. Команда su по- зволяет предстать перед системой другим пользователем, и если ее вы- полняет root, вводить пароль не нужно. Команда sudo позволяет выпол- нить от имени root отдельную команду. На загрузочном диске 1 коман- да sudo для простоты настроена на выполнение без ввода пароля. Эти команды дают простой способ переключения между пользователями. reader@hacking:/booksrc $ sudo su jose 1 Который можно скачать по адресу www.symbol.ru/library/hacking-2ed. – Прим. ред. 0x280 Опираясь на основы 107 jose@hacking:/home/reader/booksrc $ id uid=501(jose) gid=501(jose) groups=501(jose) jose@hacking:/home/reader/booksrc $ Пользователь jose может запустить программу simplenote, которая бу- дет выполняться с его кодом идентификации, но у нее не будет прав до- ступа к файлу /tmp/notes. Владельцем этого файла является пользова- тель reader, и только у него есть право его чтения и записи . jose@hacking:/home/reader/booksrc $ ls -l /tmp/notes -rw------- 1 reader reader 36 2007-09-07 05:20 /tmp/notes jose@hacking:/home/reader/booksrc $ ./simplenote “a note for jose” [DEBUG] buffer @ 0x804a008: ‘a note for jose’ [DEBUG] datafile @ 0x804a070: ‘/tmp/notes’ [!!] Fatal Error in main() while opening file: Permission denied jose@hacking:/home/reader/booksrc $ cat /tmp/notes cat: /tmp/notes: Permission denied jose@hacking:/home/reader/booksrc $ exit exit reader@hacking:/booksrc $ Все прекрасно, если reader – единственный пользователь программы simplenote; однако очень часто нескольким пользователям требуется до- ступ к определенным частям одного и того же файла. Например, в фай- ле /etc/passwd хранятся данные об учетных записях всех пользовате- лей системы, включая информацию о том, какая оболочка запускается для каждого пользователя по умолчанию. Команда chsh дает возмож- ность каждому пользователю изменить свою оболочку по умолчанию. Эта программа должна внести изменения в файл /etc/passwd, но только в ту строку, которая относится к учетной записи данного пользователя. В UNIX эта проблема решается с помощью права доступа setuid (set user ID ). Этот дополнительный бит в правах доступа можно установить с помощью команды chmod. При запуске программы с этим флагом она выполняется с идентификатором пользователя, который является вла- дельцем файла. reader@hacking:/booksrc $ which chsh /usr/bin/chsh reader@hacking:/booksrc $ ls -l /usr/bin/chsh /etc/passwd -rw-r--r-- 1 root root 1424 2007-09-06 21:05 /etc/passwd -rwsr-xr-x 1 root root 23920 2006-12-19 20:35 /usr/bin/chsh reader@hacking:/booksrc $ У программы chsh установлен флаг setuid, о чем свидетельствует сим- вол s в правах доступа. Так как владельцем файла является root и уста- новлен бит setuid, при запуске программы любым пользователем она будет выполняться от имени root. Владельцем файла /etc/passwd, в который должна сделать запись про- грамма chsh, также является root, и разрешение на запись предоставле- но только владельцу. Программа chsh устроена так, что разрешает за- 108 0x200 Программирование пись только той строки /etc/passwd, которая относится к пользовате- лю, запустившему программу, даже если реально она выполняется от имени root. Таким образом, у работающей программы есть обычный (действительный) ID и эффективный ID. Эти идентификаторы можно получить с помощью функций getuid() и geteuid() соответственно, как показано в программе uid_demo.c. uid_demo.c #include int main() { printf(“real uid: %d\n”, getuid()); printf(“effective uid: %d\n”, geteuid()); } Результат компиляции и выполнения uid_demo.c: reader@hacking:/booksrc $ gcc -o uid_demo uid_demo.c reader@hacking:/booksrc $ ls -l uid_demo -rwxr-xr-x 1 reader reader 6825 2007-09-07 05:32 uid_demo reader@hacking:/booksrc $ ./uid_demo real uid: 999 effective uid: 999 reader@hacking:/booksrc $ sudo chown root:root ./uid_demo reader@hacking:/booksrc $ ls -l uid_demo -rwxr-xr-x 1 root root 6825 2007-09-07 05:32 uid_demo reader@hacking:/booksrc $ ./uid_demo real uid: 999 effective uid: 999 reader@hacking:/booksrc $ Вывод программы uid_demo показывает, что после выполнения про - граммы оба ID равны 999, поскольку 999 – это идентификатор поль- зователя для reader. Далее команда sudo используется с командой chown, чтобы изменить владельца и группу uid_demo на root. Программу по- прежнему можно запустить, потому что у нее есть права исполнения для всех остальных, и она покажет, что оба ID остались равными 999, потому что это по-прежнему идентификатор пользователя. reader@hacking:/booksrc $ chmod u+s ./uid_demo chmod: changing permissions of `./uid_demo’: Operation not permitted reader@hacking:/booksrc $ sudo chmod u+s ./uid_demo reader@hacking:/booksrc $ ls -l uid_demo -rwsr-xr-x 1 root root 6825 2007-09-07 05:32 uid_demo reader@hacking:/booksrc $ ./uid_demo real uid: 999 effective uid: 0 reader@hacking:/booksrc $ Так как теперь владельцем программы является root, нужно с помо- щью sudo изменить права доступа к ней. Команда chmod u+s включа- ет флаг setuid, что видно по результату выполнения ls -l. Если теперь |