• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Статья Переполнение буфера и перезапись переменных к конкретному значению - Изучение методов эксплуатации на примерах, часть 2

reverse_engineering(1).jpg


Все части переполнение буфера
Предыдущая часть Переполнение буфера и перезапись значения переменных - разработка эксплойтов, часть 1
Следующая часть Использование среды переменных окружения для переполнения буфера - разработка эксплойтов, часть 3

Доброго времени суток, посетители портала Codeby =) В предыдущей статье мы научились перезаписывать значение переменной используя уязвимость переполнения буфера. Ну чтож скажу я, продолжим изучать эту уязвимость.

Описание ExploitMe
На этом уровне рассматривается концепция изменения переменных к конкретным значениям в программе и то, как переменные располагаются в памяти.

Этот уровень находится в / opt / protostar / bin / stack1

Переходить в каталог не нужно, так как мы сделали это в прошлый раз.

, VM

Советы
  • Если вы не знакомы с отображаемым шестнадцатеричным числом, «man ascii» — ваш друг.
  • little endian
Исходный код
C:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];

  if(argc == 1) {
      errx(1, "please specify an argument\n");
  }

  modified = 0;
  strcpy(buffer, argv[1]);

  if(modified == 0x61626364) {
      printf("you have correctly got the variable to the right value\n");
  } else {
      printf("Try again, you got 0x%08x\n", modified);
  }
}

Решение
В этом примере у нас так же есть буфер на 64 байта, присутствует ключевое слово volatile, но при этом тут нет теперь функции gets(), а в место неё есть функция strcpy().

Посмотрим описании функции.

Функция strcpy (destination, source) копирует данные из строки, на которую указывает аргумент source, в строку, на которую указывает аргумент destination, пока не встретится символ конца строки (нулевой символ). Копирование производится вместе с символом конца строки. Если строки перекрываются, результат копирования будет не определен.

Посмотрим на второе условие в программе, если переменная modified равняется значению «0x61626364», тогда печатается текст «you have correctly got the variable to the right value» иначе «Try again, you got 0x%08x», плюс указывается значение переменной modified в шестнадцатеричной системе счисления. Поскольку в программе, есть еще одно условие где используется аргумент argc. Запустить питон код, как в прошлой программе не получится. Воспользуемся BASH для подстановки значения, чтобы использовать наш вывод, в качестве аргумента.

Последовательность команд будет следующей.

./stack1 $(python -c "print 'a' * 65")

В итоге мы получим следующий текст на экране «Try again, you got 0x00000061», как видим переменная modified приняло текущее значение. Мы писали в буфер последовательность букв из «aaaa…», из них 64 байта пошли в буфер, последним перезаписали значение переменной modified. Стало быть modified равняется ‘a’. Посмотрим чему равно ‘a’ в хексе, воспользуемся . И видим, что ‘a’ равняется значению 0x61. Теперь еще раз взглянем на значение во втором условии «0x61626364», значит остальные числа в значении это «bcd», а всё вместе «abcd».

Пробуем эксплуатировать, передадим 64 байта в буфер и еще 4 байта для перезаписи значения переменной modified.

./stack1 $(python -c "print 'a' * 64 + 'abcd' ")

Получаем строку «Try again, you got 0x64636261» , хм, что-то пошло не так и строка записалась в перевёрнутом виде. Почему? Если погуглить, то можно узнать о такой вещи, как , т.е. в памяти, числа перевёрнутые. Значит делаем реверс строки и эксплуатируем уязвимость переполнения буфера в функции strcpy().

./stack1 $(python -c "print 'a' * 64 + 'abcd'[::-1]")

Отлично, переменная перезаписана, а на экране отображается текст «you have correctly got the variable to the right value» цель достигнута!
Если в прошлом ExploitMe нашей задачей было просто изменить значение переменной, то в этой задаче мы научились устанавливать нужное нам значение для конкретной переменной используя уязвимость переполнения буфера. Теперь переходим на следующий уровень .
 

Вложения

  • reverse_engineering.jpg
    reverse_engineering.jpg
    67,3 КБ · Просмотры: 79
Последнее редактирование модератором:

ActionNum

