{
	"id": "332f97fd-02e9-4195-bdf7-ae7f6f2252b4",
	"created_at": "2026-04-06T00:17:09.261489Z",
	"updated_at": "2026-04-10T03:21:43.894259Z",
	"deleted_at": null,
	"sha1_hash": "e18c275955e400fe207516f80b6d4a3685244bbb",
	"title": "XWorm Part 1 - Unraveling a Steganography-Based Downloader",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1879000,
	"plain_text": "XWorm Part 1 - Unraveling a Steganography-Based Downloader\r\nPublished: 2025-07-03 · Archived: 2026-04-05 22:21:59 UTC\r\nOverview\r\nToday, we’ll analyze a sample tagged as XWorm, sourced from Malware Bazaar, with a SHA-256 hash of\r\n0fd706ebd884e6678f5d0c73c42d7ee05dcddd53963cf53542d5a8084ea82ad1 . This sample will be referred to as the first stage.\r\nAccording to a recent AnyRun report1, XWorm is a remote access trojan (RAT) sold as a service, capable of exfiltrating\r\nfiles, stealing various application credentials, and maintaining remote access. It also states that XWorm is commonly\r\ndelivered in multi-stage attacks, starting with phishing emails.\r\nTechnical Analysis\r\nStage 1\r\nThe first stage is a JScript file employing junk code, junk delimiter strings, and string concatenation for obfuscation.\r\nhttps://malwaretrace.net/posts/xworm-part-1/\r\nPage 1 of 5\n\nfigure 1 - obfuscated stage 1 downloader\r\nThis can be trivially deobfuscated using a text editor like Sublime Text to replace the delimiter string defined on line 79\r\nwith an empty string, followed by replacing the below regular expression with an empty string to clean up string\r\nconcatenation.\r\n1 \";[\\s\\n]+[a-z]+\\s\\+=\\s\"\r\nAfter further deobfuscation and variable renaming, the below downloader is identified, which reaches out to a URL, checks\r\nfor a status code of 200 , then executes the HTTP response as code through use of an immediately invoked function\r\nexpression2 (IIFE).\r\nfigure 2 - deobfuscated stage 1 downloader script\r\nStage 2\r\nThe next stage is further JScript with obfuscation identical to stage 1. Repeating previous deobfuscation steps, we observe\r\nuse of PowerShell to execute another stage seen in figure 3. This is responsible for executing the PowerShell script in figure\r\n4.\r\nfigure 3 - stage 2 downloader script\r\nfigure 4 - PowerShell script invoked by stage 2 downloader script\r\nhttps://malwaretrace.net/posts/xworm-part-1/\r\nPage 2 of 5\n\nAfter some code beautifying using CyberChef and variable renaming, we can see it loads an image hosted on “Internet\r\nArchive” into memory as a byte array.\r\nfigure 5 - PowerShell code responsible for loading image into memory\r\n figure 6 -\r\nuniverse themed image hosted on Internet Archive\r\nOnce loaded into memory, the array is scanned for hard-coded bitmap magic bytes 3 42 4D . Once found, the index position\r\nof the byte sequence start is stored in variable $bitmap_begin .\r\nfigure 7 - code responsible for locating bitmap embedded in image\r\nStarting at $bitmap_begin , the remaining image bytes are stored in a memory buffer, used to construct a .NET Bitmap\r\nobject. It then loops through each pixel and loads their RGB byte values into variable $byte_list .\r\nhttps://malwaretrace.net/posts/xworm-part-1/\r\nPage 3 of 5\n\nfigure 8 - code responsible for image manipulation\r\nFrom the newly created byte list, a .NET assembly is loaded where method ClassLibrary1.Home::VAI() is invoked.\r\nfigure 9 - extraction and execution of .NET assembly embedded within bitmap\r\nNow having an understanding of its functionality, we can dump the assembly using a dynamic approach by replacing lines\r\n55 and onwards with the following and executing the script.\r\n1 [System.IO.File]::WriteAllBytes(\"assembly.mal\", $dotnet_assembly);\r\nAlternatively, we can leverage the Pillow Python library4 to work with the image data and extract the embedded PE. The\r\nreferenced blog on looping through pixel data with Python5 was helpful during development.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\nimport sys\r\nimport os\r\nfrom PIL import Image\r\n# take image as input\r\npolyglot = sys.argv[1]\r\nwith open(polyglot, \"rb\") as f:\r\nimage_bytes = f.read()\r\n# convert image to byte array\r\nimage_byte_array = bytearray(image_bytes)\r\nsize = len(image_byte_array)\r\n# find bitmap start\r\nbitmap_start = image_byte_array.find(b\"\\x42\\x4D\\x72\\x6E\\x37\\x00\\x00\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x64\\x00\\x00\\\r\n# extract bitmap based on start location\r\nextracted_bitmap = image_byte_array[slice(bitmap_start, size)]\r\n# temporarily write bitmap to disk\r\n# (the Image module only handles file paths)\r\nwith open(\"bitmap.tmp\", \"wb\") as f:\r\nf.write(extracted_bitmap)\r\ntry:\r\nimg = Image.open(\"bitmap.tmp\")\r\nhttps://malwaretrace.net/posts/xworm-part-1/\r\nPage 4 of 5\n\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\n35\r\n36\r\n37\r\n38\r\n39\r\n40\r\n41\r\n42\r\n43\r\n44\r\n45\r\n46\r\n47\r\n48\r\n49\r\n50\r\n51\r\n52\r\n53\r\n54\r\nexcept FileNotFoundError:\r\nprint(\"Error: temporary bitmap file not found\")\r\nwidth, height = img.size # get dimensions\r\nimage = img.convert(\"RGB\") # read image using RGB mode\r\nbyte_list = [] # define ouput byte list\r\n# extract each pixel's RGB byte values\r\nfor y in range(height):\r\nfor x in range(width):\r\nr, g, b = image.getpixel((x, y))\r\nbyte_list.append(r)\r\nbyte_list.append(g)\r\nbyte_list.append(b)\r\n# bitmap cleanup\r\nimg.close()\r\nimage.close()\r\nos.remove(\"bitmap.tmp\")\r\n# extract assembly\r\nassembly_len = int.from_bytes(byte_list[:4], byteorder=\"little\")\r\nextracted_assembly = bytes(byte_list[4:assembly_len])\r\n# write assembly to file\r\nwith open(\"extracted_assembly.mal\", \"wb\") as f:\r\nf.write(extracted_assembly)\r\nUntil next time! See you in part two, where we will analyze the extracted .NET assembly. All hashes from the below IOC\r\ntable will be available for download on MalShare.\r\nIOCs\r\nType IOC\r\nStage 1\r\nDownloader SHA-2560fd706ebd884e6678f5d0c73c42d7ee05dcddd53963cf53542d5a8084ea82ad1\r\nStage 1\r\nDownloader User-AgentMyCustomAgent/1.0\r\nStage 2 URL hxxp[://]deadpoolstart[.]lovestoblog[.]com/arquivo_fb2497d842454850a250bf600d899709[.]txt\r\nStage 2\r\nDownloader SHA-256ad25fffedad9a82f6c55c70c62c391025e74c743a8698c08d45f716b154f86da\r\nImage SHA-256 89959ad7b1ac18bbd1e850f05ab0b5fce164596bce0f1f8aafb70ebd1bbcf900\r\nImage URL\r\nhxxps[://]archive[.]org/download/universe-1733359315202-8750/universe-1733359315202-\r\n8750[.]jpg\r\nReferences and Resources\r\n1. https://any.run/malware-trends/xworm/ ↩︎\r\n2. https://developer.mozilla.org/en-US/docs/Glossary/IIFE ↩︎\r\n3. https://en.wikipedia.org/wiki/List_of_file_signatures ↩︎\r\n4. https://pillow.readthedocs.io/en/stable/ ↩︎\r\n5. https://www.nemoquiz.com/python/loop-through-pixel-data/ ↩︎\r\nSource: https://malwaretrace.net/posts/xworm-part-1/\r\nhttps://malwaretrace.net/posts/xworm-part-1/\r\nPage 5 of 5",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://malwaretrace.net/posts/xworm-part-1/"
	],
	"report_names": [
		"xworm-part-1"
	],
	"threat_actors": [],
	"ts_created_at": 1775434629,
	"ts_updated_at": 1775791303,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/e18c275955e400fe207516f80b6d4a3685244bbb.pdf",
		"text": "https://archive.orkl.eu/e18c275955e400fe207516f80b6d4a3685244bbb.txt",
		"img": "https://archive.orkl.eu/e18c275955e400fe207516f80b6d4a3685244bbb.jpg"
	}
}