Skip content
LRQA Cyber Labs

Repurposing Real TTPs for use on Red Team Engagements

Brendan Ortiz

I recently read an interesting article by Elastic. It provides new analysis of a sophisticated, targeted campaign against several organizations. This has been labelled ‘Bleeding Bear’. The articles analysis of Bleeding Bear tactics, techniques and procedures left me with a couple of thoughts. The first was, “hey, I can probably perform some of these techniques!” and the second was, “how can I improve on them?”

With that in mind, I decided to create a proof of concept for elements of Operation Bleeding Bear TTPs. This is not an exact replica, or even an attempt to be an exact replica, because I found a lot of the actions the threat actors were performing were unnecessary for my objectives. I dub this altered set of techniques BreadBear.

Where there are changes, I’ll point them out along with the reasons for them. To help you to follow along with this blog post, I have posted the code to my GitHub repository, which you are welcome to download, examine, and run. This post will be separated into three distinct sections which will mark each stage of the campaign; initial payload delivery, payload execution, and finally document encryption.

Stage 1 – Initial Payload and Delivery

The first section of the Bleeding Bear campaign is described as a WhisperGate MBR wiper. Essentially, this technique will make any machine affected unbootable on the next boot operation. The attackers replace the contents of the MBR with a message that usually says something along the lines of “To get your data back, send crypto currency to xyz address”. I didn’t implement this because it’s a proof of concept and I didn’t want to wreck my development VM 100 times to test this out.

Instead, I created a stage 1 as a fake phishing scenario to be the initial delivery of the payload. The payload itself is delivered via a static webpage that upon loading will execute JavaScript to automatically download the stage 2 payload. However, it’s up to the end user to click past a few different warnings to run the executable. I’d like to mention that initial payload delivery is probably my weakest point in all of this, so if you’re reading this and can think of a million ways to improve upon this technique, please reach out to me on twitter or LinkedIn with recommendations.

The initial payload delivery is facilitated by a static web page with some JavaScript that has the user automatically download the targeted file upon loading of the page. The webpage itself is hosted by IPFS (Inter-Planetary File System). Once you have IPFS installed on your system, all you need to do is import your web-pages root folder to IPFS and retrieve the URL to your files. This process is very simple and looks as follows.

Once IPFS is installed, first hit Import, then Folder.

Graphical user interface, text, application, Teams Description automatically generated

Next, when the browser window opens, you’ll want to browse to your static webpages root folder. A sample provided by Black Hills Information Security is included in the GitHub repo under x64/release. With tools like zphisher you can create your own, more complex, phishing sites.

Once your folder has been imported, your files will be shared via the IPFS peer-to-peer network. Additionally, they will be reachable from a common gateway that you can set in your IPFS settings. IPFS has a list of gateways that can be used, located on this site. However, to retrieve the URL that can access your files you’ll want to right click on the folder, click share link, and then copy.

Graphical user interface, application, Word Description automatically generated

Graphical user interface, application Description automatically generated

Then, all you need to do is distribute this link with the proper context for your target. When the user clicks your link, they’ll be presented with the following page:

Graphical user interface, application, Word Description automatically generated

On Chrome, if they press keep, the file finishes the download and is ready for execution. The JavaScript code that performs the automatic download to force Chrome to ask to keep the file is shown below:

Text Description automatically generated

An element variable is initialized to the download href tag. Then, we set the element to our executable file named MicrosoftUpdater.exe. Finally, we click the element programmatically which starts the download process. For more information about how IPFS can be used as a malware hosting service, read this blog by Steve Borosh who was the inspiration of the initial payload delivery.

Stage 2 – Payload Execution

Once the user has been successfully phished, phase 1 has been completed and we transition into phase 2, with the execution of stage2.exe or, in this case, the MicrosoftUpdater.exe program. In the Bleeding Bear campaign, the heavy lifting is performed by the stage2.exe binary, which uses Discord to download and execute malicious programs. My stage 2 binary also utilizes the Discord CDN to download, reflectively load, and execute stage 3. However, that’s pretty much where the comparison stops.

 

The stage 2 Discord downloader in the Bleeding Bear campaign downloads an obfuscated .NET assembly and uses reflection to load it. However, mine is a compiled PE binary. Additionally, the Bleeding Bear campaign performs a lot of operations which require either a UAC bypass or a UAC accept from the user to perform. These actions include writing a VBScript payload to disk which will set a Defender exclusion path on the C drive.

 

"C:\Windows\System32\WScript.exe""C:\Users\jim\AppData\Local\Temp\Nmddfrqqrbyjeygggda.vbs"
powershell.exe Set-MpPreference -ExclusionPath 'C:\'

