{
	"id": "fc939079-dd11-429c-8aa9-b89b90373515",
	"created_at": "2026-04-06T00:19:48.032986Z",
	"updated_at": "2026-04-10T03:21:57.156431Z",
	"deleted_at": null,
	"sha1_hash": "03c8336688e8d8b769702e81bf6b6a85771140b4",
	"title": "[0001] AmberAmethystDaisy -\u003e QuartzBegonia -\u003e LummaStealer",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1856750,
	"plain_text": "[0001] AmberAmethystDaisy -\u003e QuartzBegonia -\u003e LummaStealer\r\nBy 0x1c\r\nPublished: 2024-06-21 · Archived: 2026-04-05 16:25:16 UTC\r\nDisclaimer: I have personally noticed a significant difficulty in finding names for many loaders, even if\r\nthey have been reported on due to the overwhelming focus on the final payload within infection chains.\r\nWith this in mind, I utilize a custom loader taxonomy system, with the name of the loader in open-source reporting as a secondary identifier. More information on this taxonomy system can be found\r\nhere. If you happen to know the name of a loader that I report on, please let me know!\r\nRecently, I stumbled across a video on YouTube from \"The PC Security Channel\", which noted that there was\r\nmalware being distributed through fake cracked software on GitHub. Unfortunately, the extent of the analysis\r\nperformed within the video was to check VirusTotal in order to see if the file is malicious or not.\r\nVideo: How not to Pirate: Malware in cracks on Github (youtube.com)\r\nAlthough this might be good enough for most, my disappointment is immeasurable, and my day is nearly ruined.\r\nHowever, we can do the digging ourselves and get to the bottom of this!\r\nAlthough the original GitHub repo that was shown within the video is now taken down, the actual download URL\r\nfor the first stage seems to be hosted on another repo, as seen in the hyperlink within the video:\r\nThe URL seen in the hyperlink leads us to\r\nhttps[:]//github[.]com/ravindrauppalapati/RoleManager/releases/tag/Client , which is still up and\r\navailable for download!\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 1 of 22\n\nStage 1 - QuartzDahlia\r\nAlso known as: Launch4j\r\nTL;DR:\r\nInitial sample can be executed as a normal executable as well as a JAR\r\nSHA-256 Filename\r\n8ed6a84101dfcafeac6ddbf5020312b0094576fd3f9106f7df460e1b8a7bd5e1 Win.Installer.x64.zip\r\n94edf5396599aaa9fca9c1a6ca5d706c130ff1105f7bd1acff83aff8ad513164 Win Installer x64.exe\r\nUnpacking the ZIP archive, we can observe the following file structure:\r\n│ Win Installer x64.exe\r\n│\r\n└───v2024\r\n ├───bin\r\n │ │ awt.dll\r\n │ │ glass.dll\r\n │ │ java.dll\r\n │ │ javafx_font.dll\r\n │ │ javafx_iio.dll\r\n │ │ javaw.exe\r\n │ │ msvcp120.dll\r\n │ │ msvcr100.dll\r\n │ │ msvcr120.dll\r\n │ │ net.dll\r\n │ │ nio.dll\r\n │ │ prism_d3d.dll\r\n │ │ sunec.dll\r\n │ │ sunmscapi.dll\r\n │ │ verify.dll\r\n │ │ zip.dll\r\n │ │\r\n │ └───client\r\n │ jvm.dll\r\n │\r\n └───lib\r\n │ jce.jar\r\n │ jfr.jar\r\n │ jsse.jar\r\n │ resources.jar\r\n │ rt.jar\r\n │\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 2 of 22\n\n└───ext\r\n jfxrt.jar\r\n sunec.jar\r\n sunjce_provider.jar\r\n sunmscapi.jar\r\nTaking a look at the executable, it's unclear at first as to where the malicious code lies. With this in mind, I\r\ndecided to load it up in x64dbg to do some quick preliminary dynamic analysis.\r\nStepping through a few functions, I was able to see that the malware attempts to calls its own binary with the -\r\njar flag using its bundled Java runtime. It turns out that this actually a tool named Launch4j which allows for\r\nJava applications to be wrapped in an executable.\r\nSince JAR files are able to be unzipped, we can go ahead and extract the contents of this executable with 7-Zip.\r\nNote: Detect-It-Easy also notifies us that this executable contains a ZIP archive, and we could have gone\r\nabout it that way as well!\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 3 of 22\n\nStage 2 - AmberAmethystDaisy\r\nAlso known as: D3F@ck Loader, NestoLoader\r\nSHA-256 Filename\r\n515d025ba2aa1096f65c13569de283b83d86824d08ca48c1fc3bc407d4cf3266 MainForm.phb\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 4 of 22\n\nTL;DR:\r\nExtracted contents of the JAR contains files with the .phb extension, indicative of JPHP\r\nThe entry point for JPHP-based applications can be found within .system/application.conf\r\nIn this case, the entry point resides in app/forms/MainForm.phb\r\nUtilizing Binary Refinery and jadx, the next stage payload URL is retrieved.\r\nA few of the extracted files have the .phb extension, which is indicative of JPHP, an implementation of PHP on\r\nthe Java VM. For more information on triaging JPHP malware, this same malware family was recently showcased\r\non a MalwareAnalysisForHedgehogs video.\r\nThe entry point for JPHP-based applications can be found within .system/application.conf . The content of this\r\nfile is as follows:\r\n# MAIN CONFIGURATION\r\napp.name = DarkLauncher\r\napp.uuid = 6ccf8f8e-fb00-441b-a0f5-f3bc2fa6619b\r\napp.version = 1\r\n# APP\r\napp.namespace = app\r\napp.mainForm = MainForm\r\napp.showMainForm = 1\r\napp.fx.splash.autoHide = 0\r\nWe now know that the entry point that we are interested in would be located within the app folder and should be\r\ncalled MainForm . Let's go and take a look! Sure enough, a file titled MainForm.phb exists in the forms folder\r\nlocated within app .\r\nViewing this file with a hex editor, we can very quickly see what looks to be parts of an embedded configuration.\r\nNow we can be fairly sure that this is the file we want to be looking further into.\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 5 of 22\n\nAlthough we see a C2 IP address of 194.147.35[.]251 here, this is seemingly not where the next payload is\r\nhosted. Let's dig deeper to figure out where the next payload is actually hosted.\r\nDealing with PHB files\r\nPHB files contain Java class files within them, which are denoted with a magic of CAFEBABE . We can utilize these\r\nmagic bytes as a marker in order to extract the embedded .class files.\r\nI set up the following Binary Refinery pipeline to extract the 2 class files from app/forms/MainForm.phb :\r\nef MainForm.phb | resplit h:CAFEBABE [ \\\r\n| pop \\\r\n| ccp h:CAFEBABE \\\r\n| dump extracted_class_{index}.class \\\r\n]\r\nUnit Name Definition\r\nef Emit File Places a file into the pipeline\r\nresplit\r\nRegular\r\nExpression Split\r\nSplits the data in the pipeline by the supplied regular expression\r\npop Pop\r\nRemoves a chunk from the frame (and stores it in a meta variable) - Used\r\nhere to remove the first chunk in the pipeline, which contains data before the\r\nfirst CAFEBABE header\r\nccp ConCat Prepend Concatenates a value to the beginning of each chunk\r\ndump Dump Dumps the data stored in each chunk to disk\r\nUsing jadx, we can decompile the recovered Java class files in order to get a better idea as to what the malicious\r\ncode does.\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 6 of 22\n\nLooking through the code, we come across 2 base64 encoded strings which decode to URLs. We can set up the\r\nfollowing Binary Refinery pipeline to extract, defang, and print these indicators:\r\nef MainForm.phb | carve b64 [ \\\r\n | b64 \\\r\n | xtp url \\\r\n | defang \\\r\n | cfmt \"{}\\n\" \\\r\n]\r\nhttps[:]//pastebin[.]com/raw/md5jVrEB\r\nhttps[:]//t[.]me/+JBdY0q1mUogwZWMy\r\nUnit Name Definition\r\nef Emit File Places a file into the pipeline\r\ncarve Carve\r\nExtracts pieces of the pipeline that matches a given format - in this case,\r\nbase64\r\nb64 Base64 Base64 decodes each chunk in the pipeline\r\nxtp eXtracT Pattern Extracts indicators from the data within the pipeline by a given pattern\r\ndefang Defang Defangs indicators within the pipeline\r\ncfmt\r\nConvert to\r\nForMaT\r\nTransforms each chunk in the pipeline by applying a string format\r\noperation\r\nThe Pastebin URL holds a paste that contains the IP address 78.47.105[.]28 , which is where the next payload is\r\nhosted. We can now reconstruct the true URL of the next-stage payload:\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 7 of 22\n\nhttp[:]//78.47.105[.]28/auto/b0573cef5fbfef5a15e8a6527080ad25/93.exe\r\nStage 3 - QuartzBegonia\r\nAlso known as: N/A\r\nSHA-256 Filename\r\n5b751d8100bbc6e4c106b4ef38f664fb031c86f919c3e2db59a36c70c57f54e0 93.exe\r\nThe third-stage payload in this infection chain is a loader written in C++. Loading the sample in Binary Ninja\r\nquickly reveals a large amount of non-code data, which is very likely the encrypted payload.\r\nWithin the main function, we can see that a thread would be created, which would execute a function which I've\r\nnamed thread_start_addr ( 0x401750 ) with an argument - a pointer to a function I've named\r\nmal::thread_main ( 0x41d7b0 ).\r\nWhen called, the function thread_start_addr executes the function at the address that was passed-in as an\r\nargument:\r\nDiving into the mal::thread_main function, we come across an encrypted buffer and its corresponding\r\ndecryption loop:\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 8 of 22\n\nRe-implementing this decryption loop in Python, we can recover the content of the encrypted buffer:\r\ndec_buf = bytearray()\r\nfor b in enc_buf:\r\n first_dec = (b ^ 0x73) - 0x15\r\n second_dec = ((((((((first_dec - 0x57) ^ 0x74) + 0x4e) ^ 0x70) - 0x65) ^ 0x22) - 0x73) ^ 0x2a) % 256\r\n dec_buf.append(second_dec)\r\n\u003e\u003e dec_buf\r\nbytearray(b'U\\x05\\x00\\x007\\x13\\x00\\x00\\x00\\x00\\x00\\x00user32.dll\\x00CreateProcessA\\x00VirtualAlloc\\x00GetThreadC\r\nHowever, this is very ugly, so I created a colorful and pretty template for the decrypted data within 010Editor in\r\norder to make better sense of it visually. Now we can see that the data is mostly a few function names and a\r\nshellcode buffer used in order to inject the final payload into RegAsm.exe .\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 9 of 22\n\nDiamondDaffodil shellcode seen in buffer decrypted within QuartzBegonia\r\nOne thing that I tend to do when triaging loaders is to find the beginning of what is likely the encrypted content of\r\nthe payload in order to find functions that cross-reference these buffers. I was able to locate a very large buffer\r\n( 0x46600 bytes long) at 0x428038 , as well as a smaller buffer ( 0x31 bytes long) at 0x428000 .\r\nA function located at 0x41d4d0 references both of these buffers and taking a look at the function—my suspicions\r\nof these buffers being the next-stage payload and its corresponding decryption key were confirmed.\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 10 of 22\n\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 11 of 22\n\nKey and encrypted content of the final payload, located within the .data segment\r\nTaking a look at the function located at 0x41d4d0 , we can see telltale signs of the RC4 encryption algorithm:\r\nTip: Seeing two loops and the number 256 (0x100) is often indicative of the RC4 encryption\r\nalgorithm\r\nWith this information, I set up a Binary Refinery pipeline to decrypt the final payload:\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 12 of 22\n\nef 93.exe | \\\r\nvsnip 0x428038:0x46600 | \\\r\nrc4 h:22a43b87df1edee294decd10f5e85c468fccf9fdda2e48841717965abedcd61ce4dbe9f3e0c9ca66fcea73762a5b0e5c5\r\ndump stage4.bin\r\nUnit Name Definition\r\nef Emit File Places a file into the pipeline\r\nvsnip Virtual Snip Snips (extracts) data from PE/ELF/MACHO files based on virtual offset\r\nrc4 RC4 RC4 decrypts the data in the pipeline, given a key\r\ndump Dump Dumps the data stored in each chunk to disk\r\nStage 4 - LummaStealer\r\nAlso known as: LummaC2 Stealer\r\nSHA-256 Filename\r\n0cf55c7e1a19a0631b0248fb0e699bbec1d321240208f2862e37f6c9e75894e7 N/A\r\nLoading the LummaStealer sample in Binary Ninja, we see the following function:\r\nTaking a look at the function sub_432130 , we immediately come across a problem:\r\nOpaque Predicates\r\nHere, we have an example of an obfuscation technique called Opaque Predicates. The jumps to the next section of\r\ncode are obfuscated by making their destination the result of a mathematical operation. Typically, we would deal\r\nwith these via patching, which is possible (this is not at the same place in code, but is an example of this\r\ntechnique):\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 13 of 22\n\nHowever, I was recently informed by Xusheng from the Binary Ninja / Vector35 team (huge shoutouts to the\r\nteam!) of a better way to tackle this:\r\nBy default, Binary Ninja believes that the value defined at data_440fe8 and data_440fec can be modified by\r\nthe program. Although this may be true, we know that this is likely not the case. With this in mind, if we convert\r\nthe types—which are by default void* —to const int32_t , Binary Ninja can do its magic (dataflow analysis)\r\nin order to solve the opaque predicate for us!\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 14 of 22\n\nJust like that, we can save our precious reverse-engineering time (and sanity...)! I originally was manually\r\npatching a whole bunch of these, and let me tell you—it was miserable.\r\nHowever, going through the code a little more, we hit yet another roadblock:\r\nIn this case, the value data_440ffc holds the address of 2 possible values used in order to calculate the\r\ndestination. If we take a look at data_440ffc , right now, it is only showing up as a void* :\r\nLet's go ahead and change this to a const int32_t[2] in order to correctly reflect its type.\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 15 of 22\n\nNow, if we change the type of data_441004 to const int32_t , we can now see that the variable named\r\ndata_440ffc has automatically been changed to jump_table_440ffc :\r\nGoing back to our function, we now see that the dataflow analysis has taken care of the opaque predicate! (and left\r\ntwo more of them in its wake...)\r\nWe'll have to go and do this a whole bunch of times, but it is still much better than calculating the location of the\r\njump and patching it all manually (by a long shot).\r\nAfter patching up the functions called by the main method, we have a much cleaner look at the binary. Let's move\r\nour focus over to the function located at 0x409f50 .\r\nAPI Hash Resolution\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 16 of 22\n\nHere, we come across a case of API Hash Resolution. The function sub_434a60 is used to take a module\r\n( data_4431bc , which is a pointer to the base address of WinHttp.dll ) and a corresponding hash in order to\r\nresolve a function for further use.\r\nI won't showcase sub_434a60 here, as it goes out of scope for this post—but this function essentially goes\r\nthrough the exports of WinHttp.dll , hashes all the function names, and returns a pointer to the function\r\nmatching the provided hash.\r\nI was able to deduce that this copy of LummaStealer is utilizing a hashing algorithm, namely FNV-1a with a\r\nmodified offset. I went ahead and added this modified hashing algorithm to the hashdb project.\r\nNow that the modified hashing algorithm has been deployed within hashdb, we can go ahead and simply utilize\r\nthe hashdb plugin within Binary Ninja to find the names of the APIs used:\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 17 of 22\n\nDecrypting C2 Addresses\r\nNow that we have both the opaque predicates and API hash resolution out of the way, let's try to find the C2\r\naddresses for LummaStealer.\r\nWithin the function that resolves the WinHttp functions, we see a variable being assigned to a list of pointers. If\r\nwe investigate this further, we see that the list of pointers contains what looks to be base64 encoded strings.\r\nHowever, if we try to base64 decode the strings, we do not end up with readable text. Let's dig deeper to see how\r\nthese strings are decrypted!\r\nEncrypted LummaStealer C2 addresses\r\nIn this case, it seems that each string is passed in as the first argument to a function at 0x00409cb0 .\r\nLet's take a further look at that function:\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 18 of 22\n\nAt the beginning, we see that the length of the current encrypted C2 address is being calculated, alongside a call to\r\na function at 0x00409e10 which calculates the length of the blob, if you were to base64 decode it. This is\r\nfollowed by a function that actually base64 decodes the data.\r\nContinuing through the function, we see the following code:\r\nThis code takes the first 32 (0x20) bytes of the decoded blob as a key and XORs the rest of the data with it. The\r\nresulting output is a C2 address for LummaStealer!\r\nWith this in mind, I set up the following Binary Refinery pipeline in order to decrypt the LummaStealer C2\r\naddresses:\r\nef stage4.bin \\\r\n| vsnip 0x438df8:0x451 \\\r\n| carve b64 -n 5 [ \\\r\n| b64 \\\r\n| push [ \\\r\n| snip :32 \\\r\n| pop key \\\r\n] \\\r\n| snip 32: \\\r\n| xor var:key \\\r\n| defang \\\r\n| cfmt \"{}\\n\" \\\r\n]\r\nassociationokeo[.]shop\r\nturkeyunlikelyofw[.]shop\r\npooreveningfuseor[.]pw\r\nedurestunningcrackyow[.]fun\r\ndetectordiscusser[.]shop\r\nrelevantvoicelesskw[.]shop\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 19 of 22\n\ncolorfulequalugliess[.]shop\r\nwisemassiveharmonious[.]shop\r\nsailsystemeyeusjw[.]shop\r\nUnit Name Definition\r\nef Emit File Places a file into the pipeline\r\nvsnip Virtual Snip Snips (extracts) data from PE/ELF/MACHO files based on virtual offset\r\ncarve Carve\r\nExtracts pieces of the pipeline that matches a given format—in this case, base64\r\nwith a minimum length of 5 characters\r\nb64 Base64 Base64 decodes each chunk in the pipeline\r\npush Push\r\nTemporarily sets aside the current chunk of data and replaces it with a new chunk.\r\nThis is useful if you want to perform operations on a piece of data while keeping\r\nthe original data intact for later use.\r\nThink of this as a way to create a copy of the data in order to do some work on\r\nthe data, before restoring the original data.\r\nsnip Snip On the copy of the data, retrieves (snips) the first 32 bytes, which is the XOR key\r\npop Pop\r\nPlaces the modified copy of the data into a meta-variable. Meta-variables can be\r\nlater utilized with the var keyword\r\nsnip Snip\r\nOn the original data, retrieves (snips) everything after the first 32 bytes, which is\r\nthe encrypted C2 address\r\nxor XOR\r\nPerforms an exclusive-or operation on the data within the chunk with the popped\r\nkey\r\ndefang Defang Defangs indicators within the pipeline\r\ncfmt\r\nConvert to\r\nForMaT\r\nTransforms each chunk in the pipeline by applying a string format operation\r\nAnd now, we can happily say that we actually know what this infection chain is, how it works, and we've\r\nsuccessfully retrieved the final payload and its C2 addresses. Thanks for reading! 💖\r\nIndicators of Compromise:\r\nIoC Description\r\nhttps[:]//github[.]com/ravindrauppalapati/RoleManager/releases/tag/Client\r\nSample Download\r\nURL\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 20 of 22\n\nIoC Description\r\n8ed6a84101dfcafeac6ddbf5020312b0094576fd3f9106f7df460e1b8a7bd5e1 Sample ZIP\r\n94edf5396599aaa9fca9c1a6ca5d706c130ff1105f7bd1acff83aff8ad513164 QuartzDahlia EXE\r\n515d025ba2aa1096f65c13569de283b83d86824d08ca48c1fc3bc407d4cf3266\r\nAmberAmethystDaisy\r\nPHB\r\n194.147.35[.]251\r\nAmberAmethystDaisy\r\nEvent Server\r\nhttps[:]//pastebin[.]com/raw/md5jVrEB\r\nAmberAmethystDaisy\r\nDead-Drop\r\nhttps[:]//t[.]me/+JBdY0q1mUogwZWMy\r\nAmberAmethystDaisy\r\nTelegram\r\nhttp[:]//78.47.105[.]28/auto/b0573cef5fbfef5a15e8a6527080ad25/93.exe\r\nQuartzBegonia\r\nDownload URL\r\n5b751d8100bbc6e4c106b4ef38f664fb031c86f919c3e2db59a36c70c57f54e0 QuartzBegonia EXE\r\n0cf55c7e1a19a0631b0248fb0e699bbec1d321240208f2862e37f6c9e75894e7\r\nDiamondDaffodil\r\nShellcode\r\nd6a40534d8a76509605e67ead55ef3506050c7df86701db13443d091c7a4bce2 LummaStealer EXE\r\nassociationokeo[.]shop LummaStealer C2\r\nturkeyunlikelyofw[.]shop LummaStealer C2\r\npooreveningfuseor[.]pw LummaStealer C2\r\nedurestunningcrackyow[.]fun LummaStealer C2\r\ndetectordiscusser[.]shop LummaStealer C2\r\nrelevantvoicelesskw[.]shop LummaStealer C2\r\ncolorfulequalugliess[.]shop LummaStealer C2\r\nwisemassiveharmonious[.]shop LummaStealer C2\r\nsailsystemeyeusjw[.]shop LummaStealer C2\r\nP.S - Huge thanks to my friend donaldduck8 for proofreading this post, be sure to check out his blog at\r\nhttps://sinkhole.dev\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 21 of 22\n\nSource: https://www.0x1c.zip/0001-lummastealer/\r\nhttps://www.0x1c.zip/0001-lummastealer/\r\nPage 22 of 22",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://www.0x1c.zip/0001-lummastealer/"
	],
	"report_names": [
		"0001-lummastealer"
	],
	"threat_actors": [],
	"ts_created_at": 1775434788,
	"ts_updated_at": 1775791317,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/03c8336688e8d8b769702e81bf6b6a85771140b4.pdf",
		"text": "https://archive.orkl.eu/03c8336688e8d8b769702e81bf6b6a85771140b4.txt",
		"img": "https://archive.orkl.eu/03c8336688e8d8b769702e81bf6b6a85771140b4.jpg"
	}
}