# AgentTesla dropped via NSIS installer **[l1v1ngc0d3.wordpress.com/2021/11/12/agenttesla-dropped-via-nsis-installer/](http://l1v1ngc0d3.wordpress.com/2021/11/12/agenttesla-dropped-via-nsis-installer/)** dominik 12 Nov 2021 Lately one of our customers received a suspicious file which was blocked by our sandbox solution but it was unclear if this was malicious and if so what malware it was so I did an analysis and want to share my results with you. The main goal of this article is to show how to extract the final payload. The sample is now available on VT: ce8a9bf908ce35bf0c034c61416109a44f015eabf058b12485450cd40af95fc3 ## NSIS installer If we do some static analysis via DiE (Detect it Easy) we see that the file is of type NSIS installer. One easy way to obtain the files is to just simply extract the files with 7-ZIP. Unfortunately I’m not aware of any way to reverse the NSIS script (any hint welcome ;-)). After extracting the file we find a folder $PLUGINDIR as expected and another file with a random name and some bytes in it. ## First dll – swfmwfkkeh.dll Inside of the $PLUGINSDIR directory we find one file named swfmwfkkeh.dll (SHA1: 56f3d68f10bde42216634f987b421feee696506e). Once again we open it up in DiE and find out that its written in C/C++ and some exports which look a little strange. In the imports there are some false flags but the VirtualProtect seems to be reasonable ----- Now we open up the file in IDA Pro and take a look at the export functions We assume that the NSIS installer will start the DLL and call the exported function „zznqqqjqi“ so we start our analysis there. After setting up the stack the function intiliazes „var_14“ which then is compared if its above 4722 (in fact the function does a „jump not below“ with the main functionality in the false tree). We will start with the right block first because there you can see that the memory address at 10009014 will receive RWX permissions and after that will be called. So we can assume that at this location there must be some assembly code. Now lets take a look what happens in the left block because this is where we land after the first comparison. At this point „var_14“ is still below 4722. As you can see the variable (now in ecx) is used as a pointer in the marked area by utilizing „byte ptr loc_10009014[ecx]“. So it grabs the first bytes of what ever is at this address. If ----- we take a look what is there we see some „strange assembly code – this doesn t look as valid assembly code at all. If we look further down in the left block we see some xor, sub, add and mov operations so we can assume that this code will be modified. After all the byte manipulations are done the byte is written back to the address 10009014. This time esi receives the pointer (mov esi, [ebp+var_14]). Then we jump back to the comparison if var_14 is already above 4722. If this is the case we change the page permissions to RWX and execute the now modified code. ## Let’s debug some code The decryption routine is very very long and we don’t want to go into reversing this algorithm. Below is a screenshot of the algorithm – hell no! ----- ----- So lets open the file up in x64dbg and jump to user code but stop there. We want to start at the function „zznqqqjqi“. If we switch to text mode in IDA we can see the address. We have two options to change the instruction pointer (EIP) to continue from this address. First we can rightclick on the EIP register and modify the value. Or we can jump to the address in the disassembly view (Ctrl + G) and then right-click „Set new origin here“. ----- After that we can start debugging. ----- We also see as we did in IDA Pro the initialization of the pointer and the comparision with 1272 (hex) = 4722 (dez). Here is on speciality about assembly. Instead of the jnb operation we saw in IDA Pro we see a jae (jump if above or equal). In fact the operations are interchangeable because both check if the ZERO flag is set. We know from our static analysis that after the decryption loop we change the permission of the page so lets follow the jump and place a breakpoint at this point. Now we want to obverse how the code changes (without reversing the algorithm). Follow the address 10009014 in disassembler to see the code. ----- **Before decryption** **After decryption** As you can see now the code changed fundamentally and it starts with a jmp to another offset in the code. Jump back to the current instruction by double-clicking the EIP register. Lets single step over the VirtualProtect and then jump into the decrypted code (F7). We follow the jump and land at this address where we can see values being push/popped and then moved into a local variable (ebp-28). These values are ASCII codes so we can convert them manually or step over until we reach 0x10009849 where the string is terminated by a 0 (xor eax,eax = 0; mov memory,ax). ----- If we follow the address ebp-28 we can see the string After that the next call will get us the Magic (4D 5A) of Kernel32.dll After that we see a lot of calls to the same function and what we suspect to be API hashes. So lets dive into this function and try to figure out what algorithm is used to hash the API function names. For easier analysis I dumped the DLL again with Scylla and opened it up again in IDA Pro. ## API Hashing We will go into the details of the function but what it mainly does is hashing the function names (exports) of the DLL and comparing it with the provided hash. If they match the function is found and a pointer to the function is stored. We know that the function receives the address to the module base of kernel32.dll. The address is passed via the ECX register. Additionally the function receives the precomputed hash via the EDX register. The first steps in the program are to store the passed arguments into edi respectively in var 4 Then the function goes over the memory region and reads in the PE ----- structure looking for the export directory. To better unterstand whats going on here and what offsets are used take a look at this (huge) diagram: [https://raw.githubusercontent.com/corkami/pics/master/binary/pe102/pe102.svg](https://raw.githubusercontent.com/corkami/pics/master/binary/pe102/pe102.svg) Now the function gets the first name from the export directory (edx+esi*4) and calls the function sub_100096B6. The function name (better: the address of the function name) is passed via the ECX register. ----- And here we have find the hashing algorithm. First the address of the function name is stored in ESI. Then we move the constant 2326 (hex) into EDX register and jump down to loc_100096CF where we get the first char by utilizing „movsx edi, byte ptr [esi]“. Then the constant 2326h is copied into EAX. With „test edi, edi“ the function checks if there are still characters in the function name left or if the string termination („0“) is reached. ----- ### Pseudocode of the hashing algorithm If we remove some of the optimizations by the compiler and do the shr / shl operations inline (these operations can only be done on registers therefore the compiler had to assemble it this way) then we get a very simple code. Basically its just starting with a constant (I called iv for Initialization Vector) and then shifts right and left and adds the current char. This way we go over all the chars in the string until we reach the end of the string. To simplify things I use a fixed string of „GetTempPathW“. To account for the EDX register (which is 32 bytes long) we have to make sure that we stay inside this range and therefore have to do an AND operation with 0xFFFFFFFF. ``` iv = 0x2326 name = "GetTempPathW" hash = 0 for i in range(len(name)): iv = (iv + (iv >> 1 | iv << 7) + ord(name[i])) & 0xFFFFFFFF hash = iv print(hex(hash)) ### Resolve the called API hashes ``` ----- If we jump over one of these function calls we see the pointer in the EAX being returned. So we can jump over all the function call and make notes of the resolved API hashes. You could write a IDAPython script if you want but there are not many hashes so I decided to do it manually. From what we see we can expect some file operations. After all necessary APIs are resolved we can see that the malware does the same push/pop trick as before – so we do the same and run to the end and jump to the address in the dump ----- There we see a strange string „w66zlsqpnyue6“. ## Reading and decrypting main payload Lets continue debugging. And find out what this string is used for. Next we have a call to a memory region and as you might have expected its an API call. We call GetTempPathW. With the next call we are able to figure out what the previous strange string means. The string is appended to the result of GetTempPathW and therefore it must be a file. If you recall ebp-44 is the string and ebp-480 contains the string of the temp folder. After the call the newly formed string is stored at ebp-480 and we can follow this memory in the dump and see the final result ----- Next we have a call to CreateFileW. Remember that the parameters are pushed to the stack in reverse order. One interesting fact about this call is the value 80000000 which is a constant for GENERIC_READ which means that the file must already exist or we will get an error. At this point we can assume that the NSIS installer will copy the file over to the temp directory. To proceed with our debugging we have to copy the file ourselves. You can find the file in the „root“ folder. If the call succeeds EAX will contain the handle to the file. The result of the call is storend and checked in the next line. If this is successful the malware gets the size of the file with a call to GetFileSize The same logic as above is used to check if the file size is not equal 0. Next we allocate virtual memory with a call to VirtualAlloc and pass the size (stored at ebp-8) to the function. The allocated memory is stored in ebp-C. ----- We do another check if the function succeeded and continue with a call to read file. The destination buffer is the just allocated memory region. In my case its 0x750000. After the file is read we can take a look at the memory region. This does look like encrypted data but lets continue our analysis. After reading the file we close the handle to the file and jump into another function at 10009A0E. Notice that the functions receives two parameters. First the file size (ebp-8) and the allocated region of memory (ebp-C). In the function we see a familiar code structure – it has great similarities with the first decryption loop. First there is a pointer initialized with zero stored as local variable at ebp-8. Then the pointer is incremented by one and check against the argument at ebp+C which is the file size. If the pointer value is lower than the size of data (jae) the encryption will continue otherwise we jump to 10009C8B. After the jump is NOT taken we see that the value at ebp+8 (the data itself) is moved into eax and then the pointer (counter) gets added. ----- As we did with the previous encryption loop we don’t want to dig into the algorithm and just see whats happening. We know that this code will manipulate the read file so go to address 10009C8B and set a breakpoint. After the decryption is complete we can see a MZ header in the dump. So right-click on the address 750000 and follow in memory map. Then right-click the address again and choose „Dump memory to file“. We want to stop our analysis of the initial sample here and continue with the dumped PE. ## Analyzing the dumped PE We once again start with some basic static analysis and open the file in DiE. ----- Immediately we can see that the PE imports APIs to handle resources so lets check if there is something interesting. And wow … there is an unencrypted PE inside of the resource section. To dump this PE we will utilize Resource Hacker and dump the file via „Save Resource to a BIN file…“ ## Analyzing the dumped resource Once again we open the dumped PE in DiE and see that its written in .NET and obfuscated. ----- From this point on I just dropped this sample into the CAPE and got a hit on AgentTeslaV3 YARA signatures. Mission complete! ## IoCs **NSIS Installer** ce8a9bf908ce35bf0c034c61416109a44f015eabf058b12485450cd40af95fc3 **swfmwfkkeh.dll** 6d8bc73c6f2ef4ee700fc8bc4088f73a14dab355a2dd4e3e9aa3ddf52f7e946e **Encrypted** **resource** **(inside of NSIS** **data)** **w66zlsqpnyue6** c02ff5253bf3930f1ee14e088f50c827bf2209f3a7e9f00ed3994fd417d790b2 **Dumped PE** 9a72e5859b5564cecff5d5a4a929e81595d68aca1972ea2cf0fcf71c518d2cb9 **AgentTesla V3** 5459e87eb0a39243a35405866b2dca1d57c2c1ee02d24052635fcc48de5d397c -----