giantbranch 发表于 2018-11-6 21:14

通过chkrootkit学习如何在linux下检测RootKit



Rootkit是一种特殊的恶意软件,它的功能是在安装目标上隐藏自身及指定的文件、进程和网络链接等信息,比较多见到的是Rootkit一般都和木马、后门等其他恶意程序结合使用。Rootkit一词更多地是指被作为驱动程序,加载到操作系统内核中的恶意软件。

# chkrootkit简介

chkrootkit是一个linux下检RootKit的脚本,在某些检测中会调用当前目录的检测程序

官网:http://www.chkrootkit.org/

下载源码:ftp://ftp.pangeia.com.br/pub/seg/pac/chkrootkit.tar.gz

解压后执行 `make` 命令,就可以使用了

一般直接运行,一旦有INFECTED,说明可能被植入了RootKit

```
./chkrootkit | grep INFECTED
```

# 总体流程

首先删除别名,确保接下来的一些操作不会用了被RootKit改变了的别名

```
### workaround for some Bourne shell implementations
unalias login > /dev/null 2>&1
unalias ls > /dev/null 2>&1
unalias netstat > /dev/null 2>&1
unalias ps > /dev/null 2>&1
unalias dirname > /dev/null 2>&1
```


一开始会检测一些必要的命令是否可用,可执行,因为检测基于这些命令

```
cmdlist="
awk
cut
echo
egrep
find
head
id
ls
netstat
ps
sed
ssh
strings
uname
"
```
接下来就是检测ps的参数ax好不好使,好使就使用ax,不好使就用-fe

```
# Check if ps command is ok
if ${ps} ax >/dev/null 2>&1 ; then
   ps_cmd="ax"
else
   ps_cmd="-fe"
fi
```
当然还需检测你是否是root,就根据你的id号是否为0

```
if [ `${id} | ${cut} -d= -f2 | ${cut} -d\( -f1` -ne 0 ]; then
   echo "$0 need root privileges"
   exit 1
fi
```

接下来就是默认执行所有测试,当然你也可以指定测试的命令

```
if [ $# -gt 0 ]
then
    ### perform only tests supplied as arguments
    for arg in $*
    do
      ### check if is a valid test name
      if echo "${TROJAN} ${TOOLS}"| \
         ${egrep} -v "${L_REGEXP}$arg${R_REGEXP}" > /dev/null 2>&1
      then
            echo >&2 "$0: \`$arg': not a known test"
            exit 1
      fi
    done
    LIST=$*
else
    ### this is the default: perform all tests
    LIST="${TROJAN} ${TOOLS}"
fi
```

接下来只是对是否开启调试模式,用户是否指定了要检测的根目录进行处理

```
if [ "${DEBUG}" = "t" ]; then
    set -x
fi

if [ "${ROOTDIR}" != "/" ]; then

    ### remove trailing `/'
    ROOTDIR=`echo ${ROOTDIR} | ${sed} -e 's/\/*$//g'`

    for dir in ${pth}
    do
      if echo ${dir} | ${egrep} '^/' > /dev/null 2>&1
      then
      newpth="${newpth} ${ROOTDIR}${dir}"
      else
      newpth="${newpth} ${ROOTDIR}/${dir}"
      fi
    done
    pth=${newpth}
   ROOTDIR="${ROOTDIR}/"
fi
```

最后便是循环调用各个check函数进行处理了

```
for cmd in ${LIST}
do

    if echo "${TROJAN}" | \
    ${egrep} "${L_REGEXP}$cmd${R_REGEXP}" > /dev/null 2>&1
    then
      if [ "${EXPERT}" != "t" -a "${QUIET}" != "t" ]; then
         printn "Checking \`${cmd}'... "
      fi
      chk_${cmd}
      STATUS=$?

      ### quiet mode
      if [ "${QUIET}" = "t" ]; then
            ### show only INFECTED status
            if [ ${STATUS} -eq 0 ]; then
                echo "Checking \`${cmd}'... INFECTED"
            fi
            continue
      fi

      case $STATUS in
      0) echo "INFECTED";;
      1) echo "not infected";;
      2) echo "not tested";;
      3) echo "not found";;
      4) echo "infected but disabled";;
      5) ;;   ### expert mode
      esac
    else
      ### external tool
      if [ "${EXPERT}" != "t" -a "${QUIET}" != "t" ]; then
            printn "Checking \`$cmd'... "
      fi
      ${cmd}

    fi
