LimeRAT
Open Source gone wrong with this RAT
Overview
This is a trash rat that started as an open source project but has since been adopted for various campaigns, both ecrime and espionage (lol).
References
- LimeRAT Malware Analysis: Extracting the Config
-
https://github.com/NYAN-x-CAT/Lime-RAT
### Sample
-
6d08ed6acac230f41d9d6fe2a26245eeaf08c84bc7a66fddc764d82d6786d334
UnpacMe
import base64
from Crypto.Cipher import AES
from Crypto.Util import Padding
import hashlib
data = b'At2C9Qk3d7SA7+3KqcaDzAGk3UjkKgbD1CC2tXzgWnvXISV8gQCyC4DHdLLTVSy/'
key = b'20.199.13.167'
m = hashlib.md5()
m.update(key)
hex_key = m.digest()
full_key = hex_key[:15] + hex_key + b'\x00'
data_raw = base64.b64decode(data)
cipher = AES.new(full_key, AES.MODE_ECB)
tmp_out = cipher.decrypt(data_raw)
out = Padding.unpad(tmp_out, block_size=AES.block_size, style='pkcs7')
print(out)
def decrypt(data, key):
m = hashlib.md5()
m.update(key)
hex_key = m.digest()
full_key = hex_key[:15] + hex_key + b'\x00'
try:
data_raw = base64.b64decode(data)
cipher = AES.new(full_key, AES.MODE_ECB)
tmp_out = cipher.decrypt(data_raw)
out = Padding.unpad(tmp_out, block_size=AES.block_size, style='pkcs7')
except:
return None
return out
import re
import struct
from dnfile import dnPE
from dnfile.mdtable import MethodDefRow
import dnfile
from dnfile.enums import MetadataTables
from dncil.cil.body import CilMethodBody
from dncil.cil.error import MethodBodyFormatError
from dncil.clr.token import Token, StringToken, InvalidToken
from dncil.cil.body.reader import CilMethodBodyReaderBase
class DnfileMethodBodyReader(CilMethodBodyReaderBase):
def __init__(self, pe, row):
""" """
self.pe = pe
self.offset = self.pe.get_offset_from_rva(row.Rva)
def read(self, n):
""" """
data = self.pe.get_data(self.pe.get_rva_from_offset(self.offset), n)
self.offset += n
return data
def tell(self):
""" """
return self.offset
def seek(self, offset):
""" """
self.offset = offset
return self.offset
class DnfileParse():
DOTNET_META_TABLES_BY_INDEX = {table.value: table.name for table in MetadataTables}
@staticmethod
def read_dotnet_user_string(pe, token):
"""read user string from #US stream"""
try:
user_string = pe.net.user_strings.get_us(token.rid)
except UnicodeDecodeError as e:
return InvalidToken(token.value)
if user_string is None:
return InvalidToken(token.value)
return user_string.value
@staticmethod
def resolve_token(pe, token):
""" """
if isinstance(token, StringToken):
return DnfileParse.read_dotnet_user_string(pe, token)
table_name = DnfileParse.DOTNET_META_TABLES_BY_INDEX.get(token.table, "")
if not table_name:
# table_index is not valid
return InvalidToken(token.value)
table = getattr(pe.net.mdtables, table_name, None)
if table is None:
# table index is valid but table is not present
return InvalidToken(token.value)
try:
return table.rows[token.rid - 1]
except IndexError:
# table index is valid but row index is not valid
return InvalidToken(token.value)
@staticmethod
def read_method_body(pe, row):
""" """
return CilMethodBody(DnfileMethodBodyReader(pe, row))
@staticmethod
def format_operand(pe, operand):
""" """
if isinstance(operand, Token):
operand = DnfileParse.resolve_token(pe, operand)
if isinstance(operand, str):
return f'"{operand}"'
elif isinstance(operand, int):
return hex(operand)
elif isinstance(operand, list):
return f"[{', '.join(['({:04X})'.format(x) for x in operand])}]"
elif isinstance(operand, dnfile.mdtable.MemberRefRow):
if isinstance(operand.Class.row, (dnfile.mdtable.TypeRefRow,)):
return f"{str(operand.Class.row.TypeNamespace)}.{operand.Class.row.TypeName}::{operand.Name}"
elif isinstance(operand, dnfile.mdtable.TypeRefRow):
return f"{str(operand.TypeNamespace)}.{operand.TypeName}"
elif isinstance(operand, (dnfile.mdtable.FieldRow, dnfile.mdtable.MethodDefRow)):
return f"{operand.Name}"
elif operand is None:
return ""
return str(operand)
@staticmethod
def get_instruction_text(pe, insn):
return "{:04X}".format(insn.offset) \
+ " " \
+ f"{' '.join('{:02x}'.format(b) for b in insn.get_bytes()) : <20}" \
+ f"{str(insn.opcode) : <15}" \
+ DnfileParse.format_operand(pe, insn.operand)
file_data = open('/tmp/old.bin','rb').read()
pe = dnfile.dnPE(data=file_data)
def ctor_hunt(pe):
for row in pe.net.mdtables.MethodDef:
if not row.ImplFlags.miIL or any((row.Flags.mdAbstract, row.Flags.mdPinvokeImpl)):
# skip methods that do not have a method body
continue
try:
body = DnfileParse.read_method_body(pe, row)
except MethodBodyFormatError as e:
print(e)
continue
if not body.instructions:
continue
if 'ctor' not in row.Name:
continue
if len(body.instructions) < 30:
continue
arr_data = None
print('\n\n\n')
ldstr_count = 0
for ptr in range(0,len(body.instructions)):
insn = body.instructions[ptr]
insn_text = DnfileParse.get_instruction_text(pe, insn)
print(insn_text)
if str(insn.opcode) == 'ldstr':
ldstr_count += 1
print(f"ldstr count: {ldstr_count}")
raw_config = {}
config_ptr = 0
if ldstr_count == 15:
# Found our config!
for ptr in range(0,len(body.instructions)):
insn = body.instructions[ptr]
if str(insn.opcode) == 'ldstr':
raw_config[config_ptr] = DnfileParse.resolve_token(pe, insn.operand)
config_ptr += 1
break
return raw_config
raw_config = ctor_hunt(pe)
# Public Shared HOST As String 'IP
# Public Shared PORT As Integer 'PORT
# Public Shared EncryptionKey As String = "NYANCAT" 'encryption/decryption key
# Public Shared ENDOF As String = "|'N'|" 'endof
# Public Shared SPL As String = "|'L'|" 'split data
# Public Shared EXE As String = "CLIENT.exe" 'client drop name
# Public Shared MTX As Threading.Mutex
# Public Shared USB As Boolean = False 'usb spread
# Public Shared PIN As Boolean = False 'pin spread
# Public Shared ANTI As Boolean = False 'anti virtual machines
# Public Shared DROP As Boolean = False 'drop and install client
# Public Shared PATH1 As String = "Temp" 'Main Folder
# Public Shared PATH2 As String = "\Lime\" 'Sub Folder
# Public Shared fullpath = Environ(PATH1) & PATH2 & EXE
# Public Shared BTC_ADDR As String = "THIS IS YOUR BTC 1234567890" 'Bitcoin address
# Public Shared DWN_CHK As Boolean = True 'downloader once
# Public Shared DWN_LINK As String = "" 'downloader link
# Public Shared Delay As Integer = "3" 'Delay AKA Sleep
config_enum = {'pastebin':0,
'key':1,
'endof':2,
'split_data':3,
'client_drop_name':4,
'usb_spread':5,
'pin_spread':6,
'anti_virtual_machines':7,
'drop_and_install_client':8,
'main_folder':9,
'sub_folder':10,
'bitcoin_address':11,
'downloader_once':12,
'download_link':13,
'sleep':14 }
pastebin_url = decrypt(raw_config[config_enum['pastebin']].encode('utf-8'), raw_config[config_enum['key']].encode('utf-8'))
pastebin_url
def get_config_data(pe):
for row in pe.net.mdtables.MethodDef:
if not row.ImplFlags.miIL or any((row.Flags.mdAbstract, row.Flags.mdPinvokeImpl)):
# skip methods that do not have a method body
continue
try:
body = DnfileParse.read_method_body(pe, row)
except MethodBodyFormatError as e:
print(e)
continue
if not body.instructions:
continue
if 'ctor' not in row.Name:
continue
if len(body.instructions) < 30:
continue
arr_data = None
#print('\n\n\n')
ldstr_count = 0
for ptr in range(0,len(body.instructions)):
insn = body.instructions[ptr]
insn_text = DnfileParse.get_instruction_text(pe, insn)
#print(insn_text)
if str(insn.opcode) == 'ldstr':
ldstr_count += 1
#print(f"ldstr count: {ldstr_count}")
raw_config = {}
config_ptr = 0
if ldstr_count == 15:
# Found our config!
for ptr in range(0,len(body.instructions)):
insn = body.instructions[ptr]
if str(insn.opcode) == 'ldstr':
raw_config[config_ptr] = DnfileParse.resolve_token(pe, insn.operand)
config_ptr += 1
break
return raw_config
def print_config(file_path):
file_data = open(file_path,'rb').read()
pe = dnfile.dnPE(data=file_data)
raw_config = get_config_data(pe)
pastebin_url = decrypt(raw_config[config_enum['pastebin']].encode('utf-8'), raw_config[config_enum['key']].encode('utf-8'))
print(f"c2: {pastebin_url}")
for k,v in config_enum.items():
if v > 1:
print(f"{k}: {raw_config[v]}")
print_config('/tmp/new.bin')
import os
# assign directory
directory = '/tmp/limes/'
# iterate over files in
# that directory
for filename in os.listdir(directory):
f = os.path.join(directory, filename)
# checking if it is a file
if os.path.isfile(f):
print("\n\n\n")
print(f)
try:
print_config(f)
except:
print("failed!")