- Original packed samples
- Frenchy shellcode v1 + autoit packer: 0a1340bb124cd0d79fa19a09c821a049 (Avemaria)
- Frenchy shellcode v2 + autoit packer: d009bfed001586db95623e2896fb93aa
- Frenchy shellcode v2 + autoit packer: 20de5694d7afa40cf8f0c88c86d22b1d (Formbook)
- Frenchy shellcode v3 + .Net packer: 21c1d45977877018568e8073c3Acf7c5 (Netwire)
- Extracted frenchy shellcodes:
- Frenchy shellcode v1 at hybrid analysis
- Frenchy shellcode v2 at hybrid analysis
- Frenchy shellcode v3 at hybrid analysis
- Related links:
- https://tccontre.blogspot.com/2019/07/autoit-compiled-formbook-malware.html (I recommend to read this post about the AutoIt script that loads frenchy shellcode).
- https://twitter.com/P3pperP0tts/status/1135976656751996928?s=20
- https://twitter.com/JayTHL/status/1146482606185308160?s=20
- https://twitter.com/James_inthe_box/status/1148966237684133888?s=20
- https://cape.contextis.com/analysis/85189/
- https://twitter.com/James_inthe_box/status/1146527056567472128?s=20
Most of the samples that I have analyzed are packed with a AutoIt-based packer (however, recently I analyzed a v3 Frenchy shellcode whose packer is .Net) that decrypts and loads the shellcode (and then, the shellcode loads the next stage executable by using process hollowing method).
First sample where I found the Frenchy shellcode (v1, mutex: frenchy_shellcode_01) was Emotet and the packer was AutoIt-based, I recommend to read this twitter thread. Later, in this twitter thread, @JayTHL commented about an AveMaria Stealer, again packed with AutoIt-based packer that uses the shellcode (v2, mutex: frenchy_shellcode_002). An specific campaing of Agenttesla looked to be using this packer too. In this great post, the author (@tccontre18) analyzed a variant of the obfuscated autoit script that loads the Frenchy shellcode (in this case, it loaded a Formbook Stealer). Searching for the string "frenchy_shellcode_003" I found another sample at Cape Sandbox using v3 shellcode (@james_in_the_box identified it as netwire), and in this case the packer is not AutoIt-based, but .Net-based.
It looks like this shellcode has been used for a time together with different packers, malware families and campaigns.
Analysis
- 1. Packers
- 1.1. AutoIt-based Packer
- 1.2. DotNet-based Packer
- 2. Frenchy Shellcode
- 2.1. Frenchy Shellcode V3
- 2.1.1. Entrypoint and arguments
- 2.1.2. Duplicated system libraries
- 2.1.3. API usage
- 2.1.4. Process Hollowing
- 2.2. Playing With Frenchy Shellcode
- 3. Who is Frenchy?
1.1. Packers
I am not going to dig too much into the packers that have been seen together with the Frenchy shellcode, only some notes about them.
I am not going to dig too much into the packers that have been seen together with the Frenchy shellcode, only some notes about them.
1.1. AutoIt-based Packer
This packer executes a very obfuscated autoit script that decrypts and loads the frenchy shellcode. Here is a couple of examples of these autoit scripts:
This one (recovered by @DbgShell) loaded a frenchy_shellcode_01: https://pastebin.com/raw/xsUqCdRj
This other one loaded a frenchy_shellcode_002: https://pastebin.com/raw/Knk2iJPF
I recommend this post about the AutoIt script that loads frenchy shellcode.
1.2. DotNet-based Packer
In the case of the sample 21c1d45977877018568e8073c3Acf7c5 the packer is .Net. To check that the dotnet packer is loading the frenchy shellcode we set a bp at CreateMutexW and we wait for the creation of the frenchy_shellcode_03 mutex:
Now we know the current thread is executing the Frenchy shellcode, so we display the call-stack to check the thread that calls the frenchy shellcode comes from .Net:
I recommend this post about the AutoIt script that loads frenchy shellcode.
1.2. DotNet-based Packer
In the case of the sample 21c1d45977877018568e8073c3Acf7c5 the packer is .Net. To check that the dotnet packer is loading the frenchy shellcode we set a bp at CreateMutexW and we wait for the creation of the frenchy_shellcode_03 mutex:
Now we know the current thread is executing the Frenchy shellcode, so we display the call-stack to check the thread that calls the frenchy shellcode comes from .Net:
2. Frenchy Shellcode
2.1. Frenchy Shellcode v3
I have focused the analysis on the v3 shellcode that I have gotten from the sample 21c1d45977877018568e8073c3Acf7c5 (you can download it from hybrid analysis).
The main purpose of this shellcode is to inject a PE into a new process by using the hollow process method.
2.1.1. Entrypoint and arguments
Shellcode's entrypoint is located at offset 0, where the shellcode jumps to the main function:
The shellcode receives as first argument the path of the executable that is going to be launched (suspended) to perform hollow process. Second argument is the content (PE) to be injected.
2.1.2. Duplicated system libraries
The shellcode loads copies foreach system library that it is going to use:
If we enumerate the regions of the address space we can check there are some duplicated dlls:
This could make harder debugging the shellcode. API hooks (such as hooks inserted by cuckoo framework for example) won't work. If you set breakpoints at common APIs that are usually executed by malware (CreateProcessW, WriteProcessMemory, SetThreadContext, etc...) to catch the malware execution at that point, it won't work, because you would need to set breakpoints at the duplicated dlls.
2.1.3. API usage
The shellcode gets a pointer to a lot of APIs, but it only uses a subset of them. I feel like this is a very configurable shellcode, it always loads all the API pointers, but depending of the configuration and the code that it is added to the specific version of the shellcode, some API pointers will be used and other pointers won't be used.
Here is the full list of APIs that the shellcode loads:
Sometimes the shellcode gets pointers to the APIs on the originally loaded dlls. For example this happends with cryptoapi libraries. I guess this is because they don't work fine when they are called through a secondary copy of the dll.
2.1.4. Process Hollowing
The malware creates a new suspended process from the path of the given executable, and then it injects the given PE into the address space of that process by using the process hollowing method. It uses a set of native APIs to perform this task.
In the following capture we can see how the malware creates a new process and unmap the main module of the new created process. In addition, it maps the PE to be injected (to get a mapped copy of this PE) by calling NtCreateSection+NtMapViewOfSection:
Once it has unmapped the main module of the target process to be hollowed, and it has gotten a mapped view of the PE to be injected, it creates a new section into the target process address space to copy the PE to be injected there. It will use NtCreateSection + NtMapViewOfSection + NtWriteProcessMemory to perform this task:
Finally it changes the context of the main thread of the injected process to set EIP = injected code's starting address, and resumes the thread.
2.2. Playing With Frenchy Shellcode
To be honest, I consider this shellcode quite well-coded, it works fine. I decided to write a tiny PoC, a python script that loads and calls it, pushing as arguments the path of notepad.exe (target executable to use when performing hollow process) and the content of calc.exe'sPE file (the PE to be injected), to execute in this way a calc.exe in the context of notepad.exe, by using process hollowing.
Here you can find the PoC together with the Frenchy shellcode v3:
https://github.com/p3pperp0tts/PoC_FrenchyShellcode
from ctypes import *
import struct
f = open("frenchyshellcode.bin", "rb")
frenchy = f.read()
f.close()
f = open("c:\\windows\\system32\\calc.exe", "rb")
calc = f.read()
f.close()
hollowpath = "c:\\windows\\notepad.exe\x00"
#to test, full shellcode = frenchy + arguments for frenchy + code to jmp
lenshellcode = len(frenchy) + len(calc) + len(hollowpath) + len("\x68\x00\x00\x00\x00\x68\x78\x56\x34\x12\x68\x78\x56\x34\x12\x68\x78\x56\x34\x12\xc3")
ptr = windll.kernel32.VirtualAlloc(None, lenshellcode, 0x3000, 0x40)
shellcode = frenchy
shellcode += calc
shellcode += hollowpath
shellcode += "\x68" + struct.pack("<L", ptr + len(frenchy)) #push path to process to hollow
shellcode += "\x68" + struct.pack("<L", ptr + len(frenchy)+len(calc)) #push address of pe to inject
shellcode += "\x68\x00\x00\x00\x00" #fake ret addr
shellcode += "\x68" + struct.pack("<L", ptr) #push address of frenchy shellcode entry point
shellcode += "\xc3" #jmp to frenchy
hproc = windll.kernel32.OpenProcess(0x1F0FFF,False,windll.kernel32.GetCurrentProcessId())
windll.kernel32.WriteProcessMemory(hproc, ptr, shellcode, len(shellcode), byref(c_int(0)))
windll.kernel32.CreateThread(0,0,ptr+len(frenchy)+len(calc)+len(hollowpath),0,0,0)
windll.kernel32.WaitForSingleObject(c_int(-1), c_int(-1))
3. Who is Frenchy?
No comments:
Post a Comment