done
```
那么接下来每个check方法到底是怎么检测的呢?接下来

# 检测方法

通过分析脚本,总结出检测方法如下:

> 1. 搜索通用的ROOTKIT特征的字符串
> 2. 对某种特定的rootkits,或者命令的特殊的感染特征进行检测
> 3. 对某种特定的rootkits的生成的特定文件的检测
> 4. 对程序的SUID位的设置进行检测
> 5. 对ldsopreload的检测
> 6. 查找可疑的log文件
> 7. 查找可疑的php文件
> 8. 检测.history文件
> 9. 检测有无程序监听了一些可疑的端口
> 10. 检测Linux可加载内核模块
> 11. 检测有无隐藏进程
> 12. 检测目录的软链接异常
> 13. 检测网络接口的异常
> 14. 检测用户的登录日志
> 15. 检测上一次登录
> 16. 检测可疑的没有tty记录的进程

下面对上面这些方法结合脚本代码进行简单说明

## 搜索通用的ROOTKIT特征的字符串

搜索的是下面的比较通用的ROOTKIT字符串

```
# Many trojaned commands have this label
GENERIC_ROOTKIT_LABEL="^/bin/.*sh$|bash|elite$|vejeta|\.ark|iroffer"
```

可以看到前两个都是shell相关的,相关的示例代码如下:

```
chk_chfn () {
    STATUS=${NOT_INFECTED}
    CMD=`loc chfn chfn $pth`
    [ ${?} -ne 0 ] &&return ${NOT_FOUND}

    if [ "${EXPERT}" = "t" ]; then
      expertmode_output "${strings} -a ${CMD}"
      return 5
    fi

    case "${SYSTEM}" in
       Linux)
          if ${strings} -a ${CMD} | ${egrep} "${GENERIC_ROOTKIT_LABEL}" \
             >/dev/null 2>&1
          then
             STATUS=${INFECTED}
          fi;;
       FreeBSD)
          [ `echo $V | ${awk} '{ if ( $1 >= 5.0) print 1; else print 0 }'` -eq 1 ] && n=1 || n=2
          if [ `${strings} -a ${CMD} | \
                ${egrep} -c "${GENERIC_ROOTKIT_LABEL}"` -ne $n ]
          then
             STATUS=${INFECTED}
          fi;;
    esac
    return ${STATUS}
}
```

程序针对Linux和FreeBSD系统分开处理,都是通过strings获取二进制程序中的字符串,再使用egrep命令去正则匹配,匹配成功就将返回值STATUS设置为INFECTED这个常量(这个在文件开头处定义了)

## 对某种特定的rootkits,或者命令的特殊的感染特征进行检测

比如这个amd命令的某个感染特征

```
chk_amd () {
    STATUS=${NOT_INFECTED}
    AMD_INFECTED_LABEL="blah"
    CMD=`loc amd amd $pth`
    if [ ! -x "${CMD}" ]; then
         return ${NOT_FOUND}
    fi
    if [ "${EXPERT}" = "t" ]; then
      expertmode_output "${strings} -a ${CMD}"
      return 5
    fi
    if ${strings} -a ${CMD} | ${egrep} "${AMD_INFECTED_LABEL}" >/dev/null 2>&1
    then
       STATUS=${INFECTED}
    fi
    return ${STATUS}
}
```

下面这个检测crontab的nobody用户,并且定时任务中有数字的, 可能是Lupper.Worm
当然还是有CRONTAB_I_L这个特殊的检测

```
chk_crontab () {
    STATUS=${NOT_INFECTED}
    CRONTAB_I_L="crontab.*666"

    CMD=`loc crontab crontab $pth`

    if [ ! -r ${CMD} ]
       then
      return ${NOT_FOUND}
    fi

    if [ "${EXPERT}" = "t" ]; then
      expertmode_output "${CMD} -l -u nobody"
      return 5
    fi
    # slackware's crontab have a bug
    if( ${CMD} -l -u nobody | $egrep ) >/dev/null 2>&1 ; then
      ${echo} "Warning: crontab for nobody found, possible Lupper.Worm... "
      if ${CMD} -l -u nobody 2>/dev/null| ${egrep} $CRONTAB_I_L >/dev/null 2>&1
         then
         STATUS=${INFECTED}
      fi
    fi
    return ${STATUS}
}
```

对Ramen Worm进行特征匹配

```
if ${egrep} "^asp" ${ROOTDIR}etc/inetd.conf >/dev/null 2>&1; then
    echo "Warning: Possible Ramen Worm installed in inetd.conf"
    STATUS=${INFECTED}
