blank edited this page Aug 11, 2017 · 8 revisions

vlany features

Process hiding

All processes spawned in an owner shell are hidden by the use of the magic GID. For example, a user with the GID of 1337 spawning a bash shell will also give that bash shell process a GID of 1337. Simple enough right? Let's say the magic GID is in fact 1337. A normal user spawning a bash shell will give that process a GID of, let's say, 1000. So that's not a rootkit-related process. However, if a user with the magic GID spawns a bash shell, they'll give that process a GID of 1337. vlany sees that the process has a GID of 1337, and therefore hides it from any regular user.

GID based process hiding is much more reliable than what Azazel attempted. Azazel relied on the contents of /proc/id/environ to determine if a process is hidden or not by using the special environment variable. This works... but not for normal non-root users. If a normal user was to view the running processes, they wouldn't have the correct permissions to open the environ entry for reading, and so the process would show up anyway. Protecting the entire proc dir with a magic GID prevents any user, root or normal, from even opening the proc dir.

User hiding

vlany hooks the utmp symbols in order to hide backdoor user existence on the system. This only applies to the PAM backdoor however, since the accept() backdoor relies on a reverse shell which inherits the permissions of whatever service you're connecting to.

Network hiding

vlany hides connections from source ports of the PAM backdoor port or any source ports between the accept low and high backdoor accepted range. Only one connection can be made at a time from the hidden ports. Being connected to the PAM backdoor under the hidden port and the accepted backdoor at the same time will cause one of the "hidden" ports to be visible. I'm uncertain of why this bug exists. But it's easily avoidable. vlany now hooks libpcap functions to avoid network packet sniffing. I'm currently working on hiding LXC container network interfaces from ifconfig and other similar utils.

LXC container

The rootkit user has the choice of utilising the small utility in their hidden directory, called enter_lxc. It allows for on-the-fly creation and destruction of hidden container environments. This file system is however, completely temporary, and will be destroyed when the user types 'exit' to exit the container. This small utility does also require the presence of liblxc, which can be installed from any lxc-dev packages or the likes.


vlany used to offer during installation if they wanted to break libc's ptrace function. This is now redacted and the only way of enabling this is by changing the argument given to config.py during install.sh's runtime from a 0 to a 1. Instead, the ptrace hook in vlany prevents any regular users from calling it on a hidden process.

Additionally, as of [some date here, I forgot the date] vlany now breaks any 'strings' calls to the rootkit's shared libraries. Strings calls will still seem to work perfectly fine for any other binary.


By forging /proc/self/maps and /proc/self/smaps, I prevent users from tracing the rootkit shared library back to its hidden directory. Although users cannot see the location of the shared library, they can however see that other libraries are being loaded, such as libopenssl and libcrypt. A paranoid sysadmin could see this and wonder why their system is loading those libraries without them asking ld to do so. Possibly a red flag, but even if they know this, there's not much they can do to remove the rootkit without a complete image copy of the disk. Additionally, by forging /proc/self/numa_maps, I hide the rootkit information (shared library path, most importantly) from calls to this file.

If someone is eager to hunt for potential infections or general inconsistencies, they could compare the list of mapped addresses from proc/self/maps, /proc/self/smaps, and /proc/self/numa_maps and read the directory listing from /proc/self/map_files/.

Also by hooking execve(), I'm able to check if a user is attempting to run ldd to reveal all the current loaded shared libraries, and if they are, I can unload vlany temporarily (see more about this in the next section) so that the rootkit's shared library isn't shown anywhere, and then when ldd is done doing what it's doing, I can reload vlany into ld.so.preload. The same applies to users who try to load their own shared library through use of LD_PRELOAD. Another advantage of hooking execve() is that I can check for environment variables, and if a user is trying to set LD_AUDIT or LD_TRACE_LOADED_OBJECTS, I can also temporarily hide again.

Persistent (re)installation & Anti-Detection

Since the rootkit's shared library constructor and destructor gets called at the start and end of every process, I can persistently heck for possible inconsistencies in ld.so.preload, or if it's straight up missing from /etc/. By persistently doing these checks, I can rewrite the shared library's path back to ld.so.preload should the file be inconsistent or missing so that vlany is incredibly difficult to remove from the rooted box.

By using this same methodology, I can also temporarily unlink ld.so.preload during a process' runtime, and then rewrite ld.so.preload once the process has finished executing. vlany checks if chkproc (a counterpart of chkrootkit), rkhunter, unhide, or ldd are being executed, and if they are, vlany temporarily removes ld.so.preload so that these tools don't detect us and alert the sysadmin running the tool. Theoretically, vlany can hide from literally anything by doing this.

Dynamic linker modifications

By modifying the dynamic linker's libraries, we're able to overwrite any occurrences of "/etc/ld.so.preload" with our own file location. This nullifies the functionality of /etc/ld.so.preload, so trying to remove, write, or read /etc/ld.so.preload will prove fruitless if attempting to remove any LD_PRELOAD malware on a box.
The only easy way to revert the dynamic linker libraries back to normality is by using the same method as patch_ld.py, but the 'new' ld.so.preload file location has to be available to whoever's trying to fix the dynamic linker.


  • accept() reverse shell backdoor

Derived from Jynx2, this backdoor allows you to connect to any running service (such as sshd, apache, etc) via netcat to spawn a /bin/sh shell by using your port range (LOW_PORT + HIGH_PORT) and password defined at runtime. Unlike Jynx2, vlany allows for plaintext communications AND encrypted SSL communications via the accept() backdoor. Just make sure to restart the service you want to connect to after installing vlany. To connect to the accept() backdoor using SSL, the service you're connecting to MUST be listening with SSL, and you MUST enable the --ssl flag when running ncat to connect. Additionally, unlike Jynx2, this implementation of the accept hook backdoor isn't trash.

The only function hooked for this backdoor is accept().
At the end of install.sh, the script attempts to restart the ssh daemon in /etc/init.d if it exists.

  • PAM backdoor

Backdoor that allows ssh/sftp connections. This backdoor is much superior than the accept() backdoor. Some functions that work in this backdoor will /only/ work in this backdoor.


The 'UsePAM' line in the sshd_config file located in /etc/ssh must say 'yes' for this backdoor to work. By default it is usually 'yes', but some sysadmin might manually change this to try to prevent PAM backdoors. If this is the case, install.sh will attempt to correct this.

There's a script in 'misc/' called 'ssh.sh'. Use this to connect to your PAM backdoor under the hidden PAM port. Usage instructions for this script can be seen upon execution of the script. Connections to the PAM backdoor can also be made without this script, but your connection will be visible.

Your backdoor password is hashed using SHA-512. (I RECOMMEND YOU USE THIS BACKDOOR. PLEASE USE THIS BACKDOOR. USE IT.)

Hooked functions:

The required functions to hook for the PAM backdoor are as follows: getpwnam(), getpwnam_r(), getpwuid(), getspnam() - these are all standard libc functions. pam_open_session(), pam_authenticate(), pam_acct_mgmt(), pam_prompt(), pam_vprompt() - all functions owned by libpam By hooking the four said standard libc functions, we can trick the system into thinking our backdoor user actually exists without having to edit any files. This works fine, however log files will still be written to as if the account really does exist. We combat this by hooking utmp functions (getutent(), getutxent(), pututline() and syslog()).

PAM auth logger

Vlany also hooks the pam_vprompt() function to catch login attempts and then places them in pam_auth_logs inside vlany's hidden directory. Everytime you login it will prompt you with how many passwords have been logged.

Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.