Hacking the Linux Kernel Network Stack(译本)(2)

2020-05-28 00:00:00 函数 模块 数据包 钩子 套结

Hacking the Linux Kernel Network Stack(译本[chp5-end])

第五章  NetFilter钩子其他可能的用法

在这里我将会就Netfilter在其它方面的更有趣的应用给你作一些建议。在5.1我会给你提供一些思想源泉。5.2节将会讨论并提供能运行的代码,这个代码使一个基于内核的FTP密码嗅探器,能够远程获取密码。事实上,它运行的很好以至于我有些惊恐,所以将它写了出来。

5.1 隐藏后门守护进程

内核模块编程实际上是Linux开发有意思的领域之一。在内核中写代码意味着你在一个只被你的想象力限制的地方写代码。从恶意一点的观点来思考,你可以隐藏一个文件,一个进程,或者说你能做任何rootkit能实现的很酷的事情。或者说从不太恶意(有这种观点的人)的观点来说,你可以隐藏文件,进程,和各种各样很酷的动作,内核真正是一个很迷人的地方。
        拥有一个内核级的程序员所具有的所有能力,许多事情都是可能的。或许有趣(对于系统管理员来说这可是很恐怖的事情)的一件事情就是在内核植入一个后门程序。毕竟,当一个后门没有作为进程而运行的时候,你怎么会知道它在运行?当然肯定存在一些可以使你的内核能够嗅到这些后门的方法,但是这些方法却绝不会象运行PS命令那样的简单。将后门代码植入内核中并不是一个很新的话题。我这里要讲的,却是利用(你能够猜到的)Netfilter钩子植入简单的网络服务,将之作为内核后门。
如果你有必要的技能并且愿意承担在做实验时将你的内核导致崩溃的风险的话,你可以构造一个简单而有用的网络服务,将能够完全的装入内核并能进行远程访问。基本上说,Netfilter可以从所有接收到的数据包中查找指定的“神秘”数据包,当这个神秘的数据包被接收到的时候,可以进行一些特殊的处理。结果可以通过Netfilter钩子函数发送出去,Netfilter钩子函数然后返回一个NF_STOLEN结果以便这个神秘的数据包不会被继续传递下去。但是必须注意一点,以这样的方式来发送输出数据的时候,向外发送的数据包对于输出Netfilter钩子函数仍然是可见的。因此对于用户空间来说,完全看不到这个“神秘”数据包曾经来过,但是他们却能够看到你发送出来的数据。你必须留意,泄密主机上的Sniffer程序不能发现这个数据包并不意味着中间的宿主机上的嗅探器(sniffer)也不能发现这个数据包。
Kossak和lifeline曾为Phrack杂志写过一篇精彩的文章,文中描述了如何通过注册数据包类型处理器的方法来坐这些事情。虽然这片文章是关于Netfilter钩子的,我还是强烈建议你阅读一下那片文章(Issue 55, file 12),这片文章非常有趣,向你展示了很多有趣的思想。
那么,后门的Netfilter钩子到底能做哪种工作呢?好的,下面给出一些建议:
-------远程访问的击键记录器。模块会记录键盘的点击并在远程客户机发送一个Ping包的时候,将结果发送给客户机。因此,一连串的击键记录信息流会被伪装成稳定的Ping包返回流发送回来。你也可以进行简单的加密以便按键的ASC 值不会马上暴露出来,一些警觉的系统管理员回想:“坚持,我以前都是通过SSH会话来键入这些的,Oh $%%@T%%&!”
--------简单的管理任务,例如获取机器当前的登录用户列表,或者获取打开的网络连接信息。
--------一个并非真正的后门,而是位于网络边界的模块,并且阻挡任何被疑为来自特洛伊木马、ICMP隐蔽通道或者像KaZaa这样的文件共享工具的通信。
--------文件传输服务器。我近已经实现了这个想法。终得到的Linux内核模块会给你带来数小时的愉悦。
--------数据包跳跃。将发送到装有后门程序主机的特定端口的数据重新定向到另外一个IP主机的不同端口。并且将这个客户端发送的数据包返回给发起者。没有创建进程,妙的是,没有打开网络套接字。
--------利用上面说到的数据包跳跃技术已以一种半传输的方式实现与网络上关键系统的交互。例如配置路由等。
--------FTP/POP3/Telnet的密码嗅探器。嗅探向外发送的密码并保存起来,直到神秘数据包到来所要这些信息的时候,就将它发送出去。
好了,上面是一些简单的思想列表。后一个想法将会在下一节中进行详细的介绍,因为这一节为读者提供了一个很好的机会,使得我们能够接触更多的内核内部的网段络代码。

