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.
Once IPFS is installed, first hit Import, then Folder.
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.
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:
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.
"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
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
GetModuleHandle()implementation to retrieve function calls
LoadLibrary()function that will dynamically retrieve the pointer to
LoadLibraryW()at each run.
- Has a custom
- 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.
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.
First, we dynamically resolve pointers to the Windows APIs
GetModuleFileNameW(). Following that we create some variables to store necessary information.
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.
The disabling of event tracing for windows is simple (function template credits to Sektor7 institute):
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:
Once you’ve uploaded the file, you can right click the download link at the bottom of the above screenshot, then select Copy Link.
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.
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.
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.
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
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.
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.
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,
‘..’. 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.
To finish the function off, we recursively call
encryptDirectory() until there are no more folders in the linked list of folders we identified.
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:
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.
The post Repurposing Real TTPs for use on Red Team Engagements appeared first on Nettitude Labs.
1 post - 1 participant