首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第43期->技术专题
期刊号: 类型: 关键词:
PktFilter使用参考

作者:tombkeeper@nsfocus.com
主页:http://www.nsfocus.com
日期:2003-08-06

/*
这份文档主要是基于PktFilter 所附带的PktFilter.pdf,修正了一些原文的错误,增
加了一些我自己的使用经验。

我喜欢这个防火墙主要是因为资源占用比较少,UNIX风格,没有图形界面,没有讨厌的
的系统托盘图标。

和IP Filter一样,这个防火墙不是为对安全一无所知或者略有所知的人设计的,要打
算使用它请确定你了解TCP/IP 协议等基础知识。如果对程序有问题,请写信问程序作
者:Jean-Baptiste.Marchand@hsc.fr。如果对使用有问题,也请写信问作者。我对这
份文档、规则生成脚本、修改过的程序不提供任何支持。
*/



PktFilter是一个运行在Windows 2000/XP/2003上的包过滤防火墙。

PktFilter自己并没有实现网络过滤驱动,事实上它是系统本身包过滤机制的一个配置
界面。Windows 2000以上的操作系统都有一个IpFilterDriver 服务,但是系统本身却
没有提供好用的配置界面。

因为是调用系统自身的机制,所以PktFilter工作得很稳定,占用资源也较少,但也正
因为如此,PktFilter的功能实现也受限于系统。

PktFilter的规则语法其实是IP Filter4的一个子集。可以参考http://www.ipfilter.org/。


----------
安装使用:
----------

为PktFilter创建一个目录,将pktfltsrv.exe和pktctl.exe拷贝进来。

创建规则文件。大家可以根据下面介绍的语法自己编写规则。如果把PktFilter安装在
一台作为网关的机器上,并编写相应的规则,那么PktFilter 可以很好地作为一个网
络防火墙工作。

如果只是用来保护个人计算机,不需要设置太复杂的规则,那么你可以使用我写的脚本
rulesbuild.cmd。只需设置文件开头的一些变量,就可以迅速地生成一个规则。

安装启动服务:
C:\PktFilter> pktfltsrv -i "C:\PktFilter\PktFilter.conf" "C:\PktFilter\PktFilter.log"

C:\PktFilter> net start pktfilter


-------------
过滤规则介绍:
-------------

全局选项
    option
        small_frags, 拒绝太小的的分片包,默认small_frags是指小于16 bytes的
                     分片包。这个值可以通过建立注册表
                     HKLM\SYSTEM\CurrentControlSet\Services\IpFilterDriver\FragmentThreshold
                     来设定。
        strong_host, 这个文档里面没说,但是程序支持,MSDN的解释是
                     “Causes a check of the destination address of incoming packets.”
        check_frags,这个文档里面没说,但是程序支持,MSDN的解释是
                     “Causes a check of the fragments from the cache.”
过滤动作
    pass, 允许
    block, 阻塞

方向
    in, 进来的
    out, 出去的

协议
    proto, 包括:
        tcp, TCP 协议
        udp, UDP 协议
        icmp, ICMP 协议
        [number], 指定其他的IP协议号
        [empty], 所有协议

源地址
    from [addr], 指定一个IPv4地址
    from [subnet/mask],指定一个网络

源端口(仅限TCP/UDP协议)
    port [表达式] [端口号]
    表达式包括:
        =
        >=
        >
        <=
        <
        ><    端口范围

目标地址
    to [addr], 指定一个IPv4地址
    to [subnet/mask],指定一个网络

目标端口(仅限TCP/UDP协议)
    port [表达式] [端口号]
    表达式包括:
        =
        >=
        >
        <=
        <
        ><    端口范围

ICMP类型和代码
    icmp-type [type]
    icmp-code [code]

TCP连接建立
    established, 阻塞只有SYN标志位而没有ACK标志位的TCP包,放在规则末尾表示
                 只允许连接建立后的TCP包通过。

-----
例子:
-----