Then the payload will download and run AdvancedRun in a higher integrity to stop Windows Defender and delete all files in the Windows Defender directory.

"C:\Users\jim\AppData\Local\Temp\AdvancedRun.exe" /EXEFilename "C:\Windows\System32\sc.exe" `
/WindowState 0 /CommandLine "stop WinDefend" /StartDirectory "" /RunAs 8 /Run
"C:\Users\jim\AppData\Local\Temp\AdvancedRun.exe" `
/EXEFilename "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" /WindowState 0 `
/CommandLine "rmdir 'C:\ProgramData\Microsoft\Windows Defender' -Recurse" `
/StartDirectory "" /RunAs 8 /Run

Next, InstallUtil.exe is downloaded to the user’s Temp directory. The InstallUtil program is used for process hollowing. This means that the executable is started in a suspended state, then the memory of the process is overwritten with a malicious payload which is then executed instead. To the computer, it will look like InstallUtil is running, however, it is actually the payload. In the Bleeding Bear campaign, that malicious payload happens to be a File Corruptor, which overwrites 1MB of the byte 0xCC over all files that end with the following extensions:

.3DM .3DS .602 .7Z .ACCDB .AI .ARC .ASC .ASM .ASP .ASPX .BACKUP .BAK .BAT .BMP .BRD .BZ .BZ2 .C .CGM .CLASS .CMD .CONFIG .CPP .CRT .CS .CSR .CSV .DB .DBF .DCH .DER .DIF .DIP .DJVU.SH .DOC .DOCB .DOCM .DOCX .DOT .DOTM .DOTX .DWG .EDB .EML .FRM .GIF .GO .GZ .H .HDD .HTM .HTML .HWP .IBD .INC .INI .ISO .JAR .JAVA .JPEG .JPG .JS .JSP .KDBX .KEY .LAY .LAY6 .LDF .LOG .MAX .MDB .MDF .MML .MSG .MYD .MYI .NEF .NVRAM .ODB .ODG .ODP .ODS .ODT .OGG .ONETOC2 .OST .OTG .OTP .OTS .OTT .P12 .PAQ .PAS .PDF .PEM .PFX .PHP .PHP3 .PHP4 .PHP5 .PHP6 .PHP7 .PHPS .PHTML .PL .PNG .POT .POTM .POTX .PPAM .PPK .PPS .PPSM .PPSX .PPT .PPTM .PPTX .PS1 .PSD .PST .PY .RAR .RAW .RB .RTF .SAV .SCH .SHTML .SLDM .SLDX .SLK .SLN .SNT .SQ3 .SQL .SQLITE3 .SQLITEDB .STC .STD .STI .STW .SUO .SVG .SXC .SXD .SXI .SXM .SXW .TAR .TBK .TGZ .TIF .TIFF .TXT .UOP .UOT .VB .VBS .VCD .VDI .VHD .VMDK .VMEM .VMSD .VMSN .VMSS .VMTM .VMTX .VMX .VMXF .VSD .VSDX .VSWP .WAR .WB2 .WK1 .WKS .XHTML .XLC .XLM .XLS .XLSB .XLSM .XLSX .XLT .XLTM .XLTX .XLW .YML .ZIP

I found a lot of these steps to be unnecessary; therefore, I did not perform them. I wanted to leave as minimal trace on the system as possible. I also didn’t see a need for a high integrity process to be spawned to perform ancillary functions, such as deleting Windows Defender, when we can just bypass it. However, my stage 2 code does contain a failed UAC bypass even though it is not used.

The differences between my stage 2/3 will become apparent as we walk through the code. Before we start walking through the code, I’d like to mention the features of my stage 2 so that when you see auxiliary function names through the code – it’ll make sense. My stage 2 does the following:

  • Dynamically retrieves function pointers to any Windows APIs used maliciously
    • Has a custom GetProcAddress() & GetModuleHandle() implementation to retrieve function calls
    • Custom LoadLibrary() function that will dynamically retrieve the pointer to LoadLibraryW() at each run.
  • Hides the console window at startup.
  • Has a self-delete function which will delete the file on disk at runtime once the PE has been loaded into memory and executed.
  • Unhooks DLLs using the system calls for native windows APIs (using the Halo’s Gate technique).
  • Disables Event Tracing for Windows (ETW).
  • Uses a simple XOR decrypt function to decrypt strings such as Discord CDN URLs at runtime.
  • Performs a web request to Discord CDN using Windows APIs to retrieve stage3 in a base64 encoded format.
  • Reflectively loads a stage 3 payload in memory and executes.
  • Lazy attempts at string obfuscation.

With that said, I will only cover techniques I found particularly interesting or important in this blog post for brevity.