fi
```

## 对某种特定的rootkits生成的特定文件的检测

如下面的HiDrootkit和t0rn

```
### HiDrootkit
if [ "${QUIET}" != "t" ]; then printn \
"Searching for HiDrootkit's default dir... "; fi
if [ -d ${ROOTDIR}var/lib/games/.k ]
then
echo "Possible HiDrootkit installed"
else
if [ "${QUIET}" != "t" ]; then echo "nothing found"; fi
fi

### t0rn
if [ "${QUIET}" != "t" ]; then printn\
"Searching for t0rn's default files and dirs... "; fi
if [ -f ${ROOTDIR}etc/ttyhash -o -f ${ROOTDIR}sbin/xlogin -o \
    -d ${ROOTDIR}usr/src/.puta-o -r ${ROOTDIR}lib/ldlib.tk -o \
    -d ${ROOTDIR}usr/info/.t0rn ]
then
echo "Possible t0rn rootkit installed"
else
if [ "${QUIET}" != "t" ]; then echo "nothing found"; fi
fi
```

## 对程序的SUID位的设置进行检测

```
chk_basename () {
   STATUS=${NOT_INFECTED}
   CMD=`loc basename basename $pth`

   if [ "${EXPERT}" = "t" ]; then
       expertmode_output "${strings} -a ${CMD}"
       expertmode_output "${ls} -l ${CMD}"
       return 5
   fi
   if ${strings} -a ${CMD} | ${egrep} "${GENERIC_ROOTKIT_LABEL}" > /dev/null 2>&1
   then
       STATUS=${INFECTED}
   fi

   [ "$SYSTEM" != "OSF1" ] &&
   {
      if ${ls} -l ${CMD} | ${egrep} "^...s" > /dev/null 2>&1
      then
         STATUS=${INFECTED}
      fi
   }
   return ${STATUS}
}
```

这个除了检测有无关键字,还检测SUID位有无设置



## 对ldsopreload的检测

```
chk_ldsopreload() {
   STATUS=${NOT_INFECTED}
   CMD="${ROOTDIR}lib/libshow.so ${ROOTDIR}lib/libproc.a"

   if [ "${SYSTEM}" = "Linux" ]
   then
      if [ ! -x ./strings-static ]; then
      printn "can't exec ./strings-static, "
      return ${NOT_TESTED}
      fi

      if [ "${EXPERT}" = "t" ]; then
          expertmode_output "./strings-static -a ${CMD}"
          return 5
      fi

      ### strings must be a statically linked binary.
      if ./strings-static -a ${CMD} > /dev/null 2>&1
      then
         STATUS=${INFECTED}
      fi
   else
   STATUS=${NOT_TESTED}
   fi
   return ${STATUS}
}
```

检测是否有libshow.so,libproc.a,有就说明感染了恶意so文件

可以看到为了保险起见,作者使用的是自己目录下静态编译的strings进行检测


## 查找可疑的log文件
例子如下:

```
files=`${find} ${ROOTDIR}dev ${ROOTDIR}tmp ${ROOTDIR}lib ${ROOTDIR}etc ${ROOTDIR}var \
${findargs} \( -name "tcp.log" -o -name ".linux-sniff" -o -name "sniff-l0g" -o -name "core_" \) \
2>/dev/null`
if [ "${files}" = "" ]
then
if [ "${QUIET}" != "t" ]; then echo "nothing found"; fi
else
echo
echo ${files}
fi
```

## 查找可疑的php文件

查找一些可疑的php文件

```
###
### Suspect PHP files
###
if [ "${QUIET}" != "t" ]; then
    printn "Searching for suspect PHP files... "; fi
    files="`${find} ${ROOTDIR}tmp ${ROOTDIR}var/tmp ${findargs} -name '*.php' 2> /dev/null`"