Red Team
27.11.2016
80
93
BIT
0
Хотел выразить свою точку зрения, касательно замечания по отсутствию на начальном этапе дизассемблера:

Вы совершенно правы, но я решил пойти другим путем. На данном этапе он не нужен. Всему свое время... Это обязательно будет, objdump, gdb, ldd в следующих частях, а пока я решил огородить от этого читателей на некоторое время.
Это интересный подход, так как прорешав первые три таска, мы начинаем понимать какие слабые места могут быть в программе и какие точки входа для поиска ошибок в коде (в данном случае это функции gets(), strcpy(), getenv() + strcpy()) могут встретиться в приложении. Мне нравится такой подход, так как в дальнейшем будет понимание как эти вещи работают и что нужно искать с помощью дизассемблера.
 
  • Нравится
Реакции: ZealotTV и Sykes

swagcat228

Заблокирован
19.12.2019
341
86
BIT
0
вопрос, как понять что хранится по адресу: инструкция или значение?

=> 0x555555555178 <main+19>: jne 0x555555555190 <main+43>
(gdb) x/x 0x555555555190
0x555555555190 <main+43>: 0x00fc45c7
(gdb) x/i 0x555555555190
=> 0x555555555190 <main+43>: mov DWORD PTR [rbp-0x4],0x0

посмотреть в регистр i r ?

вводная была такой:
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
C-подобный:
(gdb) disassemble
Dump of assembler code for function main:
   0x0000555555555165 <+0>:    push   rbp
   0x0000555555555166 <+1>:    mov    rbp,rsp
   0x0000555555555169 <+4>:    sub    rsp,0x60
   0x000055555555516d <+8>:    mov    DWORD PTR [rbp-0x54],edi
   0x0000555555555170 <+11>:    mov    QWORD PTR [rbp-0x60],rsi
   0x0000555555555174 <+15>:    cmp    DWORD PTR [rbp-0x54],0x1
   0x0000555555555178 <+19>:    jne    0x555555555190 <main+43>
   0x000055555555517a <+21>:    lea    rsi,[rip+0xe87]        # 0x555555556008
   0x0000555555555181 <+28>:    mov    edi,0x1
   0x0000555555555186 <+33>:    mov    eax,0x0
   0x000055555555518b <+38>:    call   0x555555555050 <errx@plt>
   0x0000555555555190 <+43>:    mov    DWORD PTR [rbp-0x4],0x0
   0x0000555555555197 <+50>:    mov    rax,QWORD PTR [rbp-0x60]
   0x000055555555519b <+54>:    add    rax,0x8
   0x000055555555519f <+58>:    mov    rdx,QWORD PTR [rax]
   0x00005555555551a2 <+61>:    lea    rax,[rbp-0x50]
   0x00005555555551a6 <+65>:    mov    rsi,rdx
   0x00005555555551a9 <+68>:    mov    rdi,rax
   0x00005555555551ac <+71>:    call   0x555555555030 <strcpy@plt>
   0x00005555555551b1 <+76>:    mov    eax,DWORD PTR [rbp-0x4]
=> 0x00005555555551b4 <+79>:    cmp    eax,0x61626364
   0x00005555555551b9 <+84>:    jne    0x5555555551c9 <main+100>
   0x00005555555551bb <+86>:    lea    rdi,[rip+0xe66]        # 0x555555556028
   0x00005555555551c2 <+93>:    call   0x555555555040 <puts@plt>
   0x00005555555551c7 <+98>:    jmp    0x5555555551df <main+122>
   0x00005555555551c9 <+100>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00005555555551cc <+103>:    mov    esi,eax
   0x00005555555551ce <+105>:    lea    rdi,[rip+0xe8a]        # 0x55555555605f
   0x00005555555551d5 <+112>:    mov    eax,0x0
   0x00005555555551da <+117>:    call   0x555555555060 <printf@plt>
   0x00005555555551df <+122>:    mov    eax,0x0
   0x00005555555551e4 <+127>:    leave
   0x00005555555551e5 <+128>:    ret