# drop packets composed of small fragments
option small_frags on eth0
# default behavior = deny everything
block in on eth0 all
block out on eth0 all
# allow DNS resolution to our nameserver
pass out on eth0 proto udp from 192.168.1.1 port > 1023 to 192.168.1.254 port = 53
pass in on eth0 proto udp from 192.168.1.254 port = 53 to 192.168.1.1 port > 1023
# allow inbound ICMP traffic (ping)
pass in on eth0 proto icmp from any to 192.168.1.1 icmp-type echo
pass out on eth0 proto icmp from 192.168.1.1 type echo-rep to any
# allow RDP (Terminal Services) administration from our administration subnet
pass in on eth0 proto tcp from 10.42.42.0/24 port > 1024 to 192.168.1.1 port = 3389
pass out on eth0 proto tcp from 192.168.1.1 port = 3389 to 10.42.42.0/24 port > 1024 established


-------------
过滤规则参考:
-------------

过滤规则由全局选项(global-option)和普通规则(normal-rule)组成。

全局选项的语法:
"option" global_option iface

现在支持的global-option只有"small_frags"。

普通规则的语法:
action [in-out] iface [proto_spec] ip [proto-options]

action = "pass" | "block"
in-out = "in" | "out"
iface = "on" ifname digit
ifname = "eth*" | "ppp" | "sl" | "lo" | "tr" | "fd"
proto_spec = "proto" [proto]
proto = "tcp" | "udp" | "icmp" | "any" | ip_proto
ip_proto = decnumber
decnumber = digit [decnumber]
ip = "all" | "from" ip-addr [port-comp | port-range] "to" ip-addr
[port-comp | port-range]
ip-addr = "any" | ip-dotted-addr [ip-mask]
ip-dotted-addr = host-num "." host-num "." host-num "." host-num
host-num = digit [digit [digit]]
ip-mask = "/" ip-addr | decnumber
port-comp = "port" comparator decnumber
comparator = ">" | ">=" | "<" | "<=" | "="
port-range = "port" decnumber "><" decnumber
proto-options = "icmp-type" icmp-type ["code" icmp-code] | "established"
icmp-type = "echorep" | "unreach" | "squench" | "redir" | "echo" | "router_adv"|
            "router_sol" | "timex" | "paramprob" | "timest" | "timestrep" |
            "inforeq" | "inforep" | "maskreq" | "maskrep"
icmp-code = decnumber


-----------------
pktctl命令的用法:
-----------------

pktctl有两种用法:命令行模式和交互模式。pktctl -i进入交互模式。


列出网络接口:
C:\> pktctl -I
eth0: (3Com EtherLink PCI): 192.168.0.1

加载规则文件:
C:\> pktctl -f rules.txt
pktctl> source rules.txt

先清除所有已加载的规则再加载规则文件:
C:\> pktctl -F rules.txt
pktctl> reload rules.txt

手工临时添加一条规则:
C:\> pktctl -a "pass in on eth0 from 10.0.0.42 to any"
pktctl> pass in on eth0 proto udp from 10.0.0.42 to any

列出指定接口上的规则:
C:\> pktcl -l eth0
pktcl> list on eth0

列出指定接口上的规则和规则号:
option:
C:\> pktcl -L eth0
pktcl> List on eth0
在删除规则的时候需要指定规则号。

删除规则:
C:\> pktctl -d 2 eth0

删除指定接口上的所有规则:
C:\> pktctl -Fa eth0
pktctl> flush on eth0

删除所有接口上的所有规则:
C:\> pktctl -Fa all
C:\> pktctl -Fa
pktctl> flush on all

显示过滤情况的统计数据:
C:\> pktctl -s eth0
pktctl> stats on eth0

显示过滤情况的详细统计数据:
C:\> pktctl -S eth0
pktctl> Stats on eth0


----------------------
pktfltsrv的命令行选项:
----------------------
-i "path_to_rules_file" "path_to_log_file"
-u: uninstalls the service.



一些问题:

1、在Windows XP + SP1 上不能记录日志。
   这是XP的问题,SP1 iphlpapi.dll的PfSetLogBuffer函数工作不正常。(这个问题是PktFilter邮件列表上的)

2、添加某些格式不正确的规则时可导致服务崩溃。

3、因为只有SDK的WinBase.h才定义了INVALID_SET_FILE_POINTER。所以编译pktfltsrv的时候需要在
   VS的tools--options里面把SDK的include目录放到最前面。我现在的机器没有安装SDK,
   是把以前的安装目录copy过来用的,所以需要手工设。我不记得直接安装SDK后是否会直接用SDK的文件覆盖VC的文件,
   如果那样的话,可能就不需要多此一举了。

4、在Windows 2000 Pro版本上未必能够运行,我没试验,只是MSND谈到那些函数的时候总是说:
   “Included in Windows 2000 Server and Windows .NET Server.”

5、pktctl.exe -l命令列规则的时候适配器和协议两个字段之间没有空格,我给加了一个。filter_stats.c/70行。
   可能作者没留神少了一句,所以ICMP类型和状态码不能记录下来,加上了。logging.c/187行。
   不能记录ttl,tcp窗口值等信息,加上了。
   有些记录格式我看着不习惯,改了。

6、程序实现原理可以参考:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rras/rras/packet_filtering_reference.asp

附:规则生成脚本:


@echo off

echo # 规则由PktFilter 规则生成脚本 RulesBuild.cmd v0.1 生成
echo # tombkeeper@whitecell.org
echo.

setlocal

rem 不打算填写内容的变量就让它空着,

rem 指定要要设置规则的网络接口:
set interface=eth0

rem 指定要生成的规则文件:
set rules_file=PktFilter.conf

rem 指定本机IP,这里只考虑了一个IP的情况:
set local_ip=192.168.0.1

rem 指定信任的IP,注意!来自这些IP的访问将完全不受防火墙控制。多个IP之间用逗号分隔:
set trust_ip=192.168.0.2,192.168.0.3

rem 指定网关,某些网关会设置会定时ping主机:
set gateway=192.168.0.254

rem 指定qq服务器,多个IP之间用逗号分隔:
set qq_server=219.133.40.15

rem 是否要使用主动模式ftp:
set ftp_active=true

rem 是否要访问DNS:
set dns=true

rem 是否要访问tftp:
set tftp=true

rem 是否要访问snmp:
set snmp=true

rem 是否要使用msn直接文件传输:
set msn=true

rem 是否要使用QQ:
set qq=true

rem 是否使用金山毒霸在线升级:
set kav=true

rem 是否使用netbios:
set netbios=true

rem 其它要开放的TCP端口,多个端口之间用逗号分隔:
set other_tcp=


echo #####################################################################################
echo # 全局规则
echo #####################################################################################
echo.

echo # 丢掉小的分片包
echo option small_frags on %interface%
echo.

echo # 默认阻塞所有进出数据
echo block in  on %interface% all
echo block out on %interface% all
echo.

echo # 允许 %local_ip% 发起向任意地址的TCP连接
echo pass out on %interface% proto tcp from %local_ip% port 1 ^>^< 65535 to any port 1 ^>^< 65535
echo pass in  on %interface% proto tcp from any port 1 ^>^< 65535 to %local_ip% port 1 ^>^< 65535 established
echo.

:trust_ip
if "%trust_ip%"=="" goto tcp
echo # 设置信任主机 %trust_ip% 可完全访问 %local_ip%
for %%I in (%trust_ip%,) do echo pass in  on %interface% from %%I to %local_ip% && echo pass out on %interface% from %local_ip% to %%I
rem %trust_ip%后面加一个逗号是考虑只有一个%trust_ip%时候的情况
echo.

:tcp
echo #####################################################################################
echo # TCP 规则
echo #####################################################################################
echo.

:ftp_active
if not "%ftp_active%"=="true" goto msn
echo # 开放TCP 20 ,FTP 主动模式
echo pass in on %interface% proto tcp from any port = 20 to %local_ip% port ^> 1023
echo.