if [ `echo abc | head -n 1` = "abc" ]; then
    fileshead="`${find} ${ROOTDIR}tmp ${ROOTDIR}var/tmp ${findargs} -type f -exec head -n 1 {} \; | $egrep '#!.*php' 2> /dev/null`"
else
    fileshead="`${find} ${ROOTDIR}tmp ${ROOTDIR}var/tmp ${findargs} -type f -exec head -1 {} \; | grep '#!.*php' 2> /dev/null`"
fi
if [ "${files}" = "" -a "${fileshead}" = "" ]; then
    if [ "${QUIET}" != "t" ]; then echo "nothing found"; fi
else
   echo
   echo "${files}"
   echo "${fileshead}"
fi
```

## 检测.history文件

看看history有没有被清空了,或者软连接到其他地方了

```
if [ "${QUIET}" != "t" ]; then \
      printn "Searching for anomalies in shell history files... "; fi
   files=""
   if [ ! -z "${SHELL}" -a ! -z "${HOME}" ]; then
      files=`${find} ${ROOTDIR}${HOME} ${findargs} -name '.*history' -size 0`
      [ ! -z "${files}" ] && \
      echo "Warning: \`${files}' file size is zero"
      files1=`${find} ${ROOTDIR}${HOME} ${findargs} -name '.*history' \( -links 2 -o -type l \)`
      [ ! -z "${files1}" ] && \
      echo "Warning: \`${files1}' is linked to another file"
   fi
   if [ -z "${files}" -a -z "${files1}" ]; then
      if [ "${QUIET}" != "t" ]; then echo "nothing found"; fi
   fi