5.2 基于内核的FTP密码获取Sniffer

针对前面谈到的概念,这里给出了一个例证—一个后门Netfilter程序。这个模块嗅探流向服务器的外出的FTP数据包,寻找USER和PASSWD命令对,当获取到一对用户名和密码时,模块就会等待一个神秘的并且有足够大空间能存储用户名和密码的ICMP包(Ping包)的到来,收到这个包后,模块会将用户名和密码返回。很快的发送一个神秘的数据包,获取回复并且打印信息。一旦一对用户名和密码从模块中读走都,模块便会开始下一对数据的嗅探。注意模块平时多能存储一对信息。已经大致介绍过了,我们现在对模块具体怎样工作进行详尽的讲解。当模块被加载的时候,init_module()函数简单的注册两个Netfilter钩子。个钩子负责从进入的数据包(在NF_IP_PRE_ROUTING时机调用)中寻找神秘的ICMP数据包。另外一个负责监视离开(在NF_IP_POST_ROUTING时调用)安装本模块的机器的数据包。在这里寻找和俘获FTP的登录用户名和密码,cleanup_module()负责注销这两个钩子。
        watch_out()函数是在NF_IP_POST_ROUTING时调用的钩子函数。看一下这个函数你就会发现它的动作很简单。当一个数据包进入的时候,它会被经过多重的检测以便确认这个数据包是否是一个FTP数据包。如果不是一个FTP数据包,将会立即返回一个NF_ACCEPT。如果是一个FTP数据包,模块会确认是否已经获取并存储了一对用户名和密码。如果已经存储了的话(这时 have_pari变量的值非零),那么就会返回一个NF_ACCPET值,并且数据包终可以离开这个系统。否则的话,check_ftp()方法将会被调用。通常在这里密码被提取出来,如果以前没有接收到数据包的话,target_ip和target_port这两个变量将会被清空。
        Check_ftp()一开始在数据段的开头寻找“USER”,“PASS”或者“QUIT”字段。注意,在没有“USER”字段被处理之前通常不处理“PASS”字段。这是为了防止在收到密码后连接断开,而这时没有获取到用户名,就会陷入锁中。同样,当收到一个“QUIT”字段时,如果这时只有一个“USER”字段的话,就将所有变量复位,以便于Sniffer能继续对新的连接进行嗅探。当“PASS”或者“USER”命令被收到时,在必要的完整性校验之后,命令的参数会被拷贝下来。通常操作中都是在check_ftp()函数结束之前,检验有无用户名和密码者两个命令字段。如果有的话,have_pair会被设置,并且在这对数据被取走之前不会再次获取新的用户名和密码。
        到目前为止你已经知道了这个模块怎样安装自己并且查找用户名和密码并记录下来。下面你将会看到“神秘”数据包到来时会发生什么。在这块儿要特别留意,因为开发中的大多数问题会在此处出现。如果没有记错的话,我在这里遇到了16个内核错误。当数据到达安装此模块的机器时,watch_in()将会检查每一个数据包看他是否是一个神秘的数据包。如果数据包没有满足被判定为神秘数据包的条件的话,watch_in()会简单的返回一个NF_ACCEPT来忽略这个数据包。注意,神秘数据包的判定标准就是这个数据包有足够的空间能够容纳IP地址,用户名和密码这些字符串。这样做是为了使得数据的回复更容易些。可能需要申请一个新的sk_buff结构体。但是要保证所有的数据域都正确却是件不容易的事情,所以你必须想办法确保这些域的键值正确无误。因此,我们在此并不创建一个新的结构体,而是直接修改请求数据包的结构,将其作为一个返回数据包。为了能正确返回,需要做几个修改。首先,IP地址进行交换,结构体(sk_buff)中的数据包类型这个域的值要改为“PACKET_OUTGOING”,这个在linux/if_packet.h中定义了。第二步要确保每个链路层信息已经被包含在其中。我们接收到数据包的数据域就是链路层头信息后面的指向sk_buff结构体的指针,并且指向数据包中数据开头的指针传递了数据域。所以,对于需要链路层头信息的接口(以太网卡,回环设备和点对点设备的原始套结字)而言,我们的数据域指向mac.ethernet或者mac.raw结构。你可以通过检测sb->dev->type的值(sb是指向sk_buff结构体的指针)的值来判断这个数据包进入了什么类型的接口。你可以在linux/ip_arp.h中找到这些有效的值。有用的都在表三列了出来。

表三.常见接口(网卡)类型
  1. 类型码        接口类型
  2. ARPHRD_ETHER        以太网卡
  3. ARPHRD_LOOPBACK        回环设备
  4. ARPHRD_PPP        点对点设备

相关文章