A picture containing text Description automatically generated

First, we see a dynamically resolved ShowWindow() used to hide the window. Next, we see SelfDelete() which will delete itself from disk even if the executable is running still. I believe this function is a neat trick and worth going over.

A picture containing text Description automatically generated

First, we dynamically resolve pointers to the Windows APIs CloseHandle(), SetFileInformationByHandle(), CreateFileW(), and GetModuleFileNameW(). Following that we create some variables to store necessary information.

Text Description automatically generated

Next, we resolve the path that our stage 2 is downloaded to disk using GetModuleFileNameW(). We then obtain a handle to stage 2 using CreateFileW() and the OPEN_EXISTING flag. We create a FILE_RENAME_INFO structure and populate its contents with the rename string “:breadman” and a flag to replace the file if it exists already. We make a call to setFileInformationByHandle() using our file handle, our rename information structure, and the FileRenameInfo() flag. This renaming of the file handle will allow us to delete the file on disk. This is because the file lock occurs on the renamed file handle. We can then reopen a file handle to the original file on disk and delete it. Thus, we close our handle and reopen it using the original filename path. After, we call SetFileInformationByHandle() again with a File Disposition Info structure and the DeleteFileW() flag set to true. Finally, we close our file handle, which will cause the file to be deleted from disk and we continue our code execution back in main.

With that done, we perform the unhooking of our DLLs using System Calls and Native APIs to bypass AV/EDR hooking. I won’t cover this in depth, however, the same exact code is used in another of my blog posts.

The next important functions in main() are disabling event tracing for windows and decrypting the encrypted Discord CDN strings.

Text Description automatically generated

The disabling of event tracing for windows is simple (function template credits to Sektor7 institute):

Text Description automatically generated

First, we obtain a handle to the function EventWrite() in the NTDLL.dll library. Then we change the memory protections of a single page to execute+read+write, copy in the byte equivalent to xor rax,rax ; ret at the first four bytes. This will eventually set the return value of the function to zero (probably indicating success) and then returning. The function essentially returns without performing any actions, and therefore disables event tracing for windows.

I won’t go over the XOR decryption since it’s a rudimentary technique. However, I will go over how you can use Discord CDN as a MDN ‘Malware Distribution Network’.

In Discord, anyone can create their own private server to upload files, messages, pictures, etc. to. However, access to anything uploaded, even to a private server, does not require authentication. One caveat to keep in mind, however, is that executables need to be converted to a base64 string. When I downloaded them manually from the CDN, I ran into problems (likely compression) where the size was smaller when I downloaded it using APIs. The same problem did not occur with text files. Therefore, I put the base64 encoded PE file into a text file and downloaded that instead. This looks like the following:

Graphical user interface, text, application Description automatically generated

Once you’ve uploaded the file, you can right click the download link at the bottom of the above screenshot, then select Copy Link.

Graphical user interface, text, application, website Description automatically generated

Once that has been completed, you have your Discord CDN URL that is accessible from anywhere in the world without authentication. Additionally, these URLs are valid forever even if the file has been deleted from the server.

It’s as simple as that. Obviously, there might be some red team infrastructure you’d want to standup in-between the CDN and the target host to redirect any potential security analysts who go snooping, but it’s an effective method for serving up malware.

Next, to finish up main(), we perform the following tasks. We first parse our Discord CDN URL that was just decrypted into separate parts. Then we perform a request to download our targeted file by calling the do_request() function using the parsed URL pieces.

Text Description automatically generated

We open the do_request() function by dynamically resolving pointers to any Windows APIs we will use to perform the HTTPS request to Discord. We then follow that up by initializing variables we’re going to use as parameters to the following WinInet function calls.

Graphical user interface, text Description automatically generated

There aren’t too many interesting pieces of information regarding our Internet API calls, aside from the InternetOpenA() and the HttpOpenRequestA() calls. For the first, we specify INTERNET_OPEN_TYPE_DIRECT to ensure that there is no proxy. We can put default options here to specify the default system proxy settings. Additionally, for HttpOpenRequestA() we specify the INTERNET_FLAG_NO_CACHE_WRITE flag to ensure the call doesn’t cache the file download in the %LocalAppData%\Microsoft\Windows\INetCache. Next, we make a call to HttpQueryInfoA() with the HTTP_QUERY_CUSTOM flag. This ensures that we can receive the value of a custom HTTP Response header that we got back when we made our HTTP request. The specified custom query header is passed to do_request() from main and is the content length header. We will use this value to allocate memory for our stage 3 payload that was just downloaded.

Text Description automatically generated