```

## 检测有无程序监听了一些可疑的端口

检测代码如下:

```
bindshell () {
PORT="114|145|465|511|600|1008|1524|1999|1978|2881|3049|3133|3879|4000|4369|5190|5665|6667|10008|12321|23132|27374|29364|30999|31336|31337|37998|45454|47017|47889|60001|7222"
   OPT="-an"
   PI=""
   if [ "${ROOTDIR}" != "/" ]; then
   echo "not tested"
   return ${NOT_TESTED}
   fi

   if [ "${EXPERT}" = "t" ]; then
       expertmode_output "${netstat} ${OPT}"
       return 5
   fi
   for P in `echo $PORT | ${sed} 's/|/ /g'`; do
      if ${netstat} "${OPT}" | ${egrep} "^tcp.*LIST|^udp" | ${egrep} \
"[.:]${P}[^0-9.:]" >/dev/null 2>&1
      then
         PI="${PI} ${P}"
      fi
   done
   if [ "${PI}" != "" ]
   then
      echo "INFECTED PORTS: ($PI)"
   else
      if [ "${QUIET}" != "t" ]; then echo "not infected"; fi
   fi
}
```

## 检测Linux可加载内核模块

```
lkm ()
{
    prog=""
    if [\( "${SYSTEM}" = "Linux"-o \( "${SYSTEM}" = "FreeBSD" -a \
       `echo ${V} | ${awk} '{ if ($1 > 4.3 || $1 < 6.0) print 1; else print 0 }'` -eq 1 \) \) -a "${ROOTDIR}" = "/" ]; then
      [-x ./chkproc -a "`find /proc | wc -l`" -gt 1 ] && prog="./chkproc"
      [-x ./chkdirs ] && prog="$prog ./chkdirs"
      if [ "$prog" = "" ]; then
         echo "not tested: can't exec $prog"
         return ${NOT_TESTED}
      fi

      if [ "${EXPERT}" = "t" ]; then
         [ -r /proc/$KALLSYMS ] &&${egrep} -i "adore|sebek" < /proc/$KALLSYMS 2>/dev/null
         [ -d /proc/knark ] &&${ls} -la /proc/knark 2> /dev/null
         PV=`$ps -V 2>/dev/null| $cut -d " " -f 3 |${awk} -F . '{ print $1 "." $2 $3 }' | ${awk} '{ if ($0 > 3.19) print 3; else if ($0 < 2.015) print 1; else print 2 }'`
         [ "$PV" = "" ] &&PV=2
         [ "${SYSTEM}" = "SunOS" ] && PV=0
         expertmode_output "./chkproc -v -v -p $PV"
         return 5
      fi

      ### adore LKM
      [ -r /proc/$KALLSYMS ] && \
      if `${egrep} -i adore < /proc/$KALLSYMS >/dev/null 2>&1`; then
         echo "Warning: Adore LKM installed"
      fi

      ### sebek LKM (Adore based)
      [ -r /proc/$KALLSYMS ] && \
      if `${egrep} -i sebek < /proc/$KALLSYMS >/dev/null 2>&1`; then
         echo "Warning: Sebek LKM installed"
      fi

      ### knark LKM
      if [ -d /proc/knark ]; then
         echo "Warning: Knark LKM installed"
      fi

      PV=`$ps -V 2>/dev/null| $cut -d " " -f 3 |${awk} -F . '{ print $1 "." $2 $3 }' | ${awk} '{ if ($0 > 3.19) print 3; else if ($0 < 2.11) print 1; else print 2 }'`
      [ "$PV" = "" ] &&PV=2
      [ "${SYSTEM}" = "SunOS" ] && PV=0
      if [ "${DEBUG}" = "t" ]; then
         ${echo} "*** PV=$PV ***"
      fi
      if ./chkproc -p ${PV}; then
         if [ "${QUIET}" != "t" ]; then echo "chkproc: nothing detected"; fi
      else
         echo "chkproc: Warning: Possible LKM Trojan installed"
      fi
      dirs="/tmp"
      for i in /usr/share /usr/bin /usr/sbin /lib; do
         [ -d $i ] && dirs="$dirs $i"
      done
      if ./chkdirs $dirs;then
         if [ "${QUIET}" != "t" ]; then echo "chkdirs: nothing detected"; fi
      else
            echo "chkdirs: Warning: Possible LKM Trojan installed"
      fi
   else
         if [ "${QUIET}" != "t" ]; then echo "chkproc: not tested"; fi
   fi
}
```

loadable kernel module (LKM),这个是检测内核模块的 ,看看有无adore,sebek这些内核模块

之后调用chkproc,chkdirs进行检测,这两个下面检测有无隐藏进程,会说到

## 检测有无隐藏进程

这个代码在chkproc.c中,它通过暴力递归,看看有没有/proc目录存在,而ps查不出来的进程,那么就说明有进程隐藏了


```
/* Brute force */
   strcpy(buf, "/proc/");
   retps = retdir = 0;
   for (i = FIRST_PROCESS; i <= MAX_PROCESSES; i++)
   {
      // snprintf(&buf, 6, "%d", i);
       snprintf(&buf, 8, "%d", i);
      if (!chdir(buf))
      {
         if (!dirproc && !psproc)
         {
#if defined(__linux__)
            if (!isathread) {
#endif
            retdir++;
            if (verbose)
               printf ("PID %5d(%s): not in readdir output\n", i, buf);
#if defined(__linux__)
            }
#endif
         }
         if (!psproc ) /* && !kill(i, 0)) */
         {
#if defined(__linux__)
            if(!isathread) {
#endif
            retps++;
            if (verbose)
               printf ("PID %5d: not in ps output\n", i);
#if defined(__linux__)
            }
#endif
         }
```

## 检测目录的软链接异常

chkdirs比较的是父目录的软链接数和子目录的个数

正常情况下,父目录的软链接数 = 子目录的个数 + 2

```
if (!linkcount) {
    if (lstat(".", &statinfo)) {
      fprintf(stderr, "lstat(%s): %s\n", fullpath, strerror(errno));
      goto abort;
    }
    linkcount = statinfo.st_nlink;   //获取符号链接数
}

if (!(dirhandle = opendir("."))) {
    fprintf(stderr, "opendir(%s): %s\n", fullpath, strerror(errno));
    goto abort;
}

numdirs = 0;
dl = (struct dirinfolist *)NULL;
while ((finfo = readdir(dirhandle))) {
    if (!strcmp(finfo->d_name, ".") || !strcmp(finfo->d_name, ".."))
      continue;

    if (lstat(finfo->d_name, &statinfo)) {
      fprintf(stderr, "lstat(%s/%s): %s\n",
            fullpath, finfo->d_name, strerror(errno));
      closedir(dirhandle);
      goto abort;
    }

    if (S_ISDIR(statinfo.st_mode)) {    //判断是否是目录
      numdirs++;

      if (norecurse) continue;               /* just count subdirs if "-n" */

      /* Otherwise, keep a list of all directories found that have link
         count > 2 (indicating directory contains subdirectories).We'll
         call check_dir() on each of these subdirectories in a moment...
      */
      if (statinfo.st_nlink > 2) {
      dptr = dl;
      if (!(dl = (struct dirinfolist *)malloc(sizeof(struct dirinfolist)))) {
          fprintf(stderr, "malloc() failed: %s\n", strerror(errno));
          norecurse = 1;
          while (dptr) {
            dl = dptr->dil_next;
            free((void *)dptr);
            dptr = dl;
          }
          continue;
      }

      strncpy(dl->dil_name, finfo->d_name, sizeof(dl->dil_name));
      dl->dil_lc = statinfo.st_nlink;
      dl->dil_next = dptr;
      }
    }
}
closedir(dirhandle);

/* Parent directory link count had better equal #subdirs+2... */
diff = linkcount - numdirs - 2;   //
if (diff) printf("%d\t%s\n", diff, fullpath);
```

## 检测网络接口的异常

```
sniffer () {
    if [ "${ROOTDIR}" != "/" ]; then
      echo "not tested"
      return ${NOT_TESTED}
    fi

    if [ "$SYSTEM" = "SunOS" ]; then
       return ${NOT_TESTED}
    fi

    if [ "${EXPERT}" = "t" ]; then
      expertmode_output "./ifpromisc" -v
      return 5
    fi
    if [ ! -x ./ifpromisc ]; then
      echo "not tested: can't exec ./ifpromisc"
      return ${NOT_TESTED}
    else
      [ "${QUIET}" != "t" ] && ./ifpromisc -v || ./ifpromisc -q
    fi
}
```

这个是对网络接口的检测,看看有无开启网卡混杂模式(英语:promiscuous mode)

而PF_PACKET可以操作链路层的数据,可以读取和发送链路层的数据包

```
./ifpromisc -v
ens3: PF_PACKET(/sbin/dhclient)
virbr0: not promisc and no PF_PACKET sockets
docker0: not promisc and no PF_PACKET sockets
br-47a3d838588a: not promisc and no PF_PACKET sockets
```

## 检测用户的登录日志

检测用户的登录相关的log文件

SunOS使用的是check_wtmpx,比较的文件是/var/adm/wtmp,/var/adm/wtmpx,check_wtmpx部分代码,比较这两个文件的一些差异,比如下面的比较uid

```
if ( memcmp( utmp_entry.ut_id, utmpx_entry.ut_id, 4 ) != 0 )
{
    fprintf( stderr, "[ %u ] utmp_entry.ut_id != utmpx_entry.ut_id\n", wtmp_read_counter );
    break;
}
```

其他linux检测的是var/log/wtmp或者var/adm/wtmp
chkwtmp部分代码,查看有无删除了登录时间

```
gettimeofday(&mytime, &dummy);
       act_time=mytime.tv_sec;
       wtmpfile='\0';
       memcpy(wtmpfile, WTMP_FILENAME, 127);
       if ( argc == 3 && !memcmp("-f", argv, 2) && *argv)
          memcpy(wtmpfile, argv, 127);

      if ((filehandle=open(wtmpfile,O_RDONLY)) < 0) {
                fprintf(stderr, "unable to open wtmp-file %s\n", wtmpfile);
                return(2);
      }

      while (read (filehandle, (char *) &utmp_ent, sizeof (struct utmp)) > 0) {
                if (utmp_ent.ut_time == 0)
                        del_counter++;
                else {
                        if (del_counter) {
                              printit(del_counter, start_time,
                                        utmp_ent.ut_time);
                              t_del++;
                              del_counter=0;
                        }
                        start_time=utmp_ent.ut_time;
                }
      }
      close(filehandle);
      if (del_counter)
         printit(del_counter, start_time, act_time);
      exit((int) t_del+del_counter);
```


## 检测上一次登录

使用chklastlog程序检测,下面是部分代码,用户的数据通过getpwent函数获取,其实就是通过/etc/passwd获取,检测基于两点

1、通过比较MAX_ID,与当前的遍历的用户的id,看看id是否大于环境变量MAX_ID
2、看看是否有这样的情况:用户名出现在lastlog,wtmp文件中,而在/etc/passwd中没有的

```
if ( !nonuser(utmp_ent) && strncmp(utmp_ent.ut_line, "ftp", 3) &&
         (uid=localgetpwnam(localpwd,utmp_ent.ut_name)) != NULL )
                {
                        if (*uid > MAX_ID)
                        {
                           fprintf(stderr, "MAX_ID is %ld and current uid is %ld, please check\n\r", MAX_ID, *uid );
                           exit (1);

                        }
      if (!userid[*uid])
                        {
                lseek(fh_lastlog, (long)*uid * sizeof (struct lastlog), 0);
                if ((wtmp_bytes_read = read(fh_lastlog, &lastlog_ent, sizeof (struct lastlog))) > 0)
                              {
                                        if (wtmp_bytes_read < sizeof(struct lastlog))
                                        {
                                           fprintf(stderr, "lastlog entry may be corrupted");
                                           break;
                                        }
                                        if (lastlog_ent.ll_time == 0)
                                        {
                                           if (-1 != (slot = getslot(localpwd, *uid)))
                                                   printf("user %s deleted or never logged from lastlog!\n",
                                                      NULL != localpwd->uname ?
                                                      (char*)localpwd->uname : "(null)");
                                           else
                                                printf("deleted user uid(%d) not in passwd\n", *uid);
                                           ++status;
                                        }
                                        userid[*uid]=TRUE;
                              }
      }
         }
}
```

## 检测可疑的没有tty记录的进程

检测的是/var/run/utmp或者/var/adm/utmpx,方法是比较的是ps命令与/var/run/utmp文件之间的差别

```
y = fetchps(ps_l);
z = fetchutmp(ut_l);
hdr_prntd = 0;
for (h = 0; h < y; h++) {      /* loop through 'ps' data */
mtch_fnd = 0;
for (i = 0; i < z; i++) {      /* try and match the tty from 'ps' to one in utmp */
      if (ut_l.ut_type == LOGIN_PROCESS      /* ignore getty processes with matching pid from 'ps' */
      && ut_l.ut_pid == ps_l.ps_pid)
   {
      mtch_fnd = 1;
                break;
         }
      else if (strncmp(ps_l.ps_tty, ut_l.ut_tty,      /* compare the tty's */
                         strlen(ps_l.ps_tty)) == 0)
      {
      mtch_fnd = 1;
                break;
      }
}
if (!mtch_fnd) {
      if (!hdr_prntd) {
      printf
                (" The tty of the following user process(es) were not found\n");
      printf(" in %s !\n", UTMP);
      printf("! %-9s %7s %-6s %s\n", "RUID", "PID", "TTY",
                   "CMD");
      hdr_prntd = 1;
      }
      printf("! %-9s %7d %-6s %s", ps_l.ps_user,
         ps_l.ps_pid, ps_l.ps_tty, ps_l.ps_args);
}
```

比如下面的检测结果,而我的/var/run/utmp中是没有tty7这个tty的记录的

```
Checking `chkutmp'...The tty of the following user process(es) were not found
in /var/run/utmp !
! RUID          PID TTY    CMD
! root         1076 tty7   /usr/lib/xorg/Xorg -core :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
```

linuxprobe 发表于 2018-11-6 21:25

shell脚本写的很多,但总体看来好像只能是centos和redhat能检测,其它的就不是很容易检测了。

Dream漂移 发表于 2018-11-6 21:53


支持一下,谢谢分享!

kanxue2018 发表于 2018-11-6 23:27

支持一下,谢谢分享!

pppp7777 发表于 2018-11-6 23:30

感谢分享

chen4321 发表于 2018-11-7 08:08

谢谢分享,天书看不懂留着以后看

﹏﹏静止° 发表于 2018-11-7 10:19

RadishHuang 发表于 2018-11-7 11:07


学习一下

Alyoyojie 发表于 2018-11-7 11:12

收藏下,谢谢

寂寞无名 发表于 2018-11-8 08:43

不错,应急的时候可以用,先收藏备用!!
页: [1] 2
查看完整版本: 通过chkrootkit学习如何在linux下检测RootKit