{"id":2379,"date":"2024-09-25T17:49:34","date_gmt":"2024-09-25T17:49:34","guid":{"rendered":"https:\/\/xdr-mdr.lmntrix.com\/main_web\/?p=2379"},"modified":"2025-07-29T03:39:01","modified_gmt":"2025-07-29T03:39:01","slug":"winapi-excel-4-0-macros-and-red-team-phishing","status":"publish","type":"post","link":"https:\/\/lmntrix.com\/blog\/winapi-excel-4-0-macros-and-red-team-phishing\/","title":{"rendered":"WinAPI, Excel 4.0 Macros and Red Team Phishing"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1600\" height=\"900\" src=\"https:\/\/lmntrix.com\/blog\/wp-content\/uploads\/2024\/09\/LMNTRIX-Labs-WinAPI-Excel-4.0-Macros-and-Red-Team-Phishing.webp\" alt=\"LMNTRIX Labs: WinAPI, Excel 4.0 Macros and Red Team Phishing\" class=\"wp-image-2397\" srcset=\"https:\/\/lmntrix.com\/blog\/wp-content\/uploads\/2024\/09\/LMNTRIX-Labs-WinAPI-Excel-4.0-Macros-and-Red-Team-Phishing.webp 1600w, https:\/\/lmntrix.com\/blog\/wp-content\/uploads\/2024\/09\/LMNTRIX-Labs-WinAPI-Excel-4.0-Macros-and-Red-Team-Phishing-1536x864.webp 1536w\" sizes=\"(max-width: 1600px) 100vw, 1600px\" \/><figcaption class=\"wp-element-caption\">LMNTRIX Labs: WinAPI, Excel 4.0 Macros and Red Team Phishing<\/figcaption><\/figure>\n\n\n\n<p><a href=\"http:\/\/lmntrix.com\">LMNTRIX<\/a> has been using Macros for quite some time now. We\u2019ve seen it being used over and over again with word documents for several phishing activities during <a href=\"https:\/\/lmntrix.com\/white_paper\/thinking-of-starting-a-adversary-hunting-program-then-you-must-do-these-to-get-it-right\/\">threat hunting<\/a> and responding to IR cases over quite a brief period of time. Microsoft introduced Excel 4.0 Macros in 1992. But due to the complexity of writing Excel 4.0 Macros, there weren\u2019t a lot of defensive tools which detected them. Excel 4.0 Macros introduced several new techniques which made detections even harder to perform. Macros are used heavily especially in Excel in most large organizations to automate the tasks of calculations and building charts based on these calculations. This is one of the main reason why it becomes easier to phish CEOs and Accountants with Excel 4.0 Macros rather than using Word or PowerPoint. With enough creativity, you can use these Macros with Salary\/Appraisal-based Excel templates to phish even usual employees. In this blog post, we will take a look at running shellcode via Excel 4.0 Macros.<\/p>\n\n\n\n<p>We will first start Excel with an empty sheet and enable Macros. Macros in excel can be enabled by going to <em>File -&gt; Options -&gt; Trust Center -&gt; Macro Settings<\/em>. Here we will enable all macros for the time being.<\/p>\n\n\n<div class=\"wp-block-image size-full wp-image-1671\">\n<figure class=\"aligncenter\"><img decoding=\"async\" width=\"626\" height=\"517\" src=\"https:\/\/lmntrix.com\/blog\/wp-content\/uploads\/2024\/09\/Figure-1-Enable-macros-in-Excel-document.webp\" alt=\"\" class=\"wp-image-2389\"\/><figcaption class=\"wp-element-caption\">Figure 1: Enable macros in Excel document<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Make sure to save this file as a 97-2003 .xls file format. Using anything else will either not work or likely trigger an alert (xlsm) that a macro is being run on the host machine. We have had several successful engagements by using .xls format.<\/p>\n\n\n<div class=\"wp-block-image size-full wp-image-1670\">\n<figure class=\"aligncenter\"><img decoding=\"async\" width=\"463\" height=\"161\" src=\"https:\/\/lmntrix.com\/blog\/wp-content\/uploads\/2024\/09\/Figure-2-Save-as-Excel-97-2003-XLS-format.webp\" alt=\"\" class=\"wp-image-2390\"\/><figcaption class=\"wp-element-caption\">Figure 2: Save as Excel 97-2003 XLS format<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Right click on the \u2018Sheet1\u2019 of your excel file and add an Excel 4.0 Macro Sheet here.<\/p>\n\n\n<div class=\"wp-block-image size-full wp-image-1669\">\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"626\" height=\"405\" src=\"https:\/\/lmntrix.com\/blog\/wp-content\/uploads\/2024\/09\/Figure-3-Adding-Excel-4.0-Macro-Sheet.webp\" alt=\"\" class=\"wp-image-2391\" srcset=\"https:\/\/lmntrix.com\/blog\/wp-content\/uploads\/2024\/09\/Figure-3-Adding-Excel-4.0-Macro-Sheet.webp 626w, https:\/\/lmntrix.com\/blog\/wp-content\/uploads\/2024\/09\/Figure-3-Adding-Excel-4.0-Macro-Sheet-280x180.webp 280w\" sizes=\"(max-width: 626px) 100vw, 626px\" \/><figcaption class=\"wp-element-caption\">Figure 3: Adding Excel 4.0 Macro Sheet<\/figcaption><\/figure>\n<\/div>\n\n\n<p>We will write our code in this sheet and then hide it to avoid being suspicious from the target user. We can simply execute a simple on-disk executable using excel 4.0 macros using the below command:<\/p>\n\n\n<div class=\"wp-block-image size-full wp-image-1668\">\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"474\" height=\"109\" src=\"https:\/\/lmntrix.com\/blog\/wp-content\/uploads\/2024\/09\/Figure-4-Executing-calc.exe-from-disk-using-Excel-4.0-macros.webp\" alt=\"\" class=\"wp-image-2392\"\/><figcaption class=\"wp-element-caption\">Figure 4: Executing calc.exe from disk using Excel 4.0 macros<\/figcaption><\/figure>\n<\/div>\n\n\n<p>We can execute the above macro by right clicking the box and running it. This should pop a calc.exe on the host.<\/p>\n\n\n<div class=\"wp-block-image size-full wp-image-1667\">\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"516\" height=\"745\" src=\"https:\/\/lmntrix.com\/blog\/wp-content\/uploads\/2024\/09\/Figure-5-Executing-macro-from-Excel-sheet.webp\" alt=\"\" class=\"wp-image-2393\"\/><figcaption class=\"wp-element-caption\">Figure 5: Executing macro from Excel sheet<\/figcaption><\/figure>\n<\/div>\n\n\n<p>But however, this is way too simple. During a phishing campaign, we might need to execute shellcode, HTAs or even simple C# script sometimes. XLM macros were beautifully designed by Microsoft in such a way that it becomes very interesting to see the different ways we can use it to call WinAPIs. So, we will need to write a macro to find <em>VirtualAlloc<\/em>, <em>WriteProcessMemory<\/em>, <em>CreateThread<\/em> from kernel32.dll, copy our shellcode to a block where the macro can read it and use it to call <em>CreateThread<\/em> on it. We can do that by using the <a href=\"https:\/\/support.microsoft.com\/en-us\/office\/using-the-call-and-register-functions-06fa83c1-2869-4a89-b665-7e63d188307f?ui=en-us&amp;rs=en-us&amp;ad=us\" target=\"_blank\" rel=\"noopener\">REGISTER<\/a> COM object. More information on EXCEL functions can be found <a href=\"https:\/\/d13ot9o61jdzpp.cloudfront.net\/files\/Excel%204.0%20Macro%20Functions%20Reference.pdf\" target=\"_blank\" rel=\"noopener\">here<\/a>. Microsoft introduced this merger of COM object with Excel 4.0 macros in 1993. The format to load a DLL in memory using the <em>REGISTER<\/em> function is as follows:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>=REGISTER(dll_to_load, symbol_name, return_types, symbol_alias, symbol_argument, macro_type, macro_category)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>In our case, the above function would work like this:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>dll_to_load: kernel32.dll.<\/li>\n\n\n\n<li>symbol_names: <em>VirtualAlloc, WriteProcessMemory <\/em>and<em> CreateThread<\/em><\/li>\n\n\n\n<li>return_types: These are the return types of the symbol that we will execute<\/li>\n\n\n\n<li>symbol_alias: An alias for the symbol which will store the symbol\u2019s address<\/li>\n\n\n\n<li>symbol_argument: Optional arguments to pass to the symbol<\/li>\n\n\n\n<li>macro_type: Type of the macro. It will be 1 for us which reflects function<\/li>\n\n\n\n<li>macro_category: This is only for old Excel compatibility. Use any arbitrary number between 1 to 14.<\/li>\n<\/ol>\n\n\n\n<p>The return_types specify the return type and the argument types of the symbol that we would execute from the DLL. For Example, <em>VirtualAlloc<\/em> takes 4 arguments:<\/p>\n\n\n<div class=\"wp-block-image size-full wp-image-1666\">\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"220\" height=\"123\" src=\"https:\/\/lmntrix.com\/blog\/wp-content\/uploads\/2024\/09\/Figure-6-VirtualAlloc-arguments-as-shown-by-Microsoft.webp\" alt=\"\" class=\"wp-image-2394\"\/><figcaption class=\"wp-element-caption\">Figure 6: VirtualAlloc arguments as shown by Microsoft<\/figcaption><\/figure>\n<\/div>\n\n\n<p>So, for us, the return type for this Symbol would be \u201c<em>JJJJJ<\/em>\u201d. According to <a href=\"https:\/\/support.microsoft.com\/en-us\/office\/using-the-call-and-register-functions-06fa83c1-2869-4a89-b665-7e63d188307f?ui=en-us&amp;rs=en-us&amp;ad=us\" target=\"_blank\" rel=\"noopener\">Microsoft documentation<\/a>, each data has a type ranging from A to R. Our return type has 5 \u2018<em>J<\/em>\u2019. The first one stands for the return type which is LPVOID and as we know LPVOID is a 4-byte signed integer. Similar is the case for the 4 arguments for VirtualAlloc. All the types are pointers which basically becomes the remaining 4 \u2018<em>J<\/em>\u2019s. So, we have 5 \u2018J\u2019s as arguments in total. If we have a string pointer, then the type would be \u2018<em>C<\/em>\u2019, which is what we will use in <em>WriteProcessMemory<\/em> WinAPI. If you are planning to execute x64 shellcode, better use 8 byte signed integers instead of J. If we wrap what we know till now, the final XLM code for finding the Symbol addresses will look like this:<\/p>\n\n\n<div class=\"wp-block-image size-full wp-image-1665\">\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"524\" height=\"68\" src=\"https:\/\/lmntrix.com\/blog\/wp-content\/uploads\/2024\/09\/Figure-7-Registering-Symbols-with-Kernel32.dll-to-find-the-symbol-addresses.webp\" alt=\"\" class=\"wp-image-2395\"\/><figcaption class=\"wp-element-caption\">Figure 7: Registering Symbols with Kernel32.dll to find the symbol addresses<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Once we have the required addresses for these functions, we will run these Symbol\u2019s function like we would usually do in any other programming language by providing arguments to them.<\/p>\n\n\n\n<p>You can create shellcode payloads using Metasploit, Cobalt Strike (raw format) or any other Command and Control that you use to create raw shellcode. We will be using our internal C2 for this task and convert the opcodes of the shellcode to simple CHAR variable of Excel. These CHAR variables are just the ASCII code for each hex value for your shellcode.<\/p>\n\n\n\n<p>Once we have the addresses for each of the Symbols from Figure 7, we will Call the alias VAlloc along with the 4 <em>VirtualAlloc<\/em> parameters. We will be assigning 1000000 bytes with <em>VirtualAlloc<\/em> since these are CHAR characters. It all depends on the length of you shellcode however. If you assign more space than the size of your shellcode, it doesn\u2019t matter, but don\u2019t assign lesser size. Our payload is of 54 Kilobytes, and we are assigning more size here for <em>VirtualAlloc<\/em>. We can call this symbol as follows:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>=Valloc(0,1000000,4096,64)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>One more thing to make sure of is to stop writing to memory when we find the final byte of our payload. We can do this using an \u201cEND\u201d string at the end of our payload as can be seen in Figure 8. We will split our payload since the size of the payload is extremely huge and a single Excel Cell cannot contain characters of that size. You can split it in any length you want. We will equally split the payload and store it in Column B in vertical manner like B1, B2, B3 and so on. Since we are splitting our payload, we will need to add them up before we write it to a <em>Virtual Memory<\/em>. We can do this by writing another small XLM function <em>SELECT<\/em> which will add them up before calling <em>WriteProcessMemory<\/em>. The code should look like this:<\/p>\n\n\n<div class=\"wp-block-image size-full wp-image-1664\">\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"524\" height=\"68\" src=\"https:\/\/lmntrix.com\/blog\/wp-content\/uploads\/2024\/09\/Figure-7-Registering-Symbols-with-Kernel32.dll-to-find-the-symbol-addresses.webp\" alt=\"\" class=\"wp-image-2395\"\/><figcaption class=\"wp-element-caption\">Figure 8: Concatenating the shellcode and storing them before writing to Allocated memory<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Make sure you add \u201cEND\u201d at the end of the CHAR shellcode. We will use this to calculate the length of the shellcode which we did in Cell A7 and A8. Once we have the shellcode, we will write this to the allocated process memory using our alias WProcessMemory (A9).<\/p>\n\n\n\n<p><em>WriteProcessMemory<\/em> will loop until it reaches the string \u201cEND\u201d which we looped on cell A7. After writing the process memory, we will move the output in C1+1 which we stored earlier (Figure 8: A9 -&gt; C1 * 255) to just C1. We will select the rows using the SELECT Function and move to the next cell using the NEXT function. Here we will call the buffer which we allocated and stored in cell A4 (Valloc) using <em>CreateThread<\/em> (alias: CThread). We will use the function HALT to wait till the shellcode completes running.<\/p>\n\n\n\n<p>Macros are fun to write, but can become complicated pretty quickly. For a defender, the main task would be to find the obfuscated shellcode since Excel with its complicated formulas can make even simple tasks look complicated, let alone a motivated attacker who is ready to sit day and night just to write a hardcore payload to just evade the endpoint. We used <em>WriteProcessMemory<\/em> and <em>CreateThread<\/em> to inject our shellcode to the current process. This blog was just a POC for now, but it would be a bad idea to inject the shellcode in the same Excel process. Any EDR will easily catch an Excel document making an internet connection. Worse scenario would be if the target user decides to close the excel sheet, the shellcode would also be terminated from the Excel\u2019s memory. During a real engagement, it\u2019s always better to enumerate running process and inject it to a common process using <em>VirtualAllocEx<\/em>, <em>WriteProcessMemory<\/em> and <em>CreateRemoteThread<\/em>.<\/p>\n\n\n\n<p>One final thing to note is that Microsoft replaced Excel 4.0 macros with VBA in the early 90s. So even though this feature works for now, you never know when Microsoft will remove this functionality since this is mostly a deprecated feature as compared to our latest VBA.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>LMNTRIX has been using Macros for quite some time now. We&rsquo;ve seen it being used over and over again with word documents for several phishing activities during threat hunting and responding to IR cases over quite a brief period of time. Microsoft introduced Excel 4.0 Macros in 1992. But due to the complexity of writing [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2397,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21],"tags":[],"class_list":["post-2379","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-labs"],"_links":{"self":[{"href":"https:\/\/lmntrix.com\/blog\/wp-json\/wp\/v2\/posts\/2379","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lmntrix.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lmntrix.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lmntrix.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lmntrix.com\/blog\/wp-json\/wp\/v2\/comments?post=2379"}],"version-history":[{"count":3,"href":"https:\/\/lmntrix.com\/blog\/wp-json\/wp\/v2\/posts\/2379\/revisions"}],"predecessor-version":[{"id":4250,"href":"https:\/\/lmntrix.com\/blog\/wp-json\/wp\/v2\/posts\/2379\/revisions\/4250"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/lmntrix.com\/blog\/wp-json\/wp\/v2\/media\/2397"}],"wp:attachment":[{"href":"https:\/\/lmntrix.com\/blog\/wp-json\/wp\/v2\/media?parent=2379"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lmntrix.com\/blog\/wp-json\/wp\/v2\/categories?post=2379"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lmntrix.com\/blog\/wp-json\/wp\/v2\/tags?post=2379"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}