[EXPL] An Alternative Method to Check for LKM Backdoor/Rootkit

From: support@securiteam.com
Date: 04/17/02


From: support@securiteam.com
To: list@securiteam.com
Date: Wed, 17 Apr 2002 11:52:43 +0200 (CEST)

The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion

When was the last time you checked your server's security?
How about a monthly report?
http://www.AutomatedScanning.com - Know that you're safe.
- - - - - - - - -

  An Alternative Method to Check for LKM Backdoor/Rootkit
------------------------------------------------------------------------

SUMMARY

LKM backdoor allows local attackers to hide themselves (allowing hiding of
running processes, loadable kernel modules, and arbitrary files). This is
accomplished by changing the kernel behavior. There are a few methods to
detect the presence of LKM, we will illustrate one of them here.

DETAILS

There are several ways to discover the presence of LKM. The main two
methods of doing this are:
1. Find the differences between the two systems by looking into what is
displayed before and after the LKM has been installed.
2. Try to detect the files used by LKM, by directly probing the system.

We will focus on the second method: "Find the differences between the
"live" view and the "real" view".

Since the LKM backdoor uses stealth techniques, it cannot be simply
discovered by just scanning the file system and looking for rouge files.
However if we read the raw disk and traverse the file system on the disk,
bypassing the "live" (in our case the fake one) file system, we can create
a "real" view of files on disk.

This allows us to compare the two views, and easily spot the differences.
The differences in most cases will show up as stealthy files (i.e. hidden
from our "live" view, but clearly visible in the "real" view).

Proof of concept:
At the end, there is proof of concept code. The code was created for Linux
and ext2/ext3 file systems. It has been tested on Mandrake and RedHat.
Beware, the code needs e2fsprogs 1.26 or above. A successful compilation
on RedHat involves upgrades e2fsprogs-devel; on Mandrake, you need
libext2fs2-devel.

The code is not perfect. For example, we did not add the check for files
that points to INODE 0.

How LKM can avoid detection by this method:
Since the method utilizes raw disk interface, such as /dev/rdsk/c0t0d0s2
or /dev/hda1. The LKM backdoor can intercept the interface, and change our
"real" view of the raw information.

Exploit code:
/* stealth_file_checker.c
 *
 * Copyright (C) 2002 Wang Jian, Marsec System Inc.
 *
 * http://www.marsec.net/
 *
 * Concept and Programming:
 *
 * Wang Jian <lark@marsec.net>
 *
 * Testing and Suggestion:
 *
 * Zhang JiaJun <jiajun.zhang@marsec.net>
 *
 *
 * No Warranty. This code is for educational use only, commercial use is
 * prohibited.
 *
 * This small program demonstrates how to detect stealth files/dirs
 * of lkm on linux ext2/ext3 filesystems.
 *
 * compile: gcc -o sfc main.c -lext2fs
 *
 * NOTE: You need e2fsprogs-1.26 or above to compile
 *
 * usage: run it without args to get hints
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <ext2fs/ext2fs.h>

ext2_ino_t root, cwd;
ext2_filsys fs;

ext2_ino_t string_to_inode(ext2_filsys fs, char *str)
{
    ext2_ino_t ino;
    int ret;

    ret = ext2fs_namei(fs, root, cwd, str, &ino);
    if (ret) {
        return 0;
    }
    return ino;
}

int list_dir_proc(ext2_ino_t dir,
        int entry,
        struct ext2_dir_entry *dirent,
        int offset,
        int blocksize,
        char *buf,
        void *private)
{
    char name[EXT2_NAME_LEN];
    char tmp[EXT2_NAME_LEN + 256];
    char tmppath[EXT2_NAME_LEN + 256];
    int len;
    char *path;
    struct stat stat_buf;
    int flag;

    char *prefix = (char *)private;

    DIR * dp;
    struct dirent *dirp;
    
    len = ((dirent->name_len & 0xFF) < EXT2_NAME_LEN) ?
        (dirent->name_len & 0xFF) : EXT2_NAME_LEN;

    strncpy(name, dirent->name, len);
    name[len] = '\0';

    if(entry == DIRENT_DELETED_FILE) {
        return 0;
    }

    ext2fs_get_pathname(fs, dir, 0, &path);
    sprintf(tmp, "%s%s/%s", prefix, path, name);
    sprintf(tmppath, "%s%s", prefix, path);

    /* chdir() then readdir() is for adore LKM, anyway it works
     * for other LKM.
     */
    chdir(tmppath);
    if( (dp = opendir(".")) == NULL ) {
    printf("open dir %s error\n", tmppath);
    exit(1);
    }
    flag=1;
    while ((dirp = readdir(dp)) != NULL) {
        if (strcmp(dirp->d_name, name) == 0) {
            flag=0;
            break;
        }
    }
    closedir(dp);
    
    if(flag) {
        printf("%s\n", tmp);
    }

    /* this will print files pointing to Inode 0 */
    if (lstat(tmp, &stat_buf)) {
        printf("%s\n", tmp);
    }

    free(path);

    if (!ext2fs_check_directory(fs, dirent->inode) &&
            strcmp(".", dirent->name) && strcmp("..", dirent->name)) {
        ext2fs_dir_iterate2(fs, dirent->inode, DIRENT_FLAG_INCLUDE_EMPTY,
            0, list_dir_proc, prefix);
    }
    return 0;
}

int main(int argc, char **argv)
{
    errcode_t ret;
    ext2_ino_t ino;

    if(argc != 3) {
        printf(
"Usage: %s <device> <path>\n"
"<device> device on which filesystem resides\n"
"<path> the path that filesystem is mounted on\n",
        argv[0]);
        exit(0);
    }

    initialize_ext2_error_table();

    ret = ext2fs_open(argv[1], 0, 0, 0, unix_io_manager, &fs);
    if (ret) {
        com_err(argv[1], ret, "while opening filesystem");
        exit(1);
    }

    printf("The following files or directories are stealth\n");

    root = cwd = EXT2_ROOT_INO;

    ino = string_to_inode(fs, "/");

    ret = ext2fs_dir_iterate2(fs, ino, DIRENT_FLAG_INCLUDE_EMPTY,
            0, list_dir_proc, (void *) argv[2]);

    ext2fs_free(fs);

    return 0;
}

ADDITIONAL INFORMATION

The information has been provided by <mailto:lark@marsec.net> Wang Jian.

========================================

This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com

====================
====================

DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.