End of assembler dump.
(gdb) i r
rax            0x63413563          1665217891
rbx            0x0                 0
rcx            0x7ffff7e9efe0      140737352691680
rdx            0x714135            7422261
rsi            0x7fffffffe600      140737488348672
rdi            0x7fffffffe231      140737488347697
rbp            0x7fffffffe090      0x7fffffffe090
rsp            0x7fffffffe030      0x7fffffffe030
r8             0x7ffff7fbba50      140737353857616
r9             0x7ffff7fe3780      140737354020736
r10            0x55555555442b      93824992232491
r11            0x7ffff7f7eb40      140737353608000
r12            0x555555555080      93824992235648
r13            0x7fffffffe170      140737488347504
r14            0x0                 0
r15            0x0                 0
rip            0x5555555551b4      0x5555555551b4 <main+79>
eflags         0x202               [ IF ]
cs             0xe033              57395
ss             0xe02b              57387
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
(gdb) x/x $rax
0x63413563:    Cannot access memory at address 0x63413563
(gdb) x/x $rip
0x5555555551b4 <main+79>:    0x6263643d
(gdb) x/x $eax
0x63413563:    Cannot access memory at address 0x63413563
(gdb)

получается, что я перезаписал RAX, который равен EAX
оффсет = 76

только я не пойму как же оттуда значение станет адресом возврата.


C-подобный:
   0x00005555555551b1 <+76>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00005555555551b4 <+79>:    cmp    eax,0x61626364
=> 0x00005555555551b9 <+84>:    jne    0x5555555551c9 <main+100>
   0x00005555555551bb <+86>:    lea    rdi,[rip+0xe66]        # 0x555555556028
   0x00005555555551c2 <+93>:    call   0x555555555040 <puts@plt>
   0x00005555555551c7 <+98>:    jmp    0x5555555551df <main+122>
   0x00005555555551c9 <+100>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00005555555551cc <+103>:    mov    esi,eax
   0x00005555555551ce <+105>:    lea    rdi,[rip+0xe8a]        # 0x55555555605f
   0x00005555555551d5 <+112>:    mov    eax,0x0
   0x00005555555551da <+117>:    call   0x555555555060 <printf@plt>
   0x00005555555551df <+122>:    mov    eax,0x0
   0x00005555555551e4 <+127>:    leave
   0x00005555555551e5 <+128>:    ret 
End of assembler dump.
(gdb) x/i 0x5555555551c9
   0x5555555551c9 <main+100>:    mov    eax,DWORD PTR [rbp-0x4]
(gdb) x/x $rbp-0x4
0x7fffffffe08c:    0x63413563
(gdb) x/i $rbp-0x4
   0x7fffffffe08c:    movsxd esi,DWORD PTR [rip+0x41366341]        # 0x8000413643d3
(gdb) x/x $esi
0xffffffffffffe600:    Cannot access memory at address 0xffffffffffffe600
(gdb) x/x 0x41366341
0x41366341:    Cannot access memory at address 0x41366341
(gdb) x/x $rip+0x41366341
0x5555968bb4fa:    Cannot access memory at address 0x5555968bb4fa
(gdb)

то бишь

0x00005555555551b4 <+79>: cmp eax,0x61626364 ---- false
=> 0x00005555555551b9 <+84>: jne 0x5555555551c9 <main+100> ---- переход если false сюда 0x5555555551c9
0x00005555555551c9 <+100>: mov eax,DWORD PTR [rbp-0x4] ---- пишем из rbp-0x4 в eax
0x00005555555551cc <+103>: mov esi,eax ---- пишем из eax в esi
в esi оказывается наша строка с оффсетом 76

esi - это указатель, на сколько я помню. индекс источника.

то самое чувство, когда пользуешься такой gdb, и тут внезапно:

C-подобный:
(gdb) x/1cw $rsi
0x7fffffffe600:    67 'C'
(gdb) x/4cw $rsi
0x7fffffffe600:    67 'C'    83 'S'    76 'L'    105 'i'
(gdb) x/s $rsi
0x7fffffffe600:    "CCC"
(gdb) x/2s $rsi
0x7fffffffe600:    "CCC"
0x7fffffffe604:    "SHELL=/bin/bash"
О_О
но ты ещё не ложил шелл в память!
 

Medvedev

New member
09.08.2022
1
0
BIT
0
для тех кто будет компилить на линуксе x64 не забудьте - #include <err.h>
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!