★ 前言
事实上我并没有翻译Phrack 55-12,它的文字如此之少,当然必须承认其中闪烁
的聪明之光。阅读lifeline < mailto: arai@hackers-pt.org >的程序,它就在文章
里,然后再来看这篇文章。
★ 目录
1. IDS、Firewall的某些特性
2. LLKM能做什么
3. 一个完整的LLKM例子,简单、临时改变TCP/IP协议栈的行为
4. 获取TCP自动机状态,更改出现在网络传输线路上的TCP标志位
5. 另外一种截留本机发送报文、修改、再发送技术
6. 内核模块里设置混杂模式
7. 内核模块里文件I/O操作
8. 内核包转发的讨论
★ 正文
1. IDS、Firewall的某些特性
以snort为例,其大量IDS规则对TCP的PSH、ACK、SYN标志进行判定。比如,在
syn-flood告警中,判定短时间内出现的大量SYN包。而更多的对TCP数据区进行内容
鉴别前,判定了PSH+ACK标志。具体snort规则请参看snort源代码包中举例。
Firewall阻塞来自外部的TCP连接请求时,需要判定SYN标志。
IDS和Firewall还有个更重要的通性,对端口的敏感性。21、23、110、513等端
口都属于敏感端口,许多告警规则、阻塞规则是基于端口的。
2. LLKM能做什么
利用LLKM简单、临时改变TCP/IP协议栈的行为。考虑三种情况:
a. 更改出现在网络传输线路上的TCP标志位
b. 更改出现在网络传输线路上的端口
c. 对IP数据区(TCP协议部分)加密处理后传输
A Host ----+----- firewall -----+---- B Host
(内部) (外部)
C Host(IDS系统) D Host(普通Sniffer)
假设A和B都是我们控制的主机,在这两台主机上都加载LLKM。防火墙不答应来自
外部的任何TCP连接请求,它靠的是判定SYN标志。现在B想telnet到A,LLKM将把B到A
的SYN标志换成ACK、PSH、RST、RES1、RES2中的任意一个或者几个的组合,以能渗透
通过防火墙为原则;A上的LLKM先于正常的TCP/IP协议栈接收到这个扭曲处理了的请
求报文,按照约定好的规则逆向处理,恢复SYN标志后再交给正常的TCP/IP协议栈处
理。同样,A回送SYN+ACK到B的时候,也做一些转换,B上LLKM会恢复成正常的
SYN+ACK。对于A、B上的TCP/IP协议栈,它们意识不到发生过转换,用netstat -na看
到的还是正常的、意料中的状态。对于防火墙,意识不到已经从外部主机成功访问了
内部主机。对于C、D,会看到希奇的TCP标志出现。在做标志转换时,还需要考虑对
抗IDS规则,因具体情况而定。比如,避免在网络传输中出现PSH+ACK标志。
防火墙和IDS对端口相当敏感,比如不答应telnet、FTP协议通过,只答应http协
议通过。要做的仅仅是让A、B把23端口换成80端口出现在网络传输中。可能有人认为
修改双方的/etc/services文件更好些,当然,那也是一种可行的考虑。不过有太多
情况下利用LLKM动态修改端口更彻底更灵活。至于IDS,对于大多数非周知端口并不
敏感,意味着逃脱了监测。
最后要做的就是对IP数据区进行加密传输,IPSec能做到,可我需要的可能仅仅
是异或处理,仅仅是避开IDS的端口监测、内容监测。一个简单的insmod就能完成的
任务为什么一定要搬出IPSec呢。
3. 一个完整的LLKM例子,简单、临时改变TCP/IP协议栈的行为
例子程序的想法来自华中地区网络中心(bbs.whnet.edu.cn)Security版的
difeijing朋友,同时感谢AngelFalls朋友参与该版讨论,并提供了八篇谢绝转站的
<<Linux的TCP/IP协议栈阅读笔记>>。
程序演示了
a. LLKM的基本框架和技巧
b. 利用dev_add_pack()对本机即将发送出去的报文进行修改再发送
syn半开扫描依靠于被扫描主机返回ACK+RST标志和ACK+SYN标志两种情况,前者
意味着相应端口未开。connect扫描则完全依靠TCP连接的成功建立。difeijing提出
了这样一个想法,利用LLKM转换ACK+RST成ACK+SYN,此时syn半开扫描和connect扫描
都将认定相应端口是打开的。
--------------------------------------------------------------------------
/*
* File : openallport.c
* Author : scz < mailto: scz@nsfocus.com >
* : http://www.nsfocus.com
* Kernel : 2.2.16 or 2.2.14
* Complie : gcc -O3 -DMODULE -D__KERNEL__ -c openallport.c
* Usage : insmod openallport.o [dev=eth0] -x -y -f
* Date : 2000-10-10 17:40
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/netdevice.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/tcp.h>
/*
* 2.2.16内核的/usr/include/linux/version.h文件里定义了这个宏
* 但2.0.35内核里没有定义
*/
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#endif
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
#include <asm/uAccess.h>
#endif
static strUCt device * openallport_dev = NULL;
static char * dev = NULL;
/* 定义insmod命令行参数 */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
MODULE_PARM( dev, "s" );
#endif
static inline u_long csum_tcpudp_nofold ( u_long saddr, u_long daddr,
u_short len, u_short proto,
unsigned int sum )
{
__asm__
("
addl %1, %0
adcl %2, %0
adcl %3, %0
adcl $0, %0
"
: "=r" ( sum )
: "g" ( daddr ), "g" ( saddr ), "g" ( ( ntohs( len ) << 16 ) + proto * 256 ), "0" ( sum )
);
return sum;
}
static inline unsigned int csum_fold ( unsigned int sum )
{
__asm__
("
addl %1, %0
adcl $0xffff, %0
"
: "=r" ( sum )
: "r" ( sum << 16 ), "0" ( sum & 0xffff0000 )
);
return( ( ~sum ) >> 16 );
} /* end of csum_fold */
static inline u_short check_tcpudp ( u_long saddr, u_long daddr,
u_short len, u_short proto,
unsigned int sum )
{
return( csum_fold( csum_tcpudp_nofold( saddr, daddr, len, proto, sum ) ) );
} /* end of check_tcpudp */
int openallport_rcv ( struct sk_buff * skb, struct device * dv, struct packet_type * pt )
{
/* 注重pkt_type是什么 */
if ( ( skb->pkt_type == PACKET_OUTGOING ) && ( skb->protocol == __constant_htons( ETH_P_IP) ) )
{
if ( ( skb->nh.iph->version == 4 ) && ( skb->nh.iph->protocol == IPPROTO_TCP ) ) /* 不考虑ipv6 */
{
skb->h.raw = skb->nh.raw + ( skb->nh.iph->ihl << 2 );
if ( ( skb->h.th->ack == 1 ) && ( skb->h.th->rst == 1 ) )
{
u_short size;
int doff = 0;
skb->h.th->rst = 0;
skb->h.th->syn = 1;
size = ntohs( skb->nh.iph->tot_len ) - ( skb->nh.iph->ihl * 4 ); /* IP数据区长度 */
doff = skb->h.th->doff << 2; /* TCP头部长度 */
/* 重新计算校验和 */
skb->csum = 0;
skb->csum = csum_partial( skb->h.raw + doff, size - doff, 0 ); /* data checksum */
skb->h.th->check = 0;
skb->h.th->check = check_tcpudp( skb->nh.iph->saddr, /* tcp or udp checksum */
skb->nh.iph->daddr,
size,
IPPROTO_TCP,
csum_partial( skb->h.raw, doff, skb->csum ) );
}
}
}
kfree_skb( skb );
return( 0 );
} /* end of openallport_rcv */
static struct packet_type openallport_packet_type =
{
__constant_htons( ETH_P_ALL ), /* 此时可以接收到来自lo的回送报文,比如本机发送出去的 */
NULL, /* All devices */
openallport_rcv,