{
	"id": "d2974427-59f4-4af0-8377-70806b0c4c2b",
	"created_at": "2026-04-06T01:32:28.069329Z",
	"updated_at": "2026-04-10T13:12:20.920945Z",
	"deleted_at": null,
	"sha1_hash": "28205ddd18fc792320a164ed195c06ce26e25744",
	"title": "GitHub - karcherm/xz-malware: Stuff discovered while analyzing the malware hidden in xz-utils 5.6.0 and 5.6.1",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 54639,
	"plain_text": "GitHub - karcherm/xz-malware: Stuff discovered while analyzing\r\nthe malware hidden in xz-utils 5.6.0 and 5.6.1\r\nBy karcherm\r\nArchived: 2026-04-06 00:26:23 UTC\r\nInformation about the liblzma (xz-utils) backdoor\r\nDecoder for the string recognition automaton\r\nThe backdoor code, in the version extracted by Florian Weimer, contains no readable ASCII strings. It also\r\ncontains no obfuscated ASCII strings. Instead, it has a single state automaton to recognize the required strings.\r\nSearching for a string is performed by inputting all candidate start addresses into the string detection automaton,\r\nand checking whether the intended string is recognized. The string detection automaton returns a string ID.\r\nThe string detection automaton is implemented in the function _Lsimple_coder_update_0 , which has this\r\nsignature:\r\nint detect_string(const void* startptr, const void* optional_endptr);\r\nIt returns 0 if no known string is detected at startptr (the search is aborted if endptr is encountered before a match\r\nis detected), otherwise it returns a \"string ID\".\r\nThis is the table that is generated by running the script in this repository:\r\n 810: ' from '\r\n 678: ' ssh2'\r\n d8: '%.48s:%.48s():%d (pid=%ld)\\x00'\r\n 708: '%s'\r\n 108: '/usr/sbin/sshd\\x00'\r\n 870: 'Accepted password for '\r\n 1a0: 'Accepted publickey for '\r\n c40: 'BN_bin2bn\\x00'\r\n 6d0: 'BN_bn2bin\\x00'\r\n 958: 'BN_dup\\x00'\r\n 418: 'BN_free\\x00'\r\n 4e0: 'BN_num_bits\\x00'\r\n 790: 'Connection closed by '\r\n 18: 'Could not chdir to home directory %s: %s\\n\\x00'\r\n b0: 'Could not get agent socket\\x00'\r\n 960: 'DISPLAY='\r\n 9d0: 'DSA_get0_pqg\\x00'\r\nhttps://github.com/karcherm/xz-malware\r\nPage 1 of 4\n\n468: 'DSA_get0_pub_key\\x00'\r\n 7e8: 'EC_KEY_get0_group\\x00'\r\n 268: 'EC_KEY_get0_public_key\\x00'\r\n 6e0: 'EC_POINT_point2oct\\x00'\r\n b28: 'EVP_CIPHER_CTX_free\\x00'\r\n 838: 'EVP_CIPHER_CTX_new\\x00'\r\n 2a8: 'EVP_DecryptFinal_ex\\x00'\r\n c08: 'EVP_DecryptInit_ex\\x00'\r\n 3f0: 'EVP_DecryptUpdate\\x00'\r\n f8: 'EVP_Digest\\x00'\r\n 408: 'EVP_DigestVerify\\x00'\r\n 118: 'EVP_DigestVerifyInit\\x00'\r\n d10: 'EVP_MD_CTX_free\\x00'\r\n af8: 'EVP_MD_CTX_new\\x00'\r\n 6f8: 'EVP_PKEY_free\\x00'\r\n 758: 'EVP_PKEY_new_raw_public_key\\x00'\r\n 510: 'EVP_PKEY_set1_RSA\\x00'\r\n c28: 'EVP_chacha20\\x00'\r\n c60: 'EVP_sha256\\x00'\r\n 188: 'EVP_sm'\r\n 8c0: 'GLIBC_2.2.5\\x00'\r\n 6a8: 'GLRO(dl_naudit) \u003c= naudit\\x00'\r\n 1e0: 'KRB5CCNAME\\x00'\r\n cf0: 'LD_AUDIT='\r\n bc0: 'LD_BIND_NOT='\r\n a90: 'LD_DEBUG='\r\n b98: 'LD_PROFILE='\r\n 3e0: 'LD_USE_LOAD_BIAS='\r\n a88: 'LINES='\r\n ac0: 'RSA_free\\x00'\r\n 798: 'RSA_get0_key\\x00'\r\n 918: 'RSA_new\\x00'\r\n 1d0: 'RSA_public_decrypt\\x00'\r\n 540: 'RSA_set0_key\\x00'\r\n 8f8: 'RSA_sign\\x00'\r\n 990: 'SSH-2.0'\r\n 4a8: 'TERM='\r\n e0: 'Unrecognized internal syslog level code %d\\n\\x00'\r\n 158: 'WAYLAND_DISPLAY='\r\n 878: '__errno_location\\x00'\r\n 2b0: '__libc_stack_end\\x00'\r\n 228: '__libc_start_main\\x00'\r\n a60: '_dl_audit_preinit\\x00'\r\n 9c8: '_dl_audit_symbind_alt\\x00'\r\n 8a8: '_exit\\x00'\r\n 5b0: '_r_debug\\x00'\r\n 5b8: '_rtld_global\\x00'\r\nhttps://github.com/karcherm/xz-malware\r\nPage 2 of 4\n\na98: '_rtld_global_ro\\x00'\r\n b8: 'auth_root_allowed\\x00'\r\n 1d8: 'authenticating'\r\n 28: 'demote_sensitive_data\\x00'\r\n 348: 'getuid\\x00'\r\n a48: 'ld-linux-x86-64.so'\r\n 7d0: 'libc.so'\r\n 7c0: 'libcrypto.so'\r\n 590: 'liblzma.so'\r\n 938: 'libsystemd.so'\r\n 20: 'list_hostkey_types\\x00'\r\n 440: 'malloc_usable_size\\x00'\r\n c0: 'mm_answer_authpassword\\x00'\r\n c8: 'mm_answer_keyallowed\\x00'\r\n d0: 'mm_answer_keyverify\\x00'\r\n 948: 'mm_answer_pam_start\\x00'\r\n 78: 'mm_choose_dh\\x00'\r\n 40: 'mm_do_pam_account\\x00'\r\n 50: 'mm_getpwnamallow\\x00'\r\n a8: 'mm_log_handler\\x00'\r\n 38: 'mm_pty_allocate\\x00'\r\n a0: 'mm_request_send\\x00'\r\n 48: 'mm_session_pty_cleanup2\\x00'\r\n 70: 'mm_sshpam_free_ctx\\x00'\r\n 58: 'mm_sshpam_init_ctx\\x00'\r\n 60: 'mm_sshpam_query\\x00'\r\n 68: 'mm_sshpam_respond\\x00'\r\n 30: 'mm_terminate\\x00'\r\n c58: 'parse PAM\\x00'\r\n 400: 'password\\x00'\r\n 4f0: 'preauth'\r\n 690: 'pselect\\x00'\r\n 7b8: 'publickey\\x00'\r\n 308: 'read\\x00'\r\n 710: 'rsa-sha2-256\\x00'\r\n 428: 'setlogmask\\x00'\r\n 5f0: 'setresgid\\x00'\r\n ab8: 'setresuid\\x00'\r\n 760: 'shutdown\\x00'\r\n d08: 'ssh-2.0'\r\n 2c8: 'ssh-rsa-cert-v01@openssh.com\\x00'\r\n 88: 'sshpam_auth_passwd\\x00'\r\n 90: 'sshpam_query\\x00'\r\n 80: 'sshpam_respond\\x00'\r\n 98: 'start_pam\\x00'\r\n 9f8: 'system\\x00'\r\n 198: 'unknown\\x00'\r\nhttps://github.com/karcherm/xz-malware\r\nPage 3 of 4\n\nb10: 'user'\r\n 380: 'write\\x00'\r\n 10: 'xcalloc: zero size\\x00'\r\n b00: 'yolAbejyiejuvnup=Evjtgvsh5okmkAvj\\x00'\r\n 300: '\\x7fELF'\r\nA fake allocator\r\nliblzma has a memory allocation layer that just forwards allocation and freeing calls to dedicated allocators.\r\nCalling lzma_alloc or lzma_free with a given allocator object basically just calls a function pointer in that\r\nallocator object, which may or may not be related to actual memory allocation.\r\nThe backdoor contains a fake allocator object that looks up symbols instead of allocating and does nothing on\r\nfreeing. The look-up function takes a string ID (see previous section) as size. As string IDs are divisible by 8 and\r\nbetween 10 and 0xd10, they look like plausible sizes at first.\r\nThis allocator object is returned by .Lstream_decoder_memconfig.part.1 . The allocator structure contains a\r\ncontext pointer that is passed to the allocation and freeing functions. For this fake allocator, the opaque member\r\npoints to an internal ELF module descriptor records.\r\nThe usage pattern (with a sane symbol name for the function that returns the fake allocator) thus is something like\r\nthis:\r\n lzma_allocator* fake_alloc = GetFakeAllocator();\r\n fake_alloc-\u003eopaque = libc_elfmodule;\r\n void* symbol = lzma_alloc(0xAB8, fake_allocator); // 0xAB8: string ID of \"setresuid\"\r\n // use symbol, maybe call it, maybe store it somewhere\r\n lzma_free(symbol, fake_allocator); // just decoy, does nothing\r\nNote that lzma_alloc and lzma_free are not included in the backdoor object, but just the standard funktions\r\nprovided by non-backdoor code in liblzma.\r\nSource: https://github.com/karcherm/xz-malware\r\nhttps://github.com/karcherm/xz-malware\r\nPage 4 of 4",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://github.com/karcherm/xz-malware"
	],
	"report_names": [
		"xz-malware"
	],
	"threat_actors": [],
	"ts_created_at": 1775439148,
	"ts_updated_at": 1775826740,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/28205ddd18fc792320a164ed195c06ce26e25744.pdf",
		"text": "https://archive.orkl.eu/28205ddd18fc792320a164ed195c06ce26e25744.txt",
		"img": "https://archive.orkl.eu/28205ddd18fc792320a164ed195c06ce26e25744.jpg"
	}
}