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
會在系統日志中記錄類似:
snoopy[25505]: [uid:0 sid:9701 tty:/dev/pts/15 cwd:/root filename:/bin/ls]: ls /notfound
這樣的内容。通過ldd,可以看下一個指令加載的動态連結庫:
#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)
果然snoopy被最早加載了。
那麼如果想不要snoopy加載,又有什麼辦法呢?
搜尋了半天,沒有看見linux中有什麼辦法能夠将設定在ld.so.preload中預先加載的so給解除安裝掉,但是有一個值得注意的是:ld_preload加載的優先級高于ld.so.preload。
我們能不能通過更早的加載被劫持的系統調用,來避免被劫持呢?答案是肯定的。
首先,我們可以猜測出來這貨是不會劫持太多的系統調用,通過nm指令看下snoopy的符号表:
#nm /usr/local/snoopy/lib/snoopy.so
…
0000000000000890 t execv
0000000000000b00 t execve
這兩個系統調用應該是非常熟悉的,函數定義在unistd.h中,主要用于建立新的程序,并加載另一個可執行程式。隻要截獲了這幾個系統調用,就能知道要執行什麼指令了。
我們還可以再用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
從這裡可以看出兩個地方,首先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);
知道了原理,想到繞開,就很簡單了。首先,找到execve真實的提供者:從之前ls指令的動态連結庫就可以看見大部分系統的系統調用,都在/lib64/libc.so.6中,用nm指令驗證下:
#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
是以,然後嘗試執行:
ld_preload=”/lib64/libc.so.6″ bash -c “ls /abc”
再去檢視系統日志,會發現隻記錄了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
也就是說,如果你登陸伺服器之後,執行了:
ld_preload=”/lib64/libc.so.6″ bash
用目前被snoopy劫持的bash再建立一個沒有被snoopy劫持的bash,之後執行的所有指令,都不會再被記錄。
轉載自:https://coolex.info/blog/445.html