:msn
if not "%msn%"=="true" goto other_tcp
echo # 开放TCP 6890 - 6900,允许MSN直接传输文件
echo pass in on %interface% proto tcp from any port ^> 1023 to %local_ip% port 6890 ^>^< 6900
echo.

:other_tcp
if "%other_tcp%"=="" goto udp
echo # 其他要开放的TCP 端口:%other_tcp%
for %%I in (%other_tcp%,) do echo pass in on %interface% proto tcp from any port ^> 1023 to %local_ip% port = %%I
echo.

:udp
echo #####################################################################################
echo # UDP 规则
echo #####################################################################################
echo.

:dns
if not "%dns%"=="true" goto snmp
echo # 访问DNS服务
echo pass out on %interface% proto udp from %local_ip% port ^> 1023 to any port = 53
echo pass in  on %interface% proto udp from any port = 53 to %local_ip% port ^> 1023
echo.

:snmp
if not "%snmp%"=="true" goto tftp
echo # 访问snmp服务
echo pass out on %interface% proto udp from %local_ip% port ^> 1023 to any port = 161
echo pass in  on %interface% proto udp from any port = 161 to %local_ip% port ^> 1023
echo.

:tftp
if not "%tftp%"=="true" goto netbios
echo # 访问tftp服务
echo pass out on %interface% proto udp from %local_ip% port ^> 1023 to any port = 69
echo pass in  on %interface% proto udp from any port = 69 to %local_ip% port ^> 1023
echo.

:netbios
if not "%netbios%"=="true" goto kav
echo # 访问netbios-ns(UDP 137) netbios-dgm(UDP 138)服务
echo pass out on %interface% proto udp from any port = 137 to any port = 137
echo pass in  on %interface% proto udp from any port = 137 to any port = 137
echo pass out on %interface% proto udp from any port = 138 to any port = 138
echo pass in  on %interface% proto udp from any port = 138 to any port = 138
echo.

:kav
if not "%kav%"=="true" goto qq
echo # 访问金山毒霸升级验证端口
echo pass out on %interface% proto udp from %local_ip% port ^> 1023 to any port = 6868
echo pass in  on %interface% proto udp from any port = 6868 to %local_ip% port ^> 1023
echo.

:qq
if not "%qq%"=="true" goto icmp
echo # 使用udp方式访问QQ
for %%I in (%qq_server%,) do echo pass out on %interface% proto udp from %local_ip% port = 6000 to %%I port = 8000 && echo pass in  on %interface% proto udp from %%I port = 8000 to %local_ip% port = 6000
rem %qq_server%后面加一个逗号是考虑只有一个%qq_server%时候的情况
echo.

:icmp
echo #####################################################################################
echo # ICMP 规则
echo #####################################################################################
echo.

:ping
echo # %local_ip% 可以 ping 任意地址
echo pass out on %interface% proto icmp from %local_ip% to any icmp-type echo
echo pass in  on %interface% proto icmp from any to %local_ip% icmp-type echorep
echo.

:gateway
if "%gateway"=="" goto write
echo # 网关可以ping %local_ip%
echo pass in  on %interface% proto icmp from %gateway% to %local_ip% icmp-type echo
echo pass out on %interface% proto icmp from %local_ip% to %gateway% icmp-type echorep

:write
call %0 1> %rules_file% 2>nul
@echo off
rem 用这种方法写文件就不用每行后面都重定向而多次执行写操作,速度快,便于维护。
rem 用call或者cmd /c都可以,写入一次后会因为文件被另起的cmd进程占用而出错退出。
rem 如果不用call而直接在批处理中调用,则命令是在同一个cmd进程中执行,会出现死循环。
rem 事实上这种写法更适用于在交互脚本中指定要写的文件,
rem 譬如前面设定变量的过程就可以用set /p 来分别从控制台上取得。

endlocal

@echo on
版权所有,未经许可,不得转载