We now allocate memory for our downloaded file using malloc() and the size of our content length value. Following that, we make a call to InternetReadFile() function to load the base64 encoded data into our allocated memory space. Once it has been successfully loaded, we make a call to pCryptStringToBinaryW(), which will convert our base64 encoded data into the byte code that makes up our stage 3 payload. We then free the allocated memory region and call the final function of do_request() which is reflectiveLoader().

Text Description automatically generated

I won’t go over the reflective loading / execution of our PE File in memory because I’ve written a previous blog post about it already. However, I used the code from this resource as the base of my loader.

Stage 3 – File Corruptor

Stage 3 probably has the biggest differences in functionality from the Bleeding Bear campaign. The stage 3 of the Bleeding Bear campaign is a “File Corruptor”, and not an encryption scheme. What this means is that the Bleeding Bear campaign’s third stage will overwrite the first 1mb of data of all files it finds on disk that are not critical to system operation. If the file is smaller than 1mb of data, it will overwrite the whole file and add the difference to make a 1mb file. As far as I know, the campaign does not download the unaffected files before overwriting, therefore all data will be lost. This file corruptor is also not a reflectively loaded PE file. Instead, the file corruptor is likely a piece of shellcode that is executed via a process hollowing technique. The stage 2 of the Bleeding Bear campaign downloads InstallUtil.exe to disk, executes it in a suspended state, overwrites the process memory to the corruptor shellcode, and then resumes the process execution.

The BreadBear technique uses a file encryptor rather than a corruptor. I decided to use an encryptor because eventually I plan to add the functionality of downloading the unencrypted data, the keys used to encrypt the file, and to add a decryption function. I believe this would be beneficial to clients who want to test against a simulated ransomware campaign. Additionally, since I am reflectively loading the stage 3 executable in memory, there’s no need to perform process hollowing, or even writing the InstallUtil binary to disk. I believe my approach is more operationally secure than the Bleeding Bear’s alternative.

Additionally, with my approach you can swap out your stage 3 from file encryptor to implant shellcode. I have successfully tested my stage 3 payload with the binary from my previous blog post BreadMan Module Stomping. The only requirement for the reflective loading is that the file chosen is compiled in a valid PE format.

 

With that being said, let’s dive into stage 3: the file encryptor.

 

I would like to note that no attempts at obfuscation or evasion were made in the stage 3 payload. This is because it is being loaded into a process memory space that was unhooked from AV/EDR, and ETW patching have already occurred, so it is not needed.

Text Description automatically generated

In main(), all we do is call the encryptDirectory() function with the argument of our target directory. Note, that since this is a proof of concept, I did not implement functionality to encrypt entire drives.

encryptDirectory() starts by initializing a variable to hold a new directory path called tmpDirectory(). We add the portion “\\*” to our target directory which will indicate we want to retrieve all files. Then, we initialize a WIN32_FIND_DATAW and a file handle variable.

Text Description automatically generated

Next, we call FindFirstFileW() using the target directory and our FIND_DATAW variables as parameters. Then, we create a linked list of directories.

To follow that up, we enter a do-while loop, which continues while we have more directories or files to encrypt in our current directory. We initialize two more file directory path variables. The tmp2 variable stores the name of the next file/directory we need to traverse/encrypt, and the tmp3 variable stores the randomized encrypted file name after the file has been encrypted. Next, we check if the object we obtained a handle to is a directory and if it is the current or previous directory, ‘.’, or ‘..’. If it is, we skip them.

If it’s any other directory, we append the name of that directory to the current directory, add it as a node to our linked list, and continue. If it’s a file, we generate a random string, append that string to the current directory path, and call encryptFile(). This function takes the following parameters: the full path to the unencrypted file, the full path name of the encrypted file, and the password used to encrypt. We then call DeleteFile() on the unencrypted file. Finally, we obtain a handle to the next file in the folder.

Text Description automatically generated

To finish the function off, we recursively call encryptDirectory() until there are no more folders in the linked list of folders we identified.

Text Description automatically generated

I won’t dive too deep into the file encryption function for two reasons. First, I am not a big cryptography guy. I don’t know much about it, and I don’t want to give any false information. Second, I took this proof of concept and just implemented it in C instead of CPP.

However, the important part I’d like to highlight is that I used the same determination scheme the Bleeding Bear campaign uses to ascertain if a file should be corrupted or not. BreadBear and Bleeding Bear both use the following file extension list to determine if a file should be altered:

Text Description automatically generated

Conclusion

With BreadBear, I took an analysis of a real threat actors TTPs and created a working proof of concept, which I believe improves upon some of their tooling. This work can help organizations visualize how a campaign can be easily created and defend accordingly. More importantly, it was an educational exercise. Feel free to contribute to the code base over on GitHub.

Latest Cyber Labs articles