{
	"id": "51e14dae-3ab1-421c-ac77-cb8ab4f23d81",
	"created_at": "2026-04-06T01:30:33.554317Z",
	"updated_at": "2026-04-10T03:21:16.231254Z",
	"deleted_at": null,
	"sha1_hash": "37683fdd1f97ccc7f2ef40f56b423a377f47a1d5",
	"title": "YARA for config extraction",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 390352,
	"plain_text": "YARA for config extraction\r\nBy Abdallah Elshinbary\r\nPublished: 2022-08-08 · Archived: 2026-04-06 00:42:45 UTC\r\nYARA is a tool aimed at helping malware researchers to identify and classify malware samples. It’s considered to\r\nbe the pattern matching swiss knife for malware researchers.\r\nIf you are not familiar with writing YARA rules, the official docs would be a great start.\r\nIn this blog I will go through how YARA rules can be used for malware config extraction.\r\nYARA has come a long way since its original release and it now has some awesome modules for writing better\r\nand more complex rules.\r\nWhat is a YARA modulePermalink\r\nA YARA module is like a plugin for extending YARA features, it allows you to define data structures and\r\nfunctions which can be used in your rules.\r\nTo use a YARA module you simply import it using import \"module_name\" , you can refer to the docs to learn\r\nabout the available functions of each module.\r\nExample:\r\nimport \"pe\"\r\nrule test {\r\n condition:\r\n pe.number_of_sections == 1\r\n}\r\nWith that said let’s now jump into malware land, I will demonstrate on two variants of RedLine Stealer which is a\r\nvery popular dotnet stealer.\r\nRedLine Stealer Variant1Permalink\r\nThe first variant stores the config in plaintext, we are only interested in two fields (C2 and BotnetID).\r\nhttps://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nPage 1 of 13\n\nTo read these fields we need to understand how ldstr instruction works. The instruction’s opcode is 0x72\r\nfollowed by 4 bytes which represent the string token.\r\nA token is a DWORD value that represents a table and an index into that table. For example, the\r\nEntryPointToken 0x0600002C, references table 0x06 (MethodDef) and its row 0x2C. The table index is\r\n1 byte and the row index is 3 bytes.\r\nIn the following instruction for example, the string token is 0x7000067B (little-endian) and the row index is\r\n0x67B .\r\n727B060070 // ldstr \"87.251.71.4:80\"\r\nThe dotnet module already has the functionality to retrieve all user strings from a dotnet sample.\r\nimport \"dotnet\"\r\nimport \"console\"\r\nrule Test {\r\n condition:\r\n for all i in (0..dotnet.number_of_user_strings-1): (\r\n console.log(dotnet.user_strings[i])\r\n )\r\n}\r\nNotice that I used sed to remove null characters because dotnet user strings are stored as an array of\r\nunicode strings.\r\nThis is cool but we need to get the user strings using the row index from the string token.\r\nhttps://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nPage 2 of 13\n\nTo achieve this we need to make a couple of changes to the dotnet module source file at\r\nlibyara/modules/dotnet/dotnet.c .\r\nThis will index the user strings array by row index (offset from the start of the strings table).\r\nTo compile and install yara you need to run these two scripts for the first time only:\r\n$ ./bootstrap.sh\r\n$ ./configure\r\nThen you build YARA with your changes:\r\n$ make\r\n$ sudo make install\r\nWe can now write a simple rule to read the config fields.\r\nimport \"dotnet\"\r\nimport \"console\"\r\nrule Redline {\r\n strings:\r\n $get_conf_v1 = {\r\n 72 ?? ?? ?? 70 // IL_0000: ldstr \"87.251.71.4:80\"\r\n 80 ?? ?? ?? 04 // IL_0005: stsfld \u003cIP\u003e\r\n 72 ?? ?? ?? 70 // IL_000A: ldstr \"lyla\"\r\n 80 ?? ?? ?? 04 // IL_000F: stsfld \u003cID\u003e\r\n 72 ?? ?? ?? 70 // IL_0014: ldstr \"\"\r\n 28 ?? ?? ?? ?? // IL_0019: call set_Message(string)\r\n 2A // IL_001E: ret\r\n }\r\n condition:\r\n $get_conf_v1\r\n and console.log(\"[+] C2: \",\r\nhttps://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nPage 3 of 13\n\ndotnet.user_strings[int32(@get_conf_v1+1) \u0026 0xffffff]\r\n )\r\n and console.log(\"[+] Botnet: \",\r\n dotnet.user_strings[int32(@get_conf_v1+11) \u0026 0xffffff]\r\n )\r\n}\r\n@get_conf_v1 : address of the first match of $get_conf_v1\r\nint32 : reads 4 bytes (string token) from an offset, I used 0xffffff bit mask to only get the\r\nrow index.\r\nCool, Let’s move to the second variant.\r\nRedLine Stealer Variant2Permalink\r\nThis variant stores the config in an encrypted form.\r\nThe decryption algorithm looks as follows:\r\nCurrently YARA doesn’t have a module to do base64 and xor operations in conditions, so why not write our\r\nown module :)\r\nWriting our own YARA modulePermalink\r\nModules are written in C and built into YARA as part of the compiling process.\r\nI will explain briefly how to write a YARA module, for more details refer to the official docs.\r\nYARA modules reside in libyara/modules , it’s recommended to use the module name as the file name for the\r\nsource file. Here I created a new module directory named malutils and inside it is the source file named\r\nhttps://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nPage 4 of 13\n\nmalutils.c , now let’s go through the source code.\r\nFirst we need to include the required headers to be able to use YARA’s module API.\r\nNext we define the required functions:\r\nXor decryption function which takes a buffer and a key and returns the decrypted string buffer.\r\nBase64 decoding function which takes a base64 encoded string and returns the decoded value.\r\nhttps://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nPage 5 of 13\n\nHelper function to convert dotnet user strings from wide to ascii.\r\nThen comes the declaration section where we declare the functions and data structures that will be available for\r\nour YARA rules.\r\nhttps://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nPage 6 of 13\n\nAfter that we have 2 pairs of functions, the first pair is module_initialize \u0026 module_finalize .\r\nThese functions allow you to initialize and finalize any global data structure you may need to use in your module,\r\nand both functions are invoked whether or not the module is being imported by some rule.\r\nThe second pair is module_load \u0026 module_unload .\r\nThe module_load function is invoked once for each scanned file (only if the module is imported in your rule).\r\nIt’s is where your module can inspect the file being scanned, parse or analyze it in the way preferred, and then\r\npopulate the data structures defined in the declarations section.\r\nFor each call to module_load there is a corresponding call to module_unload . This function allows your module\r\nto free any resource allocated during module_load .\r\nhttps://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nPage 7 of 13\n\nFinal TouchesPermalink\r\nBefore we test our module there’s a nasty bug we need to take care of.\r\nWhen writing a YARA module, instead of using the C return statement in your declared functions you must use\r\nreturn_string(x) , return_integer(x) or return_float(x) to return from a function.\r\nThe problem occurs when we return from base64d function, the decoded string might contain null bytes so\r\nreturn_string won’t return the full buffer.\r\nAs you can see below, return_string uses strlen to determine the length of the returned string so it will stop\r\nat the first null byte.\r\nhttps://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nPage 8 of 13\n\nAs a workaround, I defined a new return macro called return_sized_string which enables us to set the length\r\nof the returned string rather than relying on strlen .\r\nBuilding our modulePermalink\r\nTo include our module in the compiling process of YARA we must follow two further steps:\r\nAdd our module name to the module_list at libyara/modules/module_list\r\nMODULE(malutils)\r\nAdd our module source file to the must compiled modules at libyara/Makefile.am\r\nMODULES += modules/malutils/malutils.c\r\nhttps://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nPage 9 of 13\n\nFinally we build YARA with our module:\r\n$ make\r\n$ sudo make install\r\nWith everything in place, let’s now test our module.\r\nTesting our modulePermalink\r\nBelow is the final YARA rule that handles both RedLine variants.\r\nimport \"dotnet\"\r\nimport \"console\"\r\nimport \"malutils\"\r\nrule Redline {\r\n meta:\r\n date = \"2022-08-08\"\r\n author = \"Abdallah 'n1ghtw0lf' Elshinbary\"\r\n description = \"Extracts Redline config (educational)\"\r\n strings:\r\n $get_conf_v1 = {\r\n 72 ?? ?? ?? 70 // IL_0000: ldstr \"87.251.71.4:80\"\r\n 80 ?? ?? ?? 04 // IL_0005: stsfld \u003cIP\u003e\r\n 72 ?? ?? ?? 70 // IL_000A: ldstr \"lyla\"\r\n 80 ?? ?? ?? 04 // IL_000F: stsfld \u003cID\u003e\r\n 72 ?? ?? ?? 70 // IL_0014: ldstr \"\"\r\n 28 ?? ?? ?? ?? // IL_0019: call set_Message(string)\r\n 2A // IL_001E: ret\r\n }\r\n $get_conf_v2 = {\r\n 72 ?? ?? ?? 70 // IL_0000: ldstr \"CyYOXysPAwUnB1NQCxtdWioxKUInBC5QCDNUUw==\"\r\n 80 ?? ?? ?? 04 // IL_0005: stsfld \u003cIP\u003e\r\n 72 ?? ?? ?? 70 // IL_000A: ldstr \"FzcNJDEOEDw7O1Y/FEM/IQ==\"\r\n 80 ?? ?? ?? 04 // IL_000F: stsfld \u003cID\u003e\r\n 72 ?? ?? ?? 70 // IL_0014: ldstr \"\"\r\n 80 ?? ?? ?? 04 // IL_0019: stsfld \u003cMessage\u003e\r\n 72 ?? ?? ?? 70 // IL_001E: ldstr \"Baying\"\r\n 80 ?? ?? ?? 04 // IL_0023: stsfld \u003cKey\u003e\r\n }\r\n condition:\r\n dotnet.is_dotnet and\r\n (\r\n (\r\nhttps://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nPage 10 of 13\n\n$get_conf_v1\r\n and console.log(\"[+] C2: \",\r\n malutils.wtoa(dotnet.user_strings[int32(@get_conf_v1+1) \u0026 0xffffff])\r\n )\r\n and console.log(\"[+] Botnet: \",\r\n malutils.wtoa(dotnet.user_strings[int32(@get_conf_v1+11) \u0026 0xffffff])\r\n )\r\n )\r\n or\r\n (\r\n $get_conf_v2\r\n and console.log(\"[+] C2: \",\r\n malutils.base64d(\r\n malutils.xord(\r\n malutils.base64d(\r\n malutils.wtoa(dotnet.user_strings[int32(@get_conf_v2+1) \u0026 0xffffff]) // enc c2\r\n ), malutils.wtoa(dotnet.user_strings[int32(@get_conf_v2+31) \u0026 0xffffff]) // xor key\r\n )\r\n )\r\n )\r\n and console.log(\"[+] Botnet: \",\r\n malutils.base64d(\r\n malutils.xord(\r\n malutils.base64d(\r\n malutils.wtoa(dotnet.user_strings[int32(@get_conf_v2+11) \u0026 0xffffff]) // enc botnet\r\n ), malutils.wtoa(dotnet.user_strings[int32(@get_conf_v2+31) \u0026 0xffffff]) // xor key\r\n )\r\n )\r\n )\r\n and console.log(\"[+] Key: \",\r\n malutils.wtoa(dotnet.user_strings[int32(@get_conf_v2+31) \u0026 0xffffff])\r\n )\r\n )\r\n )\r\n}\r\nRunning the rule on a list of samples produces the following output:\r\nhttps://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nPage 11 of 13\n\nBeautiful right!\r\nYou can pull the code and try it yourself at https://github.com/N1ght-W0lf/yara/tree/malutils.\r\nIt was just for learning purposes so not the best code :)\r\nSamplesPermalink\r\nfed976b2d134008fd6daec8edc00099935df756beb721034f71db33e4d675a6e\r\ne4f7246b103d9bda3a7604bea12dc5ac1064764c0f3691617c9829c4e5d469b5\r\n2d3503d8540e319851a67e55f06ed9e5ba060e821eec6dbc83960a5947ad1310\r\na8c498f5129af0229081edf1e535ac9dab6ad568befcbcecbfc7cc4c61e0a8eb\r\nc19938f0b9648dc1f6b95d0e767164da832b9a92f8128ab47dcb81c5e1ceb31a\r\ne94d48e09cace8937941fbf81d1a466fa2b2b6acfd0d6142fc3443c70e067294\r\nf343005539a589ec5512559e0bdc824c1069196ae39d519e5b1f3257f4a6660b\r\nReferencesPermalink\r\nhttps://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nPage 12 of 13\n\nhttps://yara.readthedocs.io/en/stable/index.html\r\nhttps://www.ntcore.com/files/dotnetformat.htm\r\nSource: https://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nhttps://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/\r\nPage 13 of 13",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://n1ght-w0lf.github.io/tutorials/yara-for-config-extraction/"
	],
	"report_names": [
		"yara-for-config-extraction"
	],
	"threat_actors": [],
	"ts_created_at": 1775439033,
	"ts_updated_at": 1775791276,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/37683fdd1f97ccc7f2ef40f56b423a377f47a1d5.pdf",
		"text": "https://archive.orkl.eu/37683fdd1f97ccc7f2ef40f56b423a377f47a1d5.txt",
		"img": "https://archive.orkl.eu/37683fdd1f97ccc7f2ef40f56b423a377f47a1d5.jpg"
	}
}