snoopy是什么?刚了解这货的时候,是公司服务器上有snoopy的so无法加载的错误,然后是系统日志里面一堆日志,导致机器空间不足。官方说明是:
Snoopy is designed to aid a sysadmin by providing a log of commands executed.也就是说这货会监控服务器上的命令执行,并记录到syslog。
首先来看下这个功能是怎么被完成的。首先会发现,服务器上的/etc/ld.so.preload这个文件被修改,强制可执行程序加载之前加载snoopy的so:
[cce]
#cat /etc/ld.so.preload
/usr/local/snoopy/lib/snoopy.so
[/cce]
关于ld.so.preload和LD_PRELOAD,可以参考man ld.so:
LD_PRELOAD
A whitespace-separated list of additional, user-specified, ELF shared libraries to be loaded before all others. This can be used to selectively override functions in other shared libraries. For set-user-ID/set-group-ID ELF binaries, only libraries in the standard search directories that are also set-user-ID will be loaded.
/etc/ld.so.preload
File containing a whitespace separated list of ELF shared libraries to be loaded before the program.
也就是说,snoopy.so会在所有ELF文件加载之前预加载,确保将一些系统调用被这货劫持。
如果在机器上执行
[cce lang="bash"]
ls /notfound
[/cce]
会在系统日志中记录类似:
snoopy[25505]: [uid:0 sid:9701 tty:/dev/pts/15 cwd:/root filename:/bin/ls]: ls /notfound
这样的内容。通过ldd,可以看下一个命令加载的动态链接库:
[cce lang="bash"]
#ldd /bin/ls
linux-vdso.so.1 => (0x00007fff99fff000)
/usr/local/snoopy/lib/snoopy.so (0x00007fc407938000)
librt.so.1 => /lib64/librt.so.1 (0x0000003e4f600000)
libacl.so.1 => /lib64/libacl.so.1 (0x0000003e50200000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x0000003e4fa00000)
libc.so.6 => /lib64/libc.so.6 (0x0000003e4e200000)
libdl.so.2 => /lib64/libdl.so.2 (0x0000003e4e600000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003e4ea00000)
/lib64/ld-linux-x86-64.so.2 (0x0000003e4de00000)
libattr.so.1 => /lib64/libattr.so.1 (0x0000003e4f200000)
libsepol.so.1 => /lib64/libsepol.so.1 (0x0000003e4fe00000)
[/cce]
果然snoopy被最早加载了。
那么如果想不要snoopy加载,又有什么办法呢?
搜索了半天,没有看见Linux中有什么办法能够将设置在ld.so.preload中预先加载的so给卸载掉,但是有一个值得注意的是:LD_PRELOAD加载的优先级高于ld.so.preload。
我们能不能通过更早的加载被劫持的系统调用,来避免被劫持呢?答案是肯定的。
首先,我们可以猜测出来这货是不会劫持太多的系统调用,通过nm命令看下snoopy的符号表:
[cce lang="bash"]
#nm /usr/local/snoopy/lib/snoopy.so
...
0000000000000890 T execv
0000000000000b00 T execve
...
[/cce]
这两个系统调用应该是非常熟悉的,函数定义在unistd.h中,主要用于创建新的进程,并加载另一个可执行程序。只要截获了这几个系统调用,就能知道要执行什么命令了。
我们还可以再用bash来验证下:
[cce lang="bash"]
#strace bash -c "ls /abc"
...
connect(3, {sa_family=AF_FILE, path="/dev/log"...}, 110) = 0
sendto(3, "<86>Sep 23 04:34:56 snoopy[28052"..., 104, MSG_NOSIGNAL, NULL, 0) = 104
execve("/bin/ls", ["ls", "/abc"], [/* 29 vars */]) = 0
...
[/cce]
从这里可以看出两个地方,首先bash在执行ls的时候,的确是通过execve这个系统调用的;其次,这个系统调用已经被snoopy劫持了(向日志设备发送了数据)。
最后,通过代码也验证了这个猜想:代码在这里
[cce lang="c"]
int execv (const char *filename, char *const argv[]) {
static int (*func)(const char *, char **);
FN(func,int,"execv",(const char *, char **const));
snoopy_log_syscall_execv(filename, argv);
return (*func) (filename, (char **) argv);
}
int execve (const char *filename, char *const argv[], char *const envp[])
{
static int (*func)(const char *, char **, char **);
FN(func,int,"execve",(const char *, char **const, char **const));
snoopy_log_syscall_execve(filename, argv, envp);
return (*func) (filename, (char**) argv, (char **) envp);
}
[/cce]
知道了原理,想到绕开,就很简单了。首先,找到execve真实的提供者:从之前ls命令的动态链接库就可以看见大部分系统的系统调用,都在/lib64/libc.so.6中,用nm命令验证下:
[cce lang="bash"]
#nm /lib64/libc.so.6 | grep execv
0000003e4e29acf0 t __GI_execvp
0000003e4e29a880 t __execve
0000003e4e29a980 T execv
0000003e4e29a880 W execve
0000003e4e29acf0 T execvp
0000003e4e29a8b0 T fexecve
[/cce]
因此,然后尝试执行:
[cce lang="bash"]
LD_PRELOAD="/lib64/libc.so.6" bash -c "ls /abc"
[/cce]
再去查看系统日志,会发现只记录了bash -c "ls /abc"这个命令,真正执行的ls /abc这个命令没有记录。
通过strace也可以看见,在执行ls之前,没有了和系统日志交互的连接了。
stat("/bin/ls", {stmode=SIFREG|0755, st_size=91272, ...}) = 0
access("/bin/ls", X_OK) = 0
access("/bin/ls", R_OK) = 0
rtsigaction(SIGINT, {SIGDFL, [], SARESTORER, 0x3e4e2302d0}, {SIGDFL, [], SA_RESTORER, 0x3e4e2302d0}, 8) = 0
rtsigaction(SIGQUIT, {SIGDFL, [], SARESTORER, 0x3e4e2302d0}, {0x1, [], SARESTORER, 0x3e4e2302d0}, 8) = 0
rtsigaction(SIGCHLD, {SIGDFL, [], SARESTORER, 0x3e4e2302d0}, {0x436360, [], SARESTORER, 0x3e4e2302d0}, 8) = 0
execve("/bin/ls", ["ls", "/abc"], [/* 30 vars */]) = 0
也就是说,如果你登陆服务器之后,执行了:
[cce lang="bash"]
LD_PRELOAD="/lib64/libc.so.6" bash
[/cce]
用当前被snoopy劫持的bash再创建一个没有被snoopy劫持的bash,之后执行的所有命令,都不会再被记录。