amccormack.net

Things I've learned and suspect I'll forget.

(More) Safely Downloading Malicious Content 2013-05-19

I was on malwr.com the other day and I found a sample I thought was interesting. I went to download it and it triggered my antivirus and chrome wouldn't accept the download. A lot of the times, malicious files are saved in encrypted zip files with the password 'infected'. In this case, the malicious file is dropped with no protection or obfuscation. The first solution I had for this problem was to change my networking settings in my virtual machine to allow internet access so that I could download the file. The problem with this approach is it is somewhat time consuming, and I have to remember to change my networking settings when I am done. In addition, I have found VMware to be a bit finicky when changing networking settings.

At first, I wanted to download the file in memory, and then put it into a password protected zip file. This isn't possible, though, because python's zip module does not support the use of passwords when writing zips (but it can use passwords when reading). Using a 3rd party utility wouldn't work either, because it would require writing the malicious file to disk (which would trigger the AV) before actually making the zip file. Because I have python installed on all my analysis VMs, I decided the following approach would work:

  1. Download the malicious file in memory
  2. Base64 encode the malicious file. This should prevent the new file from matching AV signatures.
  3. Write the base64 encoded malicious file to a new python file. The new python file contains the encoded malware as well as functionality to decode and drop the malware

Downloading the malicious file to memory

To download a file over HTTP to memory you can use the urllib2 module.

import urllib2
response = urllib2.urlopen('http://awebsiteyouwanttodownload.com/thefileyouwant.bin')
data = response.read()

Malwr.com is run over HTTPS, and requires you to log in, so while the code above works for most websites, it won't work for us because we have to log in. To download the the malicious file from malwr.com, we can use the mechanize library. Mechanize is not only great at emulating a browser, but also providing hours of frustration as you search the internet for decent documentation. The code I used to download with mechanize is:

import mechanize
browser = mechanize.Browser()
login_url = 'https://malwr.com/account/login/'
browser.open(login_url)
browser.select_form(nr=0)
browser.form['username'] = malwr_username #argument from function
browser.form['password'] = malwr_password #argument from function
#log into the site
browser.submit()
response = browser.response()
response = response.read()
if 'View Profile' not in response:
    print '"View Profile" was not found in the response.'+\
          'Your credentials may not be valid. Here is the response:'
    print response
    return
#'Logged in. Trying to download malicious file...'
response = browser.open(url_to_download)
data = response.read()

Base64 Encoding the File

Base64 Encoding in python is a piece of cake.

import base64
encoded_data = base64.encodestring(data)

Writing a Self-Extracting Python File

I had a few goals for the self extracting python file. First, I wanted to produce the original binary. But I also wanted to put in some protection mechanism so that a user doesn't accidently self extract the file. So I use the following code in my self extracting python file:

def extract():
    import base64
    filename = '%(filename)s'
    filedata = '%(filedata)s'
    f = open(filename,'wb')
    f.write(base64.decodestring(filedata))
    f.close()
if __name__ == '__main__':
    import sys
    if len(sys.argv) > 1:
        extract()
    else:
        #prevent accidental double click
        warning = 'Warning, this will drop malware on your machine.'
        warning += 'usage:%%s infectme' %% sys.argv[0]
        print warning
        raw_input('hit enter to close')

This code will be included in the original downloader as a string. That is why there are named substitutions for filename and filedata as well as escaping in the string when printing the warning.

The Malwr.com Downloader Script

Here is my downloader script in its entirety. In its current form, it prompts the user for the malwr.com password and username, but there is no reason why you couldn't hardcode that into the file.

def makeSelfExtracting(url_to_download, malwr_username, malwr_password):
    import base64
    import mechanize
    import urlparse
    import string
    #NB: Do not tab the string below.   
    extract_program = ''+\
'''
def extract():
    import base64
    filename = '%(filename)s'
    filedata = ''+\
    \'''%(filedata)s\'''
    f = open(filename,'wb')
    f.write(base64.decodestring(filedata))
    f.close()
if __name__ == '__main__':
    import sys
    if len(sys.argv) > 1:
        extract()
    else:
        warning = 'Warning, this will drop malware on your machine.'
        warning += 'usage:%%s infectme' %% sys.argv[0]
        print warning
        raw_input('hit enter to close')
'''

    filename = 'malware.bin' #default
    parts = urlparse.urlparse(url_to_download)
    if len(parts.path) > 0:
        if '/' in parts.path:
            filename = parts.path
            if filename[-1] == '/':
                filename = filename[:-1]
            filename = filename[filename.rindex('/')+1:]
        else:
            filename = parts.path
    #sanitize unwanted characters and enforce max length
    max_length = 30
    valid_chars = "-_.%s%s" % (string.ascii_letters, string.digits)
    filename = ''.join(c for c in filename if c in valid_chars)[:max_length]

    browser = mechanize.Browser()
    login_url = 'https://malwr.com/account/login/'
    browser.open(login_url)
    browser.select_form(nr=0)
    browser.form['username'] = malwr_username #argument from function
    browser.form['password'] = malwr_password #argument from function

    print 'Trying to log in...'

    browser.submit()
    response = browser.response()
    response = response.read()
    if 'View Profile' not in response:
        print '"View Profile" was not found in the response.'+\
              'Your credentials may not be valid. Here is the response:'
        print response
        return
    print 'Logged in. Trying to download...'
    response = browser.open(url_to_download)
    rawdata = response.read()
    filedata = base64.encodestring(rawdata)



    extract_filename = 'extract_%(filename)s.py' % {'filename' : filename}
    extract_file = open(extract_filename,'wb')
    extract_file.write(extract_program % {'filename':filename, 'filedata':filedata})
    extract_file.close()

    print 'Wrote downloaded file to: %s' % extract_filename

if __name__ == '__main__':
    import getpass
    urlToDownload = raw_input('url: ')
    username = raw_input('username: ')
    password1 = ''
    password2 = ';'
    while password1 != password2:
        password1 = getpass.getpass('Password (will not echo):')
        password2 = getpass.getpass('Password again:')
    makeSelfExtracting(urlToDownload,username,password1)
    raw_input('Hanging out!')

published on 2013-05-19 by almac