Jul 2, 2011

Shell code 4(another trick)

By far, the shell code can print a message and exit the program normally.
Now I add a new feature in the previous shell code program. That is let the program wait 2 seconds and then exit.

In order to do this, I need to use a new system call called "nanosleep". (my OS is ubuntu 10.10)
use the following command to see what nanosleep do:
man nonosleep
#include <time.h>
int nanosleep(const struct timespec *req, struct timespec *rem);
....
struct timespec {
               time_t tv_sec;        /* seconds */
               long   tv_nsec;       /* nanoseconds */
           };

P.S since I write a simple program to see the size of the timespec, and also the size of each fields. the time_t type is 4 bytes and long is 4 bytes, so the timespec is totally 8 bytes long.
Tips: you can write a C program that use the "sizeof()" MACRO to see the size of each type of variable.
The above are the information that I needed.

Now, as usual, write an inline assembly program.
sleep1.c

/*the time_spec structure*/
char t1_v[]="\x02\x00\x00\x00\x00\x00\x00\x00";
int main(){
        __asm__("movl $t1_v, %ebx;\
                 movl $0, %ecx;\
                 movl $162, %eax;\
                 int  $0x80;\
                 movl $1,%eax;\
                 movl $0,%ebx;\
                 int  $0x80;\
                 ");
        return 0;
}
Compile the source code and execute it, you will see that the program actually wait about 2 seconds then exit.
However, just like the previous post: Shell code 3(cont.) I don't want the data is outside the shell code. Therefore, I use the same trick, the relative jmp/call trick, mentioned in the previous post.
sleep2.c
int main(){
        /* relative jmp/call trick */
        __asm__("jmp 2f;\n\
                 1:;\n\
                 pop %esi;\n\
                 movl %esi, %ebx;\n\
                 movl $0, %ecx;\n\
                 movl $162, %eax;\n\
                 int  $0x80;\n\
                 movl $1,%eax;\n\
                 movl $0,%ebx;\n\
                 int  $0x80;\n\
                 2:;\n\
                 call 1b;\n\
                 .long 0x00000002,0x0;\n\
                 ");
        return 0;
} 
Compile the source code and test the result. (It works :D ) 
Instead of using relative jmp/call trick to get the address of the data, is there any other way to get the address too?
Why not just push the parameter to the stack, and the %esp will content the address of our data. Let's use this push trick to write our code.
sleep3.c
int main(){
        /* push trick */
        __asm__("push $0;\n\
                 push $2;\n\
                 movl %esp, %ebx;\n\
                 movl $0, %ecx;\n\
                 movl $162, %eax;\n\
                 int  $0x80;\n\
                 movl $1,%eax;\n\
                 movl $0,%ebx;\n\
                 int  $0x80;\n\               
                 ");
        return 0;
}
P.S remember that the stack grow down, therefore push the data in reverse order.
Before writing the shellcode, I add the write system call into the inline assembly.
sleep4.c
/*
 * In this example I use both the 
 * relative jmp/call trick and
 * push trick to get the data.
 */
int main(){
        __asm__("jmp 2f;\n\
                 1:;\n\
                 popl %esi;\n\
                 movl $4,%eax;\n\
                 movl $1,%ebx;\n\
                 movl $0x7,%edx;\n\
                 movl %esi,%ecx;\n\
                 int  $0x80;\n\
                 push $0;\n\
                 push $2;\n\
                 movl %esp, %ebx;\n\
                 movl $0, %ecx;\n\
                 movl $162, %eax;\n\
                 int  $0x80;\n\
                 movl $1,%eax;\n\
                 movl $0,%ebx;\n\
                 int  $0x80;\n\
                 2:;\n\
                 call 1b;\n\
                 .string "Run Han";\n\
                ");
        return 0;
}
This is how the inline assembly looks like, it is pretty big now.

Again, compile the source code and test the result. If everything is correct, you will see the message and wait about 2 seconds then exit the program.
If everything works fine, objdump the binary files.

Copy the machine code and paste into another source file as the shellcode.
sleep5.c
/* This is the shellcode */
/* This is the shellcode */
char shellcode[]=
"\xeb\x32"
"\x5e"
"\xb8\x04\x00\x00\x00"
"\xbb\x01\x00\x00\x00"
"\xba\x07\x00\x00\x00"
"\x89\xf1"
"\xcd\x80"
"\x6a\x00"
"\x6a\x02"
"\x89\xe3"
"\xb9\x00\x00\x00\x00"
"\xb8\xa2\x00\x00\x00"
"\xcd\x80"
"\xb8\x01\x00\x00\x00"
"\xbb\x00\x00\x00\x00"
"\xcd\x80"
"\xe8\xc9\xff\xff\xff"
"Run Han";
int main(){
        int *ptr;
        int i;
        /* 
         * overflow the return address
         * transfer the execution flow to shellcode
         */
        for(i=0;i<10;i++){
                ptr = (int*)&ptr+i;
                *(ptr) = (int)shellcode;
        }
        return 0;
}

Compile the source code and use execstack to enable the executable stack.
gcc -g -o sleep4.out sleep4.c
execstack -s sleep4.out
execute the program and check the result is what we expected.
Moreover, you can even use gdb to see the result.
Result: 

No comments:

Post a Comment

Labels