misql.py: A Cuckoo Package for MySQL Commands

#######################################################################
# Cuckoo Sandbox (v0.4.2) package to monitor the MySQL server process #
# while connecting to it and issuing SQL commands from a given file   #
#                                                                     #
# misql.py v2012.12.09                                                #
# https://malwaremusings.com/scripts/misql-py/                         #
#######################################################################

###
# imports
###
from lib.common.abstracts import Package
from lib.api.process import Process
from lib.common.exceptions import CuckooError

import os
import socket

try:
    import mysql.connector
except Exception as e:
    raise CuckooError("my.py failed to import mysql.connector: " + e.message)

###
# subclass of Cuckoo's Package class so that Cuckoo can call us
###
class Misql(Package):
    """MySQL analysis package."""

    ###
    # configuration variables
    ###
    mysqldpid = "C:\\Documents and Settings\\All Users\\Application Data\\MySQL\\MySQL Server 5.5\\data\\" + socket.gethostname() + ".pid"
    mysqldexe = "C:\\Program Files\\MySQL\\MySQL Server 5.5\\bin\\mysqld.exe"
    mysqldini = "C:\\Documents and Settings\\All Users\\Application Data\\MySQL\\MySQL Server 5.5\\my.ini"

    mysqldbuser = "<mysqldbuser>"
    mysqldbpass = "<mysqldbpassword>"

    procmon = "C:\\processmonitor\\procmon.exe"
    procmonfile = "C:\\cuckoo\\logs\\events"

    misqllog = "C:\\cuckoo\\logs\\misql.log"

    ###
    # class global variables
    ###
    logfile = None

    ###
    # doqry(): submit a MySQL query and print out the results
    #     The results must be read otherwise the server won't accept next query
    ###
    def doqry(self,connection,query):
        self.logfile.write("Q: %s" % query)

        try:
            result = connection.cmd_query(query)
            if 'columns' in result:
                done = False
                while (not done):
                    (row,status) = connection.get_row()
                    if (row != None):
                        for col in range(0,len(row)):
                            self.logfile.write(row[col] + "\t")
                        self.logfile.write("\n")
                    done = (row == None)
        except mysql.connector.errors.ProgrammingError as pe:
            self.logfile.write("ProgrammingError: %s\n" % pe.msg)
        except mysql.connector.errors.DatabaseError as de:
            self.logfile.write("DatabaseError: %s\n" % de.msg)
        except Exception as e:
            self.logfile.write("Exception: %s\n" % e.message)

    ###
    # start(): method called by Cuckoo to start the process(es) of interest
    ###
    def start(self, path):
        self.logfile = open(self.misqllog,"w")

        #
        # check for mysqld pid file
        #
        try:
            pidfile = open(self.mysqldpid,"r")
            pidstr = pidfile.read()
            pid = int(pidstr)
            self.logfile.write("MySQL server pid: %d\n" % pid)
        except IOError as ioe:
            # unable to determine PID of mysqld
            self.logfile.write("Unable to determine MySQL server pid\n")
            pid = 0

        #
        # if we didn't find a pid file, then start mysqld
        #
        if (pid == 0):
            self.logfile.write("Starting MySQL server... ")
            p = Process()
            ok = p.execute(path = self.mysqldexe,args = "--defaults-file=\"" + self.mysqldini + "\"",suspended = False)
            self.logfile.write("%s\n" % ok)
        else:
            p = Process(pid = pid)

        #
        # start process monitor
        # use the 'start' command to run it in the background
        #
        os.system('start ' + self.procmon + ' /BackingFile ' + self.procmonfile + '.pml /AcceptEula /Minimized')

        #
        # inject the cuckoomon.dll so that Cuckoo can monitor it
        # note that the Process.inject() method has to be modified
        # so that it passes an absolute path to LoadLibrary() and
        # not a relative path
        #
        self.logfile.write("Injecting DLL... ")
        injectstat = p.inject()
        self.logfile.write("%s\n" % injectstat)

        #
        # open the file of SQL commands
        #
        self.logfile.write("Opening SQL command file: %s... " % path)
        sqlcmdfile = open(path,"r")
        self.logfile.write("done\n")

        #
        # connect to MySQL server and send commands
        #
        self.logfile.write("Connecting to MySQL server... ")
        try:
            #
            # establish a connection to the MySQL server on localhost
            #
            c = mysql.connector.connect(user = self.mysqldbuser,password = self.mysqldbpass)

            #
            # read one command per line and send it to the SQL server
            #
            self.logfile.write("done\nSending SQL commands:")

            for line in sqlcmdfile:
                self.doqry(c,line)

            self.logfile.write("done\n")

            #
            # close the MySQL connection
            #
            c.close()
        except Exception as e:
            self.logfile.write("%s\n" % e.message)

        #
        # close SQL command file and our logfile
        #
        sqlcmdfile.close()
        self.logfile.close()

        #
        # we're done
        #
        return p.pid

    ###
    # check(): Method periodically called by Cuckoo, to see if it should stop the guest
    ###
    def check(self):
        return True

    ###
    # finish(): Method called by Cuckoo to notify us that it is shutting down the guest
    ###
    def finish(self):
        os.system(self.procmon + ' /Terminate')
        os.system('if exist ' + self.procmonfile + '.pml ' + self.procmon + ' /OpenLog ' + self.procmonfile + '.pml /SaveAs ' + self.procmonfile + '.csv')

        return True

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s