pspy 是一款命令行工具,旨在无需 root 权限即可窥探进程。它允许你查看其他用户运行的命令、cron 作业等,在它们执行时实时观测。非常适合在 CTF 中对 Linux 系统进行枚举。同时也能向你的同事展示为什么在命令行参数中传递密码是个坏主意。
该工具通过扫描 procfs 获取信息。Inotify 监视器被放置在文件系统的选定部分,这些监视器触发扫描以捕捉短时进程。
将工具下载到你要检查的 Linux 机器上。先获取二进制文件。在此处下载发布版二进制文件:
静态编译的文件应该能在任何 Linux 系统上工作,但体积较大(约 4MB)。如果大小是个问题,可以尝试依赖 libc 并使用 UPX 压缩的小型版本(约 1MB)。
你可以使用系统上安装的 Go 进行构建,或运行用于创建发布版的 Docker 构建流程。若选择后者,请确保已安装 Docker,然后运行 make build-build-image 构建 Docker 镜像,接着运行 make build 使用该镜像构建二进制文件。
你可以运行 pspy --help 了解各标志的含义。总结如下:
默认设置适用于大多数场景。监视 /usr 内的文件最为重要,因为许多工具会访问其中的库。
一些更复杂的示例:
# 同时打印命令和文件系统事件,每 1000 毫秒(=1秒)扫描一次 procfs
./pspy64 -pf -i 1000
# 在两个目录中递归放置监视器,并在第三个目录中非递归放置
./pspy64 -r /path/to/first/recursive/dir -r /path/to/second/recursive/dir -d /path/to/the/non-recursive/dir
# 禁用打印已发现的命令,但启用文件系统事件
./pspy64 -p=false -f
要查看工具的实际效果,只需克隆仓库并运行 make example(需要 Docker)。众所周知,在命令行参数中传递密码是不安全的,本示例可用来证明这一点。该命令启动一个 Debian 容器,其中由 root 运行的秘密 cron 作业每分钟修改一次用户密码。pspy 在前台以 myuser 用户身份运行,并扫描进程。你应该会看到类似下面的输出:
~/pspy (master) $ make example
[...]
docker run -it --rm local/pspy-example:latest
[+] cron started
[+] Running as user uid=1000(myuser) gid=1000(myuser) groups=1000(myuser),27(sudo)
[+] Starting pspy now...
Watching recursively : [/usr /tmp /etc /home /var /opt] (6)
Watching non-recursively: [] (0)
Printing: processes=true file-system events=false
2018/02/18 21:00:03 Inotify watcher limit: 524288 (/proc/sys/fs/inotify/max_user_watches)
2018/02/18 21:00:03 Inotify watchers set up: Watching 1030 directories - watching now
2018/02/18 21:00:03 CMD: UID=0 PID=9 | cron -f
2018/02/18 21:00:03 CMD: UID=0 PID=7 | sudo cron -f
2018/02/18 21:00:03 CMD: UID=1000 PID=14 | pspy
2018/02/18 21:00:03 CMD: UID=1000 PID=1 | /bin/bash /entrypoint.sh
2018/02/18 21:01:01 CMD: UID=0 PID=20 | CRON -f
2018/02/18 21:01:01 CMD: UID=0 PID=21 | CRON -f
2018/02/18 21:01:01 CMD: UID=0 PID=22 | python3 /root/scripts/password_reset.py
2018/02/18 21:01:01 CMD: UID=0 PID=25 |
2018/02/18 21:01:01 CMD: UID=??? PID=24 | ???
2018/02/18 21:01:01 CMD: UID=0 PID=23 | /bin/sh -c /bin/echo -e "KI5PZQ2ZPWQXJKEL\nKI5PZQ2ZPWQXJKEL" | passwd myuser
2018/02/18 21:01:01 CMD: UID=0 PID=26 | /usr/sbin/sendmail -i -FCronDaemon -B8BITMIME -oem root
2018/02/18 21:01:01 CMD: UID=101 PID=27 |
2018/02/18 21:01:01 CMD: UID=8 PID=28 | /usr/sbin/exim4 -Mc 1enW4z-00000Q-Mk
首先,pspy 打印所有当前运行的进程,每个进程包含 PID、UID 和命令行。当 pspy 检测到新进程时,它会向日志中添加一行。在此示例中,你发现 PID 为 23 的进程似乎正在修改 myuser 的密码。这是 root 私有 crontab /var/spool/cron/crontabs/root 中一个 Python 脚本的结果,该脚本执行了此 shell 命令(查看 crontab 和 脚本)。请注意,myuser 既看不到 crontab,也看不到 Python 脚本。借助 pspy,它仍然可以查看这些命令。
下面是一个来自 Hack The Box 的 Shrek 机器示例。在这个 CTF 挑战中,任务是利用一个隐藏的 cron 作业,该作业会更改文件夹中所有文件的所有权。漏洞在于不安全地将通配符与 chmod 一起使用(感兴趣者可阅读 详情)。这需要大量猜测才能发现并利用。但有了 pspy,cron 作业很容易被发现和分析:

已有工具可以列出 Linux 系统上执行的所有进程,包括已结束的进程。例如 forkstat。它从内核接收有关进程事件(如 fork 和 exec)的通知。
这些工具需要 root 权限,但这不应该给你带来虚假的安全感。没有任何东西能阻止你在 Linux 系统上窥探正在运行的进程。只要进程正在运行,procfs 中就有大量可见的信息。唯一的问题是你必须在极短的时间窗口内捕捉到短时进程。在一个无限循环中扫描 /proc 目录以查找新的 PID 可以解决问题,但会消耗大量 CPU。
一种更隐蔽的方式是使用以下技巧。进程倾向于访问诸如 /usr 中的库、/tmp 中的临时文件、/var 中的日志文件等。使用 inotify API,你可以在这些文件被创建、修改、删除、访问等时收到通知。Linux 不要求特权用户才能使用此 API,因为它被许多无害的应用程序(例如显示最新文件资源的文本编辑器)所需要。因此,尽管非 root 用户无法直接监控进程,但他们可以监控进程对文件系统的影响。
我们可以利用文件系统事件作为触发条件来扫描 /proc,希望扫描速度足够快以捕捉到进程。这正是 pspy 所做的。不能保证你不会错过任何一个,但根据我的实验,捕获的可能性很大。通常,进程运行时间越长,捕获它的机会就越大。
Logo:"By Creative Tail [CC BY 4.0 (http://creativecommons.org/licenses/by/4.0)], via Wikimedia Commons" (链接)