In recent times i have become interested in how infostealers work so well even with modern systems security features, so i took a look at how underground market infostealers operate and tried to reproduce some of the most important features. In this post im going to show how i wrote a working proof of concept of an infostealer in CGO.
This is just for educational purposes only, you should not use it without authorization... Foremost as the author im saying this is just a POC and it lacks of sophistication so its not a good idea to use it in real world enviroments, you are more than welcome to fork it and recreate your own, as you will se trought the post the code is very comprehensive as its majority is written in golang
Hurdles
The biggets hurdle to overcome while developing such software is probabily encryption. Modern version of OSs and browsers all use encryption to safely store sensitive data, the great news is that everything is stored locally in the machine, you dont need to breach into nothing you just have to bypass the encryption mechanism of your machine/browser
There are obviusly other obstacle to overcome but those are generally more related to the concept of malware.
Microsoft DPAPI
Windows by itself implement Data Protection API for data storage security
Every application inside the system can have access to this API and use it to secure store their data, however dpapi has a big weakness: In order to access it someone only need to work from the user context, this mean that every medium-integrity process running in the user session can easily decrypt data using CryptUnprotectData function
DPAPI key generation:
User log in -> DPAPI generate master key -> m.key encrypted with users password hash
Encryption:
CryptProtectData() -> Gen random sess. key -> encrypt data with sess. key -> return encrypted blob
Decryption:
CryptUnprotectData() -> decrypt sess. key with m.key -> decrypt data with sess.key -> return plain text
This was all that Chromium based browser were doing to store data inside PCs before v20
App Bound Encryption
Google has rised the bar introducing App Bound Encryption
This ensure that only the elevated services can decrypt the stored data and only the browser process can request to decrypt the data.
Now, google recognize the fact that this architecture is not 100% secure, they them self said that a succesfull bypass would be process injection or an elevated instance of the malware, but as it is right now this work fine and achive its purpose
This is a high level overview of how ABE encryption work:
- When chrome need to pretect the app bound key it calls the IElevator::EncryptData COM method
- The service generate validation_data specifying a protection_level, If `PROTECTION_PATH_VALIDATION` is specified path validation is done
- validation_data is prepended to the plain text app_bound_key, this form the data_to_encrypt
- data_to_encrypt is encrypted using DPAPI of the user context
- The encrypted blob is again encrypted using DPAPI of the service SYSTEM context
To better understand we can have a look chromium source code
Usage of validation_data:
Usage of data_to_encrypt:
Double layer of DPAPI encryption:
The decryption is what we have just said but in reverse + the path validation if specified. Everything is then stored inside \Local State file as os_crypt.app_bound_encrypted_key.
There are some known bypass to this:
- Process injection inside chromium based browsers
- Opening the browser in debug mode, this has been patched but with some tricks maybe you can still bypass it, for reference read here
- Hooking some functions?
As you will my program use a simple implementation of dll injection
Bypassing ABE using injection
The plan for bypassing ABE encryption includes 2 components: A function working as injector and the dll to inject. What the program will achive is decrypting sensitive informations stored inside browsers appdata folder using the IElevator COM object
This methodology is highly effective beacouse it allows the dll to initialize the COM instance using chrome CLSID_Elevator from the browser context
Injector function:
- Spawn the browser process in a suspended state (--headless)
- OpenProcess, VirtualAllocEx, WriteProcessMemory, CreateRemoteThread(..., LoadLibraryA, ...)
- Dll injected inside browser process
Dll injected:
- Spawn new thread at the start of DllMain
- Initialize COM interface
- Retrieve app_bound_key
- Decrypt using IElevator::DecryptData
Dll Injection
This function is pretty straightforward, you inject a dll inside browser process spawned with the --headless argument
InjectDll will then get the handle to the new instance of the browser, allocate as much memory as the length of the dll path, write the dll inside the allocated memory and load it creating a new thread with LoadLibraryA as a argument
To make this more sophisticated someone could try some sort of Dll hijack or reflective Dll injection
Payload Dll
Majority of the dll code i have used is been taken from this public POC
The first thing (and probably the last) you want to do inside DllMain is to call CreateRemoteThread to execute the main logic. The routine executed by the thread will:
- Extract and decrypt the ABE key
- Start decrypting stuff
You initialize the COM interface with CoCreateInstance specifying the CLSID for the ChromeElevator class and the IID for the IElevator interface. The CLSID is not the same for every chromium based browsers.
After that a proxy is initialized, this allows comunications between chrome.exe and the COM object of the elevation service
Now its time to extract the ABE key from its original position
This function will retrieve the folder of Local State (%LOCALAPPDATA%\Google\Chrome\User Data\) copy its content inside an allocated buffer and start parsing its JSON content in search for the value of the app_bound_encrypted_key, finded the encrypted blob is base 64 decoded and saved inside the heap.
Using the encrypted blob now its time to decrypt it using IElevator::DecryptData
This will return a 32 byte AES key
Finally its time to extract logins, cookies, cards...
Browsers store sensitive data in AppData folder (%LOCALAPPDATA%\Google\Chrome\User Data\Default\) inside sqlite databases, accordingly we will need to interact with it trough the C code. For this you can just use the sqlite3.c and sqlite3.h of this repository.
All encrypted data blobs have a specific format:
| prefix: "v10/v11/v20" | | nonce: 12 byte | | Ciphertext | | Auth tag: 16 byte GCM auth tag |
Here you can clearly see the "v20" prefix, we can use this inside the code to see which version of chromium the browser is using and adapt the extraction strategy based on that.
This is the classic way to interact with a sqlite database in C:
The executed query to retrieve logins is the follow:
SELECT action_url, username_value, password_value FROM logins
Rows and tables name remain the same for every chromium based browsers
Now its time to decrypt the values returned by the query
First we are going to parse the encrypted blob of AES-GCM
We are using the decrypted ABE key with the Windows Cryptography API: Next Generation, for this you will need to import bcrypt.h in your code
After this everything is done and we have finally the decrypted logins.
Adding support for other browsers
For adding support to more browser we can compile as many dll as browsers we want to support, once the program start it will enumerate all the browsers on the file system and request only the necessary dlls from a remote server
There are a few minor differences to watch out for from one browser to another, one of them is the CLSID and IID, to make sure you are using the right one you can check the specifics for the browser with OleView
Other than this remember to change the %APPDATA% in order to match the one with the supported browser
Conclusion
There are more things to cover but i wont do that now, you can find the full program code here
Other functionalities not described in this post are inside the program on Github, go check it if you are interest!
Useful links:
- https://github.com/xaitax/Chrome-App-Bound-Encryption-Decryption/blob/main/docs/RESEARCH.md
- https://github.com/Maldev-Academy/DumpBrowserSecrets
- https://www.sophos.com/it-it/research/the-growing-threat-from-infostealers
- https://deceptiq.com/blog/windows-stealers-technical-analysis