Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@Tera0017S1
Last active February 14, 2023 00:08
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Tera0017S1/c62a928a911442e1509d127fdcd93b71 to your computer and use it in GitHub Desktop.
Save Tera0017S1/c62a928a911442e1509d127fdcd93b71 to your computer and use it in GitHub Desktop.
Clop-Linux ransomware variant files decryption.

Cl0p ELF Variant Files Decryptor

Python3 script which decrypts files encrypted by flawed Cl0p ELF variant.

More info regarding Cl0p ELF variant and how decryptor was created at SentinelOne post.

Usage

python3 clop_linux_file_decr.py --help
========================================
SentinelOne Cl0p ELF variant Decryptor.
Author: @Tera0017/@SentinelOne
Link: https://s1.ai/Clop-ELF
========================================
usage: clop_linux_file_decr.py [-h] [--elfile ELFILE] [--keys KEYS] [--rc4key RC4KEY]

Python3 script which decrypts files encrypted by flawed Cl0p ELF variant. More info regarding Cl0p
ELF variant and how decryptor was created at https://s1.ai/Clop-ELF

optional arguments:
  -h, --help       show this help message and exit
  --elfile ELFILE  ELF Cl0p Binary, is used to retrieve "RC4 master key" else default is used for
                   decryption, or provided with "--rc4key" argument.
  --keys KEYS      File containing result of "$ find / -name *.$cl0p_extension -print 2>/dev/null >
                   cl0p_keys.txt". Run with sudo if needed.
  --rc4key RC4KEY  RC4 master key for decryption of clop key files. If --elf is provided script will
                   dynamically retrieve it.

author:@Tera0017/@SentinelOne

Requirements

  • arc4, tested version "0.0.4"

Support

In case something is wrong or not working as supposed to please feel free to contact @Tera0017.

"""
Author: @Tera0017/@SentinelOne
Description: Clop-Linux ransomware variant files decryption.
Link: https://s1.ai/Clop-ELF
Execution help: $ python3 clop_linux_file_decr.py --help
"""
import argparse
import glob
import os.path
import struct
from arc4 import ARC4
def parse_arguments() -> argparse.Namespace:
"""
Parses commandline parameters if any.
@return: returns parse_args() result -> argparse.Namespace
"""
description = """Python3 script which decrypts files encrypted by flawed Cl0p ELF variant.
More info regarding Cl0p ELF variant and how decryptor was created at https://s1.ai/Clop-ELF
"""
print('=' * 40)
print('SentinelOne Cl0p ELF variant Decryptor.\nAuthor: @Tera0017/@SentinelOne\nLink: https://s1.ai/Clop-ELF')
print('=' * 40)
parser = argparse.ArgumentParser(
prog='clop_linux_file_decr.py',
description=description,
epilog='author:@Tera0017/@SentinelOne')
parser.add_argument('--elfile', default=None, help='ELF Cl0p Binary, is used to retrieve "RC4 master key" else default is used for decryption.')
parser.add_argument('--keys', default=None, help='File containing result of "$ find / -name *.$cl0p_extension -print 2>/dev/null > cl0p_keys.txt". Run with sudo if needed.')
parser.add_argument('--rc4key', default=None, help='RC4 master key for decryption of clop key files. If --elf is provided script will dynamically retrieve it.')
return parser.parse_args()
def message(msg: str) -> None:
"""
@param msg: message to print
@return: None
"""
print(f'* {msg}')
class ClopELFDecryptor:
def __init__(self, filepath=None, clop_find_file=None, rc4_master_key=None):
"""
@param filepath: str, filepath of cl0p elf variant ransomware found in encrypted machine.
@param clop_find_file: str, filepath containing result of "$ find / -name *.$cl0p_extension -print 2>/dev/null > clop_keys.txt"
@param rc4_master_key: str, rc4 master key is not extracted well from cl0p elf binary.
"""
# if elf sample does not exist tries with observed key.
self.elfdata = open(filepath, 'rb').read() if filepath is not None else None
# result of "$ find / -name *.$cl0p_extension -print 2>/dev/null > clop_keys.txt" containing clop keys
self.clop_keys_file = clop_find_file
self.rc4_master_key = rc4_master_key
# clop filekeys extension.
self.clop_ext = ".C_I_0P"
# RC4 generated key size.
self.rc4_gen_key_size = 0x75
def get_rc4_master_key(self) -> bytes:
"""
Retrieves RC4 master key from ELF binary. If elf is not found returns default observed key.
@return: bytes, RC4 master key.
"""
if self.rc4_master_key is not None:
message('User provided RC4 master key')
return self.rc4_master_key
elif self.elfdata is None:
message('Retrieved previous observed RC4 key.')
# observed RC4 master key
return b'Jfkdskfku2ir32y7432uroduw8y7318i9018urewfdsZ2Oaifwuieh~~cudsffdsd'
# dirty way to retrieve master key.
f = b'/root'
idx = self.elfdata.find(f) + len(f) + 1
return self.elfdata[idx: idx + 100].lstrip(b'\x00').split(b'\x00')[0]
def get_clop_keys(self) -> list:
"""
Based on the filekeys clop extension retrieves all the encrypted files from the machine.
* If you need to speed up process add specific folders where encryption took place.
* Or pass result of "$ find / -name *.$cl0p_extension -print 2>/dev/null > clop_keys.txt"
as argument to "--keys".
@return: list, encrypted filepaths
"""
if self.clop_keys_file is not None:
# get clop keys "$ find / -name *.$cl0p_extension -print 2>/dev/null > clop_keys.txt"
with open(self.clop_keys_file, 'r') as hfile:
lines = hfile.readlines()
return [l.strip() for l in lines if l.strip()]
# enumerate all folders and find clop extension files.
message(f'Searching for encrypted file extension {self.clop_ext}.')
message('This operation will take several minutes...')
message('To speed up process prefer to use "--keys", parameter.')
return glob.glob(f'/**/*{self.clop_ext}', recursive=True)
def decrypt(self) -> None:
"""
Main function decrypts Clop-ELF encrypted files.
@return: None
"""
message('Starting decryption process.')
# 1. Retrieve RC4 "master-key".
rc4_master_key = self.get_rc4_master_key()
message(f'RC4 Master Key: "{rc4_master_key}"')
# 2. Read all $filename.$clop_extension.
file_keys = self.get_clop_keys()
message(f'Encrypted Files: {len(file_keys)}')
for file_key in file_keys:
message(f'File: {file_key}')
with open(file_key, 'rb') as hfile:
file_key_data = hfile.read()
# 3. Decrypt with RC4 using the RC4 "master-key", the generated RC4 key.
cipher = ARC4(rc4_master_key)
file_rc4_key = cipher.decrypt(file_key_data)[:self.rc4_gen_key_size]
# getting encrypted file size (if file is written again after encryption then
# encrypted_file_size != file_size
size_off = 0x75 + 0x58 + 0x8 + 0x4 + 0x4
try:
encr_file_size = struct.unpack('Q', file_key_data[size_off: size_off + 0x8])[0]
except struct.error:
message(f'[ERROR] Clop key file seems corrupted: {file_key}')
continue
encr_file = file_key.replace(self.clop_ext, '')
# decrypted files have extension '.decrypted_by_S1', once validated can delete and replace encrypted.
decr_file = file_key.replace(self.clop_ext, '.decrypted_by_S1')
if os.path.isfile(encr_file):
with open(encr_file, 'rb') as hfile:
encr_file_data = hfile.read()
else:
message(f'[ERROR] Unable to find encrypted file: {encr_file}')
continue
# 4. Decrypt $filename with RC4 using the generated RC4 key.
cipher = ARC4(file_rc4_key)
decrypted_file_data = cipher.decrypt(encr_file_data[:encr_file_size]) + encr_file_data[encr_file_size:]
# 5. Write decrypted to $filename.
with open(decr_file, 'wb') as hfile:
hfile.write(decrypted_file_data)
message(f'Decrypted: {decr_file}')
if __name__ == '__main__':
# parsing command line arguments for the decryptor. Use --help for more information
parsed = parse_arguments()
ClopELFDecryptor(parsed.elfile, parsed.keys, parsed.rc4key).decrypt()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment